From c171aa27452851593fa34a47afc470a621374a63 Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Fri, 20 Nov 2020 19:51:02 -0800 Subject: [PATCH 01/12] Allow customizing XML includes when reloading pkkid/python-plexapi#603 --- plexapi/base.py | 25 ++++++++++++++++++++++--- plexapi/video.py | 34 ++++++++++++++++++++-------------- 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/plexapi/base.py b/plexapi/base.py index b8bfbd681..16bdc4585 100644 --- a/plexapi/base.py +++ b/plexapi/base.py @@ -89,6 +89,20 @@ def _buildItemOrNone(self, elem, cls=None, initpath=None): except UnknownType: return None + def _buildDetailsKey(self, **kwargs): + """ Builds the details key with the XML include parameters. + All parameters are included by default with the option to override each parameter + or disable each parameter individually by setting it to False or 0. + """ + if hasattr(self, '_includes'): + includes = {} + for k, v in self._includes.items(): + value = kwargs.get(k, v) + if value not in [False, 0, '0']: + includes[k] = 1 if value is True else value + self._details_key = self.key + '?' + urlencode(includes, doseq=True) + return self._details_key + def fetchItem(self, ekey, cls=None, **kwargs): """ Load the specified key to find and build the first item with the specified tag and attrs. If no tag or attrs are specified then @@ -203,9 +217,14 @@ def listAttrs(self, data, attr, **kwargs): results.append(elem.attrib.get(attr)) return results - def reload(self, key=None): - """ Reload the data for this object from self.key. """ - key = key or self._details_key or self.key + def reload(self, key=None, **kwargs): + """ Reload the data for this object from self.key. + + Parameters: + key (string, optional): The key to reload. + **kwargs (dict): A dictionary of XML include parameters. + """ + key = key or self._buildDetailsKey(**kwargs) or self.key if not key: raise Unsupported('Cannot reload an object not built from a URL.') self._initpath = key diff --git a/plexapi/video.py b/plexapi/video.py index e2b542301..0f4ff50a7 100644 --- a/plexapi/video.py +++ b/plexapi/video.py @@ -28,6 +28,26 @@ class Video(PlexPartialObject): updatedAt (datatime): Datetime this item was updated. viewCount (int): Count of times this item was accessed. """ + _includes = { + 'checkFiles': 1, + 'includeAllConcerts': 1, + 'includeBandwidths': 1, + 'includeChapters': 1, + 'includeChildren': 1, + 'includeConcerts': 1, + 'includeExtras': 1, + 'includeFields': [1, 'thumbBlurHash', 'artBlurHash'], + 'includeGeolocation': 1, + 'includeLoudnessRamps': 1, + 'includeMarkers': 1, + 'includeOnDeck': 1, + 'includePopularLeaves': 1, + 'includePreferences': 1, + 'includeRelated': 1, + 'includeRelatedCount': 1, + 'includeReviews': 1, + 'includeStations': 1 + } def _loadData(self, data): """ Load attribute values from Plex XML response. """ @@ -286,16 +306,12 @@ class Movie(Playable, Video): TAG = 'Video' TYPE = 'movie' METADATA_TYPE = 'movie' - _include = ('?checkFiles=1&includeExtras=1&includeRelated=1' - '&includeOnDeck=1&includeChapters=1&includePopularLeaves=1' - '&includeConcerts=1&includePreferences=1') def _loadData(self, data): """ Load attribute values from Plex XML response. """ Video._loadData(self, data) Playable._loadData(self, data) - self._details_key = self.key + self._include self.art = data.attrib.get('art') self.audienceRating = utils.cast(float, data.attrib.get('audienceRating')) self.audienceRatingImage = data.attrib.get('audienceRatingImage') @@ -401,10 +417,6 @@ class Show(Video): TYPE = 'show' METADATA_TYPE = 'episode' - _include = ('?checkFiles=1&includeExtras=1&includeRelated=1' - '&includeOnDeck=1&includeChapters=1&includePopularLeaves=1' - '&includeMarkers=1&includeConcerts=1&includePreferences=1') - def __iter__(self): for season in self.seasons(): yield season @@ -414,7 +426,6 @@ def _loadData(self, data): Video._loadData(self, data) # fix key if loaded from search self.key = self.key.replace('/children', '') - self._details_key = self.key + self._include self.art = data.attrib.get('art') self.banner = data.attrib.get('banner') self.childCount = utils.cast(int, data.attrib.get('childCount')) @@ -707,15 +718,10 @@ class Episode(Playable, Video): TYPE = 'episode' METADATA_TYPE = 'episode' - _include = ('?checkFiles=1&includeExtras=1&includeRelated=1' - '&includeOnDeck=1&includeChapters=1&includePopularLeaves=1' - '&includeMarkers=1&includeConcerts=1&includePreferences=1') - def _loadData(self, data): """ Load attribute values from Plex XML response. """ Video._loadData(self, data) Playable._loadData(self, data) - self._details_key = self.key + self._include self._seasonNumber = None # cached season number self.art = data.attrib.get('art') self.chapterSource = data.attrib.get('chapterSource') From 0d27aa3f8e9fd4e1f457f1ffa5a730f729b72d30 Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Fri, 20 Nov 2020 20:46:35 -0800 Subject: [PATCH 02/12] Replace uses of details key in Show and Collection --- plexapi/base.py | 7 +++++-- plexapi/library.py | 8 +++++--- plexapi/video.py | 6 +++--- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/plexapi/base.py b/plexapi/base.py index 16bdc4585..2b9bab7d6 100644 --- a/plexapi/base.py +++ b/plexapi/base.py @@ -89,7 +89,7 @@ def _buildItemOrNone(self, elem, cls=None, initpath=None): except UnknownType: return None - def _buildDetailsKey(self, **kwargs): + def _buildDetailsKey(self, buildOnly=False, **kwargs): """ Builds the details key with the XML include parameters. All parameters are included by default with the option to override each parameter or disable each parameter individually by setting it to False or 0. @@ -100,7 +100,10 @@ def _buildDetailsKey(self, **kwargs): value = kwargs.get(k, v) if value not in [False, 0, '0']: includes[k] = 1 if value is True else value - self._details_key = self.key + '?' + urlencode(includes, doseq=True) + details_key = self.key + '?' + urlencode(includes, doseq=True) + if buildOnly: + return details_key + self._details_key = details_key return self._details_key def fetchItem(self, ekey, cls=None, **kwargs): diff --git a/plexapi/library.py b/plexapi/library.py index 4a7c4b848..53efc79ac 100644 --- a/plexapi/library.py +++ b/plexapi/library.py @@ -1429,11 +1429,13 @@ class Collections(PlexPartialObject): TAG = 'Directory' TYPE = 'collection' - _include = "?includeExternalMedia=1&includePreferences=1" + _includes = { + 'includeExternalMedia': 1, + 'includePreferences': 1 + } def _loadData(self, data): self.ratingKey = utils.cast(int, data.attrib.get('ratingKey')) - self._details_key = "/library/metadata/%s%s" % (self.ratingKey, self._include) self.addedAt = utils.toDatetime(data.attrib.get('addedAt')) self.art = data.attrib.get('art') self.childCount = utils.cast(int, data.attrib.get('childCount')) @@ -1468,7 +1470,7 @@ def __len__(self): def _preferences(self): """ Returns a list of :class:`~plexapi.settings.Preferences` objects. """ items = [] - data = self._server.query(self._details_key) + data = self._server.query(self._buildDetailsKey(buildOnly=True)) for item in data.iter('Setting'): items.append(Setting(data=item, server=self._server)) diff --git a/plexapi/video.py b/plexapi/video.py index 0f4ff50a7..9fd2f0922 100644 --- a/plexapi/video.py +++ b/plexapi/video.py @@ -461,7 +461,7 @@ def isWatched(self): def preferences(self): """ Returns a list of :class:`~plexapi.settings.Preferences` objects. """ items = [] - data = self._server.query(self._details_key) + data = self._server.query(self._buildDetailsKey(buildOnly=True)) for item in data.iter('Preferences'): for elem in item: setting = settings.Preferences(data=elem, server=self._server) @@ -495,7 +495,7 @@ def defaultAdvanced(self): def hubs(self): """ Returns a list of :class:`~plexapi.library.Hub` objects. """ - data = self._server.query(self._details_key) + data = self._server.query(self._buildDetailsKey(buildOnly=True)) for item in data.iter('Related'): return self.findItems(item, library.Hub) @@ -503,7 +503,7 @@ def onDeck(self): """ Returns shows On Deck :class:`~plexapi.video.Video` object. If show is unwatched, return will likely be the first episode. """ - data = self._server.query(self._details_key) + data = self._server.query(self._buildDetailsKey(buildOnly=True)) return self.findItems([item for item in data.iter('OnDeck')][0])[0] def seasons(self, **kwargs): From d59d199ee7ff2ac10849a63b36ac187317f9d773 Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sat, 21 Nov 2020 12:08:27 -0800 Subject: [PATCH 03/12] Always build default details key --- plexapi/base.py | 16 ++++++++-------- plexapi/library.py | 2 +- plexapi/video.py | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/plexapi/base.py b/plexapi/base.py index 2b9bab7d6..9a27932bd 100644 --- a/plexapi/base.py +++ b/plexapi/base.py @@ -44,9 +44,9 @@ def __init__(self, server, data, initpath=None): self._server = server self._data = data self._initpath = initpath or self.key - self._details_key = '' if data is not None: self._loadData(data) + self._details_key = self._buildDetailsKey() def __repr__(self): uid = self._clean(self.firstAttr('_baseurl', 'key', 'id', 'playQueueID', 'uri')) @@ -89,22 +89,21 @@ def _buildItemOrNone(self, elem, cls=None, initpath=None): except UnknownType: return None - def _buildDetailsKey(self, buildOnly=False, **kwargs): + def _buildDetailsKey(self, **kwargs): """ Builds the details key with the XML include parameters. All parameters are included by default with the option to override each parameter or disable each parameter individually by setting it to False or 0. """ + details_key = self.key if hasattr(self, '_includes'): includes = {} for k, v in self._includes.items(): value = kwargs.get(k, v) if value not in [False, 0, '0']: includes[k] = 1 if value is True else value - details_key = self.key + '?' + urlencode(includes, doseq=True) - if buildOnly: - return details_key - self._details_key = details_key - return self._details_key + if includes: + details_key += '?' + urlencode(includes, doseq=True) + return details_key def fetchItem(self, ekey, cls=None, **kwargs): """ Load the specified key to find and build the first item with the @@ -227,7 +226,8 @@ def reload(self, key=None, **kwargs): key (string, optional): The key to reload. **kwargs (dict): A dictionary of XML include parameters. """ - key = key or self._buildDetailsKey(**kwargs) or self.key + details_key = self._buildDetailsKey(**kwargs) if kwargs else self._details_key + key = key or details_key or self.key if not key: raise Unsupported('Cannot reload an object not built from a URL.') self._initpath = key diff --git a/plexapi/library.py b/plexapi/library.py index 53efc79ac..738007097 100644 --- a/plexapi/library.py +++ b/plexapi/library.py @@ -1470,7 +1470,7 @@ def __len__(self): def _preferences(self): """ Returns a list of :class:`~plexapi.settings.Preferences` objects. """ items = [] - data = self._server.query(self._buildDetailsKey(buildOnly=True)) + data = self._server.query(self._details_key) for item in data.iter('Setting'): items.append(Setting(data=item, server=self._server)) diff --git a/plexapi/video.py b/plexapi/video.py index 9fd2f0922..0f4ff50a7 100644 --- a/plexapi/video.py +++ b/plexapi/video.py @@ -461,7 +461,7 @@ def isWatched(self): def preferences(self): """ Returns a list of :class:`~plexapi.settings.Preferences` objects. """ items = [] - data = self._server.query(self._buildDetailsKey(buildOnly=True)) + data = self._server.query(self._details_key) for item in data.iter('Preferences'): for elem in item: setting = settings.Preferences(data=elem, server=self._server) @@ -495,7 +495,7 @@ def defaultAdvanced(self): def hubs(self): """ Returns a list of :class:`~plexapi.library.Hub` objects. """ - data = self._server.query(self._buildDetailsKey(buildOnly=True)) + data = self._server.query(self._details_key) for item in data.iter('Related'): return self.findItems(item, library.Hub) @@ -503,7 +503,7 @@ def onDeck(self): """ Returns shows On Deck :class:`~plexapi.video.Video` object. If show is unwatched, return will likely be the first episode. """ - data = self._server.query(self._buildDetailsKey(buildOnly=True)) + data = self._server.query(self._details_key) return self.findItems([item for item in data.iter('OnDeck')][0])[0] def seasons(self, **kwargs): From a12f8b09e5c9b24564394b38bec5a565865f936c Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sat, 21 Nov 2020 13:20:53 -0800 Subject: [PATCH 04/12] Ensure sorted parameters in details key --- plexapi/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plexapi/base.py b/plexapi/base.py index 9a27932bd..310502b70 100644 --- a/plexapi/base.py +++ b/plexapi/base.py @@ -102,7 +102,7 @@ def _buildDetailsKey(self, **kwargs): if value not in [False, 0, '0']: includes[k] = 1 if value is True else value if includes: - details_key += '?' + urlencode(includes, doseq=True) + details_key += '?' + urlencode(sorted(includes.items()), doseq=True) return details_key def fetchItem(self, ekey, cls=None, **kwargs): From 82a9fce665dd20c2d4f4bd36cfc6e4d977b911cb Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sat, 21 Nov 2020 13:21:13 -0800 Subject: [PATCH 05/12] Add test for partial reload with disabled include --- tests/test_video.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_video.py b/tests/test_video.py index 21ca259b2..44cf97f77 100644 --- a/tests/test_video.py +++ b/tests/test_video.py @@ -66,6 +66,8 @@ def test_video_Movie_getStreamURL(movie, account): def test_video_Movie_isFullObject_and_reload(plex): movie = plex.library.section("Movies").get("Sita Sings the Blues") assert movie.isFullObject() is False + movie.reload(checkFiles=False) + assert movie.isFullObject() is False movie.reload() assert movie.isFullObject() is True movie_via_search = plex.library.search(movie.title)[0] From 387ab170723c8a733766cbf72098bc270095a3ad Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sat, 21 Nov 2020 14:49:21 -0800 Subject: [PATCH 06/12] Settings __getattr__ needs to raise AttributeError --- plexapi/settings.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plexapi/settings.py b/plexapi/settings.py index a62460bdf..8416f8718 100644 --- a/plexapi/settings.py +++ b/plexapi/settings.py @@ -21,7 +21,10 @@ def __init__(self, server, data, initpath=None): def __getattr__(self, attr): if attr.startswith('_'): - return self.__dict__[attr] + try: + return self.__dict__[attr] + except KeyError: + raise AttributeError return self.get(attr).value def __setattr__(self, attr, value): From a5690579d5e4555c18b1aa0488a5ef7f3f5e8a16 Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sat, 21 Nov 2020 19:52:01 -0800 Subject: [PATCH 07/12] Refactor includes to PlexPartialObject * Allows includes to be used by Audio, Video, Photoalbum, Photo, Collections, and Playlist --- plexapi/base.py | 25 +++++++++++++++++++++++-- plexapi/library.py | 4 ---- plexapi/video.py | 20 -------------------- 3 files changed, 23 insertions(+), 26 deletions(-) diff --git a/plexapi/base.py b/plexapi/base.py index 310502b70..fde35ef99 100644 --- a/plexapi/base.py +++ b/plexapi/base.py @@ -95,9 +95,9 @@ def _buildDetailsKey(self, **kwargs): or disable each parameter individually by setting it to False or 0. """ details_key = self.key - if hasattr(self, '_includes'): + if hasattr(self, '_INCLUDES'): includes = {} - for k, v in self._includes.items(): + for k, v in self._INCLUDES.items(): value = kwargs.get(k, v) if value not in [False, 0, '0']: includes[k] = 1 if value is True else value @@ -303,6 +303,27 @@ class PlexPartialObject(PlexObject): and if the specified value you request is None it will fetch the full object automatically and update itself. """ + _INCLUDES = { + 'checkFiles': 1, + 'includeAllConcerts': 1, + 'includeBandwidths': 1, + 'includeChapters': 1, + 'includeChildren': 1, + 'includeConcerts': 1, + 'includeExternalMedia': 1, + 'includeExtras': 1, + 'includeFields': [1, 'thumbBlurHash', 'artBlurHash'], + 'includeGeolocation': 1, + 'includeLoudnessRamps': 1, + 'includeMarkers': 1, + 'includeOnDeck': 1, + 'includePopularLeaves': 1, + 'includePreferences': 1, + 'includeRelated': 1, + 'includeRelatedCount': 1, + 'includeReviews': 1, + 'includeStations': 1 + } def __eq__(self, other): return other not in [None, []] and self.key == other.key diff --git a/plexapi/library.py b/plexapi/library.py index 738007097..0320f869e 100644 --- a/plexapi/library.py +++ b/plexapi/library.py @@ -1429,10 +1429,6 @@ class Collections(PlexPartialObject): TAG = 'Directory' TYPE = 'collection' - _includes = { - 'includeExternalMedia': 1, - 'includePreferences': 1 - } def _loadData(self, data): self.ratingKey = utils.cast(int, data.attrib.get('ratingKey')) diff --git a/plexapi/video.py b/plexapi/video.py index 0f4ff50a7..582ace113 100644 --- a/plexapi/video.py +++ b/plexapi/video.py @@ -28,26 +28,6 @@ class Video(PlexPartialObject): updatedAt (datatime): Datetime this item was updated. viewCount (int): Count of times this item was accessed. """ - _includes = { - 'checkFiles': 1, - 'includeAllConcerts': 1, - 'includeBandwidths': 1, - 'includeChapters': 1, - 'includeChildren': 1, - 'includeConcerts': 1, - 'includeExtras': 1, - 'includeFields': [1, 'thumbBlurHash', 'artBlurHash'], - 'includeGeolocation': 1, - 'includeLoudnessRamps': 1, - 'includeMarkers': 1, - 'includeOnDeck': 1, - 'includePopularLeaves': 1, - 'includePreferences': 1, - 'includeRelated': 1, - 'includeRelatedCount': 1, - 'includeReviews': 1, - 'includeStations': 1 - } def _loadData(self, data): """ Load attribute values from Plex XML response. """ From 178217a9811999749930208cbc686e31245f20c6 Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sat, 21 Nov 2020 19:52:33 -0800 Subject: [PATCH 08/12] Improve docstring for reload --- plexapi/base.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/plexapi/base.py b/plexapi/base.py index fde35ef99..553777baf 100644 --- a/plexapi/base.py +++ b/plexapi/base.py @@ -223,8 +223,32 @@ def reload(self, key=None, **kwargs): """ Reload the data for this object from self.key. Parameters: - key (string, optional): The key to reload. - **kwargs (dict): A dictionary of XML include parameters. + key (string, optional): Override the key to reload. + **kwargs (dict): A dictionary of XML include parameters to exclude or override. + All parameters are included by default with the option to override each parameter + or disable each parameter individually by setting it to False or 0. + See :class:`~plexapi.base.PlexPartialObject` for all the available include parameters. + + Example: + + .. code-block:: python + + from plexapi.server import PlexServer + plex = PlexServer('http://localhost:32400', token='xxxxxxxxxxxxxxxxxxxx') + movie = plex.library.section('Movies').get('Cars') + + # Partial reload of the movie without the `checkFiles` parameter. + # Excluding `checkFiles` will prevent the Plex server from reading the + # file to check if the file still exists and is accessible. + # The movie object will remain as a partial object. + movie.reload(checkFiles=False) + movie.isPartialObject() # Returns True + + # Full reload of the movie with all include parameters. + # The movie object will be a full object. + movie.reload() + movie.isFullObject() # Returns True + """ details_key = self._buildDetailsKey(**kwargs) if kwargs else self._details_key key = key or details_key or self.key From 9fb3eaf41c04f85165874aade8a9d90bb99522f9 Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sat, 21 Nov 2020 20:02:31 -0800 Subject: [PATCH 09/12] Add thumbBlurHash and artBlurHash attributes with includeFields --- plexapi/audio.py | 6 +++--- plexapi/base.py | 4 ++-- plexapi/library.py | 4 +++- plexapi/video.py | 2 ++ 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/plexapi/audio.py b/plexapi/audio.py index 8a8d38627..f6676fc50 100644 --- a/plexapi/audio.py +++ b/plexapi/audio.py @@ -34,6 +34,8 @@ def _loadData(self, data): self._data = data self.listType = 'audio' self.addedAt = utils.toDatetime(data.attrib.get('addedAt')) + self.art = data.attrib.get('art') + self.artBlurHash = data.attrib.get('artBlurHash') self.fields = self.findItems(data, etag='Field') self.index = data.attrib.get('index') self.key = data.attrib.get('key') @@ -42,6 +44,7 @@ def _loadData(self, data): self.ratingKey = utils.cast(int, data.attrib.get('ratingKey')) self.summary = data.attrib.get('summary') self.thumb = data.attrib.get('thumb') + self.thumbBlurHash = data.attrib.get('thumbBlurHash') self.title = data.attrib.get('title') self.titleSort = data.attrib.get('titleSort', self.title) self.type = data.attrib.get('type') @@ -126,7 +129,6 @@ class Artist(Audio): def _loadData(self, data): """ Load attribute values from Plex XML response. """ Audio._loadData(self, data) - self.art = data.attrib.get('art') self.guid = data.attrib.get('guid') self.key = self.key.replace('/children', '') # FIX_BUG_50 self.locations = self.listAttrs(data, 'path', etag='Location') @@ -219,7 +221,6 @@ def __iter__(self): def _loadData(self, data): """ Load attribute values from Plex XML response. """ Audio._loadData(self, data) - self.art = data.attrib.get('art') self.key = self.key.replace('/children', '') # fixes bug #50 self.originallyAvailableAt = utils.toDatetime(data.attrib.get('originallyAvailableAt'), '%Y-%m-%d') self.parentKey = data.attrib.get('parentKey') @@ -319,7 +320,6 @@ def _loadData(self, data): """ Load attribute values from Plex XML response. """ Audio._loadData(self, data) Playable._loadData(self, data) - self.art = data.attrib.get('art') self.chapterSource = data.attrib.get('chapterSource') self.duration = utils.cast(int, data.attrib.get('duration')) self.grandparentArt = data.attrib.get('grandparentArt') diff --git a/plexapi/base.py b/plexapi/base.py index 553777baf..b7184c179 100644 --- a/plexapi/base.py +++ b/plexapi/base.py @@ -102,7 +102,7 @@ def _buildDetailsKey(self, **kwargs): if value not in [False, 0, '0']: includes[k] = 1 if value is True else value if includes: - details_key += '?' + urlencode(sorted(includes.items()), doseq=True) + details_key += '?' + urlencode(sorted(includes.items())) return details_key def fetchItem(self, ekey, cls=None, **kwargs): @@ -336,7 +336,7 @@ class PlexPartialObject(PlexObject): 'includeConcerts': 1, 'includeExternalMedia': 1, 'includeExtras': 1, - 'includeFields': [1, 'thumbBlurHash', 'artBlurHash'], + 'includeFields': 'thumbBlurHash,artBlurHash', 'includeGeolocation': 1, 'includeLoudnessRamps': 1, 'includeMarkers': 1, diff --git a/plexapi/library.py b/plexapi/library.py index 0320f869e..9defe0760 100644 --- a/plexapi/library.py +++ b/plexapi/library.py @@ -1434,6 +1434,7 @@ def _loadData(self, data): self.ratingKey = utils.cast(int, data.attrib.get('ratingKey')) self.addedAt = utils.toDatetime(data.attrib.get('addedAt')) self.art = data.attrib.get('art') + self.artBlurHash = data.attrib.get('artBlurHash') self.childCount = utils.cast(int, data.attrib.get('childCount')) self.collectionMode = data.attrib.get('collectionMode') self.collectionSort = data.attrib.get('collectionSort') @@ -1441,7 +1442,7 @@ def _loadData(self, data): self.fields = self.findItems(data, etag='Field') self.guid = data.attrib.get('guid') self.index = utils.cast(int, data.attrib.get('index')) - self.key = data.attrib.get('key') + self.key = data.attrib.get('key').replace('/children', '') # FIX_BUG_50 self.labels = self.findItems(data, etag='Label') self.librarySectionID = data.attrib.get('librarySectionID') self.librarySectionKey = data.attrib.get('librarySectionKey') @@ -1451,6 +1452,7 @@ def _loadData(self, data): self.subtype = data.attrib.get('subtype') self.summary = data.attrib.get('summary') self.thumb = data.attrib.get('thumb') + self.thumbBlurHash = data.attrib.get('thumbBlurHash') self.title = data.attrib.get('title') self.titleSort = data.attrib.get('titleSort') self.type = data.attrib.get('type') diff --git a/plexapi/video.py b/plexapi/video.py index 582ace113..58a040803 100644 --- a/plexapi/video.py +++ b/plexapi/video.py @@ -35,6 +35,7 @@ def _loadData(self, data): self.listType = 'video' self.addedAt = utils.toDatetime(data.attrib.get('addedAt')) self.art = data.attrib.get('art') + self.artBlurHash = data.attrib.get('artBlurHash') self.fields = self.findItems(data, etag='Field') self.key = data.attrib.get('key', '') self.lastViewedAt = utils.toDatetime(data.attrib.get('lastViewedAt')) @@ -42,6 +43,7 @@ def _loadData(self, data): self.ratingKey = utils.cast(int, data.attrib.get('ratingKey')) self.summary = data.attrib.get('summary') self.thumb = data.attrib.get('thumb') + self.thumbBlurHash = data.attrib.get('thumbBlurHash') self.title = data.attrib.get('title') self.titleSort = data.attrib.get('titleSort', self.title) self.type = data.attrib.get('type') From f3e9425a2c64a2df64a661577e0fa4ef8bced95d Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sat, 21 Nov 2020 21:25:24 -0800 Subject: [PATCH 10/12] Update docstring from artBlurHash and thumbBlurHash --- plexapi/audio.py | 6 +++--- plexapi/library.py | 3 +++ plexapi/video.py | 3 +++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/plexapi/audio.py b/plexapi/audio.py index f6676fc50..c69e7eed2 100644 --- a/plexapi/audio.py +++ b/plexapi/audio.py @@ -11,6 +11,8 @@ class Audio(PlexPartialObject): Attributes: addedAt (datetime): Datetime this item was added to the library. + art (str): URL to artwork image. + artBlurHash (str): BlurHash string for artwork image. fields (list): List of :class:`~plexapi.media.Field`. index (sting): Index Number (often the track number). key (str): API URL (/library/metadata/). @@ -20,6 +22,7 @@ class Audio(PlexPartialObject): ratingKey (int): Unique key identifying this item. summary (str): Summary of the artist, track, or album. thumb (str): URL to thumbnail image. + thumbBlurHash (str): BlurHash string for thumbnail image. title (str): Artist, Album or Track title. (Jason Mraz, We Sing, Lucky, etc.) titleSort (str): Title to use when sorting (defaults to title). type (str): 'artist', 'album', or 'track'. @@ -115,7 +118,6 @@ class Artist(Audio): Attributes: TAG (str): 'Directory' TYPE (str): 'artist' - art (str): Artist artwork (/library/metadata//art/) countries (list): List of :class:`~plexapi.media.Country` objects this artist respresents. genres (list): List of :class:`~plexapi.media.Genre` objects this artist respresents. guid (str): Unknown (unique ID; com.plexapp.agents.plexmusic://gracenote/artist/05517B8701668D28?lang=en) @@ -200,7 +202,6 @@ class Album(Audio): Attributes: TAG (str): 'Directory' TYPE (str): 'album' - art (str): Album artwork (/library/metadata//art/) genres (list): List of :class:`~plexapi.media.Genre` objects this album respresents. key (str): API URL (/library/metadata/). originallyAvailableAt (datetime): Datetime this album was released. @@ -285,7 +286,6 @@ class Track(Audio, Playable): Attributes: TAG (str): 'Directory' TYPE (str): 'track' - art (str): Track artwork (/library/metadata//art/) chapterSource (TYPE): Unknown duration (int): Length of this album in seconds. grandparentArt (str): Album artist artwork. diff --git a/plexapi/library.py b/plexapi/library.py index 9defe0760..00c060b99 100644 --- a/plexapi/library.py +++ b/plexapi/library.py @@ -1403,6 +1403,8 @@ class Collections(PlexPartialObject): ratingKey (int): Unique key identifying this item. addedAt (datetime): Datetime this item was added to the library. + art (str): URL to artwork image. + artBlurHash (str): BlurHash string for artwork image. childCount (int): Count of child object(s) collectionMode (str): How the items in the collection are displayed. collectionSort (str): How to sort the items in the collection. @@ -1420,6 +1422,7 @@ class Collections(PlexPartialObject): subtype (str): Media type summary (str): Summary of the collection thumb (str): URL to thumbnail image. + thumbBlurHash (str): BlurHash string for thumbnail image. title (str): Collection Title titleSort (str): Title to use when sorting (defaults to title). type (str): Hardcoded 'collection' diff --git a/plexapi/video.py b/plexapi/video.py index 58a040803..5f0cd8e4f 100644 --- a/plexapi/video.py +++ b/plexapi/video.py @@ -14,6 +14,8 @@ class Video(PlexPartialObject): Attributes: addedAt (datetime): Datetime this item was added to the library. + art (str): URL to artwork image. + artBlurHash (str): BlurHash string for artwork image. fields (list): List of :class:`~plexapi.media.Field`. key (str): API URL (/library/metadata/). lastViewedAt (datetime): Datetime item was last accessed. @@ -22,6 +24,7 @@ class Video(PlexPartialObject): ratingKey (int): Unique key identifying this item. summary (str): Summary of the artist, track, or album. thumb (str): URL to thumbnail image. + thumbBlurHash (str): BlurHash string for thumbnail image. title (str): Artist, Album or Track title. (Jason Mraz, We Sing, Lucky, etc.) titleSort (str): Title to use when sorting (defaults to title). type (str): 'artist', 'album', or 'track'. From 7f7d03236bb917c1c213a9a79fbc1a5f11bf41e4 Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sun, 22 Nov 2020 19:06:30 -0800 Subject: [PATCH 11/12] Fix docstrings --- plexapi/alert.py | 2 +- plexapi/audio.py | 18 ++++----- plexapi/base.py | 6 +-- plexapi/client.py | 12 +++--- plexapi/library.py | 90 ++++++++++++++++++++++----------------------- plexapi/media.py | 4 +- plexapi/myplex.py | 36 +++++++++--------- plexapi/photo.py | 14 +++---- plexapi/playlist.py | 24 ++++++------ plexapi/server.py | 8 ++-- plexapi/sync.py | 6 +-- plexapi/utils.py | 2 +- plexapi/video.py | 28 +++++++------- 13 files changed, 125 insertions(+), 125 deletions(-) diff --git a/plexapi/alert.py b/plexapi/alert.py index 1a5469abe..bf6e5394f 100644 --- a/plexapi/alert.py +++ b/plexapi/alert.py @@ -54,7 +54,7 @@ def run(self): def stop(self): """ Stop the AlertListener thread. Once the notifier is stopped, it cannot be directly - started again. You must call :func:`plexapi.server.PlexServer.startAlertListener()` + started again. You must call :func:`~plexapi.server.PlexServer.startAlertListener` from a PlexServer instance. """ log.info('Stopping AlertListener.') diff --git a/plexapi/audio.py b/plexapi/audio.py index c69e7eed2..08742884f 100644 --- a/plexapi/audio.py +++ b/plexapi/audio.py @@ -76,20 +76,20 @@ def _defaultSyncTitle(self): def sync(self, bitrate, client=None, clientId=None, limit=None, title=None): """ Add current audio (artist, album or track) as sync item for specified device. - See :func:`plexapi.myplex.MyPlexAccount.sync()` for possible exceptions. + See :func:`~plexapi.myplex.MyPlexAccount.sync` for possible exceptions. Parameters: bitrate (int): maximum bitrate for synchronized music, better use one of MUSIC_BITRATE_* values from the - module :mod:`plexapi.sync`. - client (:class:`plexapi.myplex.MyPlexDevice`): sync destination, see - :func:`plexapi.myplex.MyPlexAccount.sync`. - clientId (str): sync destination, see :func:`plexapi.myplex.MyPlexAccount.sync`. + module :mod:`~plexapi.sync`. + client (:class:`~plexapi.myplex.MyPlexDevice`): sync destination, see + :func:`~plexapi.myplex.MyPlexAccount.sync`. + clientId (str): sync destination, see :func:`~plexapi.myplex.MyPlexAccount.sync`. limit (int): maximum count of items to sync, unlimited if `None`. - title (str): descriptive title for the new :class:`plexapi.sync.SyncItem`, if empty the value would be + title (str): descriptive title for the new :class:`~plexapi.sync.SyncItem`, if empty the value would be generated from metadata of current media. Returns: - :class:`plexapi.sync.SyncItem`: an instance of created syncItem. + :class:`~plexapi.sync.SyncItem`: an instance of created syncItem. """ from plexapi.sync import SyncItem, Policy, MediaSettings @@ -183,7 +183,7 @@ def download(self, savepath=None, keep_original_name=False, **kwargs): keep_original_name (bool): Set True to keep the original filename as stored in the Plex server. False will create a new filename with the format " - ". - kwargs (dict): If specified, a :func:`~plexapi.audio.Track.getStreamURL()` will + kwargs (dict): If specified, a :func:`~plexapi.audio.Track.getStreamURL` will be returned and the additional arguments passed in will be sent to that function. If kwargs is not specified, the media items will be downloaded and saved to disk. @@ -264,7 +264,7 @@ def download(self, savepath=None, keep_original_name=False, **kwargs): keep_original_name (bool): Set True to keep the original filename as stored in the Plex server. False will create a new filename with the format " - ". - kwargs (dict): If specified, a :func:`~plexapi.audio.Track.getStreamURL()` will + kwargs (dict): If specified, a :func:`~plexapi.audio.Track.getStreamURL` will be returned and the additional arguments passed in will be sent to that function. If kwargs is not specified, the media items will be downloaded and saved to disk. diff --git a/plexapi/base.py b/plexapi/base.py index b7184c179..00260ab71 100644 --- a/plexapi/base.py +++ b/plexapi/base.py @@ -81,7 +81,7 @@ def _buildItem(self, elem, cls=None, initpath=None): raise UnknownType("Unknown library type <%s type='%s'../>" % (elem.tag, etype)) def _buildItemOrNone(self, elem, cls=None, initpath=None): - """ Calls :func:`~plexapi.base.PlexObject._buildItem()` but returns + """ Calls :func:`~plexapi.base.PlexObject._buildItem` but returns None if elem is an unknown type. """ try: @@ -696,7 +696,7 @@ def getStreamURL(self, **params): offset, copyts, protocol, mediaIndex, platform. Raises: - :class:`plexapi.exceptions.Unsupported`: When the item doesn't support fetching a stream URL. + :class:`~plexapi.exceptions.Unsupported`: When the item doesn't support fetching a stream URL. """ if self.TYPE not in ('movie', 'episode', 'track'): raise Unsupported('Fetching stream URL for %s is unsupported.' % self.TYPE) @@ -761,7 +761,7 @@ def download(self, savepath=None, keep_original_name=False, **kwargs): keep_original_name (bool): Set True to keep the original filename as stored in the Plex server. False will create a new filename with the format " - ". - kwargs (dict): If specified, a :func:`~plexapi.audio.Track.getStreamURL()` will + kwargs (dict): If specified, a :func:`~plexapi.audio.Track.getStreamURL` will be returned and the additional arguments passed in will be sent to that function. If kwargs is not specified, the media items will be downloaded and saved to disk. diff --git a/plexapi/client.py b/plexapi/client.py index 4405c230e..234a76b8b 100644 --- a/plexapi/client.py +++ b/plexapi/client.py @@ -53,7 +53,7 @@ class PlexClient(PlexObject): _token (str): Token used to access this client. _session (obj): Requests session object used to access this client. _proxyThroughServer (bool): Set to True after calling - :func:`~plexapi.client.PlexClient.proxyThroughServer()` (default False). + :func:`~plexapi.client.PlexClient.proxyThroughServer` (default False). """ TAG = 'Player' key = '/resources' @@ -140,7 +140,7 @@ def proxyThroughServer(self, value=True, server=None): value (bool): Enable or disable proxying (optional, default True). Raises: - :class:`plexapi.exceptions.Unsupported`: Cannot use client proxy with unknown server. + :class:`~plexapi.exceptions.Unsupported`: Cannot use client proxy with unknown server. """ if server: self._server = server @@ -173,7 +173,7 @@ def query(self, path, method=None, headers=None, timeout=None, **kwargs): return ElementTree.fromstring(data) if data.strip() else None def sendCommand(self, command, proxy=None, **params): - """ Convenience wrapper around :func:`~plexapi.client.PlexClient.query()` to more easily + """ Convenience wrapper around :func:`~plexapi.client.PlexClient.query` to more easily send simple commands to the client. Returns an ElementTree object containing the response. @@ -183,7 +183,7 @@ def sendCommand(self, command, proxy=None, **params): **params (dict): Additional GET parameters to include with the command. Raises: - :class:`plexapi.exceptions.Unsupported`: When we detect the client doesn't support this capability. + :class:`~plexapi.exceptions.Unsupported`: When we detect the client doesn't support this capability. """ command = command.strip('/') controller = command.split('/')[0] @@ -299,7 +299,7 @@ def goToMedia(self, media, **params): **params (dict): Additional GET parameters to include with the command. Raises: - :class:`plexapi.exceptions.Unsupported`: When no PlexServer specified in this object. + :class:`~plexapi.exceptions.Unsupported`: When no PlexServer specified in this object. """ if not self._server: raise Unsupported('A server must be specified before using this command.') @@ -469,7 +469,7 @@ def playMedia(self, media, offset=0, **params): also: https://github.com/plexinc/plex-media-player/wiki/Remote-control-API#modified-commands Raises: - :class:`plexapi.exceptions.Unsupported`: When no PlexServer specified in this object. + :class:`~plexapi.exceptions.Unsupported`: When no PlexServer specified in this object. """ if not self._server: raise Unsupported('A server must be specified before using this command.') diff --git a/plexapi/library.py b/plexapi/library.py index 00c060b99..b38689890 100644 --- a/plexapi/library.py +++ b/plexapi/library.py @@ -450,13 +450,13 @@ def all(self, sort=None, **kwargs): return self.fetchItems(key, **kwargs) def folders(self): - """ Returns a list of available `:class:`~plexapi.library.Folder` for this library section. + """ Returns a list of available :class:`~plexapi.library.Folder` for this library section. """ key = '/library/sections/%s/folder' % self.key return self.fetchItems(key, Folder) def hubs(self): - """ Returns a list of available `:class:`~plexapi.library.Hub` for this library section. + """ Returns a list of available :class:`~plexapi.library.Hub` for this library section. """ key = '/hubs/sections/%s' % self.key return self.fetchItems(key) @@ -467,7 +467,7 @@ def _filters(self): return self.fetchItems(key, cls=Filter) def _sorts(self, mediaType=None): - """ Returns a list of available `:class:`~plexapi.library.Sort` for this library section. + """ Returns a list of available :class:`~plexapi.library.Sort` for this library section. """ items = [] for data in self.listChoices('sorts', mediaType): @@ -477,7 +477,7 @@ def _sorts(self, mediaType=None): return items def filterFields(self, mediaType=None): - """ Returns a list of available `:class:`~plexapi.library.FilterField` for this library section. + """ Returns a list of available :class:`~plexapi.library.FilterField` for this library section. """ items = [] key = '/library/sections/%s/filters?includeMeta=1' % self.key @@ -496,7 +496,7 @@ def filterFields(self, mediaType=None): return items def agents(self): - """ Returns a list of available `:class:`~plexapi.media.Agent` for this library section. + """ Returns a list of available :class:`~plexapi.media.Agent` for this library section. """ return self._server.agents(utils.searchType(self.type)) @@ -605,7 +605,7 @@ def deleteMediaPreviews(self): def listChoices(self, category, libtype=None, **kwargs): """ Returns a list of :class:`~plexapi.library.FilterChoice` objects for the specified category and libtype. kwargs can be any of the same kwargs in - :func:`plexapi.library.LibraySection.search()` to help narrow down the choices + :func:`~plexapi.library.LibraySection.search` to help narrow down the choices to only those that matter in your current context. Parameters: @@ -614,7 +614,7 @@ def listChoices(self, category, libtype=None, **kwargs): **kwargs (dict): Additional kwargs to narrow down the choices. Raises: - :class:`plexapi.exceptions.BadRequest`: Cannot include kwarg equal to specified category. + :class:`~plexapi.exceptions.BadRequest`: Cannot include kwarg equal to specified category. """ # TODO: Should this be moved to base? if category in kwargs: @@ -661,7 +661,7 @@ def search(self, title=None, sort=None, maxresults=None, * year: List of years to search within ([yyyy, ...]). [all] Raises: - :class:`plexapi.exceptions.BadRequest`: when applying unknown filter + :class:`~plexapi.exceptions.BadRequest`: when applying unknown filter """ # cleanup the core arguments args = {} @@ -755,20 +755,20 @@ def _locations(self): def sync(self, policy, mediaSettings, client=None, clientId=None, title=None, sort=None, libtype=None, **kwargs): """ Add current library section as sync item for specified device. - See description of :func:`~plexapi.library.LibrarySection.search()` for details about filtering / sorting - and :func:`plexapi.myplex.MyPlexAccount.sync()` for possible exceptions. + See description of :func:`~plexapi.library.LibrarySection.search` for details about filtering / sorting + and :func:`~plexapi.myplex.MyPlexAccount.sync` for possible exceptions. Parameters: - policy (:class:`plexapi.sync.Policy`): policy of syncing the media (how many items to sync and process + policy (:class:`~plexapi.sync.Policy`): policy of syncing the media (how many items to sync and process watched media or not), generated automatically when method called on specific LibrarySection object. - mediaSettings (:class:`plexapi.sync.MediaSettings`): Transcoding settings used for the media, generated + mediaSettings (:class:`~plexapi.sync.MediaSettings`): Transcoding settings used for the media, generated automatically when method called on specific LibrarySection object. - client (:class:`plexapi.myplex.MyPlexDevice`): sync destination, see - :func:`plexapi.myplex.MyPlexAccount.sync`. - clientId (str): sync destination, see :func:`plexapi.myplex.MyPlexAccount.sync`. - title (str): descriptive title for the new :class:`plexapi.sync.SyncItem`, if empty the value would be + client (:class:`~plexapi.myplex.MyPlexDevice`): sync destination, see + :func:`~plexapi.myplex.MyPlexAccount.sync`. + clientId (str): sync destination, see :func:`~plexapi.myplex.MyPlexAccount.sync`. + title (str): descriptive title for the new :class:`~plexapi.sync.SyncItem`, if empty the value would be generated from metadata of current media. sort (str): formatted as `column:dir`; column can be any of {`addedAt`, `originallyAvailableAt`, `lastViewedAt`, `titleSort`, `rating`, `mediaHeight`, `duration`}. dir can be `asc` or @@ -777,10 +777,10 @@ def sync(self, policy, mediaSettings, client=None, clientId=None, title=None, so `track`). Returns: - :class:`plexapi.sync.SyncItem`: an instance of created syncItem. + :class:`~plexapi.sync.SyncItem`: an instance of created syncItem. Raises: - :class:`plexapi.exceptions.BadRequest`: when the library is not allowed to sync + :class:`~plexapi.exceptions.BadRequest`: when the library is not allowed to sync Example: @@ -855,17 +855,17 @@ def collection(self, **kwargs): def sync(self, videoQuality, limit=None, unwatched=False, **kwargs): """ Add current Movie library section as sync item for specified device. - See description of :func:`plexapi.library.LibrarySection.search()` for details about filtering / sorting and - :func:`plexapi.library.LibrarySection.sync()` for details on syncing libraries and possible exceptions. + See description of :func:`~plexapi.library.LibrarySection.search` for details about filtering / sorting and + :func:`~plexapi.library.LibrarySection.sync` for details on syncing libraries and possible exceptions. Parameters: videoQuality (int): idx of quality of the video, one of VIDEO_QUALITY_* values defined in - :mod:`plexapi.sync` module. + :mod:`~plexapi.sync` module. limit (int): maximum count of movies to sync, unlimited if `None`. unwatched (bool): if `True` watched videos wouldn't be synced. Returns: - :class:`plexapi.sync.SyncItem`: an instance of created syncItem. + :class:`~plexapi.sync.SyncItem`: an instance of created syncItem. Example: @@ -903,11 +903,11 @@ class ShowSection(LibrarySection): CONTENT_TYPE = 'video' def searchShows(self, **kwargs): - """ Search for a show. See :func:`~plexapi.library.LibrarySection.search()` for usage. """ + """ Search for a show. See :func:`~plexapi.library.LibrarySection.search` for usage. """ return self.search(libtype='show', **kwargs) def searchEpisodes(self, **kwargs): - """ Search for an episode. See :func:`~plexapi.library.LibrarySection.search()` for usage. """ + """ Search for an episode. See :func:`~plexapi.library.LibrarySection.search` for usage. """ return self.search(libtype='episode', **kwargs) def recentlyAdded(self, libtype='episode', maxresults=50): @@ -924,17 +924,17 @@ def collection(self, **kwargs): def sync(self, videoQuality, limit=None, unwatched=False, **kwargs): """ Add current Show library section as sync item for specified device. - See description of :func:`plexapi.library.LibrarySection.search()` for details about filtering / sorting and - :func:`plexapi.library.LibrarySection.sync()` for details on syncing libraries and possible exceptions. + See description of :func:`~plexapi.library.LibrarySection.search` for details about filtering / sorting and + :func:`~plexapi.library.LibrarySection.sync` for details on syncing libraries and possible exceptions. Parameters: videoQuality (int): idx of quality of the video, one of VIDEO_QUALITY_* values defined in - :mod:`plexapi.sync` module. + :mod:`~plexapi.sync` module. limit (int): maximum count of episodes to sync, unlimited if `None`. unwatched (bool): if `True` watched videos wouldn't be synced. Returns: - :class:`plexapi.sync.SyncItem`: an instance of created syncItem. + :class:`~plexapi.sync.SyncItem`: an instance of created syncItem. Example: @@ -982,15 +982,15 @@ def stations(self): return self.fetchItems(key, cls=Station) def searchArtists(self, **kwargs): - """ Search for an artist. See :func:`~plexapi.library.LibrarySection.search()` for usage. """ + """ Search for an artist. See :func:`~plexapi.library.LibrarySection.search` for usage. """ return self.search(libtype='artist', **kwargs) def searchAlbums(self, **kwargs): - """ Search for an album. See :func:`~plexapi.library.LibrarySection.search()` for usage. """ + """ Search for an album. See :func:`~plexapi.library.LibrarySection.search` for usage. """ return self.search(libtype='album', **kwargs) def searchTracks(self, **kwargs): - """ Search for a track. See :func:`~plexapi.library.LibrarySection.search()` for usage. """ + """ Search for a track. See :func:`~plexapi.library.LibrarySection.search` for usage. """ return self.search(libtype='track', **kwargs) def collection(self, **kwargs): @@ -999,16 +999,16 @@ def collection(self, **kwargs): def sync(self, bitrate, limit=None, **kwargs): """ Add current Music library section as sync item for specified device. - See description of :func:`plexapi.library.LibrarySection.search()` for details about filtering / sorting and - :func:`plexapi.library.LibrarySection.sync()` for details on syncing libraries and possible exceptions. + See description of :func:`~plexapi.library.LibrarySection.search` for details about filtering / sorting and + :func:`~plexapi.library.LibrarySection.sync` for details on syncing libraries and possible exceptions. Parameters: bitrate (int): maximum bitrate for synchronized music, better use one of MUSIC_BITRATE_* values from the - module :mod:`plexapi.sync`. + module :mod:`~plexapi.sync`. limit (int): maximum count of tracks to sync, unlimited if `None`. Returns: - :class:`plexapi.sync.SyncItem`: an instance of created syncItem. + :class:`~plexapi.sync.SyncItem`: an instance of created syncItem. Example: @@ -1045,25 +1045,25 @@ class PhotoSection(LibrarySection): METADATA_TYPE = 'photo' def searchAlbums(self, title, **kwargs): - """ Search for an album. See :func:`~plexapi.library.LibrarySection.search()` for usage. """ + """ Search for an album. See :func:`~plexapi.library.LibrarySection.search` for usage. """ return self.search(libtype='photoalbum', title=title, **kwargs) def searchPhotos(self, title, **kwargs): - """ Search for a photo. See :func:`~plexapi.library.LibrarySection.search()` for usage. """ + """ Search for a photo. See :func:`~plexapi.library.LibrarySection.search` for usage. """ return self.search(libtype='photo', title=title, **kwargs) def sync(self, resolution, limit=None, **kwargs): """ Add current Music library section as sync item for specified device. - See description of :func:`plexapi.library.LibrarySection.search()` for details about filtering / sorting and - :func:`plexapi.library.LibrarySection.sync()` for details on syncing libraries and possible exceptions. + See description of :func:`~plexapi.library.LibrarySection.search` for details about filtering / sorting and + :func:`~plexapi.library.LibrarySection.sync` for details on syncing libraries and possible exceptions. Parameters: resolution (str): maximum allowed resolution for synchronized photos, see PHOTO_QUALITY_* values in the - module :mod:`plexapi.sync`. + module :mod:`~plexapi.sync`. limit (int): maximum count of tracks to sync, unlimited if `None`. Returns: - :class:`plexapi.sync.SyncItem`: an instance of created syncItem. + :class:`~plexapi.sync.SyncItem`: an instance of created syncItem. Example: @@ -1090,7 +1090,7 @@ def sync(self, resolution, limit=None, **kwargs): class FilterChoice(PlexObject): """ Represents a single filter choice. These objects are gathered when using filters while searching for library items and is the object returned in the result set of - :func:`~plexapi.library.LibrarySection.listChoices()`. + :func:`~plexapi.library.LibrarySection.listChoices`. Attributes: TAG (str): 'Directory' @@ -1330,7 +1330,7 @@ def _loadData(self, data): self.title = data.attrib.get('title') def subfolders(self): - """ Returns a list of available `:class:`~plexapi.library.Folder` for this folder. + """ Returns a list of available :class:`~plexapi.library.Folder` for this folder. Continue down subfolders until a mediaType is found. """ if self.key.startswith('/library/metadata'): @@ -1339,8 +1339,8 @@ def subfolders(self): return self.fetchItems(self.key, Folder) def allSubfolders(self): - """ Returns a list of all available `:class:`~plexapi.library.Folder` for this folder. - Only returns `:class:`~plexapi.library.Folder`. + """ Returns a list of all available :class:`~plexapi.library.Folder` for this folder. + Only returns :class:`~plexapi.library.Folder`. """ folders = [] for folder in self.subfolders(): diff --git a/plexapi/media.py b/plexapi/media.py index 7a106232e..5ac90a5c2 100644 --- a/plexapi/media.py +++ b/plexapi/media.py @@ -477,7 +477,7 @@ class MediaTag(PlexObject): tag (str): Name of the tag. This will be Animation, SciFi etc for Genres. The name of person for Directors and Roles (ex: Animation, Stephen Graham, etc). : Attributes only applicable in search results from - PlexServer :func:`~plexapi.server.PlexServer.search()`. They provide details of which + PlexServer :func:`~plexapi.server.PlexServer.search`. They provide details of which library section the tag was found as well as the url to dig deeper into the results. * key (str): API URL to dig deeper into this tag (ex: /library/sections/1/all?actor=9081). @@ -504,7 +504,7 @@ def _loadData(self, data): def items(self, *args, **kwargs): """ Return the list of items within this tag. This function is only applicable - in search results from PlexServer :func:`~plexapi.server.PlexServer.search()`. + in search results from PlexServer :func:`~plexapi.server.PlexServer.search`. """ if not self.key: raise BadRequest('Key is not defined for this tag: %s' % self.tag) diff --git a/plexapi/myplex.py b/plexapi/myplex.py index 8806fdb55..973227c1e 100644 --- a/plexapi/myplex.py +++ b/plexapi/myplex.py @@ -544,7 +544,7 @@ def optOut(self, playback=None, library=None): return self.query(url, method=self._session.put, data=params) def syncItems(self, client=None, clientId=None): - """ Returns an instance of :class:`plexapi.sync.SyncList` for specified client. + """ Returns an instance of :class:`~plexapi.sync.SyncList` for specified client. Parameters: client (:class:`~plexapi.myplex.MyPlexDevice`): a client to query SyncItems for. @@ -564,22 +564,22 @@ def syncItems(self, client=None, clientId=None): def sync(self, sync_item, client=None, clientId=None): """ Adds specified sync item for the client. It's always easier to use methods defined directly in the media - objects, e.g. :func:`plexapi.video.Video.sync`, :func:`plexapi.audio.Audio.sync`. + objects, e.g. :func:`~plexapi.video.Video.sync`, :func:`~plexapi.audio.Audio.sync`. Parameters: client (:class:`~plexapi.myplex.MyPlexDevice`): a client for which you need to add SyncItem to. clientId (str): an identifier of a client for which you need to add SyncItem to. - sync_item (:class:`plexapi.sync.SyncItem`): prepared SyncItem object with all fields set. + sync_item (:class:`~plexapi.sync.SyncItem`): prepared SyncItem object with all fields set. If both `client` and `clientId` provided the client would be preferred. If neither `client` nor `clientId` provided the clientId would be set to current clients`s identifier. Returns: - :class:`plexapi.sync.SyncItem`: an instance of created syncItem. + :class:`~plexapi.sync.SyncItem`: an instance of created syncItem. Raises: - :class:`plexapi.exceptions.BadRequest`: when client with provided clientId wasn`t found. - :class:`plexapi.exceptions.BadRequest`: provided client doesn`t provides `sync-target`. + :class:`~plexapi.exceptions.BadRequest`: when client with provided clientId wasn`t found. + :class:`~plexapi.exceptions.BadRequest`: provided client doesn`t provides `sync-target`. """ if not client and not clientId: clientId = X_PLEX_IDENTIFIER @@ -686,7 +686,7 @@ def tidal(self): class MyPlexUser(PlexObject): """ This object represents non-signed in users such as friends and linked - accounts. NOTE: This should not be confused with the :class:`~myplex.MyPlexAccount` + accounts. NOTE: This should not be confused with the :class:`~plexapi.myplex.MyPlexAccount` which is your specific account. The raw xml for the data presented here can be found at: https://plex.tv/api/users/ @@ -885,7 +885,7 @@ class MyPlexResource(PlexObject): key (str): 'https://plex.tv/api/resources?includeHttps=1&includeRelay=1' accessToken (str): This resources accesstoken. clientIdentifier (str): Unique ID for this resource. - connections (list): List of :class:`~myplex.ResourceConnection` objects + connections (list): List of :class:`~plexapi.myplex.ResourceConnection` objects for this resource. createdAt (datetime): Timestamp this resource first connected to your server. device (str): Best guess on the type of device this is (PS, iPhone, Linux, etc). @@ -930,7 +930,7 @@ def _loadData(self, data): self.sourceTitle = data.attrib.get('sourceTitle') # owners plex username. def connect(self, ssl=None, timeout=None): - """ Returns a new :class:`~server.PlexServer` or :class:`~client.PlexClient` object. + """ Returns a new :class:`~plexapi.server.PlexServer` or :class:`~plexapi.client.PlexClient` object. Often times there is more than one address specified for a server or client. This function will prioritize local connections before remote and HTTPS before HTTP. After trying to connect to all available addresses for this resource and @@ -942,7 +942,7 @@ def connect(self, ssl=None, timeout=None): HTTP or HTTPS connection. Raises: - :class:`plexapi.exceptions.NotFound`: When unable to connect to any addresses for this resource. + :class:`~plexapi.exceptions.NotFound`: When unable to connect to any addresses for this resource. """ # Sort connections from (https, local) to (http, remote) # Only check non-local connections unless we own the resource @@ -965,7 +965,7 @@ def connect(self, ssl=None, timeout=None): class ResourceConnection(PlexObject): """ Represents a Resource Connection object found within the - :class:`~myplex.MyPlexResource` objects. + :class:`~plexapi.myplex.MyPlexResource` objects. Attributes: TAG (str): 'Connection' @@ -1049,7 +1049,7 @@ def connect(self, timeout=None): at least one connection was successful, the PlexClient object is built and returned. Raises: - :class:`plexapi.exceptions.NotFound`: When unable to connect to any addresses for this device. + :class:`~plexapi.exceptions.NotFound`: When unable to connect to any addresses for this device. """ cls = PlexServer if 'server' in self.provides else PlexClient listargs = [[cls, url, self.token, timeout] for url in self.connections] @@ -1063,10 +1063,10 @@ def delete(self): self._server.query(key, self._server._session.delete) def syncItems(self): - """ Returns an instance of :class:`plexapi.sync.SyncList` for current device. + """ Returns an instance of :class:`~plexapi.sync.SyncList` for current device. Raises: - :class:`plexapi.exceptions.BadRequest`: when the device doesn`t provides `sync-target`. + :class:`~plexapi.exceptions.BadRequest`: when the device doesn`t provides `sync-target`. """ if 'sync-target' not in self.provides: raise BadRequest('Requested syncList for device which do not provides sync-target') @@ -1082,12 +1082,12 @@ class MyPlexPinLogin(object): This helper class supports a polling, threaded and callback approach. - The polling approach expects the developer to periodically check if the PIN login was - successful using :func:`plexapi.myplex.MyPlexPinLogin.checkLogin`. + successful using :func:`~plexapi.myplex.MyPlexPinLogin.checkLogin`. - The threaded approach expects the developer to call - :func:`plexapi.myplex.MyPlexPinLogin.run` and then at a later time call - :func:`plexapi.myplex.MyPlexPinLogin.waitForLogin` to wait for and check the result. + :func:`~plexapi.myplex.MyPlexPinLogin.run` and then at a later time call + :func:`~plexapi.myplex.MyPlexPinLogin.waitForLogin` to wait for and check the result. - The callback approach is an extension of the threaded approach and expects the developer - to pass the `callback` parameter to the call to :func:`plexapi.myplex.MyPlexPinLogin.run`. + to pass the `callback` parameter to the call to :func:`~plexapi.myplex.MyPlexPinLogin.run`. The callback will be called when the thread waiting for the PIN login to succeed either finishes or expires. The parameter passed to the callback is the received authentication token or `None` if the login expired. diff --git a/plexapi/photo.py b/plexapi/photo.py index 019b8b26f..301ec319f 100644 --- a/plexapi/photo.py +++ b/plexapi/photo.py @@ -184,20 +184,20 @@ def iterParts(self): def sync(self, resolution, client=None, clientId=None, limit=None, title=None): """ Add current photo as sync item for specified device. - See :func:`plexapi.myplex.MyPlexAccount.sync()` for possible exceptions. + See :func:`~plexapi.myplex.MyPlexAccount.sync` for possible exceptions. Parameters: resolution (str): maximum allowed resolution for synchronized photos, see PHOTO_QUALITY_* values in the - module :mod:`plexapi.sync`. - client (:class:`plexapi.myplex.MyPlexDevice`): sync destination, see - :func:`plexapi.myplex.MyPlexAccount.sync`. - clientId (str): sync destination, see :func:`plexapi.myplex.MyPlexAccount.sync`. + module :mod:`~plexapi.sync`. + client (:class:`~plexapi.myplex.MyPlexDevice`): sync destination, see + :func:`~plexapi.myplex.MyPlexAccount.sync`. + clientId (str): sync destination, see :func:`~plexapi.myplex.MyPlexAccount.sync`. limit (int): maximum count of items to sync, unlimited if `None`. - title (str): descriptive title for the new :class:`plexapi.sync.SyncItem`, if empty the value would be + title (str): descriptive title for the new :class:`~plexapi.sync.SyncItem`, if empty the value would be generated from metadata of current photo. Returns: - :class:`plexapi.sync.SyncItem`: an instance of created syncItem. + :class:`~plexapi.sync.SyncItem`: an instance of created syncItem. """ from plexapi.sync import SyncItem, Policy, MediaSettings diff --git a/plexapi/playlist.py b/plexapi/playlist.py index 04912bbe8..768f79a3d 100644 --- a/plexapi/playlist.py +++ b/plexapi/playlist.py @@ -168,7 +168,7 @@ def create(cls, server, title, items=None, section=None, limit=None, smart=False **kwargs (dict): is passed to the filters. For a example see the search method. Returns: - :class:`plexapi.playlist.Playlist`: an instance of created Playlist. + :class:`~plexapi.playlist.Playlist`: an instance of created Playlist. """ if smart: return cls._createSmart(server, title, section, limit, **kwargs) @@ -222,29 +222,29 @@ def copyToUser(self, user): def sync(self, videoQuality=None, photoResolution=None, audioBitrate=None, client=None, clientId=None, limit=None, unwatched=False, title=None): """ Add current playlist as sync item for specified device. - See :func:`plexapi.myplex.MyPlexAccount.sync()` for possible exceptions. + See :func:`~plexapi.myplex.MyPlexAccount.sync` for possible exceptions. Parameters: videoQuality (int): idx of quality of the video, one of VIDEO_QUALITY_* values defined in - :mod:`plexapi.sync` module. Used only when playlist contains video. + :mod:`~plexapi.sync` module. Used only when playlist contains video. photoResolution (str): maximum allowed resolution for synchronized photos, see PHOTO_QUALITY_* values in - the module :mod:`plexapi.sync`. Used only when playlist contains photos. + the module :mod:`~plexapi.sync`. Used only when playlist contains photos. audioBitrate (int): maximum bitrate for synchronized music, better use one of MUSIC_BITRATE_* values - from the module :mod:`plexapi.sync`. Used only when playlist contains audio. - client (:class:`plexapi.myplex.MyPlexDevice`): sync destination, see - :func:`plexapi.myplex.MyPlexAccount.sync`. - clientId (str): sync destination, see :func:`plexapi.myplex.MyPlexAccount.sync`. + from the module :mod:`~plexapi.sync`. Used only when playlist contains audio. + client (:class:`~plexapi.myplex.MyPlexDevice`): sync destination, see + :func:`~plexapi.myplex.MyPlexAccount.sync`. + clientId (str): sync destination, see :func:`~plexapi.myplex.MyPlexAccount.sync`. limit (int): maximum count of items to sync, unlimited if `None`. unwatched (bool): if `True` watched videos wouldn't be synced. - title (str): descriptive title for the new :class:`plexapi.sync.SyncItem`, if empty the value would be + title (str): descriptive title for the new :class:`~plexapi.sync.SyncItem`, if empty the value would be generated from metadata of current photo. Raises: - :class:`plexapi.exceptions.BadRequest`: when playlist is not allowed to sync. - :class:`plexapi.exceptions.Unsupported`: when playlist content is unsupported. + :class:`~plexapi.exceptions.BadRequest`: when playlist is not allowed to sync. + :class:`~plexapi.exceptions.Unsupported`: when playlist content is unsupported. Returns: - :class:`plexapi.sync.SyncItem`: an instance of created syncItem. + :class:`~plexapi.sync.SyncItem`: an instance of created syncItem. """ if not self.allowSync: diff --git a/plexapi/server.py b/plexapi/server.py index a55582bfc..8d69b3a3b 100644 --- a/plexapi/server.py +++ b/plexapi/server.py @@ -199,7 +199,7 @@ def activities(self): return activities def agents(self, mediaType=None): - """ Returns the `:class:`~plexapi.media.Agent` objects this server has available. """ + """ Returns the :class:`~plexapi.media.Agent` objects this server has available. """ key = '/system/agents' if mediaType: key += '?mediaType=%s' % mediaType @@ -270,7 +270,7 @@ def client(self, name): name (str): Name of the client to return. Raises: - :class:`plexapi.exceptions.NotFound`: Unknown client name + :class:`~plexapi.exceptions.NotFound`: Unknown client name """ for client in self.clients(): if client and client.title == name: @@ -393,7 +393,7 @@ def playlist(self, title): title (str): Title of the playlist to return. Raises: - :class:`plexapi.exceptions.NotFound`: Invalid playlist title + :class:`~plexapi.exceptions.NotFound`: Invalid playlist title """ return self.fetchItem('/playlists', title=title) @@ -495,7 +495,7 @@ def startAlertListener(self, callback=None): callback (func): Callback function to call on recieved messages. raises: - :class:`plexapi.exception.Unsupported`: Websocket-client not installed. + :class:`~plexapi.exception.Unsupported`: Websocket-client not installed. """ notifier = AlertListener(self, callback) notifier.start() diff --git a/plexapi/sync.py b/plexapi/sync.py index 0f739860c..aa612ec0e 100644 --- a/plexapi/sync.py +++ b/plexapi/sync.py @@ -78,7 +78,7 @@ def _loadData(self, data): self.location = data.find('Location').attrib.get('uri', '') def server(self): - """ Returns :class:`plexapi.myplex.MyPlexResource` with server of current item. """ + """ Returns :class:`~plexapi.myplex.MyPlexResource` with server of current item. """ server = [s for s in self._server.resources() if s.clientIdentifier == self.machineIdentifier] if len(server) == 0: raise NotFound('Unable to find server with uuid %s' % self.machineIdentifier) @@ -201,7 +201,7 @@ def createVideo(videoQuality): videoQuality (int): idx of quality of the video, one of VIDEO_QUALITY_* values defined in this module. Raises: - :class:`plexapi.exceptions.BadRequest`: when provided unknown video quality. + :class:`~plexapi.exceptions.BadRequest`: when provided unknown video quality. """ if videoQuality == VIDEO_QUALITY_ORIGINAL: return MediaSettings('', '', '') @@ -231,7 +231,7 @@ def createPhoto(resolution): module. Raises: - :class:`plexapi.exceptions.BadRequest` when provided unknown video quality. + :class:`~plexapi.exceptions.BadRequest` when provided unknown video quality. """ if resolution in PHOTO_QUALITIES: return MediaSettings(photoQuality=PHOTO_QUALITIES[resolution], photoResolution=resolution) diff --git a/plexapi/utils.py b/plexapi/utils.py index 24a9c7dde..b056848df 100644 --- a/plexapi/utils.py +++ b/plexapi/utils.py @@ -146,7 +146,7 @@ def searchType(libtype): libtype (str): LibType to lookup (movie, show, season, episode, artist, album, track, collection) Raises: - :class:`plexapi.exceptions.NotFound`: Unknown libtype + :class:`~plexapi.exceptions.NotFound`: Unknown libtype """ libtype = str(libtype) if libtype in [str(v) for v in SEARCHTYPES.values()]: diff --git a/plexapi/video.py b/plexapi/video.py index 5f0cd8e4f..7c0abbc96 100644 --- a/plexapi/video.py +++ b/plexapi/video.py @@ -216,21 +216,21 @@ def optimize(self, title=None, target="", targetTagID=None, locationID=-1, polic def sync(self, videoQuality, client=None, clientId=None, limit=None, unwatched=False, title=None): """ Add current video (movie, tv-show, season or episode) as sync item for specified device. - See :func:`plexapi.myplex.MyPlexAccount.sync()` for possible exceptions. + See :func:`~plexapi.myplex.MyPlexAccount.sync` for possible exceptions. Parameters: videoQuality (int): idx of quality of the video, one of VIDEO_QUALITY_* values defined in - :mod:`plexapi.sync` module. - client (:class:`plexapi.myplex.MyPlexDevice`): sync destination, see - :func:`plexapi.myplex.MyPlexAccount.sync`. - clientId (str): sync destination, see :func:`plexapi.myplex.MyPlexAccount.sync`. + :mod:`~plexapi.sync` module. + client (:class:`~plexapi.myplex.MyPlexDevice`): sync destination, see + :func:`~plexapi.myplex.MyPlexAccount.sync`. + clientId (str): sync destination, see :func:`~plexapi.myplex.MyPlexAccount.sync`. limit (int): maximum count of items to sync, unlimited if `None`. unwatched (bool): if `True` watched videos wouldn't be synced. - title (str): descriptive title for the new :class:`plexapi.sync.SyncItem`, if empty the value would be + title (str): descriptive title for the new :class:`~plexapi.sync.SyncItem`, if empty the value would be generated from metadata of current media. Returns: - :class:`plexapi.sync.SyncItem`: an instance of created syncItem. + :class:`~plexapi.sync.SyncItem`: an instance of created syncItem. """ from plexapi.sync import SyncItem, Policy, MediaSettings @@ -351,7 +351,7 @@ def download(self, savepath=None, keep_original_name=False, **kwargs): savepath (str): Defaults to current working dir. keep_original_name (bool): True to keep the original file name otherwise a friendlier is generated. - **kwargs: Additional options passed into :func:`~plexapi.base.PlexObject.getStreamURL()`. + **kwargs: Additional options passed into :func:`~plexapi.base.PlexObject.getStreamURL`. """ filepaths = [] locations = [i for i in self.iterParts() if i] @@ -521,8 +521,8 @@ def episode(self, title=None, season=None, episode=None): episode (int): Episode number (default:None; required if title not specified). Raises: - :class:`plexapi.exceptions.BadRequest`: If season and episode is missing. - :class:`plexapi.exceptions.NotFound`: If the episode is missing. + :class:`~plexapi.exceptions.BadRequest`: If season and episode is missing. + :class:`~plexapi.exceptions.NotFound`: If the episode is missing. """ if title: key = '/library/metadata/%s/allLeaves' % self.ratingKey @@ -543,7 +543,7 @@ def unwatched(self): return self.episodes(viewCount=0) def get(self, title=None, season=None, episode=None): - """ Alias to :func:`~plexapi.video.Show.episode()`. """ + """ Alias to :func:`~plexapi.video.Show.episode`. """ return self.episode(title, season, episode) def download(self, savepath=None, keep_original_name=False, **kwargs): @@ -553,7 +553,7 @@ def download(self, savepath=None, keep_original_name=False, **kwargs): savepath (str): Defaults to current working dir. keep_original_name (bool): True to keep the original file name otherwise a friendlier is generated. - **kwargs: Additional options passed into :func:`~plexapi.base.PlexObject.getStreamURL()`. + **kwargs: Additional options passed into :func:`~plexapi.base.PlexObject.getStreamURL`. """ filepaths = [] for episode in self.episodes(): @@ -632,7 +632,7 @@ def episode(self, title=None, episode=None): return self.fetchItem(key, parentIndex=self.index, index=episode) def get(self, title=None, episode=None): - """ Alias to :func:`~plexapi.video.Season.episode()`. """ + """ Alias to :func:`~plexapi.video.Season.episode`. """ return self.episode(title, episode) def show(self): @@ -654,7 +654,7 @@ def download(self, savepath=None, keep_original_name=False, **kwargs): savepath (str): Defaults to current working dir. keep_original_name (bool): True to keep the original file name otherwise a friendlier is generated. - **kwargs: Additional options passed into :func:`~plexapi.base.PlexObject.getStreamURL()`. + **kwargs: Additional options passed into :func:`~plexapi.base.PlexObject.getStreamURL`. """ filepaths = [] for episode in self.episodes(): From 274b05e913800f703b7b801595939f7767e2bf17 Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sun, 22 Nov 2020 20:43:59 -0800 Subject: [PATCH 12/12] Fix raise exception docs --- plexapi/base.py | 2 +- plexapi/client.py | 8 ++++---- plexapi/library.py | 6 +++--- plexapi/myplex.py | 10 +++++----- plexapi/playlist.py | 4 ++-- plexapi/server.py | 8 ++++---- plexapi/sync.py | 4 ++-- plexapi/utils.py | 2 +- plexapi/video.py | 8 ++++---- 9 files changed, 26 insertions(+), 26 deletions(-) diff --git a/plexapi/base.py b/plexapi/base.py index 00260ab71..c9853baf6 100644 --- a/plexapi/base.py +++ b/plexapi/base.py @@ -696,7 +696,7 @@ def getStreamURL(self, **params): offset, copyts, protocol, mediaIndex, platform. Raises: - :class:`~plexapi.exceptions.Unsupported`: When the item doesn't support fetching a stream URL. + :exc:`plexapi.exceptions.Unsupported`: When the item doesn't support fetching a stream URL. """ if self.TYPE not in ('movie', 'episode', 'track'): raise Unsupported('Fetching stream URL for %s is unsupported.' % self.TYPE) diff --git a/plexapi/client.py b/plexapi/client.py index 234a76b8b..83ed6e59a 100644 --- a/plexapi/client.py +++ b/plexapi/client.py @@ -140,7 +140,7 @@ def proxyThroughServer(self, value=True, server=None): value (bool): Enable or disable proxying (optional, default True). Raises: - :class:`~plexapi.exceptions.Unsupported`: Cannot use client proxy with unknown server. + :exc:`plexapi.exceptions.Unsupported`: Cannot use client proxy with unknown server. """ if server: self._server = server @@ -183,7 +183,7 @@ def sendCommand(self, command, proxy=None, **params): **params (dict): Additional GET parameters to include with the command. Raises: - :class:`~plexapi.exceptions.Unsupported`: When we detect the client doesn't support this capability. + :exc:`plexapi.exceptions.Unsupported`: When we detect the client doesn't support this capability. """ command = command.strip('/') controller = command.split('/')[0] @@ -299,7 +299,7 @@ def goToMedia(self, media, **params): **params (dict): Additional GET parameters to include with the command. Raises: - :class:`~plexapi.exceptions.Unsupported`: When no PlexServer specified in this object. + :exc:`plexapi.exceptions.Unsupported`: When no PlexServer specified in this object. """ if not self._server: raise Unsupported('A server must be specified before using this command.') @@ -469,7 +469,7 @@ def playMedia(self, media, offset=0, **params): also: https://github.com/plexinc/plex-media-player/wiki/Remote-control-API#modified-commands Raises: - :class:`~plexapi.exceptions.Unsupported`: When no PlexServer specified in this object. + :exc:`plexapi.exceptions.Unsupported`: When no PlexServer specified in this object. """ if not self._server: raise Unsupported('A server must be specified before using this command.') diff --git a/plexapi/library.py b/plexapi/library.py index b38689890..b48fb2f5e 100644 --- a/plexapi/library.py +++ b/plexapi/library.py @@ -614,7 +614,7 @@ def listChoices(self, category, libtype=None, **kwargs): **kwargs (dict): Additional kwargs to narrow down the choices. Raises: - :class:`~plexapi.exceptions.BadRequest`: Cannot include kwarg equal to specified category. + :exc:`plexapi.exceptions.BadRequest`: Cannot include kwarg equal to specified category. """ # TODO: Should this be moved to base? if category in kwargs: @@ -661,7 +661,7 @@ def search(self, title=None, sort=None, maxresults=None, * year: List of years to search within ([yyyy, ...]). [all] Raises: - :class:`~plexapi.exceptions.BadRequest`: when applying unknown filter + :exc:`plexapi.exceptions.BadRequest`: when applying unknown filter """ # cleanup the core arguments args = {} @@ -780,7 +780,7 @@ def sync(self, policy, mediaSettings, client=None, clientId=None, title=None, so :class:`~plexapi.sync.SyncItem`: an instance of created syncItem. Raises: - :class:`~plexapi.exceptions.BadRequest`: when the library is not allowed to sync + :exc:`plexapi.exceptions.BadRequest`: when the library is not allowed to sync Example: diff --git a/plexapi/myplex.py b/plexapi/myplex.py index 973227c1e..07df639e2 100644 --- a/plexapi/myplex.py +++ b/plexapi/myplex.py @@ -578,8 +578,8 @@ def sync(self, sync_item, client=None, clientId=None): :class:`~plexapi.sync.SyncItem`: an instance of created syncItem. Raises: - :class:`~plexapi.exceptions.BadRequest`: when client with provided clientId wasn`t found. - :class:`~plexapi.exceptions.BadRequest`: provided client doesn`t provides `sync-target`. + :exc:`plexapi.exceptions.BadRequest`: when client with provided clientId wasn`t found. + :exc:`plexapi.exceptions.BadRequest`: provided client doesn`t provides `sync-target`. """ if not client and not clientId: clientId = X_PLEX_IDENTIFIER @@ -942,7 +942,7 @@ def connect(self, ssl=None, timeout=None): HTTP or HTTPS connection. Raises: - :class:`~plexapi.exceptions.NotFound`: When unable to connect to any addresses for this resource. + :exc:`plexapi.exceptions.NotFound`: When unable to connect to any addresses for this resource. """ # Sort connections from (https, local) to (http, remote) # Only check non-local connections unless we own the resource @@ -1049,7 +1049,7 @@ def connect(self, timeout=None): at least one connection was successful, the PlexClient object is built and returned. Raises: - :class:`~plexapi.exceptions.NotFound`: When unable to connect to any addresses for this device. + :exc:`plexapi.exceptions.NotFound`: When unable to connect to any addresses for this device. """ cls = PlexServer if 'server' in self.provides else PlexClient listargs = [[cls, url, self.token, timeout] for url in self.connections] @@ -1066,7 +1066,7 @@ def syncItems(self): """ Returns an instance of :class:`~plexapi.sync.SyncList` for current device. Raises: - :class:`~plexapi.exceptions.BadRequest`: when the device doesn`t provides `sync-target`. + :exc:`plexapi.exceptions.BadRequest`: when the device doesn`t provides `sync-target`. """ if 'sync-target' not in self.provides: raise BadRequest('Requested syncList for device which do not provides sync-target') diff --git a/plexapi/playlist.py b/plexapi/playlist.py index 768f79a3d..fd865af70 100644 --- a/plexapi/playlist.py +++ b/plexapi/playlist.py @@ -240,8 +240,8 @@ def sync(self, videoQuality=None, photoResolution=None, audioBitrate=None, clien generated from metadata of current photo. Raises: - :class:`~plexapi.exceptions.BadRequest`: when playlist is not allowed to sync. - :class:`~plexapi.exceptions.Unsupported`: when playlist content is unsupported. + :exc:`plexapi.exceptions.BadRequest`: when playlist is not allowed to sync. + :exc:`plexapi.exceptions.Unsupported`: when playlist content is unsupported. Returns: :class:`~plexapi.sync.SyncItem`: an instance of created syncItem. diff --git a/plexapi/server.py b/plexapi/server.py index 8d69b3a3b..a9439bda6 100644 --- a/plexapi/server.py +++ b/plexapi/server.py @@ -270,7 +270,7 @@ def client(self, name): name (str): Name of the client to return. Raises: - :class:`~plexapi.exceptions.NotFound`: Unknown client name + :exc:`plexapi.exceptions.NotFound`: Unknown client name """ for client in self.clients(): if client and client.title == name: @@ -393,7 +393,7 @@ def playlist(self, title): title (str): Title of the playlist to return. Raises: - :class:`~plexapi.exceptions.NotFound`: Invalid playlist title + :exc:`plexapi.exceptions.NotFound`: Invalid playlist title """ return self.fetchItem('/playlists', title=title) @@ -494,8 +494,8 @@ def startAlertListener(self, callback=None): Parameters: callback (func): Callback function to call on recieved messages. - raises: - :class:`~plexapi.exception.Unsupported`: Websocket-client not installed. + Raises: + :exc:`plexapi.exception.Unsupported`: Websocket-client not installed. """ notifier = AlertListener(self, callback) notifier.start() diff --git a/plexapi/sync.py b/plexapi/sync.py index aa612ec0e..f88fefb23 100644 --- a/plexapi/sync.py +++ b/plexapi/sync.py @@ -201,7 +201,7 @@ def createVideo(videoQuality): videoQuality (int): idx of quality of the video, one of VIDEO_QUALITY_* values defined in this module. Raises: - :class:`~plexapi.exceptions.BadRequest`: when provided unknown video quality. + :exc:`plexapi.exceptions.BadRequest`: when provided unknown video quality. """ if videoQuality == VIDEO_QUALITY_ORIGINAL: return MediaSettings('', '', '') @@ -231,7 +231,7 @@ def createPhoto(resolution): module. Raises: - :class:`~plexapi.exceptions.BadRequest` when provided unknown video quality. + :exc:`plexapi.exceptions.BadRequest` when provided unknown video quality. """ if resolution in PHOTO_QUALITIES: return MediaSettings(photoQuality=PHOTO_QUALITIES[resolution], photoResolution=resolution) diff --git a/plexapi/utils.py b/plexapi/utils.py index b056848df..6512b7f31 100644 --- a/plexapi/utils.py +++ b/plexapi/utils.py @@ -146,7 +146,7 @@ def searchType(libtype): libtype (str): LibType to lookup (movie, show, season, episode, artist, album, track, collection) Raises: - :class:`~plexapi.exceptions.NotFound`: Unknown libtype + :exc:`plexapi.exceptions.NotFound`: Unknown libtype """ libtype = str(libtype) if libtype in [str(v) for v in SEARCHTYPES.values()]: diff --git a/plexapi/video.py b/plexapi/video.py index 7c0abbc96..65c55804f 100644 --- a/plexapi/video.py +++ b/plexapi/video.py @@ -515,14 +515,14 @@ def episodes(self, **kwargs): def episode(self, title=None, season=None, episode=None): """ Find a episode using a title or season and episode. - Parameters: + Parameters: title (str): Title of the episode to return season (int): Season number (default:None; required if title not specified). episode (int): Episode number (default:None; required if title not specified). - Raises: - :class:`~plexapi.exceptions.BadRequest`: If season and episode is missing. - :class:`~plexapi.exceptions.NotFound`: If the episode is missing. + Raises: + :exc:`plexapi.exceptions.BadRequest`: If season and episode is missing. + :exc:`plexapi.exceptions.NotFound`: If the episode is missing. """ if title: key = '/library/metadata/%s/allLeaves' % self.ratingKey