2016-03-21 04:26:02 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
2017-09-30 17:45:35 +00:00
|
|
|
from plexapi import log, utils
|
2017-02-06 04:52:10 +00:00
|
|
|
from plexapi.base import PlexObject
|
2017-02-03 07:15:41 +00:00
|
|
|
from plexapi.exceptions import BadRequest
|
2017-02-07 06:20:49 +00:00
|
|
|
from plexapi.utils import cast
|
2014-12-29 03:21:58 +00:00
|
|
|
|
|
|
|
|
2017-02-13 02:55:55 +00:00
|
|
|
@utils.registerPlexObject
|
2017-02-06 04:52:10 +00:00
|
|
|
class Media(PlexObject):
|
2017-02-04 08:08:47 +00:00
|
|
|
""" Container object for all MediaPart objects. Provides useful data about the
|
|
|
|
video this media belong to such as video framerate, resolution, etc.
|
|
|
|
|
|
|
|
Attributes:
|
2017-02-14 04:32:27 +00:00
|
|
|
TAG (str): 'Media'
|
2017-02-04 08:08:47 +00:00
|
|
|
server (:class:`~plexapi.server.PlexServer`): PlexServer object this is from.
|
|
|
|
initpath (str): Relative path requested when retrieving specified data.
|
|
|
|
video (str): Video this media belongs to.
|
|
|
|
aspectRatio (float): Aspect ratio of the video (ex: 2.35).
|
|
|
|
audioChannels (int): Number of audio channels for this video (ex: 6).
|
|
|
|
audioCodec (str): Audio codec used within the video (ex: ac3).
|
|
|
|
bitrate (int): Bitrate of the video (ex: 1624)
|
|
|
|
container (str): Container this video is in (ex: avi).
|
|
|
|
duration (int): Length of the video in milliseconds (ex: 6990483).
|
|
|
|
height (int): Height of the video in pixels (ex: 256).
|
|
|
|
id (int): Plex ID of this media item (ex: 46184).
|
|
|
|
has64bitOffsets (bool): True if video has 64 bit offsets (?).
|
|
|
|
optimizedForStreaming (bool): True if video is optimized for streaming.
|
2018-09-20 13:56:12 +00:00
|
|
|
target (str): Media version target name.
|
|
|
|
title (str): Media version title.
|
2017-02-04 08:08:47 +00:00
|
|
|
videoCodec (str): Video codec used within the video (ex: ac3).
|
|
|
|
videoFrameRate (str): Video frame rate (ex: 24p).
|
|
|
|
videoResolution (str): Video resolution (ex: sd).
|
2018-10-02 14:52:32 +00:00
|
|
|
videoProfile (str): Video profile (ex: high).
|
2017-02-04 08:08:47 +00:00
|
|
|
width (int): Width of the video in pixels (ex: 608).
|
|
|
|
parts (list<:class:`~plexapi.media.MediaPart`>): List of MediaParts in this video.
|
|
|
|
"""
|
2017-02-13 02:55:55 +00:00
|
|
|
TAG = 'Media'
|
2015-06-08 16:41:47 +00:00
|
|
|
|
2017-02-06 04:52:10 +00:00
|
|
|
def _loadData(self, data):
|
2017-02-14 04:32:27 +00:00
|
|
|
""" Load attribute values from Plex XML response. """
|
2017-02-06 04:52:10 +00:00
|
|
|
self._data = data
|
2014-12-29 03:21:58 +00:00
|
|
|
self.aspectRatio = cast(float, data.attrib.get('aspectRatio'))
|
|
|
|
self.audioChannels = cast(int, data.attrib.get('audioChannels'))
|
|
|
|
self.audioCodec = data.attrib.get('audioCodec')
|
2016-04-10 03:59:47 +00:00
|
|
|
self.bitrate = cast(int, data.attrib.get('bitrate'))
|
2014-12-29 03:21:58 +00:00
|
|
|
self.container = data.attrib.get('container')
|
2016-04-10 03:59:47 +00:00
|
|
|
self.duration = cast(int, data.attrib.get('duration'))
|
|
|
|
self.height = cast(int, data.attrib.get('height'))
|
|
|
|
self.id = cast(int, data.attrib.get('id'))
|
2017-02-04 08:08:47 +00:00
|
|
|
self.has64bitOffsets = cast(bool, data.attrib.get('has64bitOffsets'))
|
2016-04-10 03:59:47 +00:00
|
|
|
self.optimizedForStreaming = cast(bool, data.attrib.get('optimizedForStreaming'))
|
2018-09-20 13:56:12 +00:00
|
|
|
self.target = data.attrib.get('target')
|
|
|
|
self.title = data.attrib.get('title')
|
2016-04-10 03:59:47 +00:00
|
|
|
self.videoCodec = data.attrib.get('videoCodec')
|
|
|
|
self.videoFrameRate = data.attrib.get('videoFrameRate')
|
2018-09-20 13:56:12 +00:00
|
|
|
self.videoProfile = data.attrib.get('videoProfile')
|
2016-04-10 03:59:47 +00:00
|
|
|
self.videoResolution = data.attrib.get('videoResolution')
|
|
|
|
self.width = cast(int, data.attrib.get('width'))
|
2017-02-13 02:55:55 +00:00
|
|
|
self.parts = self.findItems(data, MediaPart)
|
2014-12-29 03:21:58 +00:00
|
|
|
|
2017-09-30 21:16:40 +00:00
|
|
|
def delete(self):
|
|
|
|
part = self._initpath + '/media/%s' % self.id
|
|
|
|
try:
|
|
|
|
return self._server.query(part, method=self._server._session.delete)
|
|
|
|
except BadRequest:
|
|
|
|
log.error("Failed to delete %s. This could be because you havn't allowed "
|
|
|
|
"items to be deleted" % part)
|
|
|
|
raise
|
|
|
|
|
2014-12-29 03:21:58 +00:00
|
|
|
|
2017-02-13 02:55:55 +00:00
|
|
|
@utils.registerPlexObject
|
2017-02-06 04:52:10 +00:00
|
|
|
class MediaPart(PlexObject):
|
2017-02-04 08:08:47 +00:00
|
|
|
""" Represents a single media part (often a single file) for the media this belongs to.
|
2017-02-18 00:51:06 +00:00
|
|
|
|
2017-02-04 08:08:47 +00:00
|
|
|
Attributes:
|
2017-02-14 04:32:27 +00:00
|
|
|
TAG (str): 'Part'
|
2017-02-04 08:08:47 +00:00
|
|
|
server (:class:`~plexapi.server.PlexServer`): PlexServer object this is from.
|
|
|
|
initpath (str): Relative path requested when retrieving specified data.
|
|
|
|
media (:class:`~plexapi.media.Media`): Media object this part belongs to.
|
|
|
|
container (str): Container type of this media part (ex: avi).
|
|
|
|
duration (int): Length of this media part in milliseconds.
|
|
|
|
file (str): Path to this file on disk (ex: /media/Movies/Cars.(2006)/Cars.cd2.avi)
|
|
|
|
id (int): Unique ID of this media part.
|
2017-02-18 00:51:06 +00:00
|
|
|
indexes (str, None): None or SD.
|
2017-02-04 08:08:47 +00:00
|
|
|
key (str): Key used to access this media part (ex: /library/parts/46618/1389985872/file.avi).
|
|
|
|
size (int): Size of this file in bytes (ex: 733884416).
|
|
|
|
streams (list<:class:`~plexapi.media.MediaPartStream`>): List of streams in this media part.
|
|
|
|
"""
|
2017-02-13 02:55:55 +00:00
|
|
|
TAG = 'Part'
|
2014-12-29 03:21:58 +00:00
|
|
|
|
2017-02-06 04:52:10 +00:00
|
|
|
def _loadData(self, data):
|
2017-02-14 04:32:27 +00:00
|
|
|
""" Load attribute values from Plex XML response. """
|
2017-02-06 04:52:10 +00:00
|
|
|
self._data = data
|
2016-04-10 03:59:47 +00:00
|
|
|
self.container = data.attrib.get('container')
|
2014-12-29 03:21:58 +00:00
|
|
|
self.duration = cast(int, data.attrib.get('duration'))
|
|
|
|
self.file = data.attrib.get('file')
|
2016-04-10 03:59:47 +00:00
|
|
|
self.id = cast(int, data.attrib.get('id'))
|
2017-02-18 00:51:06 +00:00
|
|
|
self.indexes = data.attrib.get('indexes')
|
2016-04-10 03:59:47 +00:00
|
|
|
self.key = data.attrib.get('key')
|
2014-12-29 03:21:58 +00:00
|
|
|
self.size = cast(int, data.attrib.get('size'))
|
2018-09-08 15:25:16 +00:00
|
|
|
self.decision = data.attrib.get('decision')
|
|
|
|
self.optimizedForStreaming = cast(bool, data.attrib.get('optimizedForStreaming'))
|
|
|
|
self.syncItemId = cast(int, data.attrib.get('syncItemId'))
|
|
|
|
self.syncState = data.attrib.get('syncState')
|
|
|
|
self.videoProfile = data.attrib.get('videoProfile')
|
2017-02-06 06:28:58 +00:00
|
|
|
self.streams = self._buildStreams(data)
|
2014-12-29 03:21:58 +00:00
|
|
|
|
2017-02-06 06:28:58 +00:00
|
|
|
def _buildStreams(self, data):
|
|
|
|
streams = []
|
|
|
|
for elem in data:
|
|
|
|
for cls in (VideoStream, AudioStream, SubtitleStream):
|
|
|
|
if elem.attrib.get('streamType') == str(cls.STREAMTYPE):
|
2017-02-09 04:13:54 +00:00
|
|
|
streams.append(cls(self._server, elem, self._initpath))
|
2017-02-06 06:28:58 +00:00
|
|
|
return streams
|
|
|
|
|
|
|
|
def videoStreams(self):
|
2017-02-14 04:32:27 +00:00
|
|
|
""" Returns a list of :class:`~plexapi.media.VideoStream` objects in this MediaPart. """
|
2017-05-13 03:25:57 +00:00
|
|
|
return [stream for stream in self.streams if stream.streamType == VideoStream.STREAMTYPE]
|
2017-02-06 06:28:58 +00:00
|
|
|
|
|
|
|
def audioStreams(self):
|
2017-02-14 04:32:27 +00:00
|
|
|
""" Returns a list of :class:`~plexapi.media.AudioStream` objects in this MediaPart. """
|
2017-05-13 03:25:57 +00:00
|
|
|
return [stream for stream in self.streams if stream.streamType == AudioStream.STREAMTYPE]
|
2017-02-06 06:28:58 +00:00
|
|
|
|
|
|
|
def subtitleStreams(self):
|
2017-02-14 04:32:27 +00:00
|
|
|
""" Returns a list of :class:`~plexapi.media.SubtitleStream` objects in this MediaPart. """
|
2017-05-13 03:25:57 +00:00
|
|
|
return [stream for stream in self.streams if stream.streamType == SubtitleStream.STREAMTYPE]
|
2019-02-04 19:45:49 +00:00
|
|
|
|
2019-02-05 02:07:22 +00:00
|
|
|
def setDefaultAudioStream(self, stream):
|
2019-02-04 19:45:49 +00:00
|
|
|
""" Set the default :class:`~plexapi.media.AudioStream` for this MediaPart.
|
|
|
|
|
|
|
|
Parameters:
|
2019-02-04 22:28:30 +00:00
|
|
|
stream (:class:`~plexapi.media.AudioStream`): AudioStream to set as default
|
|
|
|
|
|
|
|
Raises:
|
2019-02-05 02:07:22 +00:00
|
|
|
:class:`plexapi.exceptions.BadRequest`: If stream is not an AudioStream.
|
2019-02-04 19:45:49 +00:00
|
|
|
"""
|
2019-02-05 02:07:22 +00:00
|
|
|
if type(stream) == AudioStream:
|
2019-02-04 22:28:30 +00:00
|
|
|
key = "/library/parts/%d?audioStreamID=%d&allParts=1" % (self.id, stream.id)
|
2019-02-05 02:07:22 +00:00
|
|
|
self._server.query(key, method=self._server._session.put)
|
2019-02-04 22:28:30 +00:00
|
|
|
else:
|
2019-02-05 02:07:22 +00:00
|
|
|
raise BadRequest("Object 'stream' is not an AudioStream.")
|
2019-02-04 22:28:30 +00:00
|
|
|
|
2019-02-05 02:07:22 +00:00
|
|
|
def setDefaultSubtitleStream(self, stream):
|
2019-02-04 19:45:49 +00:00
|
|
|
""" Set the default :class:`~plexapi.media.SubtitleStream` for this MediaPart.
|
2019-02-05 02:07:22 +00:00
|
|
|
|
2019-02-04 19:45:49 +00:00
|
|
|
Parameters:
|
2019-02-05 02:07:22 +00:00
|
|
|
stream (:class:`~plexapi.media.SubtitleStream`): SubtitleStream to set as default.
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
:class:`plexapi.exceptions.BadRequest`: If stream is not a SubtitleStream.
|
2019-02-04 19:45:49 +00:00
|
|
|
"""
|
2019-02-05 02:07:22 +00:00
|
|
|
if type(stream) == SubtitleStream:
|
2019-02-04 22:28:30 +00:00
|
|
|
key = "/library/parts/%d?subtitleStreamID=%d&allParts=1" % (self.id, stream.id)
|
2019-02-05 02:07:22 +00:00
|
|
|
self._server.query(key, method=self._server._session.put)
|
2019-02-04 22:28:30 +00:00
|
|
|
else:
|
2019-02-05 02:07:22 +00:00
|
|
|
raise BadRequest("Object 'stream' is not a SubtitleStream.")
|
2019-02-04 19:45:49 +00:00
|
|
|
|
2019-02-05 02:07:22 +00:00
|
|
|
def resetSubtitles(self):
|
|
|
|
""" Set default subtitle of this MediaPart to 'none'. """
|
|
|
|
key = "/library/parts/%d?subtitleStreamID=0&allParts=1" % (self.id)
|
|
|
|
self._server.query(key, method=self._server._session.put)
|
2017-02-06 06:28:58 +00:00
|
|
|
|
2017-02-06 04:52:10 +00:00
|
|
|
class MediaPartStream(PlexObject):
|
2017-02-04 08:08:47 +00:00
|
|
|
""" Base class for media streams. These consist of video, audio and subtitles.
|
2017-02-18 00:51:06 +00:00
|
|
|
|
2017-02-04 08:08:47 +00:00
|
|
|
Attributes:
|
|
|
|
server (:class:`~plexapi.server.PlexServer`): PlexServer object this is from.
|
|
|
|
initpath (str): Relative path requested when retrieving specified data.
|
|
|
|
part (:class:`~plexapi.media.MediaPart`): Media part this stream belongs to.
|
|
|
|
codec (str): Codec of this stream (ex: srt, ac3, mpeg4).
|
|
|
|
codecID (str): Codec ID (ex: XVID).
|
|
|
|
id (int): Unique stream ID on this server.
|
|
|
|
index (int): Unknown
|
|
|
|
language (str): Stream language (ex: English, ไทย).
|
|
|
|
languageCode (str): Ascii code for language (ex: eng, tha).
|
|
|
|
selected (bool): True if this stream is selected.
|
|
|
|
streamType (int): Stream type (1=:class:`~plexapi.media.VideoStream`,
|
|
|
|
2=:class:`~plexapi.media.AudioStream`, 3=:class:`~plexapi.media.SubtitleStream`).
|
|
|
|
type (int): Alias for streamType.
|
|
|
|
"""
|
2017-07-21 23:07:31 +00:00
|
|
|
|
2017-02-06 04:52:10 +00:00
|
|
|
def _loadData(self, data):
|
2017-02-14 04:32:27 +00:00
|
|
|
""" Load attribute values from Plex XML response. """
|
2017-02-06 04:52:10 +00:00
|
|
|
self._data = data
|
2014-12-29 03:21:58 +00:00
|
|
|
self.codec = data.attrib.get('codec')
|
2016-03-24 06:20:08 +00:00
|
|
|
self.codecID = data.attrib.get('codecID')
|
|
|
|
self.id = cast(int, data.attrib.get('id'))
|
2014-12-29 03:21:58 +00:00
|
|
|
self.index = cast(int, data.attrib.get('index', '-1'))
|
2016-03-24 06:20:08 +00:00
|
|
|
self.language = data.attrib.get('language')
|
|
|
|
self.languageCode = data.attrib.get('languageCode')
|
|
|
|
self.selected = cast(bool, data.attrib.get('selected', '0'))
|
|
|
|
self.streamType = cast(int, data.attrib.get('streamType'))
|
|
|
|
self.type = cast(int, data.attrib.get('streamType'))
|
2014-12-29 03:21:58 +00:00
|
|
|
|
|
|
|
@staticmethod
|
2017-10-26 21:55:59 +00:00
|
|
|
def parse(server, data, initpath): # pragma: no cover seems to be dead code.
|
2017-02-04 08:08:47 +00:00
|
|
|
""" Factory method returns a new MediaPartStream from xml data. """
|
2017-02-20 05:37:00 +00:00
|
|
|
STREAMCLS = {1: VideoStream, 2: AudioStream, 3: SubtitleStream}
|
2014-12-29 03:21:58 +00:00
|
|
|
stype = cast(int, data.attrib.get('streamType'))
|
|
|
|
cls = STREAMCLS.get(stype, MediaPartStream)
|
2017-02-06 04:52:10 +00:00
|
|
|
return cls(server, data, initpath)
|
2014-12-29 03:21:58 +00:00
|
|
|
|
|
|
|
|
2017-02-13 02:55:55 +00:00
|
|
|
@utils.registerPlexObject
|
2016-03-24 06:20:08 +00:00
|
|
|
class VideoStream(MediaPartStream):
|
2017-02-04 08:08:47 +00:00
|
|
|
""" Respresents a video stream within a :class:`~plexapi.media.MediaPart`.
|
|
|
|
|
|
|
|
Attributes:
|
2017-02-14 04:32:27 +00:00
|
|
|
TAG (str): 'Stream'
|
|
|
|
STREAMTYPE (int): 1
|
2017-02-04 08:08:47 +00:00
|
|
|
bitDepth (int): Bit depth (ex: 8).
|
|
|
|
bitrate (int): Bitrate (ex: 1169)
|
|
|
|
cabac (int): Unknown
|
|
|
|
chromaSubsampling (str): Chroma Subsampling (ex: 4:2:0).
|
|
|
|
colorSpace (str): Unknown
|
|
|
|
duration (int): Duration of video stream in milliseconds.
|
|
|
|
frameRate (float): Frame rate (ex: 23.976)
|
|
|
|
frameRateMode (str): Unknown
|
|
|
|
hasScallingMatrix (bool): True if video stream has a scaling matrix.
|
|
|
|
height (int): Height of video stream.
|
|
|
|
level (int): Videl stream level (?).
|
|
|
|
profile (str): Video stream profile (ex: asp).
|
|
|
|
refFrames (int): Unknown
|
|
|
|
scanType (str): Video stream scan type (ex: progressive).
|
|
|
|
title (str): Title of this video stream.
|
|
|
|
width (int): Width of video stream.
|
|
|
|
"""
|
2017-02-13 02:55:55 +00:00
|
|
|
TAG = 'Stream'
|
2016-03-24 06:20:08 +00:00
|
|
|
STREAMTYPE = 1
|
2014-12-29 03:21:58 +00:00
|
|
|
|
2017-02-06 04:52:10 +00:00
|
|
|
def _loadData(self, data):
|
2017-02-14 04:32:27 +00:00
|
|
|
""" Load attribute values from Plex XML response. """
|
2017-02-06 04:52:10 +00:00
|
|
|
super(VideoStream, self)._loadData(data)
|
2016-03-24 06:20:08 +00:00
|
|
|
self.bitDepth = cast(int, data.attrib.get('bitDepth'))
|
2016-04-10 03:59:47 +00:00
|
|
|
self.bitrate = cast(int, data.attrib.get('bitrate'))
|
2014-12-29 03:21:58 +00:00
|
|
|
self.cabac = cast(int, data.attrib.get('cabac'))
|
2016-03-24 06:20:08 +00:00
|
|
|
self.chromaSubsampling = data.attrib.get('chromaSubsampling')
|
|
|
|
self.colorSpace = data.attrib.get('colorSpace')
|
2014-12-29 03:21:58 +00:00
|
|
|
self.duration = cast(int, data.attrib.get('duration'))
|
2016-03-24 06:20:08 +00:00
|
|
|
self.frameRate = cast(float, data.attrib.get('frameRate'))
|
|
|
|
self.frameRateMode = data.attrib.get('frameRateMode')
|
|
|
|
self.hasScallingMatrix = cast(bool, data.attrib.get('hasScallingMatrix'))
|
2014-12-29 03:21:58 +00:00
|
|
|
self.height = cast(int, data.attrib.get('height'))
|
|
|
|
self.level = cast(int, data.attrib.get('level'))
|
|
|
|
self.profile = data.attrib.get('profile')
|
2016-03-24 06:20:08 +00:00
|
|
|
self.refFrames = cast(int, data.attrib.get('refFrames'))
|
|
|
|
self.scanType = data.attrib.get('scanType')
|
2014-12-29 03:21:58 +00:00
|
|
|
self.title = data.attrib.get('title')
|
|
|
|
self.width = cast(int, data.attrib.get('width'))
|
|
|
|
|
|
|
|
|
2017-02-13 02:55:55 +00:00
|
|
|
@utils.registerPlexObject
|
2016-03-24 06:20:08 +00:00
|
|
|
class AudioStream(MediaPartStream):
|
2017-02-04 08:08:47 +00:00
|
|
|
""" Respresents a audio stream within a :class:`~plexapi.media.MediaPart`.
|
|
|
|
|
|
|
|
Attributes:
|
2017-02-14 04:32:27 +00:00
|
|
|
TAG (str): 'Stream'
|
|
|
|
STREAMTYPE (int): 2
|
2017-02-04 08:08:47 +00:00
|
|
|
audioChannelLayout (str): Audio channel layout (ex: 5.1(side)).
|
|
|
|
bitDepth (int): Bit depth (ex: 16).
|
|
|
|
bitrate (int): Audio bitrate (ex: 448).
|
|
|
|
bitrateMode (str): Bitrate mode (ex: cbr).
|
|
|
|
channels (int): number of channels in this stream (ex: 6).
|
|
|
|
dialogNorm (int): Unknown (ex: -27).
|
|
|
|
duration (int): Duration of audio stream in milliseconds.
|
|
|
|
samplingRate (int): Sampling rate (ex: xxx)
|
|
|
|
title (str): Title of this audio stream.
|
|
|
|
"""
|
2017-02-13 02:55:55 +00:00
|
|
|
TAG = 'Stream'
|
2016-03-24 06:20:08 +00:00
|
|
|
STREAMTYPE = 2
|
2014-12-29 03:21:58 +00:00
|
|
|
|
2017-02-06 04:52:10 +00:00
|
|
|
def _loadData(self, data):
|
2017-02-14 04:32:27 +00:00
|
|
|
""" Load attribute values from Plex XML response. """
|
2017-02-06 04:52:10 +00:00
|
|
|
super(AudioStream, self)._loadData(data)
|
2016-03-24 06:20:08 +00:00
|
|
|
self.audioChannelLayout = data.attrib.get('audioChannelLayout')
|
|
|
|
self.bitDepth = cast(int, data.attrib.get('bitDepth'))
|
2016-04-10 03:59:47 +00:00
|
|
|
self.bitrate = cast(int, data.attrib.get('bitrate'))
|
2016-03-24 06:20:08 +00:00
|
|
|
self.bitrateMode = data.attrib.get('bitrateMode')
|
2016-04-10 03:59:47 +00:00
|
|
|
self.channels = cast(int, data.attrib.get('channels'))
|
2016-03-24 06:20:08 +00:00
|
|
|
self.dialogNorm = cast(int, data.attrib.get('dialogNorm'))
|
2014-12-29 03:21:58 +00:00
|
|
|
self.duration = cast(int, data.attrib.get('duration'))
|
2016-03-24 06:20:08 +00:00
|
|
|
self.samplingRate = cast(int, data.attrib.get('samplingRate'))
|
2014-12-29 03:21:58 +00:00
|
|
|
self.title = data.attrib.get('title')
|
|
|
|
|
|
|
|
|
2017-02-13 02:55:55 +00:00
|
|
|
@utils.registerPlexObject
|
2016-03-24 06:20:08 +00:00
|
|
|
class SubtitleStream(MediaPartStream):
|
2017-02-04 08:08:47 +00:00
|
|
|
""" Respresents a audio stream within a :class:`~plexapi.media.MediaPart`.
|
2017-02-18 00:51:06 +00:00
|
|
|
|
2017-02-04 08:08:47 +00:00
|
|
|
Attributes:
|
2017-02-14 04:32:27 +00:00
|
|
|
TAG (str): 'Stream'
|
|
|
|
STREAMTYPE (int): 3
|
2017-02-04 08:08:47 +00:00
|
|
|
format (str): Subtitle format (ex: srt).
|
|
|
|
key (str): Key of this subtitle stream (ex: /library/streams/212284).
|
|
|
|
title (str): Title of this subtitle stream.
|
|
|
|
"""
|
2017-02-13 02:55:55 +00:00
|
|
|
TAG = 'Stream'
|
2016-03-24 06:20:08 +00:00
|
|
|
STREAMTYPE = 3
|
2014-12-29 03:21:58 +00:00
|
|
|
|
2017-02-06 04:52:10 +00:00
|
|
|
def _loadData(self, data):
|
2017-02-14 04:32:27 +00:00
|
|
|
""" Load attribute values from Plex XML response. """
|
2017-02-06 04:52:10 +00:00
|
|
|
super(SubtitleStream, self)._loadData(data)
|
2014-12-29 03:21:58 +00:00
|
|
|
self.format = data.attrib.get('format')
|
2016-04-10 03:59:47 +00:00
|
|
|
self.key = data.attrib.get('key')
|
2016-03-24 06:20:08 +00:00
|
|
|
self.title = data.attrib.get('title')
|
2014-12-29 03:21:58 +00:00
|
|
|
|
|
|
|
|
2017-02-26 22:31:09 +00:00
|
|
|
@utils.registerPlexObject
|
|
|
|
class Session(PlexObject):
|
2017-02-27 02:12:56 +00:00
|
|
|
""" Represents a current session. """
|
2017-02-26 22:31:09 +00:00
|
|
|
TAG = 'Session'
|
|
|
|
|
|
|
|
def _loadData(self, data):
|
|
|
|
self.id = data.attrib.get('id')
|
|
|
|
self.bandwidth = utils.cast(int, data.attrib.get('bandwidth'))
|
|
|
|
self.location = data.attrib.get('location')
|
|
|
|
|
|
|
|
|
2017-02-13 02:55:55 +00:00
|
|
|
@utils.registerPlexObject
|
2017-02-06 04:52:10 +00:00
|
|
|
class TranscodeSession(PlexObject):
|
2017-02-18 00:51:06 +00:00
|
|
|
""" Represents a current transcode session.
|
2017-02-14 04:32:27 +00:00
|
|
|
|
|
|
|
Attributes:
|
|
|
|
TAG (str): 'TranscodeSession'
|
|
|
|
TODO: Document this.
|
2017-02-04 08:08:47 +00:00
|
|
|
"""
|
2017-02-13 02:55:55 +00:00
|
|
|
TAG = 'TranscodeSession'
|
2015-11-05 02:10:10 +00:00
|
|
|
|
2017-02-06 04:52:10 +00:00
|
|
|
def _loadData(self, data):
|
2017-02-14 04:32:27 +00:00
|
|
|
""" Load attribute values from Plex XML response. """
|
2017-02-06 04:52:10 +00:00
|
|
|
self._data = data
|
2016-04-10 03:59:47 +00:00
|
|
|
self.audioChannels = cast(int, data.attrib.get('audioChannels'))
|
|
|
|
self.audioCodec = data.attrib.get('audioCodec')
|
|
|
|
self.audioDecision = data.attrib.get('audioDecision')
|
|
|
|
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'))
|
2015-11-05 02:10:10 +00:00
|
|
|
self.key = data.attrib.get('key')
|
|
|
|
self.progress = cast(float, data.attrib.get('progress'))
|
|
|
|
self.protocol = data.attrib.get('protocol')
|
2016-04-10 03:59:47 +00:00
|
|
|
self.remaining = cast(int, data.attrib.get('remaining'))
|
|
|
|
self.speed = cast(int, data.attrib.get('speed'))
|
|
|
|
self.throttled = cast(int, data.attrib.get('throttled'))
|
2018-03-20 15:30:07 +00:00
|
|
|
self.sourceVideoCodec = data.attrib.get('sourceVideoCodec')
|
2015-11-05 02:10:10 +00:00
|
|
|
self.videoCodec = data.attrib.get('videoCodec')
|
2016-04-10 03:59:47 +00:00
|
|
|
self.videoDecision = data.attrib.get('videoDecision')
|
2015-11-05 02:10:10 +00:00
|
|
|
self.width = cast(int, data.attrib.get('width'))
|
|
|
|
|
|
|
|
|
2017-02-06 04:52:10 +00:00
|
|
|
class MediaTag(PlexObject):
|
2017-02-03 07:15:41 +00:00
|
|
|
""" Base class for media tags used for filtering and searching your library
|
|
|
|
items or navigating the metadata of media items in your library. Tags are
|
|
|
|
the construct used for things such as Country, Director, Genre, etc.
|
|
|
|
|
|
|
|
Attributes:
|
|
|
|
server (:class:`~plexapi.server.PlexServer`): Server this client is connected to.
|
|
|
|
id (id): Tag ID (This seems meaningless except to use it as a unique id).
|
|
|
|
role (str): Unknown
|
|
|
|
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
|
2017-02-18 00:51:06 +00:00
|
|
|
PlexServer :func:`~plexapi.server.PlexServer.search()`. They provide details of which
|
2017-02-03 07:15:41 +00:00
|
|
|
library section the tag was found as well as the url to dig deeper into the results.
|
2017-02-18 00:51:06 +00:00
|
|
|
|
2017-02-03 07:15:41 +00:00
|
|
|
* 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.
|
|
|
|
"""
|
2017-07-21 23:07:31 +00:00
|
|
|
|
2017-02-06 04:52:10 +00:00
|
|
|
def _loadData(self, data):
|
2017-02-14 04:32:27 +00:00
|
|
|
""" Load attribute values from Plex XML response. """
|
2017-02-04 08:08:47 +00:00
|
|
|
self._data = data
|
2014-12-29 03:21:58 +00:00
|
|
|
self.id = cast(int, data.attrib.get('id'))
|
|
|
|
self.role = data.attrib.get('role')
|
2016-04-10 03:59:47 +00:00
|
|
|
self.tag = data.attrib.get('tag')
|
2017-02-03 07:15:41 +00:00
|
|
|
# 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')
|
2014-12-29 03:21:58 +00:00
|
|
|
|
2017-02-03 07:15:41 +00:00
|
|
|
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()`.
|
|
|
|
"""
|
|
|
|
if not self.key:
|
|
|
|
raise BadRequest('Key is not defined for this tag: %s' % self.tag)
|
2017-02-07 06:20:49 +00:00
|
|
|
return self.fetchItems(self.key)
|
2014-12-29 03:21:58 +00:00
|
|
|
|
|
|
|
|
2017-02-13 02:55:55 +00:00
|
|
|
@utils.registerPlexObject
|
2017-02-02 03:53:05 +00:00
|
|
|
class Collection(MediaTag):
|
2017-02-14 04:32:27 +00:00
|
|
|
""" Represents a single Collection media tag.
|
2017-02-18 00:51:06 +00:00
|
|
|
|
2017-02-14 04:32:27 +00:00
|
|
|
Attributes:
|
|
|
|
TAG (str): 'Collection'
|
|
|
|
FILTER (str): 'collection'
|
|
|
|
"""
|
2017-02-13 02:55:55 +00:00
|
|
|
TAG = 'Collection'
|
2017-02-02 03:53:05 +00:00
|
|
|
FILTER = 'collection'
|
|
|
|
|
2017-02-20 05:37:00 +00:00
|
|
|
|
2017-07-21 23:07:31 +00:00
|
|
|
@utils.registerPlexObject
|
|
|
|
class Label(MediaTag):
|
|
|
|
""" Represents a single label media tag.
|
|
|
|
|
|
|
|
Attributes:
|
|
|
|
TAG (str): 'label'
|
|
|
|
FILTER (str): 'label'
|
|
|
|
"""
|
|
|
|
TAG = 'Label'
|
|
|
|
FILTER = 'label'
|
|
|
|
|
|
|
|
|
2017-02-13 02:55:55 +00:00
|
|
|
@utils.registerPlexObject
|
2017-02-02 03:53:05 +00:00
|
|
|
class Country(MediaTag):
|
2017-02-14 04:32:27 +00:00
|
|
|
""" Represents a single Country media tag.
|
2017-02-18 00:51:06 +00:00
|
|
|
|
2017-02-14 04:32:27 +00:00
|
|
|
Attributes:
|
|
|
|
TAG (str): 'Country'
|
|
|
|
FILTER (str): 'country'
|
|
|
|
"""
|
2017-02-13 02:55:55 +00:00
|
|
|
TAG = 'Country'
|
2017-02-02 03:53:05 +00:00
|
|
|
FILTER = 'country'
|
|
|
|
|
2017-02-20 05:37:00 +00:00
|
|
|
|
2017-02-13 02:55:55 +00:00
|
|
|
@utils.registerPlexObject
|
2017-02-02 03:53:05 +00:00
|
|
|
class Director(MediaTag):
|
2017-02-14 04:32:27 +00:00
|
|
|
""" Represents a single Director media tag.
|
2017-02-18 00:51:06 +00:00
|
|
|
|
2017-02-14 04:32:27 +00:00
|
|
|
Attributes:
|
|
|
|
TAG (str): 'Director'
|
|
|
|
FILTER (str): 'director'
|
|
|
|
"""
|
2017-02-13 02:55:55 +00:00
|
|
|
TAG = 'Director'
|
2017-02-02 03:53:05 +00:00
|
|
|
FILTER = 'director'
|
|
|
|
|
2017-02-20 05:37:00 +00:00
|
|
|
|
2017-02-13 02:55:55 +00:00
|
|
|
@utils.registerPlexObject
|
2017-02-02 03:53:05 +00:00
|
|
|
class Genre(MediaTag):
|
2017-02-14 04:32:27 +00:00
|
|
|
""" Represents a single Genre media tag.
|
2017-02-18 00:51:06 +00:00
|
|
|
|
2017-02-14 04:32:27 +00:00
|
|
|
Attributes:
|
|
|
|
TAG (str): 'Genre'
|
|
|
|
FILTER (str): 'genre'
|
|
|
|
"""
|
2017-02-13 02:55:55 +00:00
|
|
|
TAG = 'Genre'
|
2017-02-02 03:53:05 +00:00
|
|
|
FILTER = 'genre'
|
|
|
|
|
2017-02-20 05:37:00 +00:00
|
|
|
|
2017-02-13 02:55:55 +00:00
|
|
|
@utils.registerPlexObject
|
2017-02-02 03:53:05 +00:00
|
|
|
class Mood(MediaTag):
|
2017-02-14 04:32:27 +00:00
|
|
|
""" Represents a single Mood media tag.
|
2017-02-18 00:51:06 +00:00
|
|
|
|
2017-02-14 04:32:27 +00:00
|
|
|
Attributes:
|
|
|
|
TAG (str): 'Mood'
|
|
|
|
FILTER (str): 'mood'
|
|
|
|
"""
|
2017-02-13 02:55:55 +00:00
|
|
|
TAG = 'Mood'
|
2017-02-02 03:53:05 +00:00
|
|
|
FILTER = 'mood'
|
|
|
|
|
2017-02-20 05:37:00 +00:00
|
|
|
|
2017-02-13 02:55:55 +00:00
|
|
|
@utils.registerPlexObject
|
2017-02-02 03:53:05 +00:00
|
|
|
class Producer(MediaTag):
|
2017-02-14 04:32:27 +00:00
|
|
|
""" Represents a single Producer media tag.
|
2017-02-18 00:51:06 +00:00
|
|
|
|
2017-02-14 04:32:27 +00:00
|
|
|
Attributes:
|
|
|
|
TAG (str): 'Producer'
|
|
|
|
FILTER (str): 'producer'
|
|
|
|
"""
|
2017-02-13 02:55:55 +00:00
|
|
|
TAG = 'Producer'
|
2017-02-02 03:53:05 +00:00
|
|
|
FILTER = 'producer'
|
|
|
|
|
2017-02-20 05:37:00 +00:00
|
|
|
|
2017-02-13 02:55:55 +00:00
|
|
|
@utils.registerPlexObject
|
2017-02-02 03:53:05 +00:00
|
|
|
class Role(MediaTag):
|
2017-02-14 04:32:27 +00:00
|
|
|
""" Represents a single Role (actor/actress) media tag.
|
2017-02-18 00:51:06 +00:00
|
|
|
|
2017-02-14 04:32:27 +00:00
|
|
|
Attributes:
|
|
|
|
TAG (str): 'Role'
|
|
|
|
FILTER (str): 'role'
|
|
|
|
"""
|
2017-02-13 02:55:55 +00:00
|
|
|
TAG = 'Role'
|
2017-02-02 03:53:05 +00:00
|
|
|
FILTER = 'role'
|
|
|
|
|
2017-02-20 05:37:00 +00:00
|
|
|
|
2017-02-13 02:55:55 +00:00
|
|
|
@utils.registerPlexObject
|
2017-02-02 03:53:05 +00:00
|
|
|
class Similar(MediaTag):
|
2017-02-14 04:32:27 +00:00
|
|
|
""" Represents a single Similar media tag.
|
2017-02-18 00:51:06 +00:00
|
|
|
|
2017-02-14 04:32:27 +00:00
|
|
|
Attributes:
|
|
|
|
TAG (str): 'Similar'
|
|
|
|
FILTER (str): 'similar'
|
|
|
|
"""
|
2017-02-13 02:55:55 +00:00
|
|
|
TAG = 'Similar'
|
2017-02-02 03:53:05 +00:00
|
|
|
FILTER = 'similar'
|
|
|
|
|
2017-02-20 05:37:00 +00:00
|
|
|
|
2017-02-13 02:55:55 +00:00
|
|
|
@utils.registerPlexObject
|
2017-02-02 03:53:05 +00:00
|
|
|
class Writer(MediaTag):
|
2017-02-14 04:32:27 +00:00
|
|
|
""" Represents a single Writer media tag.
|
2017-02-18 00:51:06 +00:00
|
|
|
|
2017-02-14 04:32:27 +00:00
|
|
|
Attributes:
|
|
|
|
TAG (str): 'Writer'
|
|
|
|
FILTER (str): 'writer'
|
|
|
|
"""
|
2017-02-13 02:55:55 +00:00
|
|
|
TAG = 'Writer'
|
2017-02-02 03:53:05 +00:00
|
|
|
FILTER = 'writer'
|
2016-10-02 20:05:37 +00:00
|
|
|
|
2017-02-20 05:37:00 +00:00
|
|
|
|
2018-03-02 12:15:15 +00:00
|
|
|
@utils.registerPlexObject
|
|
|
|
class Chapter(PlexObject):
|
|
|
|
""" Represents a single Writer media tag.
|
|
|
|
|
|
|
|
Attributes:
|
|
|
|
TAG (str): 'Chapter'
|
|
|
|
"""
|
|
|
|
TAG = 'Chapter'
|
|
|
|
|
|
|
|
def _loadData(self, data):
|
|
|
|
self._data = data
|
|
|
|
self.id = 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'))
|
|
|
|
|
|
|
|
|
2017-02-13 02:55:55 +00:00
|
|
|
@utils.registerPlexObject
|
2017-02-06 04:52:10 +00:00
|
|
|
class Field(PlexObject):
|
2017-02-14 04:32:27 +00:00
|
|
|
""" Represents a single Field.
|
2017-02-18 00:51:06 +00:00
|
|
|
|
2017-02-14 04:32:27 +00:00
|
|
|
Attributes:
|
|
|
|
TAG (str): 'Field'
|
|
|
|
"""
|
2017-02-13 02:55:55 +00:00
|
|
|
TAG = 'Field'
|
2016-10-02 20:05:37 +00:00
|
|
|
|
2017-02-06 04:52:10 +00:00
|
|
|
def _loadData(self, data):
|
|
|
|
self._data = data
|
2016-10-02 20:05:37 +00:00
|
|
|
self.name = data.attrib.get('name')
|
|
|
|
self.locked = cast(bool, data.attrib.get('locked'))
|