Update PlexAPI to 4.6.1

This commit is contained in:
JonnyWong16 2021-06-15 22:12:59 -07:00
parent b0a395ad0b
commit fec17a7344
No known key found for this signature in database
GPG key ID: B1F1F9807184697A
14 changed files with 1726 additions and 649 deletions

View file

@ -6,7 +6,6 @@ from urllib.parse import quote_plus
from plexapi import log, settings, utils
from plexapi.base import PlexObject
from plexapi.exceptions import BadRequest
from plexapi.utils import cast
@utils.registerPlexObject
@ -51,31 +50,31 @@ class Media(PlexObject):
def _loadData(self, data):
""" Load attribute values from Plex XML response. """
self._data = data
self.aspectRatio = cast(float, data.attrib.get('aspectRatio'))
self.audioChannels = cast(int, data.attrib.get('audioChannels'))
self.aspectRatio = utils.cast(float, data.attrib.get('aspectRatio'))
self.audioChannels = utils.cast(int, data.attrib.get('audioChannels'))
self.audioCodec = data.attrib.get('audioCodec')
self.audioProfile = data.attrib.get('audioProfile')
self.bitrate = cast(int, data.attrib.get('bitrate'))
self.bitrate = utils.cast(int, data.attrib.get('bitrate'))
self.container = data.attrib.get('container')
self.duration = cast(int, data.attrib.get('duration'))
self.height = cast(int, data.attrib.get('height'))
self.id = cast(int, data.attrib.get('id'))
self.has64bitOffsets = cast(bool, data.attrib.get('has64bitOffsets'))
self.optimizedForStreaming = cast(bool, data.attrib.get('optimizedForStreaming'))
self.duration = utils.cast(int, data.attrib.get('duration'))
self.height = utils.cast(int, data.attrib.get('height'))
self.id = utils.cast(int, data.attrib.get('id'))
self.has64bitOffsets = utils.cast(bool, data.attrib.get('has64bitOffsets'))
self.optimizedForStreaming = utils.cast(bool, data.attrib.get('optimizedForStreaming'))
self.parts = self.findItems(data, MediaPart)
self.proxyType = cast(int, data.attrib.get('proxyType'))
self.proxyType = utils.cast(int, data.attrib.get('proxyType'))
self.target = data.attrib.get('target')
self.title = data.attrib.get('title')
self.videoCodec = data.attrib.get('videoCodec')
self.videoFrameRate = data.attrib.get('videoFrameRate')
self.videoProfile = data.attrib.get('videoProfile')
self.videoResolution = data.attrib.get('videoResolution')
self.width = cast(int, data.attrib.get('width'))
self.width = utils.cast(int, data.attrib.get('width'))
if self._isChildOf(etag='Photo'):
self.aperture = data.attrib.get('aperture')
self.exposure = data.attrib.get('exposure')
self.iso = cast(int, data.attrib.get('iso'))
self.iso = utils.cast(int, data.attrib.get('iso'))
self.lens = data.attrib.get('lens')
self.make = data.attrib.get('make')
self.model = data.attrib.get('model')
@ -112,7 +111,7 @@ class MediaPart(PlexObject):
has64bitOffsets (bool): True if the file has 64 bit offsets.
hasThumbnail (bool): True if the file (track) has an embedded thumbnail.
id (int): The unique ID for this media part on the server.
indexes (str, None): sd if the file has generated BIF thumbnails.
indexes (str, None): sd if the file has generated preview (BIF) thumbnails.
key (str): API URL (ex: /library/parts/46618/1389985872/file.mkv).
optimizedForStreaming (bool): True if the file is optimized for streaming.
packetLength (int): The packet length of the file.
@ -128,25 +127,25 @@ class MediaPart(PlexObject):
def _loadData(self, data):
""" Load attribute values from Plex XML response. """
self._data = data
self.accessible = cast(bool, data.attrib.get('accessible'))
self.accessible = utils.cast(bool, data.attrib.get('accessible'))
self.audioProfile = data.attrib.get('audioProfile')
self.container = data.attrib.get('container')
self.decision = data.attrib.get('decision')
self.deepAnalysisVersion = cast(int, data.attrib.get('deepAnalysisVersion'))
self.duration = cast(int, data.attrib.get('duration'))
self.exists = cast(bool, data.attrib.get('exists'))
self.deepAnalysisVersion = utils.cast(int, data.attrib.get('deepAnalysisVersion'))
self.duration = utils.cast(int, data.attrib.get('duration'))
self.exists = utils.cast(bool, data.attrib.get('exists'))
self.file = data.attrib.get('file')
self.has64bitOffsets = cast(bool, data.attrib.get('has64bitOffsets'))
self.hasThumbnail = cast(bool, data.attrib.get('hasThumbnail'))
self.id = cast(int, data.attrib.get('id'))
self.has64bitOffsets = utils.cast(bool, data.attrib.get('has64bitOffsets'))
self.hasThumbnail = utils.cast(bool, data.attrib.get('hasThumbnail'))
self.id = utils.cast(int, data.attrib.get('id'))
self.indexes = data.attrib.get('indexes')
self.key = data.attrib.get('key')
self.optimizedForStreaming = cast(bool, data.attrib.get('optimizedForStreaming'))
self.packetLength = cast(int, data.attrib.get('packetLength'))
self.optimizedForStreaming = utils.cast(bool, data.attrib.get('optimizedForStreaming'))
self.packetLength = utils.cast(int, data.attrib.get('packetLength'))
self.requiredBandwidths = data.attrib.get('requiredBandwidths')
self.size = cast(int, data.attrib.get('size'))
self.size = utils.cast(int, data.attrib.get('size'))
self.streams = self._buildStreams(data)
self.syncItemId = cast(int, data.attrib.get('syncItemId'))
self.syncItemId = utils.cast(int, data.attrib.get('syncItemId'))
self.syncState = data.attrib.get('syncState')
self.videoProfile = data.attrib.get('videoProfile')
@ -157,6 +156,11 @@ class MediaPart(PlexObject):
streams.extend(items)
return streams
@property
def hasPreviewThumbnails(self):
""" Returns True if the media part has generated preview (BIF) thumbnails. """
return self.indexes == 'sd'
def videoStreams(self):
""" Returns a list of :class:`~plexapi.media.VideoStream` objects in this MediaPart. """
return [stream for stream in self.streams if isinstance(stream, VideoStream)]
@ -228,21 +232,21 @@ class MediaPartStream(PlexObject):
def _loadData(self, data):
""" Load attribute values from Plex XML response. """
self._data = data
self.bitrate = cast(int, data.attrib.get('bitrate'))
self.bitrate = utils.cast(int, data.attrib.get('bitrate'))
self.codec = data.attrib.get('codec')
self.default = cast(bool, data.attrib.get('default'))
self.default = utils.cast(bool, data.attrib.get('default'))
self.displayTitle = data.attrib.get('displayTitle')
self.extendedDisplayTitle = data.attrib.get('extendedDisplayTitle')
self.key = data.attrib.get('key')
self.id = cast(int, data.attrib.get('id'))
self.index = cast(int, data.attrib.get('index', '-1'))
self.id = utils.cast(int, data.attrib.get('id'))
self.index = utils.cast(int, data.attrib.get('index', '-1'))
self.language = data.attrib.get('language')
self.languageCode = data.attrib.get('languageCode')
self.requiredBandwidths = data.attrib.get('requiredBandwidths')
self.selected = cast(bool, data.attrib.get('selected', '0'))
self.streamType = cast(int, data.attrib.get('streamType'))
self.selected = utils.cast(bool, data.attrib.get('selected', '0'))
self.streamType = utils.cast(int, data.attrib.get('streamType'))
self.title = data.attrib.get('title')
self.type = cast(int, data.attrib.get('streamType'))
self.type = utils.cast(int, data.attrib.get('streamType'))
@utils.registerPlexObject
@ -293,38 +297,38 @@ class VideoStream(MediaPartStream):
""" Load attribute values from Plex XML response. """
super(VideoStream, self)._loadData(data)
self.anamorphic = data.attrib.get('anamorphic')
self.bitDepth = cast(int, data.attrib.get('bitDepth'))
self.cabac = cast(int, data.attrib.get('cabac'))
self.bitDepth = utils.cast(int, data.attrib.get('bitDepth'))
self.cabac = utils.cast(int, data.attrib.get('cabac'))
self.chromaLocation = data.attrib.get('chromaLocation')
self.chromaSubsampling = data.attrib.get('chromaSubsampling')
self.codecID = data.attrib.get('codecID')
self.codedHeight = cast(int, data.attrib.get('codedHeight'))
self.codedWidth = cast(int, data.attrib.get('codedWidth'))
self.codedHeight = utils.cast(int, data.attrib.get('codedHeight'))
self.codedWidth = utils.cast(int, data.attrib.get('codedWidth'))
self.colorPrimaries = data.attrib.get('colorPrimaries')
self.colorRange = data.attrib.get('colorRange')
self.colorSpace = data.attrib.get('colorSpace')
self.colorTrc = data.attrib.get('colorTrc')
self.DOVIBLCompatID = cast(int, data.attrib.get('DOVIBLCompatID'))
self.DOVIBLPresent = cast(bool, data.attrib.get('DOVIBLPresent'))
self.DOVIELPresent = cast(bool, data.attrib.get('DOVIELPresent'))
self.DOVILevel = cast(int, data.attrib.get('DOVILevel'))
self.DOVIPresent = cast(bool, data.attrib.get('DOVIPresent'))
self.DOVIProfile = cast(int, data.attrib.get('DOVIProfile'))
self.DOVIRPUPresent = cast(bool, data.attrib.get('DOVIRPUPresent'))
self.DOVIVersion = cast(float, data.attrib.get('DOVIVersion'))
self.duration = cast(int, data.attrib.get('duration'))
self.frameRate = cast(float, data.attrib.get('frameRate'))
self.DOVIBLCompatID = utils.cast(int, data.attrib.get('DOVIBLCompatID'))
self.DOVIBLPresent = utils.cast(bool, data.attrib.get('DOVIBLPresent'))
self.DOVIELPresent = utils.cast(bool, data.attrib.get('DOVIELPresent'))
self.DOVILevel = utils.cast(int, data.attrib.get('DOVILevel'))
self.DOVIPresent = utils.cast(bool, data.attrib.get('DOVIPresent'))
self.DOVIProfile = utils.cast(int, data.attrib.get('DOVIProfile'))
self.DOVIRPUPresent = utils.cast(bool, data.attrib.get('DOVIRPUPresent'))
self.DOVIVersion = utils.cast(float, data.attrib.get('DOVIVersion'))
self.duration = utils.cast(int, data.attrib.get('duration'))
self.frameRate = utils.cast(float, data.attrib.get('frameRate'))
self.frameRateMode = data.attrib.get('frameRateMode')
self.hasScallingMatrix = cast(bool, data.attrib.get('hasScallingMatrix'))
self.height = cast(int, data.attrib.get('height'))
self.level = cast(int, data.attrib.get('level'))
self.hasScallingMatrix = utils.cast(bool, data.attrib.get('hasScallingMatrix'))
self.height = utils.cast(int, data.attrib.get('height'))
self.level = utils.cast(int, data.attrib.get('level'))
self.profile = data.attrib.get('profile')
self.pixelAspectRatio = data.attrib.get('pixelAspectRatio')
self.pixelFormat = data.attrib.get('pixelFormat')
self.refFrames = cast(int, data.attrib.get('refFrames'))
self.refFrames = utils.cast(int, data.attrib.get('refFrames'))
self.scanType = data.attrib.get('scanType')
self.streamIdentifier = cast(int, data.attrib.get('streamIdentifier'))
self.width = cast(int, data.attrib.get('width'))
self.streamIdentifier = utils.cast(int, data.attrib.get('streamIdentifier'))
self.width = utils.cast(int, data.attrib.get('width'))
@utils.registerPlexObject
@ -362,23 +366,23 @@ class AudioStream(MediaPartStream):
""" Load attribute values from Plex XML response. """
super(AudioStream, self)._loadData(data)
self.audioChannelLayout = data.attrib.get('audioChannelLayout')
self.bitDepth = cast(int, data.attrib.get('bitDepth'))
self.bitDepth = utils.cast(int, data.attrib.get('bitDepth'))
self.bitrateMode = data.attrib.get('bitrateMode')
self.channels = cast(int, data.attrib.get('channels'))
self.duration = cast(int, data.attrib.get('duration'))
self.channels = utils.cast(int, data.attrib.get('channels'))
self.duration = utils.cast(int, data.attrib.get('duration'))
self.profile = data.attrib.get('profile')
self.samplingRate = cast(int, data.attrib.get('samplingRate'))
self.streamIdentifier = cast(int, data.attrib.get('streamIdentifier'))
self.samplingRate = utils.cast(int, data.attrib.get('samplingRate'))
self.streamIdentifier = utils.cast(int, data.attrib.get('streamIdentifier'))
if self._isChildOf(etag='Track'):
self.albumGain = cast(float, data.attrib.get('albumGain'))
self.albumPeak = cast(float, data.attrib.get('albumPeak'))
self.albumRange = cast(float, data.attrib.get('albumRange'))
self.albumGain = utils.cast(float, data.attrib.get('albumGain'))
self.albumPeak = utils.cast(float, data.attrib.get('albumPeak'))
self.albumRange = utils.cast(float, data.attrib.get('albumRange'))
self.endRamp = data.attrib.get('endRamp')
self.gain = cast(float, data.attrib.get('gain'))
self.loudness = cast(float, data.attrib.get('loudness'))
self.lra = cast(float, data.attrib.get('lra'))
self.peak = cast(float, data.attrib.get('peak'))
self.gain = utils.cast(float, data.attrib.get('gain'))
self.loudness = utils.cast(float, data.attrib.get('loudness'))
self.lra = utils.cast(float, data.attrib.get('lra'))
self.peak = utils.cast(float, data.attrib.get('peak'))
self.startRamp = data.attrib.get('startRamp')
@ -402,7 +406,7 @@ class SubtitleStream(MediaPartStream):
""" Load attribute values from Plex XML response. """
super(SubtitleStream, self)._loadData(data)
self.container = data.attrib.get('container')
self.forced = cast(bool, data.attrib.get('forced', '0'))
self.forced = utils.cast(bool, data.attrib.get('forced', '0'))
self.format = data.attrib.get('format')
self.headerCompression = data.attrib.get('headerCompression')
self.transient = data.attrib.get('transient')
@ -426,9 +430,9 @@ class LyricStream(MediaPartStream):
""" Load attribute values from Plex XML response. """
super(LyricStream, self)._loadData(data)
self.format = data.attrib.get('format')
self.minLines = cast(int, data.attrib.get('minLines'))
self.minLines = utils.cast(int, data.attrib.get('minLines'))
self.provider = data.attrib.get('provider')
self.timed = cast(bool, data.attrib.get('timed', '0'))
self.timed = utils.cast(bool, data.attrib.get('timed', '0'))
@utils.registerPlexObject
@ -491,36 +495,36 @@ class TranscodeSession(PlexObject):
def _loadData(self, data):
""" Load attribute values from Plex XML response. """
self._data = data
self.audioChannels = cast(int, data.attrib.get('audioChannels'))
self.audioChannels = utils.cast(int, data.attrib.get('audioChannels'))
self.audioCodec = data.attrib.get('audioCodec')
self.audioDecision = data.attrib.get('audioDecision')
self.complete = cast(bool, data.attrib.get('complete', '0'))
self.complete = utils.cast(bool, data.attrib.get('complete', '0'))
self.container = data.attrib.get('container')
self.context = data.attrib.get('context')
self.duration = cast(int, data.attrib.get('duration'))
self.height = cast(int, data.attrib.get('height'))
self.duration = utils.cast(int, data.attrib.get('duration'))
self.height = utils.cast(int, data.attrib.get('height'))
self.key = data.attrib.get('key')
self.maxOffsetAvailable = cast(float, data.attrib.get('maxOffsetAvailable'))
self.minOffsetAvailable = cast(float, data.attrib.get('minOffsetAvailable'))
self.progress = cast(float, data.attrib.get('progress'))
self.maxOffsetAvailable = utils.cast(float, data.attrib.get('maxOffsetAvailable'))
self.minOffsetAvailable = utils.cast(float, data.attrib.get('minOffsetAvailable'))
self.progress = utils.cast(float, data.attrib.get('progress'))
self.protocol = data.attrib.get('protocol')
self.remaining = cast(int, data.attrib.get('remaining'))
self.size = cast(int, data.attrib.get('size'))
self.remaining = utils.cast(int, data.attrib.get('remaining'))
self.size = utils.cast(int, data.attrib.get('size'))
self.sourceAudioCodec = data.attrib.get('sourceAudioCodec')
self.sourceVideoCodec = data.attrib.get('sourceVideoCodec')
self.speed = cast(float, data.attrib.get('speed'))
self.speed = utils.cast(float, data.attrib.get('speed'))
self.subtitleDecision = data.attrib.get('subtitleDecision')
self.throttled = cast(bool, data.attrib.get('throttled', '0'))
self.timestamp = cast(float, data.attrib.get('timeStamp'))
self.throttled = utils.cast(bool, data.attrib.get('throttled', '0'))
self.timestamp = utils.cast(float, data.attrib.get('timeStamp'))
self.transcodeHwDecoding = data.attrib.get('transcodeHwDecoding')
self.transcodeHwDecodingTitle = data.attrib.get('transcodeHwDecodingTitle')
self.transcodeHwEncoding = data.attrib.get('transcodeHwEncoding')
self.transcodeHwEncodingTitle = data.attrib.get('transcodeHwEncodingTitle')
self.transcodeHwFullPipeline = cast(bool, data.attrib.get('transcodeHwFullPipeline', '0'))
self.transcodeHwRequested = cast(bool, data.attrib.get('transcodeHwRequested', '0'))
self.transcodeHwFullPipeline = utils.cast(bool, data.attrib.get('transcodeHwFullPipeline', '0'))
self.transcodeHwRequested = utils.cast(bool, data.attrib.get('transcodeHwRequested', '0'))
self.videoCodec = data.attrib.get('videoCodec')
self.videoDecision = data.attrib.get('videoDecision')
self.width = cast(int, data.attrib.get('width'))
self.width = utils.cast(int, data.attrib.get('width'))
@utils.registerPlexObject
@ -558,6 +562,13 @@ class Optimized(PlexObject):
self.target = data.attrib.get('target')
self.targetTagID = data.attrib.get('targetTagID')
def items(self):
""" Returns a list of all :class:`~plexapi.media.Video` objects
in this optimized item.
"""
key = '%s/%s/items' % (self._initpath, self.id)
return self.fetchItems(key)
def remove(self):
""" Remove an Optimized item"""
key = '%s/%s' % (self._initpath, self.id)
@ -641,59 +652,43 @@ class MediaTag(PlexObject):
the construct used for things such as Country, Director, Genre, etc.
Attributes:
server (:class:`~plexapi.server.PlexServer`): Server this client is connected to.
filter (str): The library filter for the tag.
id (id): Tag ID (This seems meaningless except to use it as a unique id).
role (str): Unknown
key (str): API URL (/library/section/<librarySectionID>/all?<filter>).
role (str): The name of the character role for :class:`~plexapi.media.Role` only.
tag (str): Name of the tag. This will be Animation, SciFi etc for Genres. The name of
person for Directors and Roles (ex: Animation, Stephen Graham, etc).
<Hub_Search_Attributes>: Attributes only applicable in search results from
PlexServer :func:`~plexapi.server.PlexServer.search`. They provide details of which
library section the tag was found as well as the url to dig deeper into the results.
* key (str): API URL to dig deeper into this tag (ex: /library/sections/1/all?actor=9081).
* librarySectionID (int): Section ID this tag was generated from.
* librarySectionTitle (str): Library section title this tag was found.
* librarySectionType (str): Media type of the library section this tag was found.
* tagType (int): Tag type ID.
* thumb (str): URL to thumbnail image.
thumb (str): URL to thumbnail image for :class:`~plexapi.media.Role` only.
"""
def _loadData(self, data):
""" Load attribute values from Plex XML response. """
self._data = data
self.id = cast(int, data.attrib.get('id'))
self.filter = data.attrib.get('filter')
self.id = utils.cast(int, data.attrib.get('id'))
self.key = data.attrib.get('key')
self.role = data.attrib.get('role')
self.tag = data.attrib.get('tag')
# additional attributes only from hub search
self.key = data.attrib.get('key')
self.librarySectionID = cast(int, data.attrib.get('librarySectionID'))
self.librarySectionTitle = data.attrib.get('librarySectionTitle')
self.librarySectionType = data.attrib.get('librarySectionType')
self.tagType = cast(int, data.attrib.get('tagType'))
self.thumb = data.attrib.get('thumb')
def items(self, *args, **kwargs):
""" Return the list of items within this tag. This function is only applicable
in search results from PlexServer :func:`~plexapi.server.PlexServer.search`.
"""
parent = self._parent()
self._librarySectionID = utils.cast(int, parent._data.attrib.get('librarySectionID'))
self._librarySectionKey = parent._data.attrib.get('librarySectionKey')
self._librarySectionTitle = parent._data.attrib.get('librarySectionTitle')
self._parentType = parent.TYPE
if self._librarySectionKey and self.filter:
self.key = '%s/all?%s&type=%s' % (
self._librarySectionKey, self.filter, utils.searchType(self._parentType))
def items(self):
""" Return the list of items within this tag. """
if not self.key:
raise BadRequest('Key is not defined for this tag: %s' % self.tag)
raise BadRequest('Key is not defined for this tag: %s. '
'Reload the parent object.' % self.tag)
return self.fetchItems(self.key)
class GuidTag(PlexObject):
""" Base class for guid tags used only for Guids, as they contain only a string identifier
Attributes:
id (id): The guid for external metadata sources (e.g. IMDB, TMDB, TVDB).
"""
def _loadData(self, data):
""" Load attribute values from Plex XML response. """
self._data = data
self.id = data.attrib.get('id')
@utils.registerPlexObject
class Collection(MediaTag):
""" Represents a single Collection media tag.
@ -705,36 +700,11 @@ class Collection(MediaTag):
TAG = 'Collection'
FILTER = 'collection'
@utils.registerPlexObject
class Label(MediaTag):
""" Represents a single Label media tag.
Attributes:
TAG (str): 'Label'
FILTER (str): 'label'
"""
TAG = 'Label'
FILTER = 'label'
@utils.registerPlexObject
class Tag(MediaTag):
""" Represents a single Tag media tag.
Attributes:
TAG (str): 'Tag'
FILTER (str): 'tag'
"""
TAG = 'Tag'
FILTER = 'tag'
def _loadData(self, data):
self._data = data
self.id = cast(int, data.attrib.get('id', 0))
self.filter = data.attrib.get('filter')
self.tag = data.attrib.get('tag')
self.title = self.tag
def collection(self):
""" Return the :class:`~plexapi.collection.Collection` object for this collection tag.
"""
key = '%s/collections' % self._librarySectionKey
return self.fetchItem(key, etag='Directory', index=self.id)
@utils.registerPlexObject
@ -774,13 +744,15 @@ class Genre(MediaTag):
@utils.registerPlexObject
class Guid(GuidTag):
""" Represents a single Guid media tag.
class Label(MediaTag):
""" Represents a single Label media tag.
Attributes:
TAG (str): 'Guid'
TAG (str): 'Label'
FILTER (str): 'label'
"""
TAG = "Guid"
TAG = 'Label'
FILTER = 'label'
@utils.registerPlexObject
@ -795,60 +767,6 @@ class Mood(MediaTag):
FILTER = 'mood'
@utils.registerPlexObject
class Style(MediaTag):
""" Represents a single Style media tag.
Attributes:
TAG (str): 'Style'
FILTER (str): 'style'
"""
TAG = 'Style'
FILTER = 'style'
class BaseImage(PlexObject):
""" Base class for all Art, Banner, and Poster objects.
Attributes:
TAG (str): 'Photo'
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.
"""
TAG = 'Photo'
def _loadData(self, data):
self._data = data
self.key = data.attrib.get('key')
self.provider = data.attrib.get('provider')
self.ratingKey = data.attrib.get('ratingKey')
self.selected = cast(bool, data.attrib.get('selected'))
self.thumb = data.attrib.get('thumb')
def select(self):
key = self._initpath[:-1]
data = '%s?url=%s' % (key, quote_plus(self.ratingKey))
try:
self._server.query(data, method=self._server._session.put)
except xml.etree.ElementTree.ParseError:
pass
class Art(BaseImage):
""" Represents a single Art object. """
class Banner(BaseImage):
""" Represents a single Banner object. """
class Poster(BaseImage):
""" Represents a single Poster object. """
@utils.registerPlexObject
class Producer(MediaTag):
""" Represents a single Producer media tag.
@ -885,6 +803,30 @@ class Similar(MediaTag):
FILTER = 'similar'
@utils.registerPlexObject
class Style(MediaTag):
""" Represents a single Style media tag.
Attributes:
TAG (str): 'Style'
FILTER (str): 'style'
"""
TAG = 'Style'
FILTER = 'style'
@utils.registerPlexObject
class Tag(MediaTag):
""" Represents a single Tag media tag.
Attributes:
TAG (str): 'Tag'
FILTER (str): 'tag'
"""
TAG = 'Tag'
FILTER = 'tag'
@utils.registerPlexObject
class Writer(MediaTag):
""" Represents a single Writer media tag.
@ -897,6 +839,98 @@ class Writer(MediaTag):
FILTER = 'writer'
class GuidTag(PlexObject):
""" Base class for guid tags used only for Guids, as they contain only a string identifier
Attributes:
id (id): The guid for external metadata sources (e.g. IMDB, TMDB, TVDB).
"""
def _loadData(self, data):
""" Load attribute values from Plex XML response. """
self._data = data
self.id = data.attrib.get('id')
@utils.registerPlexObject
class Guid(GuidTag):
""" Represents a single Guid media tag.
Attributes:
TAG (str): 'Guid'
"""
TAG = 'Guid'
@utils.registerPlexObject
class Review(PlexObject):
""" Represents a single Review for a Movie.
Attributes:
TAG (str): 'Review'
filter (str): filter for reviews?
id (int): The ID of the review.
image (str): The image uri for the review.
link (str): The url to the online review.
source (str): The source of the review.
tag (str): The name of the reviewer.
text (str): The text of the review.
"""
TAG = 'Review'
def _loadData(self, data):
self._data = data
self.filter = data.attrib.get('filter')
self.id = utils.cast(int, data.attrib.get('id', 0))
self.image = data.attrib.get('image')
self.link = data.attrib.get('link')
self.source = data.attrib.get('source')
self.tag = data.attrib.get('tag')
self.text = data.attrib.get('text')
class BaseImage(PlexObject):
""" Base class for all Art, Banner, and Poster objects.
Attributes:
TAG (str): 'Photo'
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.
"""
TAG = 'Photo'
def _loadData(self, data):
self._data = data
self.key = data.attrib.get('key')
self.provider = data.attrib.get('provider')
self.ratingKey = data.attrib.get('ratingKey')
self.selected = utils.cast(bool, data.attrib.get('selected'))
self.thumb = data.attrib.get('thumb')
def select(self):
key = self._initpath[:-1]
data = '%s?url=%s' % (key, quote_plus(self.ratingKey))
try:
self._server.query(data, method=self._server._session.put)
except xml.etree.ElementTree.ParseError:
pass
class Art(BaseImage):
""" Represents a single Art object. """
class Banner(BaseImage):
""" Represents a single Banner object. """
class Poster(BaseImage):
""" Represents a single Poster object. """
@utils.registerPlexObject
class Chapter(PlexObject):
""" Represents a single Writer media tag.
@ -908,13 +942,13 @@ class Chapter(PlexObject):
def _loadData(self, data):
self._data = data
self.id = cast(int, data.attrib.get('id', 0))
self.id = utils.cast(int, data.attrib.get('id', 0))
self.filter = data.attrib.get('filter') # I couldn't filter on it anyways
self.tag = data.attrib.get('tag')
self.title = self.tag
self.index = cast(int, data.attrib.get('index'))
self.start = cast(int, data.attrib.get('startTimeOffset'))
self.end = cast(int, data.attrib.get('endTimeOffset'))
self.index = utils.cast(int, data.attrib.get('index'))
self.start = utils.cast(int, data.attrib.get('startTimeOffset'))
self.end = utils.cast(int, data.attrib.get('endTimeOffset'))
@utils.registerPlexObject
@ -935,8 +969,8 @@ class Marker(PlexObject):
def _loadData(self, data):
self._data = data
self.type = data.attrib.get('type')
self.start = cast(int, data.attrib.get('startTimeOffset'))
self.end = cast(int, data.attrib.get('endTimeOffset'))
self.start = utils.cast(int, data.attrib.get('startTimeOffset'))
self.end = utils.cast(int, data.attrib.get('endTimeOffset'))
@utils.registerPlexObject
@ -951,7 +985,7 @@ class Field(PlexObject):
def _loadData(self, data):
self._data = data
self.name = data.attrib.get('name')
self.locked = cast(bool, data.attrib.get('locked'))
self.locked = utils.cast(bool, data.attrib.get('locked'))
@utils.registerPlexObject
@ -973,7 +1007,7 @@ class SearchResult(PlexObject):
self.guid = data.attrib.get('guid')
self.lifespanEnded = data.attrib.get('lifespanEnded')
self.name = data.attrib.get('name')
self.score = cast(int, data.attrib.get('score'))
self.score = utils.cast(int, data.attrib.get('score'))
self.year = data.attrib.get('year')
@ -1018,7 +1052,7 @@ class AgentMediaType(Agent):
return '<%s>' % ':'.join([p for p in [self.__class__.__name__, uid] if p])
def _loadData(self, data):
self.mediaType = cast(int, data.attrib.get('mediaType'))
self.mediaType = utils.cast(int, data.attrib.get('mediaType'))
self.name = data.attrib.get('name')
self.languageCode = []
for code in data: