Skip to content

Commit

Permalink
media: support rss movie items with tmdb ids (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
l3uddz authored Feb 21, 2021
1 parent db9fdc9 commit 10a70f1
Show file tree
Hide file tree
Showing 10 changed files with 118 additions and 57 deletions.
3 changes: 3 additions & 0 deletions cmd/nabarr/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,9 @@ func main() {
case "imdb":
testItem.Title = "Test.Mode.2021.BluRay.1080p.TrueHD.Atmos.7.1.AVC.HYBRID.REMUX-FraMeSToR"
testItem.ImdbId = idParts[1]
case "tmdb":
testItem.Title = "Test.Mode.2021.BluRay.1080p.TrueHD.Atmos.7.1.AVC.HYBRID.REMUX-FraMeSToR"
testItem.TmdbId = idParts[1]
case "tvdb":
testItem.Title = "Test.Mode.S01E01.1080p.DTS-HD.MA.5.1.AVC.REMUX-FraMeSToR"
testItem.TvdbId = idParts[1]
Expand Down
12 changes: 9 additions & 3 deletions media/movie.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,19 @@ import (
)

func (c *Client) GetMovieInfo(item *FeedItem) (*Item, error) {
// retrieve and validate media provider data
mdp, mdi := item.GetProviderData()
if mdp == "" || mdi == "" {
return nil, fmt.Errorf("trakt: get movie: no media provider details found")
}

// lookup on trakt
t, err := c.trakt.GetMovie(item.ImdbId)
t, err := c.trakt.GetMovie(mdp, mdi)
if err != nil {
if errors.Is(err, trakt.ErrItemNotFound) {
return nil, fmt.Errorf("trakt: get movie: movie with imdbId %q: %w", item.ImdbId, ErrItemNotFound)
return nil, fmt.Errorf("trakt: get movie: movie with %sId %q: %w", mdp, mdi, ErrItemNotFound)
}
return nil, fmt.Errorf("trakt: get movie: movie with imdbId %q: %w", item.ImdbId, err)
return nil, fmt.Errorf("trakt: get movie: movie with %sId %q: %w", mdp, mdi, err)
}

// transform trakt info
Expand Down
12 changes: 9 additions & 3 deletions media/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,19 @@ import (
)

func (c *Client) GetShowInfo(item *FeedItem) (*Item, error) {
// retrieve and validate media provider data
mdp, mdi := item.GetProviderData()
if mdp == "" || mdi == "" {
return nil, fmt.Errorf("trakt: get show: no media provider details found")
}

// lookup on trakt
t, err := c.trakt.GetShow(item.TvdbId)
t, err := c.trakt.GetShow(mdp, mdi)
if err != nil {
if errors.Is(err, trakt.ErrItemNotFound) {
return nil, fmt.Errorf("trakt: get show: show with tvdbId %q: %w", item.TvdbId, ErrItemNotFound)
return nil, fmt.Errorf("trakt: get show: show with %sId %q: %w", mdp, mdi, ErrItemNotFound)
}
return nil, fmt.Errorf("trakt: get show: show with tvdbId %q: %w", item.TvdbId, err)
return nil, fmt.Errorf("trakt: get show: show with %sId %q: %w", mdp, mdi, err)
}

// transform trakt info to MediaItem
Expand Down
29 changes: 29 additions & 0 deletions media/struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"time"
)

/* Media Item(s) */

type Item struct {
TvdbId string `json:"TvdbId,omitempty"`
TmdbId string `json:"TmdbId,omitempty"`
Expand All @@ -33,6 +35,20 @@ type Item struct {
Tvdb tvdb.Item `json:"Tvdb,omitempty"`
}

func (i *Item) GetProviderData() (string, string) {
switch {
case i.TvdbId != "" && i.TvdbId != "0":
return "tvdb", i.TvdbId
case i.TmdbId != "" && i.TmdbId != "0":
return "tmdb", i.TmdbId
case i.ImdbId != "":
return "imdb", i.ImdbId
}
return "", ""
}

/* Rss Item(s) */

type Rss struct {
Channel struct {
Items []FeedItem `xml:"item"`
Expand All @@ -53,6 +69,7 @@ type FeedItem struct {
TvdbId string `xml:"tvdb,omitempty"`
TvMazeId string
ImdbId string `xml:"imdb,omitempty"`
TmdbId string `xml:"tmdb,omitempty"`

Attributes []struct {
XMLName xml.Name
Expand All @@ -61,6 +78,18 @@ type FeedItem struct {
} `xml:"attr"`
}

func (f *FeedItem) GetProviderData() (string, string) {
switch {
case f.TvdbId != "" && f.TvdbId != "0":
return "tvdb", f.TvdbId
case f.TmdbId != "" && f.TmdbId != "0":
return "tmdb", f.TmdbId
case f.ImdbId != "":
return "imdb", f.ImdbId
}
return "", ""
}

// Time credits: https://github.com/mrobinsn/go-newznab/blob/cd89d9c56447859fa1298dc9a0053c92c45ac7ef/newznab/structs.go#L150
type Time struct {
time.Time
Expand Down
8 changes: 4 additions & 4 deletions media/trakt/media.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ var (
ErrItemNotFound = errors.New("not found")
)

func (c *Client) GetShow(tvdbId string) (*Show, error) {
func (c *Client) GetShow(providerType string, providerId string) (*Show, error) {
// prepare request
reqUrl, err := util.URLWithQuery(util.JoinURL(c.apiURL, "search", "tvdb", tvdbId),
reqUrl, err := util.URLWithQuery(util.JoinURL(c.apiURL, "search", providerType, providerId),
url.Values{
"type": []string{"show"},
"extended": []string{"full"}})
Expand Down Expand Up @@ -52,9 +52,9 @@ func (c *Client) GetShow(tvdbId string) (*Show, error) {
return show, nil
}

func (c *Client) GetMovie(imdbId string) (*Movie, error) {
func (c *Client) GetMovie(providerType string, providerId string) (*Movie, error) {
// prepare request
reqUrl, err := util.URLWithQuery(util.JoinURL(c.apiURL, "search", "imdb", imdbId),
reqUrl, err := util.URLWithQuery(util.JoinURL(c.apiURL, "search", providerType, providerId),
url.Values{
"type": []string{"movie"},
"extended": []string{"full"}})
Expand Down
22 changes: 9 additions & 13 deletions radarr/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,19 +69,15 @@ func (c *Client) getQualityProfileId(profileName string) (int, error) {
}

func (c *Client) lookupMediaItem(item *media.Item) (*lookupRequest, error) {
// determine metadata id to use
mdType := "imdb"
mdId := item.ImdbId

if item.TmdbId != "" && item.TmdbId != "0" {
// radarr prefers tmdb
mdType = "tmdb"
mdId = item.TmdbId
// retrieve and validate media provider data
mdp, mdi := item.GetProviderData()
if mdp == "" || mdi == "" {
return nil, fmt.Errorf("no media provider details found")
}

// prepare request
reqUrl, err := util.URLWithQuery(util.JoinURL(c.apiURL, "movie", "lookup"),
url.Values{"term": []string{fmt.Sprintf("%s:%s", mdType, mdId)}})
url.Values{"term": []string{fmt.Sprintf("%s:%s", mdp, mdi)}})
if err != nil {
return nil, fmt.Errorf("generate movie lookup request url: %w", err)
}
Expand All @@ -106,19 +102,19 @@ func (c *Client) lookupMediaItem(item *media.Item) (*lookupRequest, error) {

// find movie
for _, s := range *b {
switch mdType {
switch mdp {
case "tmdb":
if strconv.Itoa(s.TmdbId) == item.TmdbId {
if strconv.Itoa(s.TmdbId) == mdi {
return &s, nil
}
default:
if s.ImdbId == item.ImdbId {
if s.ImdbId == mdi {
return &s, nil
}
}
}

return nil, fmt.Errorf("movie lookup %sId: %v: %w", mdType, mdId, ErrItemNotFound)
return nil, fmt.Errorf("movie lookup %sId: %v: %w", mdp, mdi, ErrItemNotFound)
}

func (c *Client) AddMediaItem(item *media.Item, opts ...nabarr.PvrOption) error {
Expand Down
29 changes: 15 additions & 14 deletions radarr/queue.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,18 @@ func (c *Client) queueProcessor(tail state.ShutdownTail) {
return
}

// validate item has required id(s)
if feedItem.ImdbId == "" {
// retrieve and validate media provider data
mdp, mdi := feedItem.GetProviderData()
if mdp == "" || mdi == "" {
continue
}

// check cache / add item to cache
pvrCacheBucket := fmt.Sprintf("pvr_%s_%s", c.Type(), c.name)
cacheKey := fmt.Sprintf("imdb_%s", feedItem.ImdbId)
cacheBucket := fmt.Sprintf("pvr_%s_%s", c.Type(), c.name)
cacheKey := fmt.Sprintf("%s_%s", mdp, mdi)
if !c.testMode {
// not running in test mode, so use cache
if cacheValue, err := c.cache.Get(pvrCacheBucket, cacheKey); err == nil {
if cacheValue, err := c.cache.Get(cacheBucket, cacheKey); err == nil {
// item already exists in the cache
switch string(cacheValue) {
case c.name:
Expand All @@ -55,7 +56,7 @@ func (c *Client) queueProcessor(tail state.ShutdownTail) {
}

// insert temp cache entry
if err := c.cache.Put(pvrCacheBucket, cacheKey, []byte(c.cacheFiltersHash), c.cacheTempDuration); err != nil {
if err := c.cache.Put(cacheBucket, cacheKey, []byte(c.cacheFiltersHash), c.cacheTempDuration); err != nil {
c.log.Error().
Err(err).
Msg("Failed storing item in temp cache")
Expand All @@ -69,7 +70,7 @@ func (c *Client) queueProcessor(tail state.ShutdownTail) {
c.log.Debug().
Err(err).
Str("feed_title", feedItem.Title).
Str("feed_imdb_id", feedItem.ImdbId).
Str(fmt.Sprintf("feed_%s_id", mdp), mdi).
Str("feed_name", feedItem.Feed).
Msg("Item was not found on trakt")
continue
Expand All @@ -78,7 +79,7 @@ func (c *Client) queueProcessor(tail state.ShutdownTail) {
c.log.Error().
Err(err).
Str("feed_title", feedItem.Title).
Str("feed_imdb_id", feedItem.ImdbId).
Str(fmt.Sprintf("feed_%s_id", mdp), mdi).
Str("feed_name", feedItem.Feed).
Msg("Failed finding item on trakt")
continue
Expand All @@ -90,11 +91,11 @@ func (c *Client) queueProcessor(tail state.ShutdownTail) {
Msg("Item found on trakt")
}

// validate tmdbId was found
// validate tmdbId was found (radarr works best with these)
if mediaItem.TmdbId == "" || mediaItem.TmdbId == "0" {
c.log.Warn().
Str("feed_title", mediaItem.FeedTitle).
Str("feed_imdb_id", feedItem.ImdbId).
Str(fmt.Sprintf("feed_%s_id", mdp), mdi).
Str("feed_name", feedItem.Feed).
Msg("Item had no tmdbId on trakt")
continue
Expand Down Expand Up @@ -133,7 +134,7 @@ func (c *Client) queueProcessor(tail state.ShutdownTail) {
c.log.Warn().
Err(err).
Str("feed_title", mediaItem.FeedTitle).
Str("feed_imdb_id", feedItem.ImdbId).
Str(fmt.Sprintf("feed_%s_id", mdp), mdi).
Str("feed_name", feedItem.Feed).
Msg("Item was not found via pvr lookup")
continue
Expand All @@ -142,7 +143,7 @@ func (c *Client) queueProcessor(tail state.ShutdownTail) {
c.log.Error().
Err(err).
Str("feed_title", mediaItem.FeedTitle).
Str("feed_imdb_id", feedItem.ImdbId).
Str(fmt.Sprintf("feed_%s_id", mdp), mdi).
Str("feed_name", feedItem.Feed).
Msg("Failed finding item via pvr lookup")
continue
Expand All @@ -160,7 +161,7 @@ func (c *Client) queueProcessor(tail state.ShutdownTail) {

// add item to perm cache (items already in pvr)
if !c.testMode {
if err := c.cache.Put(pvrCacheBucket, cacheKey, []byte(c.name), 0); err != nil {
if err := c.cache.Put(cacheBucket, cacheKey, []byte(c.name), 0); err != nil {
c.log.Error().
Err(err).
Msg("Failed storing item in perm cache")
Expand Down Expand Up @@ -215,7 +216,7 @@ func (c *Client) queueProcessor(tail state.ShutdownTail) {

// add item to perm cache (item was added to pvr)
if !c.testMode {
if err := c.cache.Put(pvrCacheBucket, cacheKey, []byte(c.name), 0); err != nil {
if err := c.cache.Put(cacheBucket, cacheKey, []byte(c.name), 0); err != nil {
c.log.Error().
Err(err).
Msg("Failed storing item in perm cache")
Expand Down
23 changes: 18 additions & 5 deletions rss/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func (j *rssJob) queueItemWithPvrs(item *media.FeedItem) {
case item.TvdbId != "" && pvr.Type() == "sonarr":
// tvdbId is present, queue with sonarr
pvr.QueueFeedItem(item)
case item.ImdbId != "" && pvr.Type() == "radarr":
case (item.ImdbId != "" || item.TmdbId != "") && pvr.Type() == "radarr":
// imdbId is present, queue with radarr
pvr.QueueFeedItem(item)
}
Expand Down Expand Up @@ -84,13 +84,13 @@ func (j *rssJob) getFeed() ([]media.FeedItem, error) {
continue
}

// guid seen before?
// item seen before?
if cacheValue, err := j.cache.Get(j.name, i.GUID); err == nil {
if string(cacheValue) == j.cacheFiltersHash {
// item has been seen before and the filters have not changed
// item has been seen before and the filter hash has not changed since
continue
}
// item has been seen, however the filters have changed since it was last seen, re-process
// item has been seen, however the filter hash has changed, re-process
}

// process feed item attributes
Expand All @@ -106,11 +106,24 @@ func (j *rssJob) getFeed() ([]media.FeedItem, error) {
} else {
b.Channel.Items[p].ImdbId = fmt.Sprintf("tt%s", a.Value)
}
case "tmdb", "tmdbid":
b.Channel.Items[p].TmdbId = a.Value
}
}

// validate item
if (b.Channel.Items[p].TvdbId == "" || b.Channel.Items[p].TvdbId == "0") && b.Channel.Items[p].ImdbId == "" {
switch {
case b.Channel.Items[p].TvdbId != "" && b.Channel.Items[p].TvdbId != "0":
// tvdb id is present, allow processing
break
case b.Channel.Items[p].ImdbId != "":
// imdb id present, allow processing
break
case b.Channel.Items[p].TmdbId != "" && b.Channel.Items[p].TmdbId != "0":
// tmdb id present, allow processing
break
default:
// skip item as an expected media provider id was not present
continue
}

Expand Down
12 changes: 9 additions & 3 deletions sonarr/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,15 @@ func (c *Client) getQualityProfileId(profileName string) (int, error) {
}

func (c *Client) lookupMediaItem(item *media.Item) (*lookupRequest, error) {
// retrieve and validate media provider data
mdp, mdi := item.GetProviderData()
if mdp == "" || mdi == "" {
return nil, fmt.Errorf("no media provider details found")
}

// prepare request
reqUrl, err := util.URLWithQuery(util.JoinURL(c.apiURL, "series", "lookup"),
url.Values{"term": []string{fmt.Sprintf("tvdb:%s", item.TvdbId)}})
url.Values{"term": []string{fmt.Sprintf("%s:%s", mdp, mdi)}})
if err != nil {
return nil, fmt.Errorf("generate series lookup request url: %w", err)
}
Expand All @@ -96,12 +102,12 @@ func (c *Client) lookupMediaItem(item *media.Item) (*lookupRequest, error) {

// find series
for _, s := range *b {
if strconv.Itoa(s.TvdbId) == item.TvdbId {
if strconv.Itoa(s.TvdbId) == mdi {
return &s, nil
}
}

return nil, fmt.Errorf("series lookup tvdbId: %v: %w", item.TvdbId, ErrItemNotFound)
return nil, fmt.Errorf("series lookup %sId: %v: %w", mdp, mdi, ErrItemNotFound)
}

func (c *Client) AddMediaItem(item *media.Item, opts ...nabarr.PvrOption) error {
Expand Down
Loading

0 comments on commit 10a70f1

Please sign in to comment.