Files
nabarr/media/tmdb/media.go
dev 719528a06b
Some checks failed
Build / build (push) Failing after 13m30s
feat: enrich RSS items without IDs via TMDB title search
Items from RSS feeds that have no media ID (tmdb/imdb/tvdb) are now
enriched automatically using TMDB's search API. The release name is
parsed to extract a clean title and year, then searched against TMDB
to retrieve the TMDB ID before validation.

- Add media/tmdb package with SearchMovies, SearchShows, and
  ExtractTitleAndYear (parses torrent release names)
- Add EnrichFeedItemWithTmdbId to media.Client, called in rss/process.go
  before the ID validation switch
- Add --run-now flag to nabarr run to trigger all feeds immediately
- Wire media.Client through rss.Client and rssJob
- Merge feature/add-tag-option (tag support for Sonarr/Radarr)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 16:53:54 +00:00

88 lines
1.9 KiB
Go

package tmdb
import (
"encoding/json"
"errors"
"fmt"
"net/url"
"strconv"
"github.com/lucperkins/rek"
"github.com/l3uddz/nabarr/util"
)
var ErrItemNotFound = errors.New("not found")
func (c *Client) SearchMovies(title string, year int) (int, error) {
vals := url.Values{
"api_key": []string{c.apiKey},
"query": []string{title},
}
if year > 0 {
vals.Set("year", strconv.Itoa(year))
}
reqUrl, err := util.URLWithQuery(util.JoinURL(c.apiURL, "search", "movie"), vals)
if err != nil {
return 0, fmt.Errorf("generate search movie url: %w", err)
}
resp, err := rek.Get(reqUrl, rek.Client(c.http))
if err != nil {
return 0, fmt.Errorf("request search movie: %w", err)
}
defer resp.Body().Close()
if resp.StatusCode() != 200 {
return 0, fmt.Errorf("validate search movie response: %s", resp.Status())
}
b := new(movieSearchResponse)
if err := json.NewDecoder(resp.Body()).Decode(b); err != nil {
return 0, fmt.Errorf("decode search movie response: %w", err)
}
if len(b.Results) < 1 {
return 0, ErrItemNotFound
}
return b.Results[0].Id, nil
}
func (c *Client) SearchShows(title string, year int) (int, error) {
vals := url.Values{
"api_key": []string{c.apiKey},
"query": []string{title},
}
if year > 0 {
vals.Set("first_air_date_year", strconv.Itoa(year))
}
reqUrl, err := util.URLWithQuery(util.JoinURL(c.apiURL, "search", "tv"), vals)
if err != nil {
return 0, fmt.Errorf("generate search tv url: %w", err)
}
resp, err := rek.Get(reqUrl, rek.Client(c.http))
if err != nil {
return 0, fmt.Errorf("request search tv: %w", err)
}
defer resp.Body().Close()
if resp.StatusCode() != 200 {
return 0, fmt.Errorf("validate search tv response: %s", resp.Status())
}
b := new(tvSearchResponse)
if err := json.NewDecoder(resp.Body()).Decode(b); err != nil {
return 0, fmt.Errorf("decode search tv response: %w", err)
}
if len(b.Results) < 1 {
return 0, ErrItemNotFound
}
return b.Results[0].Id, nil
}