Skip to content

Commit

Permalink
Merge pull request #716 from glensc/pytrakt/show/episodes
Browse files Browse the repository at this point in the history
  • Loading branch information
glensc authored Oct 23, 2022
2 parents d51020d + f30998e commit c1c01ff
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 53 deletions.
34 changes: 0 additions & 34 deletions plextraktsync/pytrakt_extensions.py
Original file line number Diff line number Diff line change
@@ -1,38 +1,4 @@
from trakt.core import get
from trakt.tv import TVEpisode


@get
def lookup_table(show):
# returns all seasons and episodes with one single call
data = yield "shows/{}/seasons?extended=episodes".format(show.trakt)
retVal = {}
for season in data:
eps = {}
if "episodes" in season.keys():
for episode in season["episodes"]:
eps[episode["number"]] = LazyEpisode(
show, season["number"], episode["number"], episode["ids"]
)
retVal[season["number"]] = eps
yield retVal


class LazyEpisode:
def __init__(self, show, season, number, ids):
self.show = show
self.season = season
self.number = number
self.ids = ids
self._instance = None

@property
def instance(self):
if self._instance is None:
self._instance = TVEpisode(
self.show.title, self.season, number=self.number, **self.ids
)
return self._instance


@get
Expand Down
53 changes: 36 additions & 17 deletions plextraktsync/trakt_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,17 +382,14 @@ def find_episode_guid(self, guid: PlexGuid, lookup: TraktLookup):
"""
Find Trakt Episode from Guid of Plex Episode
"""
te = lookup.from_number(guid.pm.season_number, guid.pm.episode_number)
if not te or (str(te.ids.get(guid.provider)) != guid.id and not guid.pm.is_legacy_agent):
te = lookup.from_id(guid.provider, guid.id)
te = lookup.from_guid(guid)
if te:
return te.instance
else:
# Retry using search for specific Plex Episode
logger.warning(f"Retry using search for specific Plex Episode {guid.guid}")
if not guid.is_episode:
return self.find_by_guid(guid)
return None
return te

logger.warning(f"Retry using search for specific Plex Episode {guid.guid}")
if not guid.is_episode:
return self.find_by_guid(guid)
return None

def flush(self):
"""
Expand Down Expand Up @@ -500,49 +497,71 @@ def remove_empty_values(result):
return result


EPISODES_ORDERING_WARNING = "episodes ordering is different in Plex and Trakt. " \
"Check your Plex media source, TMDB is recommended."


class TraktLookup:
"""
Trakt lookup table to find all Trakt episodes of a TVShow
"""
def __init__(self, tm: TVShow):
self.table = {}
self.provider_table = {}
self.tm = tm
self.same_order = True

@cached_property
@nocache
@rate_limit()
@retry()
def _lookup(self, tm: TVShow):
def table(self):
"""
Build a lookup-table accessible via table[season][episode]
- https://github.com/moogar0880/PyTrakt/pull/185
"""
self.table = pytrakt_extensions.lookup_table(tm)

seasons = {}
for season in self.tm.seasons:
episodes = {}
for episode in season.episodes:
episodes[episode.number] = episode
seasons[season.season] = episodes
return seasons

def _reverse_lookup(self, provider):
"""
Build a lookup-table accessible via table[provider][id]
only if episodes ordering is different between Plex and Trakt
"""
# NB: side effect, assumes that from_number() is called first to populate self.table
table = {}
for season in self.table.keys():
for te in self.table[season].values():
table[str(te.ids.get(provider))] = te
self.provider_table[provider] = table
logger.debug(f"{self.tm.title}: lookup table build with '{provider}' ids")

def from_guid(self, guid: PlexGuid):
"""
Find Trakt Episode from Guid of Plex Episode
"""
te = self.from_number(guid.pm.season_number, guid.pm.episode_number)
if not te or (str(te.ids.get(guid.provider)) != guid.id and not guid.pm.is_legacy_agent):
te = self.from_id(guid.provider, guid.id)

return te

def from_number(self, season, number):
if not self.table:
self._lookup(self.tm)
try:
ep = self.table[season][number]
except KeyError:
return None
return ep

def from_id(self, provider, id):
# NB: the code assumes from_id is called only if from_number fails
if self.same_order:
logger.debug(f"{self.tm.title} episodes ordering is different in Plex and Trakt. Check your Plex media source, TMDB is recommended.")
logger.warning(f'"{self.tm.title}" {EPISODES_ORDERING_WARNING}')
self.same_order = False
if provider not in self.provider_table:
self._reverse_lookup(provider)
Expand Down
33 changes: 31 additions & 2 deletions tests/test_tv_lookup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from trakt.tv import TVShow

from plextraktsync.plex_api import PlexLibraryItem
from plextraktsync.trakt_api import TraktLookup
from tests.conftest import factory, make

trakt = factory.trakt_api()
Expand All @@ -13,12 +14,36 @@ def test_tv_lookup():
)
guid = m.guids[0]
tm: TVShow = trakt.find_by_guid(guid)
lookup = trakt.lookup(tm)
te = lookup[1][2].instance
lookup = TraktLookup(tm)
te = lookup.from_number(1, 2)

assert te.imdb == "tt12057922", f"Unexpected! {te}"


def test_show_episodes_plex():
m = PlexLibraryItem(make(cls="plexapi.video.Show", guid="imdb://tt10584350", type="show"))
guid = m.guids[0]
show = trakt.find_by_guid(guid)

assert len(show.seasons) == 1
episode = show.seasons[0].episodes[1]
assert episode.title == "A Murderer's Beef – Part 2"
assert episode.imdb == "tt12057922", f"Unexpected! {episode}"


def test_show_episodes():
show = TVShow("Game of Thrones")

assert len(show.seasons) == 9
seasons = show.seasons
episodes = seasons[1].episodes
assert episodes[0].title == "Winter Is Coming"

seasons = show.seasons
assert len(seasons) == 9
assert seasons[1].episodes[0].title == "Winter Is Coming"


def test_tv_lookup_by_episode_id():
pe = PlexLibraryItem(
make(
Expand Down Expand Up @@ -59,3 +84,7 @@ def test_find_episode():
assert te.season == 1
assert te.episode == 1
assert te.imdb == "tt11909222"


if __name__ == '__main__':
test_show_episodes()

0 comments on commit c1c01ff

Please sign in to comment.