diff --git a/plexapi/base.py b/plexapi/base.py index 4d9f397f4..4dd642e03 100644 --- a/plexapi/base.py +++ b/plexapi/base.py @@ -47,6 +47,7 @@ def __init__(self, server, data, initpath=None, parent=None): self._data = data self._initpath = initpath or self.key self._parent = weakref.ref(parent) if parent else None + self._details_key = None if data is not None: self._loadData(data) self._details_key = self._buildDetailsKey() diff --git a/plexapi/video.py b/plexapi/video.py index 508dc5a4a..afc450a82 100644 --- a/plexapi/video.py +++ b/plexapi/video.py @@ -738,6 +738,7 @@ class Episode(Playable, Video): parentThumb (str): URL to season thumbnail image (/library/metadata//thumb/). parentTitle (str): Name of the season for the episode. rating (float): Episode rating (7.9; 9.8; 8.1). + skipParent (bool): True if the show's seasons are set to hidden. viewOffset (int): View offset in milliseconds. writers (List<:class:`~plexapi.media.Writer`>): List of writers objects. year (int): Year episode was released. @@ -774,10 +775,23 @@ def _loadData(self, data): self.parentThumb = data.attrib.get('parentThumb') self.parentTitle = data.attrib.get('parentTitle') self.rating = utils.cast(float, data.attrib.get('rating')) + self.skipParent = utils.cast(bool, data.attrib.get('skipParent', '0')) self.viewOffset = utils.cast(int, data.attrib.get('viewOffset', 0)) self.writers = self.findItems(data, media.Writer) self.year = utils.cast(int, data.attrib.get('year')) + # If seasons are hidden, parentKey and parentRatingKey are missing from the XML response. + # https://forums.plex.tv/t/parentratingkey-not-in-episode-xml-when-seasons-are-hidden/300553 + if self.skipParent and not self.parentRatingKey: + # Parse the parentRatingKey from the parentThumb + if self.parentThumb.startswith('/library/metadata/'): + self.parentRatingKey = utils.cast(int, self.parentThumb.split('/')[3]) + # Get the parentRatingKey from the season's ratingKey + if not self.parentRatingKey and self.grandparentRatingKey: + self.parentRatingKey = self.show().season(season=self.parentIndex).ratingKey + if self.parentRatingKey: + self.parentKey = '/library/metadata/%s' % self.parentRatingKey + def __repr__(self): return '<%s>' % ':'.join([p for p in [ self.__class__.__name__, diff --git a/tests/test_video.py b/tests/test_video.py index e60b28484..77a4d2e16 100644 --- a/tests/test_video.py +++ b/tests/test_video.py @@ -739,6 +739,21 @@ def test_video_Episode_history(episode): episode.markUnwatched() +def test_video_Episode_hidden_season(episode): + assert episode.skipParent is False + assert episode.parentRatingKey + assert episode.parentKey + assert episode.seasonNumber + show = episode.show() + show.editAdvanced(flattenSeasons=1) + episode.reload() + assert episode.skipParent is True + assert episode.parentRatingKey + assert episode.parentKey + assert episode.seasonNumber + show.defaultAdvanced() + + # Analyze seems to fail intermittently @pytest.mark.xfail def test_video_Episode_analyze(tvshows): @@ -765,6 +780,7 @@ def test_video_Episode_attrs(episode): assert episode.rating >= 7.7 assert utils.is_int(episode.ratingKey) assert episode._server._baseurl == utils.SERVER_BASEURL + assert episode.skipParent is False assert utils.is_string(episode.summary, gte=100) assert utils.is_metadata(episode.thumb, contains="/thumb/") assert episode.title == "Winter Is Coming"