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
This commit is contained in:
JonnyWong16 2024-05-18 12:16:49 -07:00 committed by GitHub
parent 7b317ef822
commit 2142514955
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 86 additions and 10 deletions

View file

@ -181,6 +181,7 @@ class Artist(
TYPE (str): 'artist'
albumSort (int): Setting that indicates how albums are sorted for the artist
(-1 = Library default, 0 = Newest first, 1 = Oldest first, 2 = By name).
audienceRating (float): Audience rating.
collections (List<:class:`~plexapi.media.Collection`>): List of collection objects.
countries (List<:class:`~plexapi.media.Country`>): List country objects.
genres (List<:class:`~plexapi.media.Genre`>): List of genre objects.
@ -188,6 +189,7 @@ class Artist(
key (str): API URL (/library/metadata/<ratingkey>).
labels (List<:class:`~plexapi.media.Label`>): List of label objects.
locations (List<str>): List of folder paths where the artist is found on disk.
rating (float): Artist rating (7.9; 9.8; 8.1).
similar (List<:class:`~plexapi.media.Similar`>): List of similar objects.
styles (List<:class:`~plexapi.media.Style`>): List of style objects.
theme (str): URL to theme resource (/library/metadata/<ratingkey>/theme/<themeid>).
@ -199,6 +201,7 @@ class Artist(
""" Load attribute values from Plex XML response. """
Audio._loadData(self, data)
self.albumSort = utils.cast(int, data.attrib.get('albumSort', '-1'))
self.audienceRating = utils.cast(float, data.attrib.get('audienceRating'))
self.collections = self.findItems(data, media.Collection)
self.countries = self.findItems(data, media.Country)
self.genres = self.findItems(data, media.Genre)
@ -206,6 +209,7 @@ class Artist(
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.rating = utils.cast(float, data.attrib.get('rating'))
self.similar = self.findItems(data, media.Similar)
self.styles = self.findItems(data, media.Style)
self.theme = data.attrib.get('theme')
@ -301,6 +305,7 @@ class Album(
Attributes:
TAG (str): 'Directory'
TYPE (str): 'album'
audienceRating (float): Audience rating.
collections (List<:class:`~plexapi.media.Collection`>): List of collection objects.
formats (List<:class:`~plexapi.media.Format`>): List of format objects.
genres (List<:class:`~plexapi.media.Genre`>): List of genre objects.
@ -329,6 +334,7 @@ class Album(
def _loadData(self, data):
""" Load attribute values from Plex XML response. """
Audio._loadData(self, data)
self.audienceRating = utils.cast(float, data.attrib.get('audienceRating'))
self.collections = self.findItems(data, media.Collection)
self.formats = self.findItems(data, media.Format)
self.genres = self.findItems(data, media.Genre)
@ -426,6 +432,7 @@ class Track(
Attributes:
TAG (str): 'Directory'
TYPE (str): 'track'
audienceRating (float): Audience rating.
chapters (List<:class:`~plexapi.media.Chapter`>): List of Chapter objects.
chapterSource (str): Unknown
collections (List<:class:`~plexapi.media.Collection`>): List of collection objects.
@ -451,6 +458,7 @@ class Track(
parentThumb (str): URL to album thumbnail image (/library/metadata/<parentRatingKey>/thumb/<thumbid>).
parentTitle (str): Name of the album for the track.
primaryExtraKey (str) API URL for the primary extra for the track.
rating (float): Track rating (7.9; 9.8; 8.1).
ratingCount (int): Number of listeners who have scrobbled this track, as reported by Last.fm.
skipCount (int): Number of times the track has been skipped.
sourceURI (str): Remote server URI (server://<machineIdentifier>/com.plexapp.plugins.library)
@ -465,6 +473,7 @@ class Track(
""" Load attribute values from Plex XML response. """
Audio._loadData(self, data)
Playable._loadData(self, data)
self.audienceRating = utils.cast(float, data.attrib.get('audienceRating'))
self.chapters = self.findItems(data, media.Chapter)
self.chapterSource = data.attrib.get('chapterSource')
self.collections = self.findItems(data, media.Collection)
@ -488,6 +497,7 @@ class Track(
self.parentThumb = data.attrib.get('parentThumb')
self.parentTitle = data.attrib.get('parentTitle')
self.primaryExtraKey = data.attrib.get('primaryExtraKey')
self.rating = utils.cast(float, data.attrib.get('rating'))
self.ratingCount = utils.cast(int, data.attrib.get('ratingCount'))
self.skipCount = utils.cast(int, data.attrib.get('skipCount'))
self.sourceURI = data.attrib.get('source') # remote playlist item

View file

@ -29,6 +29,7 @@ class Collection(
addedAt (datetime): Datetime the collection was added to the library.
art (str): URL to artwork image (/library/metadata/<ratingKey>/art/<artid>).
artBlurHash (str): BlurHash string for artwork image.
audienceRating (float): Audience rating.
childCount (int): Number of items in the collection.
collectionFilterBasedOnUser (int): Which user's activity is used for the collection filtering.
collectionMode (int): How the items in the collection are displayed.
@ -47,6 +48,7 @@ class Collection(
librarySectionTitle (str): :class:`~plexapi.library.LibrarySection` title.
maxYear (int): Maximum year for the items in the collection.
minYear (int): Minimum year for the items in the collection.
rating (float): Collection rating (7.9; 9.8; 8.1).
ratingCount (int): The number of ratings.
ratingKey (int): Unique key identifying the collection.
smart (bool): True if the collection is a smart collection.
@ -69,6 +71,7 @@ class Collection(
self.addedAt = utils.toDatetime(data.attrib.get('addedAt'))
self.art = data.attrib.get('art')
self.artBlurHash = data.attrib.get('artBlurHash')
self.audienceRating = utils.cast(float, data.attrib.get('audienceRating'))
self.childCount = utils.cast(int, data.attrib.get('childCount'))
self.collectionFilterBasedOnUser = utils.cast(int, data.attrib.get('collectionFilterBasedOnUser', '0'))
self.collectionMode = utils.cast(int, data.attrib.get('collectionMode', '-1'))
@ -87,6 +90,7 @@ class Collection(
self.librarySectionTitle = data.attrib.get('librarySectionTitle')
self.maxYear = utils.cast(int, data.attrib.get('maxYear'))
self.minYear = utils.cast(int, data.attrib.get('minYear'))
self.rating = utils.cast(float, data.attrib.get('rating'))
self.ratingCount = utils.cast(int, data.attrib.get('ratingCount'))
self.ratingKey = utils.cast(int, data.attrib.get('ratingKey'))
self.smart = utils.cast(bool, data.attrib.get('smart', '0'))

View file

@ -567,6 +567,19 @@ class AddedAtMixin(EditFieldMixin):
return self.editField('addedAt', addedAt, locked=locked)
class AudienceRatingMixin(EditFieldMixin):
""" Mixin for Plex objects that can have an audience rating. """
def editAudienceRating(self, audienceRating, locked=True):
""" Edit the audience rating.
Parameters:
audienceRating (float): The new value.
locked (bool): True (default) to lock the field, False to unlock the field.
"""
return self.editField('audienceRating', audienceRating, locked=locked)
class ContentRatingMixin(EditFieldMixin):
""" Mixin for Plex objects that can have a content rating. """
@ -580,6 +593,19 @@ class ContentRatingMixin(EditFieldMixin):
return self.editField('contentRating', contentRating, locked=locked)
class CriticRatingMixin(EditFieldMixin):
""" Mixin for Plex objects that can have a critic rating. """
def editCriticRating(self, criticRating, locked=True):
""" Edit the critic rating.
Parameters:
criticRating (float): The new value.
locked (bool): True (default) to lock the field, False to unlock the field.
"""
return self.editField('rating', criticRating, locked=locked)
class EditionTitleMixin(EditFieldMixin):
""" Mixin for Plex objects that can have an edition title. """
@ -751,7 +777,7 @@ class UserRatingMixin(EditFieldMixin):
""" Edit the user rating.
Parameters:
userRating (int): The new value.
userRating (float): The new value.
locked (bool): True (default) to lock the field, False to unlock the field.
"""
return self.editField('userRating', userRating, locked=locked)
@ -1145,7 +1171,8 @@ class WatchlistMixin:
class MovieEditMixins(
ArtLockMixin, PosterLockMixin, ThemeLockMixin,
AddedAtMixin, ContentRatingMixin, EditionTitleMixin, OriginallyAvailableMixin, OriginalTitleMixin, SortTitleMixin,
AddedAtMixin, AudienceRatingMixin, ContentRatingMixin, CriticRatingMixin, EditionTitleMixin,
OriginallyAvailableMixin, OriginalTitleMixin, SortTitleMixin,
StudioMixin, SummaryMixin, TaglineMixin, TitleMixin, UserRatingMixin,
CollectionMixin, CountryMixin, DirectorMixin, GenreMixin, LabelMixin, ProducerMixin, WriterMixin
):
@ -1154,7 +1181,8 @@ class MovieEditMixins(
class ShowEditMixins(
ArtLockMixin, PosterLockMixin, ThemeLockMixin,
AddedAtMixin, ContentRatingMixin, OriginallyAvailableMixin, OriginalTitleMixin, SortTitleMixin, StudioMixin,
AddedAtMixin, AudienceRatingMixin, ContentRatingMixin, CriticRatingMixin,
OriginallyAvailableMixin, OriginalTitleMixin, SortTitleMixin, StudioMixin,
SummaryMixin, TaglineMixin, TitleMixin, UserRatingMixin,
CollectionMixin, GenreMixin, LabelMixin,
):
@ -1163,7 +1191,8 @@ class ShowEditMixins(
class SeasonEditMixins(
ArtLockMixin, PosterLockMixin, ThemeLockMixin,
AddedAtMixin, SummaryMixin, TitleMixin, UserRatingMixin,
AddedAtMixin, AudienceRatingMixin, CriticRatingMixin,
SummaryMixin, TitleMixin, UserRatingMixin,
CollectionMixin, LabelMixin
):
pass
@ -1171,7 +1200,8 @@ class SeasonEditMixins(
class EpisodeEditMixins(
ArtLockMixin, PosterLockMixin, ThemeLockMixin,
AddedAtMixin, ContentRatingMixin, OriginallyAvailableMixin, SortTitleMixin, SummaryMixin, TitleMixin, UserRatingMixin,
AddedAtMixin, AudienceRatingMixin, ContentRatingMixin, CriticRatingMixin,
OriginallyAvailableMixin, SortTitleMixin, SummaryMixin, TitleMixin, UserRatingMixin,
CollectionMixin, DirectorMixin, LabelMixin, WriterMixin
):
pass
@ -1179,7 +1209,8 @@ class EpisodeEditMixins(
class ArtistEditMixins(
ArtLockMixin, PosterLockMixin, ThemeLockMixin,
AddedAtMixin, SortTitleMixin, SummaryMixin, TitleMixin, UserRatingMixin,
AddedAtMixin, AudienceRatingMixin, CriticRatingMixin,
SortTitleMixin, SummaryMixin, TitleMixin, UserRatingMixin,
CollectionMixin, CountryMixin, GenreMixin, LabelMixin, MoodMixin, SimilarArtistMixin, StyleMixin
):
pass
@ -1187,7 +1218,8 @@ class ArtistEditMixins(
class AlbumEditMixins(
ArtLockMixin, PosterLockMixin, ThemeLockMixin,
AddedAtMixin, OriginallyAvailableMixin, SortTitleMixin, StudioMixin, SummaryMixin, TitleMixin, UserRatingMixin,
AddedAtMixin, AudienceRatingMixin, CriticRatingMixin,
OriginallyAvailableMixin, SortTitleMixin, StudioMixin, SummaryMixin, TitleMixin, UserRatingMixin,
CollectionMixin, GenreMixin, LabelMixin, MoodMixin, StyleMixin
):
pass
@ -1195,7 +1227,8 @@ class AlbumEditMixins(
class TrackEditMixins(
ArtLockMixin, PosterLockMixin, ThemeLockMixin,
AddedAtMixin, TitleMixin, TrackArtistMixin, TrackNumberMixin, TrackDiscNumberMixin, UserRatingMixin,
AddedAtMixin, AudienceRatingMixin, CriticRatingMixin,
TitleMixin, TrackArtistMixin, TrackNumberMixin, TrackDiscNumberMixin, UserRatingMixin,
CollectionMixin, GenreMixin, LabelMixin, MoodMixin
):
pass
@ -1218,7 +1251,8 @@ class PhotoEditMixins(
class CollectionEditMixins(
ArtLockMixin, PosterLockMixin, ThemeLockMixin,
AddedAtMixin, ContentRatingMixin, SortTitleMixin, SummaryMixin, TitleMixin, UserRatingMixin,
AddedAtMixin, AudienceRatingMixin, ContentRatingMixin, CriticRatingMixin,
SortTitleMixin, SummaryMixin, TitleMixin, UserRatingMixin,
LabelMixin
):
pass

View file

@ -713,6 +713,7 @@ class Season(
Attributes:
TAG (str): 'Directory'
TYPE (str): 'season'
audienceRating (float): Audience rating.
audioLanguage (str): Setting that indicates the preferred audio language.
collections (List<:class:`~plexapi.media.Collection`>): List of collection objects.
guids (List<:class:`~plexapi.media.Guid`>): List of guid objects.
@ -729,6 +730,7 @@ class Season(
parentTheme (str): URL to show theme resource (/library/metadata/<parentRatingkey>/theme/<themeid>).
parentThumb (str): URL to show thumbnail image (/library/metadata/<parentRatingKey>/thumb/<thumbid>).
parentTitle (str): Name of the show for the season.
rating (float): Season rating (7.9; 9.8; 8.1).
ratings (List<:class:`~plexapi.media.Rating`>): List of rating objects.
subtitleLanguage (str): Setting that indicates the preferred subtitle language.
subtitleMode (int): Setting that indicates the auto-select subtitle mode.
@ -743,6 +745,7 @@ class Season(
def _loadData(self, data):
""" Load attribute values from Plex XML response. """
Video._loadData(self, data)
self.audienceRating = utils.cast(float, data.attrib.get('audienceRating'))
self.audioLanguage = data.attrib.get('audioLanguage', '')
self.collections = self.findItems(data, media.Collection)
self.guids = self.findItems(data, media.Guid)
@ -759,6 +762,7 @@ class Season(
self.parentTheme = data.attrib.get('parentTheme')
self.parentThumb = data.attrib.get('parentThumb')
self.parentTitle = data.attrib.get('parentTitle')
self.rating = utils.cast(float, data.attrib.get('rating'))
self.ratings = self.findItems(data, media.Rating)
self.subtitleLanguage = data.attrib.get('subtitleLanguage', '')
self.subtitleMode = utils.cast(int, data.attrib.get('subtitleMode', '-1'))

View file

@ -112,6 +112,8 @@ def test_audio_Artist_mixins_rating(artist):
def test_audio_Artist_mixins_fields(artist):
test_mixins.edit_added_at(artist)
test_mixins.edit_audience_rating(artist)
test_mixins.edit_critic_rating(artist)
test_mixins.edit_sort_title(artist)
test_mixins.edit_summary(artist)
test_mixins.edit_title(artist)
@ -239,6 +241,8 @@ def test_audio_Album_mixins_rating(album):
def test_audio_Album_mixins_fields(album):
test_mixins.edit_added_at(album)
test_mixins.edit_audience_rating(album)
test_mixins.edit_critic_rating(album)
test_mixins.edit_originally_available(album)
test_mixins.edit_sort_title(album)
test_mixins.edit_studio(album)
@ -422,6 +426,8 @@ def test_audio_Track_mixins_rating(track):
def test_audio_Track_mixins_fields(track):
test_mixins.edit_added_at(track)
test_mixins.edit_audience_rating(track)
test_mixins.edit_critic_rating(track)
test_mixins.edit_title(track)
test_mixins.edit_track_artist(track)
test_mixins.edit_track_number(track)

View file

@ -366,7 +366,9 @@ def test_Collection_mixins_rating(collection):
def test_Collection_mixins_fields(collection):
test_mixins.edit_added_at(collection)
test_mixins.edit_audience_rating(collection)
test_mixins.edit_content_rating(collection)
test_mixins.edit_critic_rating(collection)
test_mixins.edit_sort_title(collection)
test_mixins.edit_summary(collection)
test_mixins.edit_title(collection)

View file

@ -51,10 +51,18 @@ def edit_added_at(obj):
_test_mixins_field(obj, "addedAt", "AddedAt")
def edit_audience_rating(obj):
_test_mixins_field(obj, "audienceRating", "AudienceRating", default=None, value=7.7)
def edit_content_rating(obj):
_test_mixins_field(obj, "contentRating", "ContentRating")
def edit_critic_rating(obj):
_test_mixins_field(obj, "rating", "CriticRating", default=None, value=8.8)
def edit_edition_title(obj):
_test_mixins_field(obj, "editionTitle", "EditionTitle")
@ -104,7 +112,7 @@ def edit_photo_captured_time(obj):
def edit_user_rating(obj):
_test_mixins_field(obj, "userRating", "UserRating", default=None, value=10)
_test_mixins_field(obj, "userRating", "UserRating", default=None, value=10.0)
def _test_mixins_tag(obj, attr, tag_method):

View file

@ -689,7 +689,9 @@ def test_video_Movie_mixins_rating(movie):
def test_video_Movie_mixins_fields(movie):
test_mixins.edit_added_at(movie)
test_mixins.edit_audience_rating(movie)
test_mixins.edit_content_rating(movie)
test_mixins.edit_critic_rating(movie)
test_mixins.edit_originally_available(movie)
test_mixins.edit_original_title(movie)
test_mixins.edit_sort_title(movie)
@ -955,7 +957,9 @@ def test_video_Show_mixins_rating(show):
def test_video_Show_mixins_fields(show):
test_mixins.edit_added_at(show)
test_mixins.edit_audience_rating(show)
test_mixins.edit_content_rating(show)
test_mixins.edit_critic_rating(show)
test_mixins.edit_originally_available(show)
test_mixins.edit_original_title(show)
test_mixins.edit_sort_title(show)
@ -1112,6 +1116,8 @@ def test_video_Season_mixins_rating(show):
def test_video_Season_mixins_fields(show):
season = show.season(season=1)
test_mixins.edit_added_at(season)
test_mixins.edit_audience_rating(season)
test_mixins.edit_critic_rating(season)
test_mixins.edit_summary(season)
test_mixins.edit_title(season)
test_mixins.edit_user_rating(season)
@ -1322,7 +1328,9 @@ def test_video_Episode_mixins_rating(episode):
def test_video_Episode_mixins_fields(episode):
test_mixins.edit_added_at(episode)
test_mixins.edit_audience_rating(episode)
test_mixins.edit_content_rating(episode)
test_mixins.edit_critic_rating(episode)
test_mixins.edit_originally_available(episode)
test_mixins.edit_sort_title(episode)
test_mixins.edit_summary(episode)