From 566cf7d31edd37902403ade7338255d61783681e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elan=20Ruusam=C3=A4e?= Date: Sun, 23 Oct 2022 19:04:07 +0300 Subject: [PATCH 01/11] Add note about assumed side-effect --- plextraktsync/trakt_api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plextraktsync/trakt_api.py b/plextraktsync/trakt_api.py index ab2444a3fe..4625a94ccb 100644 --- a/plextraktsync/trakt_api.py +++ b/plextraktsync/trakt_api.py @@ -524,6 +524,7 @@ 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(): From 0c50b5a2916ef243fcbee3e98958c76610363a61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elan=20Ruusam=C3=A4e?= Date: Wed, 12 Jan 2022 21:57:22 +0200 Subject: [PATCH 02/11] Add show.episodes, show.seasons tests --- tests/test_tv_lookup.py | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/tests/test_tv_lookup.py b/tests/test_tv_lookup.py index 742f487929..02db27a99d 100755 --- a/tests/test_tv_lookup.py +++ b/tests/test_tv_lookup.py @@ -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() @@ -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( @@ -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() From 0d406c42689de66f5e1b65fe34e77a8c15d1fdfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elan=20Ruusam=C3=A4e?= Date: Sun, 23 Oct 2022 18:41:55 +0300 Subject: [PATCH 03/11] Use show.episodes property --- plextraktsync/trakt_api.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/plextraktsync/trakt_api.py b/plextraktsync/trakt_api.py index 4625a94ccb..ee5df44d6d 100644 --- a/plextraktsync/trakt_api.py +++ b/plextraktsync/trakt_api.py @@ -386,7 +386,7 @@ def find_episode_guid(self, guid: PlexGuid, lookup: TraktLookup): 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) if te: - return te.instance + return te else: # Retry using search for specific Plex Episode logger.warning(f"Retry using search for specific Plex Episode {guid.guid}") @@ -513,11 +513,20 @@ def __init__(self, tm: TVShow): @nocache @rate_limit() @retry() - def _lookup(self, tm: TVShow): + def _lookup(self, show: TVShow): """ 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 show.seasons: + episodes = {} + for episode in season.episodes: + episodes[episode.number] = episode + seasons[season.season] = episodes + return seasons def _reverse_lookup(self, provider): """ @@ -534,7 +543,7 @@ def _reverse_lookup(self, provider): def from_number(self, season, number): if not self.table: - self._lookup(self.tm) + self.table = self._lookup(self.tm) try: ep = self.table[season][number] except KeyError: From 8313d99c035d37d94108069f202a0fe4ecb54b9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elan=20Ruusam=C3=A4e?= Date: Wed, 12 Jan 2022 22:13:42 +0200 Subject: [PATCH 04/11] Remove unused lookup_table pytrakt_extension --- plextraktsync/pytrakt_extensions.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/plextraktsync/pytrakt_extensions.py b/plextraktsync/pytrakt_extensions.py index 7f91a6b4c8..275df4b8ca 100644 --- a/plextraktsync/pytrakt_extensions.py +++ b/plextraktsync/pytrakt_extensions.py @@ -2,22 +2,6 @@ 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 From a0bd9000990b61703cdc331f6c1446c3d47030d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elan=20Ruusam=C3=A4e?= Date: Wed, 12 Jan 2022 22:15:37 +0200 Subject: [PATCH 05/11] Drop unused LazyEpisode class --- plextraktsync/pytrakt_extensions.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/plextraktsync/pytrakt_extensions.py b/plextraktsync/pytrakt_extensions.py index 275df4b8ca..8f2ecf2677 100644 --- a/plextraktsync/pytrakt_extensions.py +++ b/plextraktsync/pytrakt_extensions.py @@ -1,22 +1,4 @@ from trakt.core import get -from trakt.tv import TVEpisode - - -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 From 5aeba758cea80613f41b845629634108cea961d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elan=20Ruusam=C3=A4e?= Date: Sun, 23 Oct 2022 20:44:42 +0300 Subject: [PATCH 06/11] Add another note about code side effects --- plextraktsync/trakt_api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plextraktsync/trakt_api.py b/plextraktsync/trakt_api.py index ee5df44d6d..bd228aebe9 100644 --- a/plextraktsync/trakt_api.py +++ b/plextraktsync/trakt_api.py @@ -551,6 +551,7 @@ def from_number(self, season, number): 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.") self.same_order = False From 25f35ddf5a746652e14c6ad8120244e2408f7cad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elan=20Ruusam=C3=A4e?= Date: Sun, 23 Oct 2022 20:48:13 +0300 Subject: [PATCH 07/11] Move logic of episode find to TraktLookup class --- plextraktsync/trakt_api.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/plextraktsync/trakt_api.py b/plextraktsync/trakt_api.py index bd228aebe9..73b6f12074 100644 --- a/plextraktsync/trakt_api.py +++ b/plextraktsync/trakt_api.py @@ -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 - 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 + + 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): """ @@ -541,6 +538,16 @@ def _reverse_lookup(self, provider): 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.table = self._lookup(self.tm) From 0d0d7eb457a8528209cc4b1da52cfcc9f06e8578 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elan=20Ruusam=C3=A4e?= Date: Sun, 23 Oct 2022 20:54:47 +0300 Subject: [PATCH 08/11] Use warning level Now that warnings can be filtered out, it's good to show info as warning --- plextraktsync/trakt_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plextraktsync/trakt_api.py b/plextraktsync/trakt_api.py index 73b6f12074..dd5ff24e52 100644 --- a/plextraktsync/trakt_api.py +++ b/plextraktsync/trakt_api.py @@ -560,7 +560,7 @@ def from_number(self, season, number): 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 is different in Plex and Trakt. Check your Plex media source, TMDB is recommended.") self.same_order = False if provider not in self.provider_table: self._reverse_lookup(provider) From db09b0b60e0699760a0ca8bcc705ab37dcf846dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elan=20Ruusam=C3=A4e?= Date: Sun, 23 Oct 2022 20:58:50 +0300 Subject: [PATCH 09/11] Add EPISODES_ORDERING_WARNING const for readability --- plextraktsync/trakt_api.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plextraktsync/trakt_api.py b/plextraktsync/trakt_api.py index dd5ff24e52..a37fbb91cc 100644 --- a/plextraktsync/trakt_api.py +++ b/plextraktsync/trakt_api.py @@ -497,6 +497,10 @@ 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 @@ -560,7 +564,7 @@ def from_number(self, season, number): def from_id(self, provider, id): # NB: the code assumes from_id is called only if from_number fails if self.same_order: - logger.warning(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) From 3263bd04733e80f07fb063a31e66c4107c210951 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elan=20Ruusam=C3=A4e?= Date: Sun, 23 Oct 2022 21:17:16 +0300 Subject: [PATCH 10/11] Use table as cached_property --- plextraktsync/trakt_api.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/plextraktsync/trakt_api.py b/plextraktsync/trakt_api.py index a37fbb91cc..d60fe935d6 100644 --- a/plextraktsync/trakt_api.py +++ b/plextraktsync/trakt_api.py @@ -506,15 +506,15 @@ 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, show: TVShow): + def table(self): """ Build a lookup-table accessible via table[season][episode] @@ -522,7 +522,7 @@ def _lookup(self, show: TVShow): """ seasons = {} - for season in show.seasons: + for season in self.tm.seasons: episodes = {} for episode in season.episodes: episodes[episode.number] = episode @@ -553,8 +553,6 @@ def from_guid(self, guid: PlexGuid): return te def from_number(self, season, number): - if not self.table: - self.table = self._lookup(self.tm) try: ep = self.table[season][number] except KeyError: From f30998ef612f71379389f59b669039ecb0078104 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elan=20Ruusam=C3=A4e?= Date: Sun, 23 Oct 2022 21:17:54 +0300 Subject: [PATCH 11/11] No rate limit for GET requests --- plextraktsync/trakt_api.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plextraktsync/trakt_api.py b/plextraktsync/trakt_api.py index d60fe935d6..d363cc9c0f 100644 --- a/plextraktsync/trakt_api.py +++ b/plextraktsync/trakt_api.py @@ -512,7 +512,6 @@ def __init__(self, tm: TVShow): @cached_property @nocache - @rate_limit() @retry() def table(self): """