From fcfda3c513b2879f798ca669cfe80bda3a2ea0fa Mon Sep 17 00:00:00 2001 From: jasonl Date: Wed, 12 Jan 2022 12:18:00 -0600 Subject: [PATCH 1/6] Initial support of artist radio stations --- plexapi/audio.py | 6 ++++++ plexapi/playlist.py | 9 +++++++++ plexapi/playqueue.py | 5 ++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/plexapi/audio.py b/plexapi/audio.py index 656b9250a..8e4255791 100644 --- a/plexapi/audio.py +++ b/plexapi/audio.py @@ -8,6 +8,7 @@ from plexapi.mixins import AdvancedSettingsMixin, ArtUrlMixin, ArtMixin, PosterUrlMixin, PosterMixin from plexapi.mixins import RatingMixin, SplitMergeMixin, UnmatchMatchMixin from plexapi.mixins import CollectionMixin, CountryMixin, GenreMixin, LabelMixin, MoodMixin, SimilarArtistMixin, StyleMixin +from plexapi.playlist import Playlist class Audio(PlexPartialObject): @@ -222,6 +223,11 @@ def download(self, savepath=None, keep_original_name=False, subfolders=False, ** filepaths += track.download(_savepath, keep_original_name, **kwargs) return filepaths + def stations(self): + """ Returns a list of :class:`~plexapi.audio.Playlist` radio stations for this artist. """ + key = '%s?includeStations=1' % self.key + return self.fetchItems(key, cls=Playlist, rtag="Stations") + @utils.registerPlexObject class Album(Audio, ArtMixin, PosterMixin, RatingMixin, UnmatchMatchMixin, diff --git a/plexapi/playlist.py b/plexapi/playlist.py index e470b76d7..c9b0de161 100644 --- a/plexapi/playlist.py +++ b/plexapi/playlist.py @@ -29,7 +29,11 @@ class Playlist(PlexPartialObject, Playable, ArtMixin, PosterMixin, SmartFilterMi icon (str): Icon URI string for smart playlists. key (str): API URL (/playlist/). leafCount (int): Number of items in the playlist view. + librarySectionID (int): Library section identifier (radio only) + librarySectionKey (str): Library section key (radio only) + librarySectionTitle (str): Library section title (radio only) playlistType (str): 'audio', 'video', or 'photo' + radio (bool): If this playlist represents a radio station ratingKey (int): Unique key identifying the playlist. smart (bool): True if the playlist is a smart playlist. summary (str): Summary of the playlist. @@ -54,7 +58,10 @@ def _loadData(self, data): self.icon = data.attrib.get('icon') self.key = data.attrib.get('key', '').replace('/items', '') # FIX_BUG_50 self.leafCount = utils.cast(int, data.attrib.get('leafCount')) + self.librarySectionKey = data.attrib.get('librarySectionKey') + self.librarySectionTitle = data.attrib.get('librarySectionTitle') self.playlistType = data.attrib.get('playlistType') + self.radio = utils.cast(bool, data.attrib.get('radio', 0)) self.ratingKey = utils.cast(int, data.attrib.get('ratingKey')) self.smart = utils.cast(bool, data.attrib.get('smart')) self.summary = data.attrib.get('summary') @@ -169,6 +176,8 @@ def item(self, title): def items(self): """ Returns a list of all items in the playlist. """ + if self.radio: + return [] if self._items is None: key = '%s/items' % self.key items = self.fetchItems(key) diff --git a/plexapi/playqueue.py b/plexapi/playqueue.py index ca6fda639..432e5d878 100644 --- a/plexapi/playqueue.py +++ b/plexapi/playqueue.py @@ -175,8 +175,11 @@ def create( args["uri"] = "library:///directory/{uri_args}".format(uri_args=uri_args) args["type"] = items[0].listType elif items.type == "playlist": - args["playlistID"] = items.ratingKey args["type"] = items.playlistType + if items.radio: + args["uri"] = f"server://{server.machineIdentifier}/{server.library.identifier}{items.key}" + else: + args["playlistID"] = items.ratingKey else: uuid = items.section().uuid args["type"] = items.listType From 8783538d9a510a5d3bb1ed651aa0635d5f9f92be Mon Sep 17 00:00:00 2001 From: jasonl Date: Wed, 12 Jan 2022 13:52:58 -0600 Subject: [PATCH 2/6] Fix docstring --- plexapi/audio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plexapi/audio.py b/plexapi/audio.py index 8e4255791..a4085423f 100644 --- a/plexapi/audio.py +++ b/plexapi/audio.py @@ -224,7 +224,7 @@ def download(self, savepath=None, keep_original_name=False, subfolders=False, ** return filepaths def stations(self): - """ Returns a list of :class:`~plexapi.audio.Playlist` radio stations for this artist. """ + """ Returns a list of :class:`~plexapi.playlist.Playlist` radio stations for this artist. """ key = '%s?includeStations=1' % self.key return self.fetchItems(key, cls=Playlist, rtag="Stations") From 7951322e0e3967eb50e0e476fff4e4aae1031154 Mon Sep 17 00:00:00 2001 From: jasonl Date: Thu, 13 Jan 2022 08:53:19 -0600 Subject: [PATCH 3/6] Change to a singular optional station --- plexapi/audio.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plexapi/audio.py b/plexapi/audio.py index a4085423f..60b119d9e 100644 --- a/plexapi/audio.py +++ b/plexapi/audio.py @@ -223,10 +223,10 @@ def download(self, savepath=None, keep_original_name=False, subfolders=False, ** filepaths += track.download(_savepath, keep_original_name, **kwargs) return filepaths - def stations(self): - """ Returns a list of :class:`~plexapi.playlist.Playlist` radio stations for this artist. """ + def station(self): + """ Returns a :class:`~plexapi.playlist.Playlist` artist radio station or `None`. """ key = '%s?includeStations=1' % self.key - return self.fetchItems(key, cls=Playlist, rtag="Stations") + return next(iter(self.fetchItems(key, cls=Playlist, rtag="Stations")), None) @utils.registerPlexObject From 0b347c5b936741dd481a73f8346437d2f24f50a0 Mon Sep 17 00:00:00 2001 From: jasonl Date: Thu, 13 Jan 2022 08:56:53 -0600 Subject: [PATCH 4/6] Change to a property --- plexapi/audio.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plexapi/audio.py b/plexapi/audio.py index 60b119d9e..b8ea821e3 100644 --- a/plexapi/audio.py +++ b/plexapi/audio.py @@ -223,6 +223,7 @@ def download(self, savepath=None, keep_original_name=False, subfolders=False, ** filepaths += track.download(_savepath, keep_original_name, **kwargs) return filepaths + @property def station(self): """ Returns a :class:`~plexapi.playlist.Playlist` artist radio station or `None`. """ key = '%s?includeStations=1' % self.key From 31cd90184f0ab2276e0f721daad7124deb15a226 Mon Sep 17 00:00:00 2001 From: jasonl Date: Thu, 13 Jan 2022 11:07:03 -0600 Subject: [PATCH 5/6] Revert property --- plexapi/audio.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plexapi/audio.py b/plexapi/audio.py index b8ea821e3..60b119d9e 100644 --- a/plexapi/audio.py +++ b/plexapi/audio.py @@ -223,7 +223,6 @@ def download(self, savepath=None, keep_original_name=False, subfolders=False, ** filepaths += track.download(_savepath, keep_original_name, **kwargs) return filepaths - @property def station(self): """ Returns a :class:`~plexapi.playlist.Playlist` artist radio station or `None`. """ key = '%s?includeStations=1' % self.key From 0408b8563ac4d7958ae34cd250ee11a172bc6e12 Mon Sep 17 00:00:00 2001 From: jjlawren Date: Sun, 23 Jan 2022 22:18:40 -0600 Subject: [PATCH 6/6] Explicitly set librarySectionID Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> --- plexapi/playlist.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plexapi/playlist.py b/plexapi/playlist.py index c9b0de161..ee4694d23 100644 --- a/plexapi/playlist.py +++ b/plexapi/playlist.py @@ -58,6 +58,7 @@ def _loadData(self, data): self.icon = data.attrib.get('icon') self.key = data.attrib.get('key', '').replace('/items', '') # FIX_BUG_50 self.leafCount = utils.cast(int, data.attrib.get('leafCount')) + self.librarySectionID = utils.cast(int, data.attrib.get('librarySectionID')) self.librarySectionKey = data.attrib.get('librarySectionKey') self.librarySectionTitle = data.attrib.get('librarySectionTitle') self.playlistType = data.attrib.get('playlistType')