From 995e98f785bc237ab8c0c6f68db9995c3f9cb9a1 Mon Sep 17 00:00:00 2001 From: Hellowlol Date: Wed, 4 Jan 2017 21:38:04 +0100 Subject: [PATCH 1/2] Suggestion for episode --- plexapi/video.py | 54 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/plexapi/video.py b/plexapi/video.py index d7434a2d..4749e167 100644 --- a/plexapi/video.py +++ b/plexapi/video.py @@ -189,12 +189,16 @@ class Show(Video): path = '/library/metadata/%s/children' % self.ratingKey return utils.listItems(self.server, path, Season.TYPE) - def season(self, title): + def season(self, title=None): """Returns a Season Args: - title (str): fx Season1 + title (str, int): fx Season 1 """ + title = str(title) + if title.isdigit(): + title = 'season %s' % title + path = '/library/metadata/%s/children' % self.ratingKey return utils.findItem(self.server, path, title) @@ -207,9 +211,18 @@ class Show(Video): leavesKey = '/library/metadata/%s/allLeaves' % self.ratingKey return utils.listItems(self.server, leavesKey, watched=watched) - def episode(self, title): - path = '/library/metadata/%s/allLeaves' % self.ratingKey - return utils.findItem(self.server, path, title) + def episode(self, title=None, season=None, episode=None): + if title: + path = '/library/metadata/%s/allLeaves' % self.ratingKey + return utils.findItem(self.server, path, title) + + elif season and episode: + results = [i for i in self.episodes() + if i.seasonNumber == season and i.index == episode] + if results: + return results[0] + + def watched(self): """Return a list of watched episodes""" @@ -244,7 +257,7 @@ class Season(Video): """ Video._loadData(self, data) self.leafCount = utils.cast(int, data.attrib.get('leafCount', NA)) - self.index = data.attrib.get('index', NA) + self.index = utils.cast(int, data.attrib.get('index', NA)) self.parentKey = data.attrib.get('parentKey', NA) self.parentRatingKey = utils.cast(int, data.attrib.get('parentRatingKey', NA)) self.viewedLeafCount = utils.cast( @@ -268,25 +281,38 @@ class Season(Video): Returns: list: of Episode - + """ childrenKey = '/library/metadata/%s/children' % self.ratingKey return utils.listItems(self.server, childrenKey, watched=watched) - def episode(self, title): - """Find a episode with a matching title. + def episode(self, title=None, season=None, episode=None): + """Find a episode with a matching title or match + against season and episode. Args: title (sting): Fx + season (int, optional): Not needed, just kept for comp with Show. + episode (int, optional): Default None Returns: Episode """ - path = '/library/metadata/%s/children' % self.ratingKey - return utils.findItem(self.server, path, title) + if title: + path = '/library/metadata/%s/children' % self.ratingKey + return utils.findItem(self.server, path, title) + + if episode: + if season is None: + season = self.index + results = [i for i in self.episodes() + if i.seasonNumber == season and i.index == episode] + if results: + return results[0] + def get(self, title): - """Get a episode witha matching title + """Get a episode witha matching title. Args: title (str): fx Secret santa @@ -332,7 +358,7 @@ class Episode(Video, Playable): self.grandparentThumb = data.attrib.get('grandparentThumb', NA) self.grandparentTitle = data.attrib.get('grandparentTitle', NA) self.guid = data.attrib.get('guid', NA) - self.index = data.attrib.get('index', NA) + self.index = utils.cast(int, data.attrib.get('index', NA)) self.originallyAvailableAt = utils.toDatetime( data.attrib.get('originallyAvailableAt', NA), '%Y-%m-%d') self.parentIndex = data.attrib.get('parentIndex', NA) @@ -369,7 +395,7 @@ class Episode(Video, Playable): """Return this episode seasonnumber.""" if self._seasonNumber is None: self._seasonNumber = self.parentIndex if self.parentIndex else self.season().seasonNumber - return self._seasonNumber + return utils.cast(int, self._seasonNumber) @property def thumbUrl(self): From 169f208e87e241228a3e906ca4f0021785d4cead Mon Sep 17 00:00:00 2001 From: Hellowlol Date: Thu, 5 Jan 2017 22:58:43 +0100 Subject: [PATCH 2/2] Fix review comments, prettier repr for season, and episode --- plexapi/video.py | 91 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 73 insertions(+), 18 deletions(-) diff --git a/plexapi/video.py b/plexapi/video.py index 4749e167..6f1bda69 100644 --- a/plexapi/video.py +++ b/plexapi/video.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from plexapi import media, utils +from plexapi.exceptions import NotFound from plexapi.utils import Playable, PlexPartialObject NA = utils.NA @@ -195,9 +196,8 @@ class Show(Video): Args: title (str, int): fx Season 1 """ - title = str(title) - if title.isdigit(): - title = 'season %s' % title + if isinstance(title, int): + title = 'Season %s' % title path = '/library/metadata/%s/children' % self.ratingKey return utils.findItem(self.server, path, title) @@ -212,6 +212,33 @@ class Show(Video): return utils.listItems(self.server, leavesKey, watched=watched) def episode(self, title=None, season=None, episode=None): + """Find a episode using a title or season and episode. + + Note: + Both season and episode is required if title is missing. + + Args: + title (str): Default None + season (int): Season number, default None + episode (int): Episode number, default None + + Raises: + ValueError: If season and episode is missing. + NotFound: If the episode is missing. + + Returns: + Episode + + Examples: + >>> plex.search('The blacklist')[0].episode(season=1, episode=1) + + >>> plex.search('The blacklist')[0].episode('The Freelancer') + + + """ + if not title and (not season or not episode): + raise TypeError('Missing argument: title or season and episode are required') + if title: path = '/library/metadata/%s/allLeaves' % self.ratingKey return utils.findItem(self.server, path, title) @@ -221,8 +248,8 @@ class Show(Video): if i.seasonNumber == season and i.index == episode] if results: return results[0] - - + else: + raise NotFound('Couldnt find %s S%s E%s' % (self.title, season, episode)) def watched(self): """Return a list of watched episodes""" @@ -260,6 +287,7 @@ class Season(Video): self.index = utils.cast(int, data.attrib.get('index', NA)) self.parentKey = data.attrib.get('parentKey', NA) self.parentRatingKey = utils.cast(int, data.attrib.get('parentRatingKey', NA)) + self.grandparentTitle = data.attrib.get('grandparentTitle', NA) self.viewedLeafCount = utils.cast( int, data.attrib.get('viewedLeafCount', NA)) @@ -286,33 +314,48 @@ class Season(Video): childrenKey = '/library/metadata/%s/children' % self.ratingKey return utils.listItems(self.server, childrenKey, watched=watched) - def episode(self, title=None, season=None, episode=None): - """Find a episode with a matching title or match - against season and episode. + def episode(self, title=None, episode=None): + """Find a episode using a title or season and episode. - Args: - title (sting): Fx - season (int, optional): Not needed, just kept for comp with Show. - episode (int, optional): Default None + Note: + episode is required if title is missing. - Returns: + Args: + title (str): Default None + episode (int): Episode number, default None + + Raises: + TypeError: If title and episode is missing. + NotFound: If that episode cant be found. + + Returns: Episode + + Examples: + >>> plex.search('The blacklist').season(1).episode(episode=1) + + >>> plex.search('The blacklist').season(1).episode('The Freelancer') + + """ + if not title and not episode: + raise TypeError('Missing argument, you need to use title or episode.') + if title: path = '/library/metadata/%s/children' % self.ratingKey return utils.findItem(self.server, path, title) - if episode: - if season is None: - season = self.index + elif episode: results = [i for i in self.episodes() - if i.seasonNumber == season and i.index == episode] + if i.seasonNumber == self.index and i.index == episode] if results: return results[0] + else: + raise NotFound('Couldnt find %s.Season %s Episode %s.' % (self.grandparentTitle, self.index. episode)) def get(self, title): - """Get a episode witha matching title. + """Get a episode with a matching title. Args: title (str): fx Secret santa @@ -334,6 +377,12 @@ class Season(Video): """Returns a list of unwatched Episode""" return self.episodes(watched=False) + def __repr__(self): + clsname = self.__class__.__name__ + key = self.key.replace('/library/metadata/', '').replace('/children', '') if self.key else 'NA' + title = self.title.replace(' ', '.')[0:20].encode('utf8') + return '<%s:%s:%s:%s>' % (clsname, key, self.grandparentTitle, title) + @utils.register_libtype class Episode(Video, Playable): @@ -385,6 +434,12 @@ class Episode(Video, Playable): # Cached season number self._seasonNumber = None + def __repr__(self): + clsname = self.__class__.__name__ + key = self.key.replace('/library/metadata/', '').replace('/children', '') if self.key else 'NA' + title = self.title.replace(' ', '.')[0:20].encode('utf8') + return '<%s:%s:%s:S%s:E%s:%s>' % (clsname, key, self.grandparentTitle, self.seasonNumber, self.index, title) + @property def isWatched(self): """Returns True if watched, False if not."""