diff --git a/plexapi/audio.py b/plexapi/audio.py index bef951d81..33716589c 100644 --- a/plexapi/audio.py +++ b/plexapi/audio.py @@ -126,7 +126,7 @@ def sync(self, bitrate, client=None, clientId=None, limit=None, title=None): @utils.registerPlexObject class Artist(Audio, AdvancedSettingsMixin, ArtMixin, PosterMixin, RatingMixin, SplitMergeMixin, UnmatchMatchMixin, - CollectionMixin, CountryMixin, GenreMixin, MoodMixin, SimilarArtistMixin, StyleMixin): + CollectionMixin, CountryMixin, GenreMixin, LabelMixin, MoodMixin, SimilarArtistMixin, StyleMixin): """ Represents a single Artist. Attributes: @@ -138,6 +138,7 @@ class Artist(Audio, AdvancedSettingsMixin, ArtMixin, PosterMixin, RatingMixin, S countries (List<:class:`~plexapi.media.Country`>): List country objects. genres (List<:class:`~plexapi.media.Genre`>): List of genre objects. key (str): API URL (/library/metadata/). + labels (List<:class:`~plexapi.media.Label`>): List of label objects. locations (List): List of folder paths where the artist is found on disk. similar (List<:class:`~plexapi.media.Similar`>): List of similar objects. styles (List<:class:`~plexapi.media.Style`>): List of style objects. @@ -153,6 +154,7 @@ def _loadData(self, data): self.countries = self.findItems(data, media.Country) self.genres = self.findItems(data, media.Genre) self.key = self.key.replace('/children', '') # FIX_BUG_50 + self.labels = self.findItems(data, media.Label) self.locations = self.listAttrs(data, 'path', etag='Location') self.similar = self.findItems(data, media.Similar) self.styles = self.findItems(data, media.Style) @@ -338,7 +340,7 @@ def _defaultSyncTitle(self): @utils.registerPlexObject class Track(Audio, Playable, ArtUrlMixin, PosterUrlMixin, RatingMixin, - CollectionMixin, MoodMixin): + CollectionMixin, LabelMixin, MoodMixin): """ Represents a single Track. Attributes: @@ -354,6 +356,7 @@ class Track(Audio, Playable, ArtUrlMixin, PosterUrlMixin, RatingMixin, grandparentThumb (str): URL to album artist thumbnail image (/library/metadata//thumb/). grandparentTitle (str): Name of the album artist for the track. + labels (List<:class:`~plexapi.media.Label`>): List of label objects. media (List<:class:`~plexapi.media.Media`>): List of media objects. originalTitle (str): The artist for the track. parentGuid (str): Plex GUID for the album (plex://album/5d07cd8e403c640290f180f9). @@ -383,6 +386,7 @@ def _loadData(self, data): self.grandparentRatingKey = utils.cast(int, data.attrib.get('grandparentRatingKey')) self.grandparentThumb = data.attrib.get('grandparentThumb') self.grandparentTitle = data.attrib.get('grandparentTitle') + self.labels = self.findItems(data, media.Label) self.media = self.findItems(data, media.Media) self.originalTitle = data.attrib.get('originalTitle') self.parentGuid = data.attrib.get('parentGuid') diff --git a/plexapi/library.py b/plexapi/library.py index c67ae9b77..6de8f32bc 100644 --- a/plexapi/library.py +++ b/plexapi/library.py @@ -2236,16 +2236,57 @@ def _loadData(self, data): self.title = data.attrib.get('title') self.type = data.attrib.get('type') - # Add additional manual sorts and fields which are available + self._librarySectionID = self._parent().key + + # Add additional manual filters, sorts, and fields which are available # but not exposed on the Plex server + self.filters += self._manualFilters() self.sorts += self._manualSorts() self.fields += self._manualFields() + def _manualFilters(self): + """ Manually add additional filters which are available + but not exposed on the Plex server. + """ + # Filters: (filter, type, title) + additionalFilters = [ + ] + + if self.type == 'season': + additionalFilters.extend([ + ('label', 'string', 'Labels') + ]) + elif self.type == 'episode': + additionalFilters.extend([ + ('label', 'string', 'Labels') + ]) + elif self.type == 'artist': + additionalFilters.extend([ + ('label', 'string', 'Labels') + ]) + elif self.type == 'track': + additionalFilters.extend([ + ('label', 'string', 'Labels') + ]) + + manualFilters = [] + for filterTag, filterType, filterTitle in additionalFilters: + filterKey = '/library/sections/%s/%s?type=%s' % ( + self._librarySectionID, filterTag, utils.searchType(self.type) + ) + filterXML = ( + '' + % (filterTag, filterType, filterKey, filterTitle) + ) + manualFilters.append(self._manuallyLoadXML(filterXML, FilteringFilter)) + + return manualFilters + def _manualSorts(self): """ Manually add additional sorts which are available but not exposed on the Plex server. """ - # Sorts: key, dir, title + # Sorts: (key, dir, title) additionalSorts = [ ('guid', 'asc', 'Guid'), ('id', 'asc', 'Rating Key'), @@ -2275,8 +2316,10 @@ def _manualSorts(self): manualSorts = [] for sortField, sortDir, sortTitle in additionalSorts: - sortXML = ('' - % (sortDir, sortField, sortField, sortTitle)) + sortXML = ( + '' + % (sortDir, sortField, sortField, sortTitle) + ) manualSorts.append(self._manuallyLoadXML(sortXML, FilteringSort)) return manualSorts @@ -2285,7 +2328,7 @@ def _manualFields(self): """ Manually add additional fields which are available but not exposed on the Plex server. """ - # Fields: key, type, title + # Fields: (key, type, title) additionalFields = [ ('guid', 'string', 'Guid'), ('id', 'integer', 'Rating Key'), @@ -2311,19 +2354,26 @@ def _manualFields(self): additionalFields.extend([ ('addedAt', 'date', 'Date Season Added'), ('unviewedLeafCount', 'integer', 'Episode Unplayed Count'), - ('year', 'integer', 'Season Year') + ('year', 'integer', 'Season Year'), + ('label', 'tag', 'Label') ]) elif self.type == 'episode': additionalFields.extend([ ('audienceRating', 'integer', 'Audience Rating'), ('duration', 'integer', 'Duration'), ('rating', 'integer', 'Critic Rating'), - ('viewOffset', 'integer', 'View Offset') + ('viewOffset', 'integer', 'View Offset'), + ('label', 'tag', 'Label') + ]) + elif self.type == 'artist': + additionalFields.extend([ + ('label', 'tag', 'Label') ]) elif self.type == 'track': additionalFields.extend([ ('duration', 'integer', 'Duration'), - ('viewOffset', 'integer', 'View Offset') + ('viewOffset', 'integer', 'View Offset'), + ('label', 'tag', 'Label') ]) elif self.type == 'collection': additionalFields.extend([ @@ -2334,8 +2384,10 @@ def _manualFields(self): manualFields = [] for field, fieldType, fieldTitle in additionalFields: - fieldXML = ('' - % (prefix, field, fieldTitle, fieldType)) + fieldXML = ( + '' + % (prefix, field, fieldTitle, fieldType) + ) manualFields.append(self._manuallyLoadXML(fieldXML, FilteringField)) return manualFields diff --git a/plexapi/video.py b/plexapi/video.py index 4049d6c53..7459221c9 100644 --- a/plexapi/video.py +++ b/plexapi/video.py @@ -574,7 +574,7 @@ def download(self, savepath=None, keep_original_name=False, subfolders=False, ** @utils.registerPlexObject -class Season(Video, ArtMixin, PosterMixin, RatingMixin, CollectionMixin): +class Season(Video, ArtMixin, PosterMixin, RatingMixin, CollectionMixin, LabelMixin): """ Represents a single Show Season (including all episodes). Attributes: @@ -584,6 +584,7 @@ class Season(Video, ArtMixin, PosterMixin, RatingMixin, CollectionMixin): guids (List<:class:`~plexapi.media.Guid`>): List of guid objects. index (int): Season number. key (str): API URL (/library/metadata/). + labels (List<:class:`~plexapi.media.Label`>): List of label objects. leafCount (int): Number of items in the season view. parentGuid (str): Plex GUID for the show (plex://show/5d9c086fe9d5a1001f4d9fe6). parentIndex (int): Plex index number for the show. @@ -607,6 +608,7 @@ def _loadData(self, data): self.guids = self.findItems(data, media.Guid) self.index = utils.cast(int, data.attrib.get('index')) self.key = self.key.replace('/children', '') # FIX_BUG_50 + self.labels = self.findItems(data, media.Label) self.leafCount = utils.cast(int, data.attrib.get('leafCount')) self.parentGuid = data.attrib.get('parentGuid') self.parentIndex = utils.cast(int, data.attrib.get('parentIndex')) @@ -710,7 +712,7 @@ def _defaultSyncTitle(self): @utils.registerPlexObject class Episode(Video, Playable, ArtMixin, PosterMixin, RatingMixin, - CollectionMixin, DirectorMixin, WriterMixin): + CollectionMixin, DirectorMixin, LabelMixin, WriterMixin): """ Represents a single Shows Episode. Attributes: @@ -733,6 +735,7 @@ class Episode(Video, Playable, ArtMixin, PosterMixin, RatingMixin, grandparentTitle (str): Name of the show for the episode. guids (List<:class:`~plexapi.media.Guid`>): List of guid objects. index (int): Episode number. + labels (List<:class:`~plexapi.media.Label`>): List of label objects. markers (List<:class:`~plexapi.media.Marker`>): List of marker objects. media (List<:class:`~plexapi.media.Media`>): List of media objects. originallyAvailableAt (datetime): Datetime the episode was released. @@ -777,6 +780,7 @@ def _loadData(self, data): self.grandparentTitle = data.attrib.get('grandparentTitle') self.guids = self.findItems(data, media.Guid) self.index = utils.cast(int, data.attrib.get('index')) + self.labels = self.findItems(data, media.Label) self.markers = self.findItems(data, media.Marker) self.media = self.findItems(data, media.Media) self.originallyAvailableAt = utils.toDatetime(data.attrib.get('originallyAvailableAt'), '%Y-%m-%d') diff --git a/tests/test_audio.py b/tests/test_audio.py index 41363ddb0..2a262392d 100644 --- a/tests/test_audio.py +++ b/tests/test_audio.py @@ -103,6 +103,7 @@ def test_audio_Artist_mixins_tags(artist): test_mixins.edit_collection(artist) test_mixins.edit_country(artist) test_mixins.edit_genre(artist) + test_mixins.edit_label(artist) test_mixins.edit_mood(artist) test_mixins.edit_similar_artist(artist) test_mixins.edit_style(artist) @@ -368,6 +369,7 @@ def test_audio_Track_mixins_rating(track): def test_audio_Track_mixins_tags(track): test_mixins.edit_collection(track) + test_mixins.edit_label(track) test_mixins.edit_mood(track) diff --git a/tests/test_video.py b/tests/test_video.py index 02dec0d6c..a12de22ea 100644 --- a/tests/test_video.py +++ b/tests/test_video.py @@ -938,6 +938,7 @@ def test_video_Season_mixins_rating(show): def test_video_Season_mixins_tags(show): season = show.season(season=1) test_mixins.edit_collection(season) + test_mixins.edit_label(season) def test_video_Season_PlexWebURL(plex, season): @@ -1149,6 +1150,7 @@ def test_video_Episode_mixins_tags(episode): test_mixins.edit_collection(episode) test_mixins.edit_director(episode) test_mixins.edit_writer(episode) + test_mixins.edit_label(episode) def test_video_Episode_media_tags(episode):