11# -*- coding: utf-8 -*-
22import os
3+ from functools import cached_property
34from pathlib import Path
45from urllib .parse import quote_plus
56
@@ -676,7 +677,7 @@ class Season(
676677 ArtMixin , PosterMixin , ThemeUrlMixin ,
677678 SeasonEditMixins
678679):
679- """ Represents a single Show Season (including all episodes) .
680+ """ Represents a single Season.
680681
681682 Attributes:
682683 TAG (str): 'Directory'
@@ -835,7 +836,7 @@ class Episode(
835836 ArtMixin , PosterMixin , ThemeUrlMixin ,
836837 EpisodeEditMixins
837838):
838- """ Represents a single Shows Episode.
839+ """ Represents a single Episode.
839840
840841 Attributes:
841842 TAG (str): 'Video'
@@ -864,7 +865,7 @@ class Episode(
864865 parentGuid (str): Plex GUID for the season (plex://season/5d9c09e42df347001e3c2a72).
865866 parentIndex (int): Season number of episode.
866867 parentKey (str): API URL of the season (/library/metadata/<parentRatingKey>).
867- parentRatingKey (int): Unique key identifying the season.
868+ parentRatingKey (int): Unique key identifying the season.
868869 parentThumb (str): URL to season thumbnail image (/library/metadata/<parentRatingKey>/thumb/<thumbid>).
869870 parentTitle (str): Name of the season for the episode.
870871 parentYear (int): Year the season was released.
@@ -885,7 +886,6 @@ def _loadData(self, data):
885886 """ Load attribute values from Plex XML response. """
886887 Video ._loadData (self , data )
887888 Playable ._loadData (self , data )
888- self ._seasonNumber = None # cached season number
889889 self .audienceRating = utils .cast (float , data .attrib .get ('audienceRating' ))
890890 self .audienceRatingImage = data .attrib .get ('audienceRatingImage' )
891891 self .chapters = self .findItems (data , media .Chapter )
@@ -909,9 +909,6 @@ def _loadData(self, data):
909909 self .originallyAvailableAt = utils .toDatetime (data .attrib .get ('originallyAvailableAt' ), '%Y-%m-%d' )
910910 self .parentGuid = data .attrib .get ('parentGuid' )
911911 self .parentIndex = utils .cast (int , data .attrib .get ('parentIndex' ))
912- self .parentKey = data .attrib .get ('parentKey' )
913- self .parentRatingKey = utils .cast (int , data .attrib .get ('parentRatingKey' ))
914- self .parentThumb = data .attrib .get ('parentThumb' )
915912 self .parentTitle = data .attrib .get ('parentTitle' )
916913 self .parentYear = utils .cast (int , data .attrib .get ('parentYear' ))
917914 self .producers = self .findItems (data , media .Producer )
@@ -925,15 +922,38 @@ def _loadData(self, data):
925922
926923 # If seasons are hidden, parentKey and parentRatingKey are missing from the XML response.
927924 # https://forums.plex.tv/t/parentratingkey-not-in-episode-xml-when-seasons-are-hidden/300553
928- if self .skipParent and data .attrib .get ('parentRatingKey' ) is None :
929- # Parse the parentRatingKey from the parentThumb
930- if self .parentThumb and self .parentThumb .startswith ('/library/metadata/' ):
931- self .parentRatingKey = utils .cast (int , self .parentThumb .split ('/' )[3 ])
932- # Get the parentRatingKey from the season's ratingKey
933- if not self .parentRatingKey and self .grandparentRatingKey :
934- self .parentRatingKey = self .show ().season (season = self .parentIndex ).ratingKey
935- if self .parentRatingKey :
936- self .parentKey = f'/library/metadata/{ self .parentRatingKey } '
925+ # Use cached properties below to return the correct values if they are missing to avoid auto-reloading.
926+ self ._parentKey = data .attrib .get ('parentKey' )
927+ self ._parentRatingKey = utils .cast (int , data .attrib .get ('parentRatingKey' ))
928+ self ._parentThumb = data .attrib .get ('parentThumb' )
929+
930+ @cached_property
931+ def parentKey (self ):
932+ """ Returns the parentKey. Refer to the Episode attributes. """
933+ return self ._parentKey or f'/library/metadata/{ self .parentRatingKey } '
934+
935+ @cached_property
936+ def parentRatingKey (self ):
937+ """ Returns the parentRatingKey. Refer to the Episode attributes. """
938+ if self ._parentRatingKey is not None :
939+ return self ._parentRatingKey
940+ # Parse the parentRatingKey from the parentThumb
941+ if self ._parentThumb and self ._parentThumb .startswith ('/library/metadata/' ):
942+ return utils .cast (int , self ._parentThumb .split ('/' )[3 ])
943+ # Get the parentRatingKey from the season's ratingKey
944+ return self ._season .ratingKey
945+
946+ @cached_property
947+ def parentThumb (self ):
948+ """ Returns the parentThumb. Refer to the Episode attributes. """
949+ return self ._parentThumb or self ._season .thumb
950+
951+ @cached_property
952+ def _season (self ):
953+ """ Returns the :class:`~plexapi.video.Season` object by querying for the show's children. """
954+ return self .fetchItem (
955+ f'{ self .grandparentKey } /children?excludeAllLeaves=1&index={ self .parentIndex } '
956+ )
937957
938958 def __repr__ (self ):
939959 return '<{}>' .format (
@@ -968,12 +988,10 @@ def episodeNumber(self):
968988 """ Returns the episode number. """
969989 return self .index
970990
971- @property
991+ @cached_property
972992 def seasonNumber (self ):
973993 """ Returns the episode's season number. """
974- if self ._seasonNumber is None :
975- self ._seasonNumber = self .parentIndex if isinstance (self .parentIndex , int ) else self .season ().seasonNumber
976- return utils .cast (int , self ._seasonNumber )
994+ return self .parentIndex if isinstance (self .parentIndex , int ) else self ._season .seasonNumber
977995
978996 @property
979997 def seasonEpisode (self ):
0 commit comments