Skip to content

Commit 2142514

Browse files
authored
Add support for editing audience ratings and critic ratings (#1417)
* Add support for editing audience ratings and critic ratings * Note: Not all types of ratings are supported/shown in the Plex UIs. However they can be edited/updated and stored in the Plex Media Server database. * Add tests for editing audience rating and critic rating
1 parent 7b317ef commit 2142514

File tree

8 files changed

+86
-10
lines changed

8 files changed

+86
-10
lines changed

plexapi/audio.py

+10
Original file line numberDiff line numberDiff line change
@@ -181,13 +181,15 @@ class Artist(
181181
TYPE (str): 'artist'
182182
albumSort (int): Setting that indicates how albums are sorted for the artist
183183
(-1 = Library default, 0 = Newest first, 1 = Oldest first, 2 = By name).
184+
audienceRating (float): Audience rating.
184185
collections (List<:class:`~plexapi.media.Collection`>): List of collection objects.
185186
countries (List<:class:`~plexapi.media.Country`>): List country objects.
186187
genres (List<:class:`~plexapi.media.Genre`>): List of genre objects.
187188
guids (List<:class:`~plexapi.media.Guid`>): List of guid objects.
188189
key (str): API URL (/library/metadata/<ratingkey>).
189190
labels (List<:class:`~plexapi.media.Label`>): List of label objects.
190191
locations (List<str>): List of folder paths where the artist is found on disk.
192+
rating (float): Artist rating (7.9; 9.8; 8.1).
191193
similar (List<:class:`~plexapi.media.Similar`>): List of similar objects.
192194
styles (List<:class:`~plexapi.media.Style`>): List of style objects.
193195
theme (str): URL to theme resource (/library/metadata/<ratingkey>/theme/<themeid>).
@@ -199,13 +201,15 @@ def _loadData(self, data):
199201
""" Load attribute values from Plex XML response. """
200202
Audio._loadData(self, data)
201203
self.albumSort = utils.cast(int, data.attrib.get('albumSort', '-1'))
204+
self.audienceRating = utils.cast(float, data.attrib.get('audienceRating'))
202205
self.collections = self.findItems(data, media.Collection)
203206
self.countries = self.findItems(data, media.Country)
204207
self.genres = self.findItems(data, media.Genre)
205208
self.guids = self.findItems(data, media.Guid)
206209
self.key = self.key.replace('/children', '') # FIX_BUG_50
207210
self.labels = self.findItems(data, media.Label)
208211
self.locations = self.listAttrs(data, 'path', etag='Location')
212+
self.rating = utils.cast(float, data.attrib.get('rating'))
209213
self.similar = self.findItems(data, media.Similar)
210214
self.styles = self.findItems(data, media.Style)
211215
self.theme = data.attrib.get('theme')
@@ -301,6 +305,7 @@ class Album(
301305
Attributes:
302306
TAG (str): 'Directory'
303307
TYPE (str): 'album'
308+
audienceRating (float): Audience rating.
304309
collections (List<:class:`~plexapi.media.Collection`>): List of collection objects.
305310
formats (List<:class:`~plexapi.media.Format`>): List of format objects.
306311
genres (List<:class:`~plexapi.media.Genre`>): List of genre objects.
@@ -329,6 +334,7 @@ class Album(
329334
def _loadData(self, data):
330335
""" Load attribute values from Plex XML response. """
331336
Audio._loadData(self, data)
337+
self.audienceRating = utils.cast(float, data.attrib.get('audienceRating'))
332338
self.collections = self.findItems(data, media.Collection)
333339
self.formats = self.findItems(data, media.Format)
334340
self.genres = self.findItems(data, media.Genre)
@@ -426,6 +432,7 @@ class Track(
426432
Attributes:
427433
TAG (str): 'Directory'
428434
TYPE (str): 'track'
435+
audienceRating (float): Audience rating.
429436
chapters (List<:class:`~plexapi.media.Chapter`>): List of Chapter objects.
430437
chapterSource (str): Unknown
431438
collections (List<:class:`~plexapi.media.Collection`>): List of collection objects.
@@ -451,6 +458,7 @@ class Track(
451458
parentThumb (str): URL to album thumbnail image (/library/metadata/<parentRatingKey>/thumb/<thumbid>).
452459
parentTitle (str): Name of the album for the track.
453460
primaryExtraKey (str) API URL for the primary extra for the track.
461+
rating (float): Track rating (7.9; 9.8; 8.1).
454462
ratingCount (int): Number of listeners who have scrobbled this track, as reported by Last.fm.
455463
skipCount (int): Number of times the track has been skipped.
456464
sourceURI (str): Remote server URI (server://<machineIdentifier>/com.plexapp.plugins.library)
@@ -465,6 +473,7 @@ def _loadData(self, data):
465473
""" Load attribute values from Plex XML response. """
466474
Audio._loadData(self, data)
467475
Playable._loadData(self, data)
476+
self.audienceRating = utils.cast(float, data.attrib.get('audienceRating'))
468477
self.chapters = self.findItems(data, media.Chapter)
469478
self.chapterSource = data.attrib.get('chapterSource')
470479
self.collections = self.findItems(data, media.Collection)
@@ -488,6 +497,7 @@ def _loadData(self, data):
488497
self.parentThumb = data.attrib.get('parentThumb')
489498
self.parentTitle = data.attrib.get('parentTitle')
490499
self.primaryExtraKey = data.attrib.get('primaryExtraKey')
500+
self.rating = utils.cast(float, data.attrib.get('rating'))
491501
self.ratingCount = utils.cast(int, data.attrib.get('ratingCount'))
492502
self.skipCount = utils.cast(int, data.attrib.get('skipCount'))
493503
self.sourceURI = data.attrib.get('source') # remote playlist item

plexapi/collection.py

+4
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class Collection(
2929
addedAt (datetime): Datetime the collection was added to the library.
3030
art (str): URL to artwork image (/library/metadata/<ratingKey>/art/<artid>).
3131
artBlurHash (str): BlurHash string for artwork image.
32+
audienceRating (float): Audience rating.
3233
childCount (int): Number of items in the collection.
3334
collectionFilterBasedOnUser (int): Which user's activity is used for the collection filtering.
3435
collectionMode (int): How the items in the collection are displayed.
@@ -47,6 +48,7 @@ class Collection(
4748
librarySectionTitle (str): :class:`~plexapi.library.LibrarySection` title.
4849
maxYear (int): Maximum year for the items in the collection.
4950
minYear (int): Minimum year for the items in the collection.
51+
rating (float): Collection rating (7.9; 9.8; 8.1).
5052
ratingCount (int): The number of ratings.
5153
ratingKey (int): Unique key identifying the collection.
5254
smart (bool): True if the collection is a smart collection.
@@ -69,6 +71,7 @@ def _loadData(self, data):
6971
self.addedAt = utils.toDatetime(data.attrib.get('addedAt'))
7072
self.art = data.attrib.get('art')
7173
self.artBlurHash = data.attrib.get('artBlurHash')
74+
self.audienceRating = utils.cast(float, data.attrib.get('audienceRating'))
7275
self.childCount = utils.cast(int, data.attrib.get('childCount'))
7376
self.collectionFilterBasedOnUser = utils.cast(int, data.attrib.get('collectionFilterBasedOnUser', '0'))
7477
self.collectionMode = utils.cast(int, data.attrib.get('collectionMode', '-1'))
@@ -87,6 +90,7 @@ def _loadData(self, data):
8790
self.librarySectionTitle = data.attrib.get('librarySectionTitle')
8891
self.maxYear = utils.cast(int, data.attrib.get('maxYear'))
8992
self.minYear = utils.cast(int, data.attrib.get('minYear'))
93+
self.rating = utils.cast(float, data.attrib.get('rating'))
9094
self.ratingCount = utils.cast(int, data.attrib.get('ratingCount'))
9195
self.ratingKey = utils.cast(int, data.attrib.get('ratingKey'))
9296
self.smart = utils.cast(bool, data.attrib.get('smart', '0'))

plexapi/mixins.py

+43-9
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,19 @@ def editAddedAt(self, addedAt, locked=True):
567567
return self.editField('addedAt', addedAt, locked=locked)
568568

569569

570+
class AudienceRatingMixin(EditFieldMixin):
571+
""" Mixin for Plex objects that can have an audience rating. """
572+
573+
def editAudienceRating(self, audienceRating, locked=True):
574+
""" Edit the audience rating.
575+
576+
Parameters:
577+
audienceRating (float): The new value.
578+
locked (bool): True (default) to lock the field, False to unlock the field.
579+
"""
580+
return self.editField('audienceRating', audienceRating, locked=locked)
581+
582+
570583
class ContentRatingMixin(EditFieldMixin):
571584
""" Mixin for Plex objects that can have a content rating. """
572585

@@ -580,6 +593,19 @@ def editContentRating(self, contentRating, locked=True):
580593
return self.editField('contentRating', contentRating, locked=locked)
581594

582595

596+
class CriticRatingMixin(EditFieldMixin):
597+
""" Mixin for Plex objects that can have a critic rating. """
598+
599+
def editCriticRating(self, criticRating, locked=True):
600+
""" Edit the critic rating.
601+
602+
Parameters:
603+
criticRating (float): The new value.
604+
locked (bool): True (default) to lock the field, False to unlock the field.
605+
"""
606+
return self.editField('rating', criticRating, locked=locked)
607+
608+
583609
class EditionTitleMixin(EditFieldMixin):
584610
""" Mixin for Plex objects that can have an edition title. """
585611

@@ -751,7 +777,7 @@ def editUserRating(self, userRating, locked=True):
751777
""" Edit the user rating.
752778
753779
Parameters:
754-
userRating (int): The new value.
780+
userRating (float): The new value.
755781
locked (bool): True (default) to lock the field, False to unlock the field.
756782
"""
757783
return self.editField('userRating', userRating, locked=locked)
@@ -1145,7 +1171,8 @@ def streamingServices(self, account=None):
11451171

11461172
class MovieEditMixins(
11471173
ArtLockMixin, PosterLockMixin, ThemeLockMixin,
1148-
AddedAtMixin, ContentRatingMixin, EditionTitleMixin, OriginallyAvailableMixin, OriginalTitleMixin, SortTitleMixin,
1174+
AddedAtMixin, AudienceRatingMixin, ContentRatingMixin, CriticRatingMixin, EditionTitleMixin,
1175+
OriginallyAvailableMixin, OriginalTitleMixin, SortTitleMixin,
11491176
StudioMixin, SummaryMixin, TaglineMixin, TitleMixin, UserRatingMixin,
11501177
CollectionMixin, CountryMixin, DirectorMixin, GenreMixin, LabelMixin, ProducerMixin, WriterMixin
11511178
):
@@ -1154,7 +1181,8 @@ class MovieEditMixins(
11541181

11551182
class ShowEditMixins(
11561183
ArtLockMixin, PosterLockMixin, ThemeLockMixin,
1157-
AddedAtMixin, ContentRatingMixin, OriginallyAvailableMixin, OriginalTitleMixin, SortTitleMixin, StudioMixin,
1184+
AddedAtMixin, AudienceRatingMixin, ContentRatingMixin, CriticRatingMixin,
1185+
OriginallyAvailableMixin, OriginalTitleMixin, SortTitleMixin, StudioMixin,
11581186
SummaryMixin, TaglineMixin, TitleMixin, UserRatingMixin,
11591187
CollectionMixin, GenreMixin, LabelMixin,
11601188
):
@@ -1163,39 +1191,44 @@ class ShowEditMixins(
11631191

11641192
class SeasonEditMixins(
11651193
ArtLockMixin, PosterLockMixin, ThemeLockMixin,
1166-
AddedAtMixin, SummaryMixin, TitleMixin, UserRatingMixin,
1194+
AddedAtMixin, AudienceRatingMixin, CriticRatingMixin,
1195+
SummaryMixin, TitleMixin, UserRatingMixin,
11671196
CollectionMixin, LabelMixin
11681197
):
11691198
pass
11701199

11711200

11721201
class EpisodeEditMixins(
11731202
ArtLockMixin, PosterLockMixin, ThemeLockMixin,
1174-
AddedAtMixin, ContentRatingMixin, OriginallyAvailableMixin, SortTitleMixin, SummaryMixin, TitleMixin, UserRatingMixin,
1203+
AddedAtMixin, AudienceRatingMixin, ContentRatingMixin, CriticRatingMixin,
1204+
OriginallyAvailableMixin, SortTitleMixin, SummaryMixin, TitleMixin, UserRatingMixin,
11751205
CollectionMixin, DirectorMixin, LabelMixin, WriterMixin
11761206
):
11771207
pass
11781208

11791209

11801210
class ArtistEditMixins(
11811211
ArtLockMixin, PosterLockMixin, ThemeLockMixin,
1182-
AddedAtMixin, SortTitleMixin, SummaryMixin, TitleMixin, UserRatingMixin,
1212+
AddedAtMixin, AudienceRatingMixin, CriticRatingMixin,
1213+
SortTitleMixin, SummaryMixin, TitleMixin, UserRatingMixin,
11831214
CollectionMixin, CountryMixin, GenreMixin, LabelMixin, MoodMixin, SimilarArtistMixin, StyleMixin
11841215
):
11851216
pass
11861217

11871218

11881219
class AlbumEditMixins(
11891220
ArtLockMixin, PosterLockMixin, ThemeLockMixin,
1190-
AddedAtMixin, OriginallyAvailableMixin, SortTitleMixin, StudioMixin, SummaryMixin, TitleMixin, UserRatingMixin,
1221+
AddedAtMixin, AudienceRatingMixin, CriticRatingMixin,
1222+
OriginallyAvailableMixin, SortTitleMixin, StudioMixin, SummaryMixin, TitleMixin, UserRatingMixin,
11911223
CollectionMixin, GenreMixin, LabelMixin, MoodMixin, StyleMixin
11921224
):
11931225
pass
11941226

11951227

11961228
class TrackEditMixins(
11971229
ArtLockMixin, PosterLockMixin, ThemeLockMixin,
1198-
AddedAtMixin, TitleMixin, TrackArtistMixin, TrackNumberMixin, TrackDiscNumberMixin, UserRatingMixin,
1230+
AddedAtMixin, AudienceRatingMixin, CriticRatingMixin,
1231+
TitleMixin, TrackArtistMixin, TrackNumberMixin, TrackDiscNumberMixin, UserRatingMixin,
11991232
CollectionMixin, GenreMixin, LabelMixin, MoodMixin
12001233
):
12011234
pass
@@ -1218,7 +1251,8 @@ class PhotoEditMixins(
12181251

12191252
class CollectionEditMixins(
12201253
ArtLockMixin, PosterLockMixin, ThemeLockMixin,
1221-
AddedAtMixin, ContentRatingMixin, SortTitleMixin, SummaryMixin, TitleMixin, UserRatingMixin,
1254+
AddedAtMixin, AudienceRatingMixin, ContentRatingMixin, CriticRatingMixin,
1255+
SortTitleMixin, SummaryMixin, TitleMixin, UserRatingMixin,
12221256
LabelMixin
12231257
):
12241258
pass

plexapi/video.py

+4
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,7 @@ class Season(
713713
Attributes:
714714
TAG (str): 'Directory'
715715
TYPE (str): 'season'
716+
audienceRating (float): Audience rating.
716717
audioLanguage (str): Setting that indicates the preferred audio language.
717718
collections (List<:class:`~plexapi.media.Collection`>): List of collection objects.
718719
guids (List<:class:`~plexapi.media.Guid`>): List of guid objects.
@@ -729,6 +730,7 @@ class Season(
729730
parentTheme (str): URL to show theme resource (/library/metadata/<parentRatingkey>/theme/<themeid>).
730731
parentThumb (str): URL to show thumbnail image (/library/metadata/<parentRatingKey>/thumb/<thumbid>).
731732
parentTitle (str): Name of the show for the season.
733+
rating (float): Season rating (7.9; 9.8; 8.1).
732734
ratings (List<:class:`~plexapi.media.Rating`>): List of rating objects.
733735
subtitleLanguage (str): Setting that indicates the preferred subtitle language.
734736
subtitleMode (int): Setting that indicates the auto-select subtitle mode.
@@ -743,6 +745,7 @@ class Season(
743745
def _loadData(self, data):
744746
""" Load attribute values from Plex XML response. """
745747
Video._loadData(self, data)
748+
self.audienceRating = utils.cast(float, data.attrib.get('audienceRating'))
746749
self.audioLanguage = data.attrib.get('audioLanguage', '')
747750
self.collections = self.findItems(data, media.Collection)
748751
self.guids = self.findItems(data, media.Guid)
@@ -759,6 +762,7 @@ def _loadData(self, data):
759762
self.parentTheme = data.attrib.get('parentTheme')
760763
self.parentThumb = data.attrib.get('parentThumb')
761764
self.parentTitle = data.attrib.get('parentTitle')
765+
self.rating = utils.cast(float, data.attrib.get('rating'))
762766
self.ratings = self.findItems(data, media.Rating)
763767
self.subtitleLanguage = data.attrib.get('subtitleLanguage', '')
764768
self.subtitleMode = utils.cast(int, data.attrib.get('subtitleMode', '-1'))

tests/test_audio.py

+6
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ def test_audio_Artist_mixins_rating(artist):
112112

113113
def test_audio_Artist_mixins_fields(artist):
114114
test_mixins.edit_added_at(artist)
115+
test_mixins.edit_audience_rating(artist)
116+
test_mixins.edit_critic_rating(artist)
115117
test_mixins.edit_sort_title(artist)
116118
test_mixins.edit_summary(artist)
117119
test_mixins.edit_title(artist)
@@ -239,6 +241,8 @@ def test_audio_Album_mixins_rating(album):
239241

240242
def test_audio_Album_mixins_fields(album):
241243
test_mixins.edit_added_at(album)
244+
test_mixins.edit_audience_rating(album)
245+
test_mixins.edit_critic_rating(album)
242246
test_mixins.edit_originally_available(album)
243247
test_mixins.edit_sort_title(album)
244248
test_mixins.edit_studio(album)
@@ -422,6 +426,8 @@ def test_audio_Track_mixins_rating(track):
422426

423427
def test_audio_Track_mixins_fields(track):
424428
test_mixins.edit_added_at(track)
429+
test_mixins.edit_audience_rating(track)
430+
test_mixins.edit_critic_rating(track)
425431
test_mixins.edit_title(track)
426432
test_mixins.edit_track_artist(track)
427433
test_mixins.edit_track_number(track)

tests/test_collection.py

+2
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,9 @@ def test_Collection_mixins_rating(collection):
366366

367367
def test_Collection_mixins_fields(collection):
368368
test_mixins.edit_added_at(collection)
369+
test_mixins.edit_audience_rating(collection)
369370
test_mixins.edit_content_rating(collection)
371+
test_mixins.edit_critic_rating(collection)
370372
test_mixins.edit_sort_title(collection)
371373
test_mixins.edit_summary(collection)
372374
test_mixins.edit_title(collection)

tests/test_mixins.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,18 @@ def edit_added_at(obj):
5151
_test_mixins_field(obj, "addedAt", "AddedAt")
5252

5353

54+
def edit_audience_rating(obj):
55+
_test_mixins_field(obj, "audienceRating", "AudienceRating", default=None, value=7.7)
56+
57+
5458
def edit_content_rating(obj):
5559
_test_mixins_field(obj, "contentRating", "ContentRating")
5660

5761

62+
def edit_critic_rating(obj):
63+
_test_mixins_field(obj, "rating", "CriticRating", default=None, value=8.8)
64+
65+
5866
def edit_edition_title(obj):
5967
_test_mixins_field(obj, "editionTitle", "EditionTitle")
6068

@@ -104,7 +112,7 @@ def edit_photo_captured_time(obj):
104112

105113

106114
def edit_user_rating(obj):
107-
_test_mixins_field(obj, "userRating", "UserRating", default=None, value=10)
115+
_test_mixins_field(obj, "userRating", "UserRating", default=None, value=10.0)
108116

109117

110118
def _test_mixins_tag(obj, attr, tag_method):

tests/test_video.py

+8
Original file line numberDiff line numberDiff line change
@@ -689,7 +689,9 @@ def test_video_Movie_mixins_rating(movie):
689689

690690
def test_video_Movie_mixins_fields(movie):
691691
test_mixins.edit_added_at(movie)
692+
test_mixins.edit_audience_rating(movie)
692693
test_mixins.edit_content_rating(movie)
694+
test_mixins.edit_critic_rating(movie)
693695
test_mixins.edit_originally_available(movie)
694696
test_mixins.edit_original_title(movie)
695697
test_mixins.edit_sort_title(movie)
@@ -955,7 +957,9 @@ def test_video_Show_mixins_rating(show):
955957

956958
def test_video_Show_mixins_fields(show):
957959
test_mixins.edit_added_at(show)
960+
test_mixins.edit_audience_rating(show)
958961
test_mixins.edit_content_rating(show)
962+
test_mixins.edit_critic_rating(show)
959963
test_mixins.edit_originally_available(show)
960964
test_mixins.edit_original_title(show)
961965
test_mixins.edit_sort_title(show)
@@ -1112,6 +1116,8 @@ def test_video_Season_mixins_rating(show):
11121116
def test_video_Season_mixins_fields(show):
11131117
season = show.season(season=1)
11141118
test_mixins.edit_added_at(season)
1119+
test_mixins.edit_audience_rating(season)
1120+
test_mixins.edit_critic_rating(season)
11151121
test_mixins.edit_summary(season)
11161122
test_mixins.edit_title(season)
11171123
test_mixins.edit_user_rating(season)
@@ -1322,7 +1328,9 @@ def test_video_Episode_mixins_rating(episode):
13221328

13231329
def test_video_Episode_mixins_fields(episode):
13241330
test_mixins.edit_added_at(episode)
1331+
test_mixins.edit_audience_rating(episode)
13251332
test_mixins.edit_content_rating(episode)
1333+
test_mixins.edit_critic_rating(episode)
13261334
test_mixins.edit_originally_available(episode)
13271335
test_mixins.edit_sort_title(episode)
13281336
test_mixins.edit_summary(episode)

0 commit comments

Comments
 (0)