Skip to content

Commit 2f101e3

Browse files
meisnate12glenscJonnyWong16
authored
Add LabelMixin to Season, Episode, Artist, and Track objects (#872)
* Add LabelMixin to Episode class * Add test_mixins.edit_label(episode) test * Add Label Mixin to Season, Artist,and Track * fix tests * Load manual FilteringFields for labels * Load manual FilteringFilters for labels Co-authored-by: Elan Ruusamäe <glen@pld-linux.org> Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>
1 parent 158dd37 commit 2f101e3

5 files changed

Lines changed: 78 additions & 14 deletions

File tree

plexapi/audio.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ def sync(self, bitrate, client=None, clientId=None, limit=None, title=None):
126126

127127
@utils.registerPlexObject
128128
class Artist(Audio, AdvancedSettingsMixin, ArtMixin, PosterMixin, RatingMixin, SplitMergeMixin, UnmatchMatchMixin,
129-
CollectionMixin, CountryMixin, GenreMixin, MoodMixin, SimilarArtistMixin, StyleMixin):
129+
CollectionMixin, CountryMixin, GenreMixin, LabelMixin, MoodMixin, SimilarArtistMixin, StyleMixin):
130130
""" Represents a single Artist.
131131
132132
Attributes:
@@ -138,6 +138,7 @@ class Artist(Audio, AdvancedSettingsMixin, ArtMixin, PosterMixin, RatingMixin, S
138138
countries (List<:class:`~plexapi.media.Country`>): List country objects.
139139
genres (List<:class:`~plexapi.media.Genre`>): List of genre objects.
140140
key (str): API URL (/library/metadata/<ratingkey>).
141+
labels (List<:class:`~plexapi.media.Label`>): List of label objects.
141142
locations (List<str>): List of folder paths where the artist is found on disk.
142143
similar (List<:class:`~plexapi.media.Similar`>): List of similar objects.
143144
styles (List<:class:`~plexapi.media.Style`>): List of style objects.
@@ -153,6 +154,7 @@ def _loadData(self, data):
153154
self.countries = self.findItems(data, media.Country)
154155
self.genres = self.findItems(data, media.Genre)
155156
self.key = self.key.replace('/children', '') # FIX_BUG_50
157+
self.labels = self.findItems(data, media.Label)
156158
self.locations = self.listAttrs(data, 'path', etag='Location')
157159
self.similar = self.findItems(data, media.Similar)
158160
self.styles = self.findItems(data, media.Style)
@@ -338,7 +340,7 @@ def _defaultSyncTitle(self):
338340

339341
@utils.registerPlexObject
340342
class Track(Audio, Playable, ArtUrlMixin, PosterUrlMixin, RatingMixin,
341-
CollectionMixin, MoodMixin):
343+
CollectionMixin, LabelMixin, MoodMixin):
342344
""" Represents a single Track.
343345
344346
Attributes:
@@ -354,6 +356,7 @@ class Track(Audio, Playable, ArtUrlMixin, PosterUrlMixin, RatingMixin,
354356
grandparentThumb (str): URL to album artist thumbnail image
355357
(/library/metadata/<grandparentRatingKey>/thumb/<thumbid>).
356358
grandparentTitle (str): Name of the album artist for the track.
359+
labels (List<:class:`~plexapi.media.Label`>): List of label objects.
357360
media (List<:class:`~plexapi.media.Media`>): List of media objects.
358361
originalTitle (str): The artist for the track.
359362
parentGuid (str): Plex GUID for the album (plex://album/5d07cd8e403c640290f180f9).
@@ -383,6 +386,7 @@ def _loadData(self, data):
383386
self.grandparentRatingKey = utils.cast(int, data.attrib.get('grandparentRatingKey'))
384387
self.grandparentThumb = data.attrib.get('grandparentThumb')
385388
self.grandparentTitle = data.attrib.get('grandparentTitle')
389+
self.labels = self.findItems(data, media.Label)
386390
self.media = self.findItems(data, media.Media)
387391
self.originalTitle = data.attrib.get('originalTitle')
388392
self.parentGuid = data.attrib.get('parentGuid')

plexapi/library.py

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2236,16 +2236,57 @@ def _loadData(self, data):
22362236
self.title = data.attrib.get('title')
22372237
self.type = data.attrib.get('type')
22382238

2239-
# Add additional manual sorts and fields which are available
2239+
self._librarySectionID = self._parent().key
2240+
2241+
# Add additional manual filters, sorts, and fields which are available
22402242
# but not exposed on the Plex server
2243+
self.filters += self._manualFilters()
22412244
self.sorts += self._manualSorts()
22422245
self.fields += self._manualFields()
22432246

2247+
def _manualFilters(self):
2248+
""" Manually add additional filters which are available
2249+
but not exposed on the Plex server.
2250+
"""
2251+
# Filters: (filter, type, title)
2252+
additionalFilters = [
2253+
]
2254+
2255+
if self.type == 'season':
2256+
additionalFilters.extend([
2257+
('label', 'string', 'Labels')
2258+
])
2259+
elif self.type == 'episode':
2260+
additionalFilters.extend([
2261+
('label', 'string', 'Labels')
2262+
])
2263+
elif self.type == 'artist':
2264+
additionalFilters.extend([
2265+
('label', 'string', 'Labels')
2266+
])
2267+
elif self.type == 'track':
2268+
additionalFilters.extend([
2269+
('label', 'string', 'Labels')
2270+
])
2271+
2272+
manualFilters = []
2273+
for filterTag, filterType, filterTitle in additionalFilters:
2274+
filterKey = '/library/sections/%s/%s?type=%s' % (
2275+
self._librarySectionID, filterTag, utils.searchType(self.type)
2276+
)
2277+
filterXML = (
2278+
'<Filter filter="%s" filterType="%s" key="%s" title="%s" type="filter" />'
2279+
% (filterTag, filterType, filterKey, filterTitle)
2280+
)
2281+
manualFilters.append(self._manuallyLoadXML(filterXML, FilteringFilter))
2282+
2283+
return manualFilters
2284+
22442285
def _manualSorts(self):
22452286
""" Manually add additional sorts which are available
22462287
but not exposed on the Plex server.
22472288
"""
2248-
# Sorts: key, dir, title
2289+
# Sorts: (key, dir, title)
22492290
additionalSorts = [
22502291
('guid', 'asc', 'Guid'),
22512292
('id', 'asc', 'Rating Key'),
@@ -2275,8 +2316,10 @@ def _manualSorts(self):
22752316

22762317
manualSorts = []
22772318
for sortField, sortDir, sortTitle in additionalSorts:
2278-
sortXML = ('<Sort defaultDirection="%s" descKey="%s:desc" key="%s" title="%s" />'
2279-
% (sortDir, sortField, sortField, sortTitle))
2319+
sortXML = (
2320+
'<Sort defaultDirection="%s" descKey="%s:desc" key="%s" title="%s" />'
2321+
% (sortDir, sortField, sortField, sortTitle)
2322+
)
22802323
manualSorts.append(self._manuallyLoadXML(sortXML, FilteringSort))
22812324

22822325
return manualSorts
@@ -2285,7 +2328,7 @@ def _manualFields(self):
22852328
""" Manually add additional fields which are available
22862329
but not exposed on the Plex server.
22872330
"""
2288-
# Fields: key, type, title
2331+
# Fields: (key, type, title)
22892332
additionalFields = [
22902333
('guid', 'string', 'Guid'),
22912334
('id', 'integer', 'Rating Key'),
@@ -2311,19 +2354,26 @@ def _manualFields(self):
23112354
additionalFields.extend([
23122355
('addedAt', 'date', 'Date Season Added'),
23132356
('unviewedLeafCount', 'integer', 'Episode Unplayed Count'),
2314-
('year', 'integer', 'Season Year')
2357+
('year', 'integer', 'Season Year'),
2358+
('label', 'tag', 'Label')
23152359
])
23162360
elif self.type == 'episode':
23172361
additionalFields.extend([
23182362
('audienceRating', 'integer', 'Audience Rating'),
23192363
('duration', 'integer', 'Duration'),
23202364
('rating', 'integer', 'Critic Rating'),
2321-
('viewOffset', 'integer', 'View Offset')
2365+
('viewOffset', 'integer', 'View Offset'),
2366+
('label', 'tag', 'Label')
2367+
])
2368+
elif self.type == 'artist':
2369+
additionalFields.extend([
2370+
('label', 'tag', 'Label')
23222371
])
23232372
elif self.type == 'track':
23242373
additionalFields.extend([
23252374
('duration', 'integer', 'Duration'),
2326-
('viewOffset', 'integer', 'View Offset')
2375+
('viewOffset', 'integer', 'View Offset'),
2376+
('label', 'tag', 'Label')
23272377
])
23282378
elif self.type == 'collection':
23292379
additionalFields.extend([
@@ -2334,8 +2384,10 @@ def _manualFields(self):
23342384

23352385
manualFields = []
23362386
for field, fieldType, fieldTitle in additionalFields:
2337-
fieldXML = ('<Field key="%s%s" title="%s" type="%s"/>'
2338-
% (prefix, field, fieldTitle, fieldType))
2387+
fieldXML = (
2388+
'<Field key="%s%s" title="%s" type="%s"/>'
2389+
% (prefix, field, fieldTitle, fieldType)
2390+
)
23392391
manualFields.append(self._manuallyLoadXML(fieldXML, FilteringField))
23402392

23412393
return manualFields

plexapi/video.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,7 @@ def download(self, savepath=None, keep_original_name=False, subfolders=False, **
574574

575575

576576
@utils.registerPlexObject
577-
class Season(Video, ArtMixin, PosterMixin, RatingMixin, CollectionMixin):
577+
class Season(Video, ArtMixin, PosterMixin, RatingMixin, CollectionMixin, LabelMixin):
578578
""" Represents a single Show Season (including all episodes).
579579
580580
Attributes:
@@ -584,6 +584,7 @@ class Season(Video, ArtMixin, PosterMixin, RatingMixin, CollectionMixin):
584584
guids (List<:class:`~plexapi.media.Guid`>): List of guid objects.
585585
index (int): Season number.
586586
key (str): API URL (/library/metadata/<ratingkey>).
587+
labels (List<:class:`~plexapi.media.Label`>): List of label objects.
587588
leafCount (int): Number of items in the season view.
588589
parentGuid (str): Plex GUID for the show (plex://show/5d9c086fe9d5a1001f4d9fe6).
589590
parentIndex (int): Plex index number for the show.
@@ -607,6 +608,7 @@ def _loadData(self, data):
607608
self.guids = self.findItems(data, media.Guid)
608609
self.index = utils.cast(int, data.attrib.get('index'))
609610
self.key = self.key.replace('/children', '') # FIX_BUG_50
611+
self.labels = self.findItems(data, media.Label)
610612
self.leafCount = utils.cast(int, data.attrib.get('leafCount'))
611613
self.parentGuid = data.attrib.get('parentGuid')
612614
self.parentIndex = utils.cast(int, data.attrib.get('parentIndex'))
@@ -710,7 +712,7 @@ def _defaultSyncTitle(self):
710712

711713
@utils.registerPlexObject
712714
class Episode(Video, Playable, ArtMixin, PosterMixin, RatingMixin,
713-
CollectionMixin, DirectorMixin, WriterMixin):
715+
CollectionMixin, DirectorMixin, LabelMixin, WriterMixin):
714716
""" Represents a single Shows Episode.
715717
716718
Attributes:
@@ -733,6 +735,7 @@ class Episode(Video, Playable, ArtMixin, PosterMixin, RatingMixin,
733735
grandparentTitle (str): Name of the show for the episode.
734736
guids (List<:class:`~plexapi.media.Guid`>): List of guid objects.
735737
index (int): Episode number.
738+
labels (List<:class:`~plexapi.media.Label`>): List of label objects.
736739
markers (List<:class:`~plexapi.media.Marker`>): List of marker objects.
737740
media (List<:class:`~plexapi.media.Media`>): List of media objects.
738741
originallyAvailableAt (datetime): Datetime the episode was released.
@@ -777,6 +780,7 @@ def _loadData(self, data):
777780
self.grandparentTitle = data.attrib.get('grandparentTitle')
778781
self.guids = self.findItems(data, media.Guid)
779782
self.index = utils.cast(int, data.attrib.get('index'))
783+
self.labels = self.findItems(data, media.Label)
780784
self.markers = self.findItems(data, media.Marker)
781785
self.media = self.findItems(data, media.Media)
782786
self.originallyAvailableAt = utils.toDatetime(data.attrib.get('originallyAvailableAt'), '%Y-%m-%d')

tests/test_audio.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ def test_audio_Artist_mixins_tags(artist):
103103
test_mixins.edit_collection(artist)
104104
test_mixins.edit_country(artist)
105105
test_mixins.edit_genre(artist)
106+
test_mixins.edit_label(artist)
106107
test_mixins.edit_mood(artist)
107108
test_mixins.edit_similar_artist(artist)
108109
test_mixins.edit_style(artist)
@@ -368,6 +369,7 @@ def test_audio_Track_mixins_rating(track):
368369

369370
def test_audio_Track_mixins_tags(track):
370371
test_mixins.edit_collection(track)
372+
test_mixins.edit_label(track)
371373
test_mixins.edit_mood(track)
372374

373375

tests/test_video.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -938,6 +938,7 @@ def test_video_Season_mixins_rating(show):
938938
def test_video_Season_mixins_tags(show):
939939
season = show.season(season=1)
940940
test_mixins.edit_collection(season)
941+
test_mixins.edit_label(season)
941942

942943

943944
def test_video_Season_PlexWebURL(plex, season):
@@ -1149,6 +1150,7 @@ def test_video_Episode_mixins_tags(episode):
11491150
test_mixins.edit_collection(episode)
11501151
test_mixins.edit_director(episode)
11511152
test_mixins.edit_writer(episode)
1153+
test_mixins.edit_label(episode)
11521154

11531155

11541156
def test_video_Episode_media_tags(episode):

0 commit comments

Comments
 (0)