mirror of
https://github.com/pkkid/python-plexapi
synced 2024-11-22 03:33:08 +00:00
Add Themes Support (#879)
* Fix typo in 'grandparentThumb' * Add Themes support * Fix Themes for Seasons and Episodes Themes are not available for Seasons or Episodes according to this: https://web.archive.org/web/20150113085312/http://dev.plexapp.com/docs/agents/models.html * Add Themes to Artists, Albums, and Tracks Themes are available for Artists according to this: https://web.archive.org/web/20150113085312/http://dev.plexapp.com/docs/agents/models.html * Update per PR review * Update BaseResource and Theme classes Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>
This commit is contained in:
parent
2f101e3899
commit
9a5170c2fb
5 changed files with 98 additions and 31 deletions
|
@ -5,9 +5,11 @@ from urllib.parse import quote_plus
|
|||
from plexapi import library, media, utils
|
||||
from plexapi.base import Playable, PlexPartialObject
|
||||
from plexapi.exceptions import BadRequest
|
||||
from plexapi.mixins import AdvancedSettingsMixin, ArtUrlMixin, ArtMixin, PosterUrlMixin, PosterMixin
|
||||
from plexapi.mixins import AdvancedSettingsMixin, ArtUrlMixin, ArtMixin, PosterUrlMixin, PosterMixin, ThemeMixin, \
|
||||
ThemeUrlMixin
|
||||
from plexapi.mixins import RatingMixin, SplitMergeMixin, UnmatchMatchMixin
|
||||
from plexapi.mixins import CollectionMixin, CountryMixin, GenreMixin, LabelMixin, MoodMixin, SimilarArtistMixin, StyleMixin
|
||||
from plexapi.mixins import CollectionMixin, CountryMixin, GenreMixin, LabelMixin, MoodMixin, SimilarArtistMixin, \
|
||||
StyleMixin
|
||||
from plexapi.playlist import Playlist
|
||||
|
||||
|
||||
|
@ -125,8 +127,9 @@ class Audio(PlexPartialObject):
|
|||
|
||||
|
||||
@utils.registerPlexObject
|
||||
class Artist(Audio, AdvancedSettingsMixin, ArtMixin, PosterMixin, RatingMixin, SplitMergeMixin, UnmatchMatchMixin,
|
||||
CollectionMixin, CountryMixin, GenreMixin, LabelMixin, MoodMixin, SimilarArtistMixin, StyleMixin):
|
||||
class Artist(Audio, AdvancedSettingsMixin, ArtMixin, PosterMixin, ThemeMixin, RatingMixin, SplitMergeMixin,
|
||||
UnmatchMatchMixin, CollectionMixin, CountryMixin, GenreMixin, LabelMixin, MoodMixin,
|
||||
SimilarArtistMixin, StyleMixin):
|
||||
""" Represents a single Artist.
|
||||
|
||||
Attributes:
|
||||
|
@ -142,6 +145,7 @@ class Artist(Audio, AdvancedSettingsMixin, ArtMixin, PosterMixin, RatingMixin, S
|
|||
locations (List<str>): 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.
|
||||
theme (str): URL to theme resource (/library/metadata/<ratingkey>/theme/<themeid>).
|
||||
"""
|
||||
TAG = 'Directory'
|
||||
TYPE = 'artist'
|
||||
|
@ -158,6 +162,7 @@ class Artist(Audio, AdvancedSettingsMixin, ArtMixin, PosterMixin, RatingMixin, S
|
|||
self.locations = self.listAttrs(data, 'path', etag='Location')
|
||||
self.similar = self.findItems(data, media.Similar)
|
||||
self.styles = self.findItems(data, media.Style)
|
||||
self.theme = data.attrib.get('theme')
|
||||
|
||||
def __iter__(self):
|
||||
for album in self.albums():
|
||||
|
@ -232,8 +237,8 @@ class Artist(Audio, AdvancedSettingsMixin, ArtMixin, PosterMixin, RatingMixin, S
|
|||
|
||||
|
||||
@utils.registerPlexObject
|
||||
class Album(Audio, ArtMixin, PosterMixin, RatingMixin, UnmatchMatchMixin,
|
||||
CollectionMixin, GenreMixin, LabelMixin, MoodMixin, StyleMixin):
|
||||
class Album(Audio, ArtMixin, PosterMixin, ThemeUrlMixin, RatingMixin, UnmatchMatchMixin,
|
||||
CollectionMixin, GenreMixin, LabelMixin, MoodMixin, StyleMixin):
|
||||
""" Represents a single Album.
|
||||
|
||||
Attributes:
|
||||
|
@ -250,6 +255,7 @@ class Album(Audio, ArtMixin, PosterMixin, RatingMixin, UnmatchMatchMixin,
|
|||
parentGuid (str): Plex GUID for the album artist (plex://artist/5d07bcb0403c64029053ac4c).
|
||||
parentKey (str): API URL of the album artist (/library/metadata/<parentRatingKey>).
|
||||
parentRatingKey (int): Unique key identifying the album artist.
|
||||
parentTheme (str): URL to artist theme resource (/library/metadata/<parentRatingkey>/theme/<themeid>).
|
||||
parentThumb (str): URL to album artist thumbnail image (/library/metadata/<parentRatingKey>/thumb/<thumbid>).
|
||||
parentTitle (str): Name of the album artist.
|
||||
rating (float): Album rating (7.9; 9.8; 8.1).
|
||||
|
@ -276,6 +282,7 @@ class Album(Audio, ArtMixin, PosterMixin, RatingMixin, UnmatchMatchMixin,
|
|||
self.parentGuid = data.attrib.get('parentGuid')
|
||||
self.parentKey = data.attrib.get('parentKey')
|
||||
self.parentRatingKey = utils.cast(int, data.attrib.get('parentRatingKey'))
|
||||
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'))
|
||||
|
@ -339,7 +346,7 @@ class Album(Audio, ArtMixin, PosterMixin, RatingMixin, UnmatchMatchMixin,
|
|||
|
||||
|
||||
@utils.registerPlexObject
|
||||
class Track(Audio, Playable, ArtUrlMixin, PosterUrlMixin, RatingMixin,
|
||||
class Track(Audio, Playable, ArtUrlMixin, PosterUrlMixin, ThemeUrlMixin, RatingMixin,
|
||||
CollectionMixin, LabelMixin, MoodMixin):
|
||||
""" Represents a single Track.
|
||||
|
||||
|
@ -353,6 +360,8 @@ class Track(Audio, Playable, ArtUrlMixin, PosterUrlMixin, RatingMixin,
|
|||
grandparentGuid (str): Plex GUID for the album artist (plex://artist/5d07bcb0403c64029053ac4c).
|
||||
grandparentKey (str): API URL of the album artist (/library/metadata/<grandparentRatingKey>).
|
||||
grandparentRatingKey (int): Unique key identifying the album artist.
|
||||
grandparentTheme (str): URL to artist theme resource (/library/metadata/<grandparentRatingkey>/theme/<themeid>).
|
||||
(/library/metadata/<grandparentRatingkey>/theme/<themeid>).
|
||||
grandparentThumb (str): URL to album artist thumbnail image
|
||||
(/library/metadata/<grandparentRatingKey>/thumb/<thumbid>).
|
||||
grandparentTitle (str): Name of the album artist for the track.
|
||||
|
@ -384,6 +393,7 @@ class Track(Audio, Playable, ArtUrlMixin, PosterUrlMixin, RatingMixin,
|
|||
self.grandparentGuid = data.attrib.get('grandparentGuid')
|
||||
self.grandparentKey = data.attrib.get('grandparentKey')
|
||||
self.grandparentRatingKey = utils.cast(int, data.attrib.get('grandparentRatingKey'))
|
||||
self.grandparentTheme = data.attrib.get('grandparentTheme')
|
||||
self.grandparentThumb = data.attrib.get('grandparentThumb')
|
||||
self.grandparentTitle = data.attrib.get('grandparentTitle')
|
||||
self.labels = self.findItems(data, media.Label)
|
||||
|
|
|
@ -5,14 +5,15 @@ from plexapi import media, utils
|
|||
from plexapi.base import PlexPartialObject
|
||||
from plexapi.exceptions import BadRequest, NotFound, Unsupported
|
||||
from plexapi.library import LibrarySection
|
||||
from plexapi.mixins import AdvancedSettingsMixin, ArtMixin, PosterMixin, RatingMixin
|
||||
from plexapi.mixins import AdvancedSettingsMixin, ArtMixin, PosterMixin, ThemeMixin, RatingMixin
|
||||
from plexapi.mixins import LabelMixin, SmartFilterMixin
|
||||
from plexapi.playqueue import PlayQueue
|
||||
from plexapi.utils import deprecated
|
||||
|
||||
|
||||
@utils.registerPlexObject
|
||||
class Collection(PlexPartialObject, AdvancedSettingsMixin, ArtMixin, PosterMixin, RatingMixin, LabelMixin, SmartFilterMixin):
|
||||
class Collection(PlexPartialObject, AdvancedSettingsMixin, ArtMixin, PosterMixin, ThemeMixin, RatingMixin,
|
||||
LabelMixin, SmartFilterMixin):
|
||||
""" Represents a single Collection.
|
||||
|
||||
Attributes:
|
||||
|
@ -43,6 +44,7 @@ class Collection(PlexPartialObject, AdvancedSettingsMixin, ArtMixin, PosterMixin
|
|||
smart (bool): True if the collection is a smart collection.
|
||||
subtype (str): Media type of the items in the collection (movie, show, artist, or album).
|
||||
summary (str): Summary of the collection.
|
||||
theme (str): URL to theme resource (/library/metadata/<ratingkey>/theme/<themeid>).
|
||||
thumb (str): URL to thumbnail image (/library/metadata/<ratingKey>/thumb/<thumbid>).
|
||||
thumbBlurHash (str): BlurHash string for thumbnail image.
|
||||
title (str): Name of the collection.
|
||||
|
@ -81,6 +83,7 @@ class Collection(PlexPartialObject, AdvancedSettingsMixin, ArtMixin, PosterMixin
|
|||
self.smart = utils.cast(bool, data.attrib.get('smart', '0'))
|
||||
self.subtype = data.attrib.get('subtype')
|
||||
self.summary = data.attrib.get('summary')
|
||||
self.theme = data.attrib.get('theme')
|
||||
self.thumb = data.attrib.get('thumb')
|
||||
self.thumbBlurHash = data.attrib.get('thumbBlurHash')
|
||||
self.title = data.attrib.get('title')
|
||||
|
|
|
@ -917,19 +917,17 @@ class Review(PlexObject):
|
|||
self.text = data.attrib.get('text')
|
||||
|
||||
|
||||
class BaseImage(PlexObject):
|
||||
""" Base class for all Art, Banner, and Poster objects.
|
||||
class BaseResource(PlexObject):
|
||||
""" Base class for all Art, Banner, Poster, and Theme objects.
|
||||
|
||||
Attributes:
|
||||
TAG (str): 'Photo'
|
||||
TAG (str): 'Photo' or 'Track'
|
||||
key (str): API URL (/library/metadata/<ratingkey>).
|
||||
provider (str): The source of the poster or art.
|
||||
ratingKey (str): Unique key identifying the poster or art.
|
||||
selected (bool): True if the poster or art is currently selected.
|
||||
thumb (str): The URL to retrieve the poster or art thumbnail.
|
||||
provider (str): The source of the art or poster, None for Theme objects.
|
||||
ratingKey (str): Unique key identifying the resource.
|
||||
selected (bool): True if the resource is currently selected.
|
||||
thumb (str): The URL to retrieve the resource thumbnail.
|
||||
"""
|
||||
TAG = 'Photo'
|
||||
|
||||
def _loadData(self, data):
|
||||
self._data = data
|
||||
self.key = data.attrib.get('key')
|
||||
|
@ -947,16 +945,24 @@ class BaseImage(PlexObject):
|
|||
pass
|
||||
|
||||
|
||||
class Art(BaseImage):
|
||||
class Art(BaseResource):
|
||||
""" Represents a single Art object. """
|
||||
TAG = 'Photo'
|
||||
|
||||
|
||||
class Banner(BaseImage):
|
||||
class Banner(BaseResource):
|
||||
""" Represents a single Banner object. """
|
||||
TAG = 'Photo'
|
||||
|
||||
|
||||
class Poster(BaseImage):
|
||||
class Poster(BaseResource):
|
||||
""" Represents a single Poster object. """
|
||||
TAG = 'Photo'
|
||||
|
||||
|
||||
class Theme(BaseResource):
|
||||
""" Represents a single Theme object. """
|
||||
TAG = 'Track'
|
||||
|
||||
|
||||
@utils.registerPlexObject
|
||||
|
|
|
@ -161,7 +161,7 @@ class PosterUrlMixin(object):
|
|||
@property
|
||||
def thumbUrl(self):
|
||||
""" Return the thumb url for the Plex object. """
|
||||
thumb = self.firstAttr('thumb', 'parentThumb', 'granparentThumb')
|
||||
thumb = self.firstAttr('thumb', 'parentThumb', 'grandparentThumb')
|
||||
return self._server.url(thumb, includeToken=True) if thumb else None
|
||||
|
||||
@property
|
||||
|
@ -209,6 +209,49 @@ class PosterMixin(PosterUrlMixin):
|
|||
self._edit(**{'thumb.locked': 0})
|
||||
|
||||
|
||||
class ThemeUrlMixin(object):
|
||||
""" Mixin for Plex objects that can have a theme url. """
|
||||
|
||||
@property
|
||||
def themeUrl(self):
|
||||
""" Return the theme url for the Plex object. """
|
||||
theme = self.firstAttr('theme', 'parentTheme', 'grandparentTheme')
|
||||
return self._server.url(theme, includeToken=True) if theme else None
|
||||
|
||||
|
||||
class ThemeMixin(ThemeUrlMixin):
|
||||
""" Mixin for Plex objects that can have themes. """
|
||||
|
||||
def themes(self):
|
||||
""" Returns list of available :class:`~plexapi.media.Theme` objects. """
|
||||
return self.fetchItems('/library/metadata/%s/themes' % self.ratingKey, cls=media.Theme)
|
||||
|
||||
def uploadTheme(self, url=None, filepath=None):
|
||||
""" Upload a theme from url or filepath.
|
||||
|
||||
Warning: Themes cannot be deleted using PlexAPI!
|
||||
|
||||
Parameters:
|
||||
url (str): The full URL to the theme to upload.
|
||||
filepath (str): The full file path to the theme to upload.
|
||||
"""
|
||||
if url:
|
||||
key = '/library/metadata/%s/themes?url=%s' % (self.ratingKey, quote_plus(url))
|
||||
self._server.query(key, method=self._server._session.post)
|
||||
elif filepath:
|
||||
key = '/library/metadata/%s/themes?' % self.ratingKey
|
||||
data = open(filepath, 'rb').read()
|
||||
self._server.query(key, method=self._server._session.post, data=data)
|
||||
|
||||
def setTheme(self, theme):
|
||||
""" Set the theme for a Plex object.
|
||||
|
||||
Parameters:
|
||||
theme (:class:`~plexapi.media.Theme`): The theme object to select.
|
||||
"""
|
||||
theme.select()
|
||||
|
||||
|
||||
class RatingMixin(object):
|
||||
""" Mixin for Plex objects that can have user star ratings. """
|
||||
|
||||
|
|
|
@ -5,9 +5,11 @@ from urllib.parse import quote_plus, urlencode
|
|||
from plexapi import library, media, utils
|
||||
from plexapi.base import Playable, PlexPartialObject
|
||||
from plexapi.exceptions import BadRequest
|
||||
from plexapi.mixins import AdvancedSettingsMixin, ArtUrlMixin, ArtMixin, BannerMixin, PosterUrlMixin, PosterMixin
|
||||
from plexapi.mixins import AdvancedSettingsMixin, ArtUrlMixin, ArtMixin, BannerMixin, PosterUrlMixin, PosterMixin, \
|
||||
ThemeUrlMixin, ThemeMixin
|
||||
from plexapi.mixins import RatingMixin, SplitMergeMixin, UnmatchMatchMixin
|
||||
from plexapi.mixins import CollectionMixin, CountryMixin, DirectorMixin, GenreMixin, LabelMixin, ProducerMixin, WriterMixin
|
||||
from plexapi.mixins import CollectionMixin, CountryMixin, DirectorMixin, GenreMixin, LabelMixin, ProducerMixin, \
|
||||
WriterMixin
|
||||
|
||||
|
||||
class Video(PlexPartialObject):
|
||||
|
@ -261,8 +263,9 @@ class Video(PlexPartialObject):
|
|||
|
||||
|
||||
@utils.registerPlexObject
|
||||
class Movie(Video, Playable, AdvancedSettingsMixin, ArtMixin, PosterMixin, RatingMixin, SplitMergeMixin, UnmatchMatchMixin,
|
||||
CollectionMixin, CountryMixin, DirectorMixin, GenreMixin, LabelMixin, ProducerMixin, WriterMixin):
|
||||
class Movie(Video, Playable, AdvancedSettingsMixin, ArtMixin, PosterMixin, ThemeMixin, RatingMixin, SplitMergeMixin,
|
||||
UnmatchMatchMixin, CollectionMixin, CountryMixin, DirectorMixin, GenreMixin, LabelMixin, ProducerMixin,
|
||||
WriterMixin):
|
||||
""" Represents a single Movie.
|
||||
|
||||
Attributes:
|
||||
|
@ -293,6 +296,7 @@ class Movie(Video, Playable, AdvancedSettingsMixin, ArtMixin, PosterMixin, Ratin
|
|||
similar (List<:class:`~plexapi.media.Similar`>): List of Similar objects.
|
||||
studio (str): Studio that created movie (Di Bonaventura Pictures; 21 Laps Entertainment).
|
||||
tagline (str): Movie tag line (Back 2 Work; Who says men can't change?).
|
||||
theme (str): URL to theme resource (/library/metadata/<ratingkey>/theme/<themeid>).
|
||||
useOriginalTitle (int): Setting that indicates if the original title is used for the movie
|
||||
(-1 = Library default, 0 = No, 1 = Yes).
|
||||
viewOffset (int): View offset in milliseconds.
|
||||
|
@ -331,6 +335,7 @@ class Movie(Video, Playable, AdvancedSettingsMixin, ArtMixin, PosterMixin, Ratin
|
|||
self.similar = self.findItems(data, media.Similar)
|
||||
self.studio = data.attrib.get('studio')
|
||||
self.tagline = data.attrib.get('tagline')
|
||||
self.theme = data.attrib.get('theme')
|
||||
self.useOriginalTitle = utils.cast(int, data.attrib.get('useOriginalTitle', '-1'))
|
||||
self.viewOffset = utils.cast(int, data.attrib.get('viewOffset', 0))
|
||||
self.writers = self.findItems(data, media.Writer)
|
||||
|
@ -377,8 +382,8 @@ class Movie(Video, Playable, AdvancedSettingsMixin, ArtMixin, PosterMixin, Ratin
|
|||
|
||||
|
||||
@utils.registerPlexObject
|
||||
class Show(Video, AdvancedSettingsMixin, ArtMixin, BannerMixin, PosterMixin, RatingMixin, SplitMergeMixin, UnmatchMatchMixin,
|
||||
CollectionMixin, GenreMixin, LabelMixin):
|
||||
class Show(Video, AdvancedSettingsMixin, ArtMixin, BannerMixin, PosterMixin, ThemeMixin, RatingMixin, SplitMergeMixin,
|
||||
UnmatchMatchMixin, CollectionMixin, GenreMixin, LabelMixin):
|
||||
""" Represents a single Show (including all seasons and episodes).
|
||||
|
||||
Attributes:
|
||||
|
@ -574,7 +579,7 @@ class Show(Video, AdvancedSettingsMixin, ArtMixin, BannerMixin, PosterMixin, Rat
|
|||
|
||||
|
||||
@utils.registerPlexObject
|
||||
class Season(Video, ArtMixin, PosterMixin, RatingMixin, CollectionMixin, LabelMixin):
|
||||
class Season(Video, ArtMixin, PosterMixin, ThemeUrlMixin, RatingMixin, CollectionMixin, LabelMixin):
|
||||
""" Represents a single Show Season (including all episodes).
|
||||
|
||||
Attributes:
|
||||
|
@ -711,8 +716,8 @@ class Season(Video, ArtMixin, PosterMixin, RatingMixin, CollectionMixin, LabelMi
|
|||
|
||||
|
||||
@utils.registerPlexObject
|
||||
class Episode(Video, Playable, ArtMixin, PosterMixin, RatingMixin,
|
||||
CollectionMixin, DirectorMixin, LabelMixin, WriterMixin):
|
||||
class Episode(Video, Playable, ArtMixin, PosterMixin, ThemeUrlMixin, RatingMixin,
|
||||
CollectionMixin, DirectorMixin, LabelMixin, WriterMixin):
|
||||
""" Represents a single Shows Episode.
|
||||
|
||||
Attributes:
|
||||
|
|
Loading…
Reference in a new issue