mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-06 13:11:15 -07:00
Update python-plexapi-4.7.2
This commit is contained in:
parent
c8b02b06d6
commit
0770a301c7
12 changed files with 283 additions and 42 deletions
|
@ -6,6 +6,7 @@ from platform import uname
|
||||||
from uuid import getnode
|
from uuid import getnode
|
||||||
|
|
||||||
from plexapi.config import PlexConfig, reset_base_headers
|
from plexapi.config import PlexConfig, reset_base_headers
|
||||||
|
import plexapi.const as const
|
||||||
from plexapi.utils import SecretsFilter
|
from plexapi.utils import SecretsFilter
|
||||||
|
|
||||||
# Load User Defined Config
|
# Load User Defined Config
|
||||||
|
@ -15,7 +16,7 @@ CONFIG = PlexConfig(CONFIG_PATH)
|
||||||
|
|
||||||
# PlexAPI Settings
|
# PlexAPI Settings
|
||||||
PROJECT = 'PlexAPI'
|
PROJECT = 'PlexAPI'
|
||||||
VERSION = '4.6.1'
|
VERSION = __version__ = const.__version__
|
||||||
TIMEOUT = CONFIG.get('plexapi.timeout', 30, int)
|
TIMEOUT = CONFIG.get('plexapi.timeout', 30, int)
|
||||||
X_PLEX_CONTAINER_SIZE = CONFIG.get('plexapi.container_size', 100, int)
|
X_PLEX_CONTAINER_SIZE = CONFIG.get('plexapi.container_size', 100, int)
|
||||||
X_PLEX_ENABLE_FAST_CONNECT = CONFIG.get('plexapi.enable_fast_connect', False, bool)
|
X_PLEX_ENABLE_FAST_CONNECT = CONFIG.get('plexapi.enable_fast_connect', False, bool)
|
||||||
|
|
|
@ -28,6 +28,7 @@ class Audio(PlexPartialObject):
|
||||||
librarySectionTitle (str): :class:`~plexapi.library.LibrarySection` title.
|
librarySectionTitle (str): :class:`~plexapi.library.LibrarySection` title.
|
||||||
listType (str): Hardcoded as 'audio' (useful for search filters).
|
listType (str): Hardcoded as 'audio' (useful for search filters).
|
||||||
moods (List<:class:`~plexapi.media.Mood`>): List of mood objects.
|
moods (List<:class:`~plexapi.media.Mood`>): List of mood objects.
|
||||||
|
musicAnalysisVersion (int): The Plex music analysis version for the item.
|
||||||
ratingKey (int): Unique key identifying the item.
|
ratingKey (int): Unique key identifying the item.
|
||||||
summary (str): Summary of the artist, album, or track.
|
summary (str): Summary of the artist, album, or track.
|
||||||
thumb (str): URL to thumbnail image (/library/metadata/<ratingKey>/thumb/<thumbid>).
|
thumb (str): URL to thumbnail image (/library/metadata/<ratingKey>/thumb/<thumbid>).
|
||||||
|
@ -59,6 +60,7 @@ class Audio(PlexPartialObject):
|
||||||
self.librarySectionTitle = data.attrib.get('librarySectionTitle')
|
self.librarySectionTitle = data.attrib.get('librarySectionTitle')
|
||||||
self.listType = 'audio'
|
self.listType = 'audio'
|
||||||
self.moods = self.findItems(data, media.Mood)
|
self.moods = self.findItems(data, media.Mood)
|
||||||
|
self.musicAnalysisVersion = utils.cast(int, data.attrib.get('musicAnalysisVersion'))
|
||||||
self.ratingKey = utils.cast(int, data.attrib.get('ratingKey'))
|
self.ratingKey = utils.cast(int, data.attrib.get('ratingKey'))
|
||||||
self.summary = data.attrib.get('summary')
|
self.summary = data.attrib.get('summary')
|
||||||
self.thumb = data.attrib.get('thumb')
|
self.thumb = data.attrib.get('thumb')
|
||||||
|
@ -78,6 +80,11 @@ class Audio(PlexPartialObject):
|
||||||
""" Returns str, default title for a new syncItem. """
|
""" Returns str, default title for a new syncItem. """
|
||||||
return self.title
|
return self.title
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hasSonicAnalysis(self):
|
||||||
|
""" Returns True if the audio has been sonically analyzed. """
|
||||||
|
return self.musicAnalysisVersion == 1
|
||||||
|
|
||||||
def sync(self, bitrate, client=None, clientId=None, limit=None, title=None):
|
def sync(self, bitrate, client=None, clientId=None, limit=None, title=None):
|
||||||
""" Add current audio (artist, album or track) as sync item for specified device.
|
""" Add current audio (artist, album or track) as sync item for specified device.
|
||||||
See :func:`~plexapi.myplex.MyPlexAccount.sync` for possible exceptions.
|
See :func:`~plexapi.myplex.MyPlexAccount.sync` for possible exceptions.
|
||||||
|
@ -227,6 +234,7 @@ class Album(Audio, ArtMixin, PosterMixin, RatingMixin, UnmatchMatchMixin,
|
||||||
TAG (str): 'Directory'
|
TAG (str): 'Directory'
|
||||||
TYPE (str): 'album'
|
TYPE (str): 'album'
|
||||||
collections (List<:class:`~plexapi.media.Collection`>): List of collection objects.
|
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.
|
genres (List<:class:`~plexapi.media.Genre`>): List of genre objects.
|
||||||
key (str): API URL (/library/metadata/<ratingkey>).
|
key (str): API URL (/library/metadata/<ratingkey>).
|
||||||
labels (List<:class:`~plexapi.media.Label`>): List of label objects.
|
labels (List<:class:`~plexapi.media.Label`>): List of label objects.
|
||||||
|
@ -241,6 +249,7 @@ class Album(Audio, ArtMixin, PosterMixin, RatingMixin, UnmatchMatchMixin,
|
||||||
rating (float): Album rating (7.9; 9.8; 8.1).
|
rating (float): Album rating (7.9; 9.8; 8.1).
|
||||||
studio (str): Studio that released the album.
|
studio (str): Studio that released the album.
|
||||||
styles (List<:class:`~plexapi.media.Style`>): List of style objects.
|
styles (List<:class:`~plexapi.media.Style`>): List of style objects.
|
||||||
|
subformats (List<:class:`~plexapi.media.Subformat`>): List of subformat objects.
|
||||||
viewedLeafCount (int): Number of items marked as played in the album view.
|
viewedLeafCount (int): Number of items marked as played in the album view.
|
||||||
year (int): Year the album was released.
|
year (int): Year the album was released.
|
||||||
"""
|
"""
|
||||||
|
@ -251,6 +260,7 @@ class Album(Audio, ArtMixin, PosterMixin, RatingMixin, UnmatchMatchMixin,
|
||||||
""" Load attribute values from Plex XML response. """
|
""" Load attribute values from Plex XML response. """
|
||||||
Audio._loadData(self, data)
|
Audio._loadData(self, data)
|
||||||
self.collections = self.findItems(data, media.Collection)
|
self.collections = self.findItems(data, media.Collection)
|
||||||
|
self.formats = self.findItems(data, media.Format)
|
||||||
self.genres = self.findItems(data, media.Genre)
|
self.genres = self.findItems(data, media.Genre)
|
||||||
self.key = self.key.replace('/children', '') # FIX_BUG_50
|
self.key = self.key.replace('/children', '') # FIX_BUG_50
|
||||||
self.labels = self.findItems(data, media.Label)
|
self.labels = self.findItems(data, media.Label)
|
||||||
|
@ -265,6 +275,7 @@ class Album(Audio, ArtMixin, PosterMixin, RatingMixin, UnmatchMatchMixin,
|
||||||
self.rating = utils.cast(float, data.attrib.get('rating'))
|
self.rating = utils.cast(float, data.attrib.get('rating'))
|
||||||
self.studio = data.attrib.get('studio')
|
self.studio = data.attrib.get('studio')
|
||||||
self.styles = self.findItems(data, media.Style)
|
self.styles = self.findItems(data, media.Style)
|
||||||
|
self.subformats = self.findItems(data, media.Subformat)
|
||||||
self.viewedLeafCount = utils.cast(int, data.attrib.get('viewedLeafCount'))
|
self.viewedLeafCount = utils.cast(int, data.attrib.get('viewedLeafCount'))
|
||||||
self.year = utils.cast(int, data.attrib.get('year'))
|
self.year = utils.cast(int, data.attrib.get('year'))
|
||||||
|
|
||||||
|
@ -415,3 +426,7 @@ class Track(Audio, Playable, ArtUrlMixin, PosterUrlMixin, RatingMixin,
|
||||||
def _defaultSyncTitle(self):
|
def _defaultSyncTitle(self):
|
||||||
""" Returns str, default title for a new syncItem. """
|
""" Returns str, default title for a new syncItem. """
|
||||||
return '%s - %s - %s' % (self.grandparentTitle, self.parentTitle, self.title)
|
return '%s - %s - %s' % (self.grandparentTitle, self.parentTitle, self.title)
|
||||||
|
|
||||||
|
def _getWebURL(self, base=None):
|
||||||
|
""" Get the Plex Web URL with the correct parameters. """
|
||||||
|
return self._server._buildWebURL(base=base, endpoint='details', key=self.parentKey)
|
||||||
|
|
|
@ -505,6 +505,17 @@ class PlexPartialObject(PlexObject):
|
||||||
""" Returns True if this is not a full object. """
|
""" Returns True if this is not a full object. """
|
||||||
return not self.isFullObject()
|
return not self.isFullObject()
|
||||||
|
|
||||||
|
def _edit(self, **kwargs):
|
||||||
|
""" Actually edit an object. """
|
||||||
|
if 'id' not in kwargs:
|
||||||
|
kwargs['id'] = self.ratingKey
|
||||||
|
if 'type' not in kwargs:
|
||||||
|
kwargs['type'] = utils.searchType(self.type)
|
||||||
|
|
||||||
|
part = '/library/sections/%s/all?%s' % (self.librarySectionID,
|
||||||
|
urlencode(kwargs))
|
||||||
|
self._server.query(part, method=self._server._session.put)
|
||||||
|
|
||||||
def edit(self, **kwargs):
|
def edit(self, **kwargs):
|
||||||
""" Edit an object.
|
""" Edit an object.
|
||||||
|
|
||||||
|
@ -517,14 +528,7 @@ class PlexPartialObject(PlexObject):
|
||||||
'collection[0].tag.tag': 'Super',
|
'collection[0].tag.tag': 'Super',
|
||||||
'collection.locked': 0}
|
'collection.locked': 0}
|
||||||
"""
|
"""
|
||||||
if 'id' not in kwargs:
|
self._edit(**kwargs)
|
||||||
kwargs['id'] = self.ratingKey
|
|
||||||
if 'type' not in kwargs:
|
|
||||||
kwargs['type'] = utils.searchType(self.type)
|
|
||||||
|
|
||||||
part = '/library/sections/%s/all?%s' % (self.librarySectionID,
|
|
||||||
urlencode(kwargs))
|
|
||||||
self._server.query(part, method=self._server._session.put)
|
|
||||||
|
|
||||||
def _edit_tags(self, tag, items, locked=True, remove=False):
|
def _edit_tags(self, tag, items, locked=True, remove=False):
|
||||||
""" Helper to edit tags.
|
""" Helper to edit tags.
|
||||||
|
@ -575,12 +579,28 @@ class PlexPartialObject(PlexObject):
|
||||||
|
|
||||||
def history(self, maxresults=9999999, mindate=None):
|
def history(self, maxresults=9999999, mindate=None):
|
||||||
""" Get Play History for a media item.
|
""" Get Play History for a media item.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
maxresults (int): Only return the specified number of results (optional).
|
maxresults (int): Only return the specified number of results (optional).
|
||||||
mindate (datetime): Min datetime to return results from.
|
mindate (datetime): Min datetime to return results from.
|
||||||
"""
|
"""
|
||||||
return self._server.history(maxresults=maxresults, mindate=mindate, ratingKey=self.ratingKey)
|
return self._server.history(maxresults=maxresults, mindate=mindate, ratingKey=self.ratingKey)
|
||||||
|
|
||||||
|
def _getWebURL(self, base=None):
|
||||||
|
""" Get the Plex Web URL with the correct parameters.
|
||||||
|
Private method to allow overriding parameters from subclasses.
|
||||||
|
"""
|
||||||
|
return self._server._buildWebURL(base=base, endpoint='details', key=self.key)
|
||||||
|
|
||||||
|
def getWebURL(self, base=None):
|
||||||
|
""" Returns the Plex Web URL for a media item.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
base (str): The base URL before the fragment (``#!``).
|
||||||
|
Default is https://app.plex.tv/desktop.
|
||||||
|
"""
|
||||||
|
return self._getWebURL(base=base)
|
||||||
|
|
||||||
|
|
||||||
class Playable(object):
|
class Playable(object):
|
||||||
""" This is a general place to store functions specific to media that is Playable.
|
""" This is a general place to store functions specific to media that is Playable.
|
||||||
|
|
|
@ -511,7 +511,9 @@ class PlexClient(PlexObject):
|
||||||
|
|
||||||
playqueue = media if isinstance(media, PlayQueue) else self._server.createPlayQueue(media)
|
playqueue = media if isinstance(media, PlayQueue) else self._server.createPlayQueue(media)
|
||||||
self.sendCommand('playback/playMedia', **dict({
|
self.sendCommand('playback/playMedia', **dict({
|
||||||
|
'providerIdentifier': 'com.plexapp.plugins.library',
|
||||||
'machineIdentifier': self._server.machineIdentifier,
|
'machineIdentifier': self._server.machineIdentifier,
|
||||||
|
'protocol': server_url[0],
|
||||||
'address': server_url[1].strip('/'),
|
'address': server_url[1].strip('/'),
|
||||||
'port': server_port,
|
'port': server_port,
|
||||||
'offset': offset,
|
'offset': offset,
|
||||||
|
|
|
@ -442,7 +442,7 @@ class Collection(PlexPartialObject, AdvancedSettingsMixin, ArtMixin, PosterMixin
|
||||||
smart (bool): True to create a smart collection. Default False.
|
smart (bool): True to create a smart collection. Default False.
|
||||||
limit (int): Smart collections only, limit the number of items in the collection.
|
limit (int): Smart collections only, limit the number of items in the collection.
|
||||||
libtype (str): Smart collections only, the specific type of content to filter
|
libtype (str): Smart collections only, the specific type of content to filter
|
||||||
(movie, show, season, episode, artist, album, track, photoalbum, photo, collection).
|
(movie, show, season, episode, artist, album, track, photoalbum, photo).
|
||||||
sort (str or list, optional): Smart collections only, a string of comma separated sort fields
|
sort (str or list, optional): Smart collections only, a string of comma separated sort fields
|
||||||
or a list of sort fields in the format ``column:dir``.
|
or a list of sort fields in the format ``column:dir``.
|
||||||
See :func:`~plexapi.library.LibrarySection.search` for more info.
|
See :func:`~plexapi.library.LibrarySection.search` for more info.
|
||||||
|
|
9
lib/plexapi/const.py
Normal file
9
lib/plexapi/const.py
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Constants used by plexapi."""
|
||||||
|
|
||||||
|
# Library version
|
||||||
|
MAJOR_VERSION = 4
|
||||||
|
MINOR_VERSION = 7
|
||||||
|
PATCH_VERSION = 2
|
||||||
|
__short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||||
|
__version__ = f"{__short_version__}.{PATCH_VERSION}"
|
|
@ -456,10 +456,45 @@ class LibrarySection(PlexObject):
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
title (str): Title of the item to return.
|
title (str): Title of the item to return.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
:exc:`~plexapi.exceptions.NotFound`: The title is not found in the library.
|
||||||
"""
|
"""
|
||||||
key = '/library/sections/%s/all?title=%s' % (self.key, quote(title, safe=''))
|
key = '/library/sections/%s/all?includeGuids=1&title=%s' % (self.key, quote(title, safe=''))
|
||||||
return self.fetchItem(key, title__iexact=title)
|
return self.fetchItem(key, title__iexact=title)
|
||||||
|
|
||||||
|
def getGuid(self, guid):
|
||||||
|
""" Returns the media item with the specified external IMDB, TMDB, or TVDB ID.
|
||||||
|
Note: This search uses a PlexAPI operator so performance may be slow. All items from the
|
||||||
|
entire Plex library need to be retrieved for each guid search. It is recommended to create
|
||||||
|
your own lookup dictionary if you are searching for a lot of external guids.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
guid (str): The external guid of the item to return.
|
||||||
|
Examples: IMDB ``imdb://tt0944947``, TMDB ``tmdb://1399``, TVDB ``tvdb://121361``.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
:exc:`~plexapi.exceptions.NotFound`: The guid is not found in the library.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
# This will retrieve all items in the entire library 3 times
|
||||||
|
result1 = library.getGuid('imdb://tt0944947')
|
||||||
|
result2 = library.getGuid('tmdb://1399')
|
||||||
|
result3 = library.getGuid('tvdb://121361')
|
||||||
|
|
||||||
|
# This will only retrieve all items in the library once to create a lookup dictionary
|
||||||
|
guidLookup = {guid.id: item for item in library.all() for guid in item.guids}
|
||||||
|
result1 = guidLookup['imdb://tt0944947']
|
||||||
|
result2 = guidLookup['tmdb://1399']
|
||||||
|
result3 = guidLookup['tvdb://121361']
|
||||||
|
|
||||||
|
"""
|
||||||
|
key = '/library/sections/%s/all?includeGuids=1' % self.key
|
||||||
|
return self.fetchItem(key, Guid__id__iexact=guid)
|
||||||
|
|
||||||
def all(self, libtype=None, **kwargs):
|
def all(self, libtype=None, **kwargs):
|
||||||
""" Returns a list of all items from this library section.
|
""" Returns a list of all items from this library section.
|
||||||
See description of :func:`~plexapi.library.LibrarySection.search()` for details about filtering / sorting.
|
See description of :func:`~plexapi.library.LibrarySection.search()` for details about filtering / sorting.
|
||||||
|
@ -979,6 +1014,8 @@ class LibrarySection(PlexObject):
|
||||||
"""
|
"""
|
||||||
args = {}
|
args = {}
|
||||||
filter_args = []
|
filter_args = []
|
||||||
|
|
||||||
|
args['includeGuids'] = int(bool(kwargs.pop('includeGuids', True)))
|
||||||
for field, values in list(kwargs.items()):
|
for field, values in list(kwargs.items()):
|
||||||
if field.split('__')[-1] not in OPERATORS:
|
if field.split('__')[-1] not in OPERATORS:
|
||||||
filter_args.append(self._validateFilterField(field, values, libtype))
|
filter_args.append(self._validateFilterField(field, values, libtype))
|
||||||
|
@ -1405,10 +1442,14 @@ class LibrarySection(PlexObject):
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
title (str): Title of the item to return.
|
title (str): Title of the item to return.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
:exc:`~plexapi.exceptions.NotFound`: Unable to find collection.
|
||||||
"""
|
"""
|
||||||
results = self.collections(title__iexact=title)
|
try:
|
||||||
if results:
|
return self.collections(title=title, title__iexact=title)[0]
|
||||||
return results[0]
|
except IndexError:
|
||||||
|
raise NotFound('Unable to find collection with title "%s".' % title) from None
|
||||||
|
|
||||||
def collections(self, **kwargs):
|
def collections(self, **kwargs):
|
||||||
""" Returns a list of collections from this library section.
|
""" Returns a list of collections from this library section.
|
||||||
|
@ -1430,15 +1471,19 @@ class LibrarySection(PlexObject):
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
title (str): Title of the item to return.
|
title (str): Title of the item to return.
|
||||||
"""
|
|
||||||
results = self.playlists(title__iexact=title)
|
|
||||||
if results:
|
|
||||||
return results[0]
|
|
||||||
|
|
||||||
def playlists(self, **kwargs):
|
Raises:
|
||||||
|
:exc:`~plexapi.exceptions.NotFound`: Unable to find playlist.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return self.playlists(title=title, title__iexact=title)[0]
|
||||||
|
except IndexError:
|
||||||
|
raise NotFound('Unable to find playlist with title "%s".' % title) from None
|
||||||
|
|
||||||
|
def playlists(self, sort=None, **kwargs):
|
||||||
""" Returns a list of playlists from this library section. """
|
""" Returns a list of playlists from this library section. """
|
||||||
key = '/playlists?type=15&playlistType=%s§ionID=%s' % (self.CONTENT_TYPE, self.key)
|
return self._server.playlists(
|
||||||
return self.fetchItems(key, **kwargs)
|
playlistType=self.CONTENT_TYPE, sectionId=self.key, sort=sort, **kwargs)
|
||||||
|
|
||||||
@deprecated('use "listFields" instead')
|
@deprecated('use "listFields" instead')
|
||||||
def filterFields(self, mediaType=None):
|
def filterFields(self, mediaType=None):
|
||||||
|
@ -1448,6 +1493,23 @@ class LibrarySection(PlexObject):
|
||||||
def listChoices(self, category, libtype=None, **kwargs):
|
def listChoices(self, category, libtype=None, **kwargs):
|
||||||
return self.listFilterChoices(field=category, libtype=libtype)
|
return self.listFilterChoices(field=category, libtype=libtype)
|
||||||
|
|
||||||
|
def getWebURL(self, base=None, tab=None, key=None):
|
||||||
|
""" Returns the Plex Web URL for the library.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
base (str): The base URL before the fragment (``#!``).
|
||||||
|
Default is https://app.plex.tv/desktop.
|
||||||
|
tab (str): The library tab (recommended, library, collections, playlists, timeline).
|
||||||
|
key (str): A hub key.
|
||||||
|
"""
|
||||||
|
params = {'source': self.key}
|
||||||
|
if tab is not None:
|
||||||
|
params['pivot'] = tab
|
||||||
|
if key is not None:
|
||||||
|
params['key'] = key
|
||||||
|
params['pageType'] = 'list'
|
||||||
|
return self._server._buildWebURL(base=base, **params)
|
||||||
|
|
||||||
|
|
||||||
class MovieSection(LibrarySection):
|
class MovieSection(LibrarySection):
|
||||||
""" Represents a :class:`~plexapi.library.LibrarySection` section containing movies.
|
""" Represents a :class:`~plexapi.library.LibrarySection` section containing movies.
|
||||||
|
@ -1849,6 +1911,7 @@ class Hub(PlexObject):
|
||||||
self.style = data.attrib.get('style')
|
self.style = data.attrib.get('style')
|
||||||
self.title = data.attrib.get('title')
|
self.title = data.attrib.get('title')
|
||||||
self.type = data.attrib.get('type')
|
self.type = data.attrib.get('type')
|
||||||
|
self._section = None # cache for self.section
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return self.size
|
return self.size
|
||||||
|
@ -1860,6 +1923,13 @@ class Hub(PlexObject):
|
||||||
self.more = False
|
self.more = False
|
||||||
self.size = len(self.items)
|
self.size = len(self.items)
|
||||||
|
|
||||||
|
def section(self):
|
||||||
|
""" Returns the :class:`~plexapi.library.LibrarySection` this hub belongs to.
|
||||||
|
"""
|
||||||
|
if self._section is None:
|
||||||
|
self._section = self._server.library.sectionByID(self.librarySectionID)
|
||||||
|
return self._section
|
||||||
|
|
||||||
|
|
||||||
class HubMediaTag(PlexObject):
|
class HubMediaTag(PlexObject):
|
||||||
""" Base class of hub media tag search results.
|
""" Base class of hub media tag search results.
|
||||||
|
|
|
@ -174,7 +174,7 @@ class MediaPart(PlexObject):
|
||||||
return [stream for stream in self.streams if isinstance(stream, SubtitleStream)]
|
return [stream for stream in self.streams if isinstance(stream, SubtitleStream)]
|
||||||
|
|
||||||
def lyricStreams(self):
|
def lyricStreams(self):
|
||||||
""" Returns a list of :class:`~plexapi.media.SubtitleStream` objects in this MediaPart. """
|
""" Returns a list of :class:`~plexapi.media.LyricStream` objects in this MediaPart. """
|
||||||
return [stream for stream in self.streams if isinstance(stream, LyricStream)]
|
return [stream for stream in self.streams if isinstance(stream, LyricStream)]
|
||||||
|
|
||||||
def setDefaultAudioStream(self, stream):
|
def setDefaultAudioStream(self, stream):
|
||||||
|
@ -731,6 +731,18 @@ class Director(MediaTag):
|
||||||
FILTER = 'director'
|
FILTER = 'director'
|
||||||
|
|
||||||
|
|
||||||
|
@utils.registerPlexObject
|
||||||
|
class Format(MediaTag):
|
||||||
|
""" Represents a single Format media tag.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
TAG (str): 'Format'
|
||||||
|
FILTER (str): 'format'
|
||||||
|
"""
|
||||||
|
TAG = 'Format'
|
||||||
|
FILTER = 'format'
|
||||||
|
|
||||||
|
|
||||||
@utils.registerPlexObject
|
@utils.registerPlexObject
|
||||||
class Genre(MediaTag):
|
class Genre(MediaTag):
|
||||||
""" Represents a single Genre media tag.
|
""" Represents a single Genre media tag.
|
||||||
|
@ -815,6 +827,18 @@ class Style(MediaTag):
|
||||||
FILTER = 'style'
|
FILTER = 'style'
|
||||||
|
|
||||||
|
|
||||||
|
@utils.registerPlexObject
|
||||||
|
class Subformat(MediaTag):
|
||||||
|
""" Represents a single Subformat media tag.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
TAG (str): 'Subformat'
|
||||||
|
FILTER (str): 'subformat'
|
||||||
|
"""
|
||||||
|
TAG = 'Subformat'
|
||||||
|
FILTER = 'subformat'
|
||||||
|
|
||||||
|
|
||||||
@utils.registerPlexObject
|
@utils.registerPlexObject
|
||||||
class Tag(MediaTag):
|
class Tag(MediaTag):
|
||||||
""" Represents a single Tag media tag.
|
""" Represents a single Tag media tag.
|
||||||
|
|
|
@ -97,6 +97,14 @@ class ArtMixin(ArtUrlMixin):
|
||||||
"""
|
"""
|
||||||
art.select()
|
art.select()
|
||||||
|
|
||||||
|
def lockArt(self):
|
||||||
|
""" Lock the background artwork for a Plex object. """
|
||||||
|
self._edit(**{'art.locked': 1})
|
||||||
|
|
||||||
|
def unlockArt(self):
|
||||||
|
""" Unlock the background artwork for a Plex object. """
|
||||||
|
self._edit(**{'art.locked': 0})
|
||||||
|
|
||||||
|
|
||||||
class BannerUrlMixin(object):
|
class BannerUrlMixin(object):
|
||||||
""" Mixin for Plex objects that can have a banner url. """
|
""" Mixin for Plex objects that can have a banner url. """
|
||||||
|
@ -138,6 +146,14 @@ class BannerMixin(BannerUrlMixin):
|
||||||
"""
|
"""
|
||||||
banner.select()
|
banner.select()
|
||||||
|
|
||||||
|
def lockBanner(self):
|
||||||
|
""" Lock the banner for a Plex object. """
|
||||||
|
self._edit(**{'banner.locked': 1})
|
||||||
|
|
||||||
|
def unlockBanner(self):
|
||||||
|
""" Unlock the banner for a Plex object. """
|
||||||
|
self._edit(**{'banner.locked': 0})
|
||||||
|
|
||||||
|
|
||||||
class PosterUrlMixin(object):
|
class PosterUrlMixin(object):
|
||||||
""" Mixin for Plex objects that can have a poster url. """
|
""" Mixin for Plex objects that can have a poster url. """
|
||||||
|
@ -184,6 +200,14 @@ class PosterMixin(PosterUrlMixin):
|
||||||
"""
|
"""
|
||||||
poster.select()
|
poster.select()
|
||||||
|
|
||||||
|
def lockPoster(self):
|
||||||
|
""" Lock the poster for a Plex object. """
|
||||||
|
self._edit(**{'thumb.locked': 1})
|
||||||
|
|
||||||
|
def unlockPoster(self):
|
||||||
|
""" Unlock the poster for a Plex object. """
|
||||||
|
self._edit(**{'thumb.locked': 0})
|
||||||
|
|
||||||
|
|
||||||
class RatingMixin(object):
|
class RatingMixin(object):
|
||||||
""" Mixin for Plex objects that can have user star ratings. """
|
""" Mixin for Plex objects that can have user star ratings. """
|
||||||
|
@ -577,7 +601,9 @@ class SmartFilterMixin(object):
|
||||||
key += '='
|
key += '='
|
||||||
value = value[1:]
|
value = value[1:]
|
||||||
|
|
||||||
if key == 'type':
|
if key == 'includeGuids':
|
||||||
|
filters['includeGuids'] = int(value)
|
||||||
|
elif key == 'type':
|
||||||
filters['libtype'] = utils.reverseSearchType(value)
|
filters['libtype'] = utils.reverseSearchType(value)
|
||||||
elif key == 'sort':
|
elif key == 'sort':
|
||||||
filters['sort'] = value.split(',')
|
filters['sort'] = value.split(',')
|
||||||
|
|
|
@ -137,6 +137,10 @@ class Photoalbum(PlexPartialObject, ArtMixin, PosterMixin, RatingMixin):
|
||||||
filepaths.append(filepath)
|
filepaths.append(filepath)
|
||||||
return filepaths
|
return filepaths
|
||||||
|
|
||||||
|
def _getWebURL(self, base=None):
|
||||||
|
""" Get the Plex Web URL with the correct parameters. """
|
||||||
|
return self._server._buildWebURL(base=base, endpoint='details', key=self.key, legacy=1)
|
||||||
|
|
||||||
|
|
||||||
@utils.registerPlexObject
|
@utils.registerPlexObject
|
||||||
class Photo(PlexPartialObject, Playable, ArtUrlMixin, PosterUrlMixin, RatingMixin, TagMixin):
|
class Photo(PlexPartialObject, Playable, ArtUrlMixin, PosterUrlMixin, RatingMixin, TagMixin):
|
||||||
|
@ -301,3 +305,7 @@ class Photo(PlexPartialObject, Playable, ArtUrlMixin, PosterUrlMixin, RatingMixi
|
||||||
if filepath:
|
if filepath:
|
||||||
filepaths.append(filepath)
|
filepaths.append(filepath)
|
||||||
return filepaths
|
return filepaths
|
||||||
|
|
||||||
|
def _getWebURL(self, base=None):
|
||||||
|
""" Get the Plex Web URL with the correct parameters. """
|
||||||
|
return self._server._buildWebURL(base=base, endpoint='details', key=self.parentKey, legacy=1)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import re
|
import re
|
||||||
from urllib.parse import quote_plus, unquote
|
from urllib.parse import quote_plus, unquote
|
||||||
|
|
||||||
from plexapi import utils
|
from plexapi import media, utils
|
||||||
from plexapi.base import Playable, PlexPartialObject
|
from plexapi.base import Playable, PlexPartialObject
|
||||||
from plexapi.exceptions import BadRequest, NotFound, Unsupported
|
from plexapi.exceptions import BadRequest, NotFound, Unsupported
|
||||||
from plexapi.library import LibrarySection
|
from plexapi.library import LibrarySection
|
||||||
|
@ -24,6 +24,7 @@ class Playlist(PlexPartialObject, Playable, ArtMixin, PosterMixin, SmartFilterMi
|
||||||
content (str): The filter URI string for smart playlists.
|
content (str): The filter URI string for smart playlists.
|
||||||
duration (int): Duration of the playlist in milliseconds.
|
duration (int): Duration of the playlist in milliseconds.
|
||||||
durationInSeconds (int): Duration of the playlist in seconds.
|
durationInSeconds (int): Duration of the playlist in seconds.
|
||||||
|
fields (List<:class:`~plexapi.media.Field`>): List of field objects.
|
||||||
guid (str): Plex GUID for the playlist (com.plexapp.agents.none://XXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXX).
|
guid (str): Plex GUID for the playlist (com.plexapp.agents.none://XXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXX).
|
||||||
icon (str): Icon URI string for smart playlists.
|
icon (str): Icon URI string for smart playlists.
|
||||||
key (str): API URL (/playlist/<ratingkey>).
|
key (str): API URL (/playlist/<ratingkey>).
|
||||||
|
@ -48,8 +49,9 @@ class Playlist(PlexPartialObject, Playable, ArtMixin, PosterMixin, SmartFilterMi
|
||||||
self.content = data.attrib.get('content')
|
self.content = data.attrib.get('content')
|
||||||
self.duration = utils.cast(int, data.attrib.get('duration'))
|
self.duration = utils.cast(int, data.attrib.get('duration'))
|
||||||
self.durationInSeconds = utils.cast(int, data.attrib.get('durationInSeconds'))
|
self.durationInSeconds = utils.cast(int, data.attrib.get('durationInSeconds'))
|
||||||
self.icon = data.attrib.get('icon')
|
self.fields = self.findItems(data, media.Field)
|
||||||
self.guid = data.attrib.get('guid')
|
self.guid = data.attrib.get('guid')
|
||||||
|
self.icon = data.attrib.get('icon')
|
||||||
self.key = data.attrib.get('key', '').replace('/items', '') # FIX_BUG_50
|
self.key = data.attrib.get('key', '').replace('/items', '') # FIX_BUG_50
|
||||||
self.leafCount = utils.cast(int, data.attrib.get('leafCount'))
|
self.leafCount = utils.cast(int, data.attrib.get('leafCount'))
|
||||||
self.playlistType = data.attrib.get('playlistType')
|
self.playlistType = data.attrib.get('playlistType')
|
||||||
|
@ -288,6 +290,11 @@ class Playlist(PlexPartialObject, Playable, ArtMixin, PosterMixin, SmartFilterMi
|
||||||
}))
|
}))
|
||||||
self._server.query(key, method=self._server._session.put)
|
self._server.query(key, method=self._server._session.put)
|
||||||
|
|
||||||
|
def _edit(self, **kwargs):
|
||||||
|
""" Actually edit the playlist. """
|
||||||
|
key = '%s%s' % (self.key, utils.joinArgs(kwargs))
|
||||||
|
self._server.query(key, method=self._server._session.put)
|
||||||
|
|
||||||
def edit(self, title=None, summary=None):
|
def edit(self, title=None, summary=None):
|
||||||
""" Edit the playlist.
|
""" Edit the playlist.
|
||||||
|
|
||||||
|
@ -300,9 +307,7 @@ class Playlist(PlexPartialObject, Playable, ArtMixin, PosterMixin, SmartFilterMi
|
||||||
args['title'] = title
|
args['title'] = title
|
||||||
if summary:
|
if summary:
|
||||||
args['summary'] = summary
|
args['summary'] = summary
|
||||||
|
self._edit(**args)
|
||||||
key = '%s%s' % (self.key, utils.joinArgs(args))
|
|
||||||
self._server.query(key, method=self._server._session.put)
|
|
||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
""" Delete the playlist. """
|
""" Delete the playlist. """
|
||||||
|
@ -341,13 +346,15 @@ class Playlist(PlexPartialObject, Playable, ArtMixin, PosterMixin, SmartFilterMi
|
||||||
return cls(server, data, initpath=key)
|
return cls(server, data, initpath=key)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _createSmart(cls, server, title, section, limit=None, sort=None, filters=None, **kwargs):
|
def _createSmart(cls, server, title, section, limit=None, libtype=None, sort=None, filters=None, **kwargs):
|
||||||
""" Create a smart playlist. """
|
""" Create a smart playlist. """
|
||||||
if not isinstance(section, LibrarySection):
|
if not isinstance(section, LibrarySection):
|
||||||
section = server.library.section(section)
|
section = server.library.section(section)
|
||||||
|
|
||||||
|
libtype = libtype or section.METADATA_TYPE
|
||||||
|
|
||||||
searchKey = section._buildSearchKey(
|
searchKey = section._buildSearchKey(
|
||||||
sort=sort, libtype=section.METADATA_TYPE, limit=limit, filters=filters, **kwargs)
|
sort=sort, libtype=libtype, limit=limit, filters=filters, **kwargs)
|
||||||
uri = '%s%s' % (server._uriRoot(), searchKey)
|
uri = '%s%s' % (server._uriRoot(), searchKey)
|
||||||
|
|
||||||
key = '/playlists%s' % utils.joinArgs({
|
key = '/playlists%s' % utils.joinArgs({
|
||||||
|
@ -361,7 +368,7 @@ class Playlist(PlexPartialObject, Playable, ArtMixin, PosterMixin, SmartFilterMi
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, server, title, section=None, items=None, smart=False, limit=None,
|
def create(cls, server, title, section=None, items=None, smart=False, limit=None,
|
||||||
sort=None, filters=None, **kwargs):
|
libtype=None, sort=None, filters=None, **kwargs):
|
||||||
""" Create a playlist.
|
""" Create a playlist.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
@ -373,6 +380,8 @@ class Playlist(PlexPartialObject, Playable, ArtMixin, PosterMixin, SmartFilterMi
|
||||||
:class:`~plexapi.video.Video`, or :class:`~plexapi.photo.Photo` objects to be added to the playlist.
|
:class:`~plexapi.video.Video`, or :class:`~plexapi.photo.Photo` objects to be added to the playlist.
|
||||||
smart (bool): True to create a smart playlist. Default False.
|
smart (bool): True to create a smart playlist. Default False.
|
||||||
limit (int): Smart playlists only, limit the number of items in the playlist.
|
limit (int): Smart playlists only, limit the number of items in the playlist.
|
||||||
|
libtype (str): Smart playlists only, the specific type of content to filter
|
||||||
|
(movie, show, season, episode, artist, album, track, photoalbum, photo).
|
||||||
sort (str or list, optional): Smart playlists only, a string of comma separated sort fields
|
sort (str or list, optional): Smart playlists only, a string of comma separated sort fields
|
||||||
or a list of sort fields in the format ``column:dir``.
|
or a list of sort fields in the format ``column:dir``.
|
||||||
See :func:`~plexapi.library.LibrarySection.search` for more info.
|
See :func:`~plexapi.library.LibrarySection.search` for more info.
|
||||||
|
@ -389,7 +398,7 @@ class Playlist(PlexPartialObject, Playable, ArtMixin, PosterMixin, SmartFilterMi
|
||||||
:class:`~plexapi.playlist.Playlist`: A new instance of the created Playlist.
|
:class:`~plexapi.playlist.Playlist`: A new instance of the created Playlist.
|
||||||
"""
|
"""
|
||||||
if smart:
|
if smart:
|
||||||
return cls._createSmart(server, title, section, limit, sort, filters, **kwargs)
|
return cls._createSmart(server, title, section, limit, libtype, sort, filters, **kwargs)
|
||||||
else:
|
else:
|
||||||
return cls._create(server, title, items)
|
return cls._create(server, title, items)
|
||||||
|
|
||||||
|
@ -455,3 +464,7 @@ class Playlist(PlexPartialObject, Playable, ArtMixin, PosterMixin, SmartFilterMi
|
||||||
raise Unsupported('Unsupported playlist content')
|
raise Unsupported('Unsupported playlist content')
|
||||||
|
|
||||||
return myplex.sync(sync_item, client=client, clientId=clientId)
|
return myplex.sync(sync_item, client=client, clientId=clientId)
|
||||||
|
|
||||||
|
def _getWebURL(self, base=None):
|
||||||
|
""" Get the Plex Web URL with the correct parameters. """
|
||||||
|
return self._server._buildWebURL(base=base, endpoint='playlist', key=self.key)
|
||||||
|
|
|
@ -427,7 +427,7 @@ class PlexServer(PlexObject):
|
||||||
smart (bool): True to create a smart collection. Default False.
|
smart (bool): True to create a smart collection. Default False.
|
||||||
limit (int): Smart collections only, limit the number of items in the collection.
|
limit (int): Smart collections only, limit the number of items in the collection.
|
||||||
libtype (str): Smart collections only, the specific type of content to filter
|
libtype (str): Smart collections only, the specific type of content to filter
|
||||||
(movie, show, season, episode, artist, album, track, photoalbum, photo, collection).
|
(movie, show, season, episode, artist, album, track, photoalbum, photo).
|
||||||
sort (str or list, optional): Smart collections only, a string of comma separated sort fields
|
sort (str or list, optional): Smart collections only, a string of comma separated sort fields
|
||||||
or a list of sort fields in the format ``column:dir``.
|
or a list of sort fields in the format ``column:dir``.
|
||||||
See :func:`~plexapi.library.LibrarySection.search` for more info.
|
See :func:`~plexapi.library.LibrarySection.search` for more info.
|
||||||
|
@ -448,7 +448,7 @@ class PlexServer(PlexObject):
|
||||||
libtype=libtype, sort=sort, filters=filters, **kwargs)
|
libtype=libtype, sort=sort, filters=filters, **kwargs)
|
||||||
|
|
||||||
def createPlaylist(self, title, section=None, items=None, smart=False, limit=None,
|
def createPlaylist(self, title, section=None, items=None, smart=False, limit=None,
|
||||||
sort=None, filters=None, **kwargs):
|
libtype=None, sort=None, filters=None, **kwargs):
|
||||||
""" Creates and returns a new :class:`~plexapi.playlist.Playlist`.
|
""" Creates and returns a new :class:`~plexapi.playlist.Playlist`.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
@ -459,6 +459,8 @@ class PlexServer(PlexObject):
|
||||||
:class:`~plexapi.video.Video`, or :class:`~plexapi.photo.Photo` objects to be added to the playlist.
|
:class:`~plexapi.video.Video`, or :class:`~plexapi.photo.Photo` objects to be added to the playlist.
|
||||||
smart (bool): True to create a smart playlist. Default False.
|
smart (bool): True to create a smart playlist. Default False.
|
||||||
limit (int): Smart playlists only, limit the number of items in the playlist.
|
limit (int): Smart playlists only, limit the number of items in the playlist.
|
||||||
|
libtype (str): Smart playlists only, the specific type of content to filter
|
||||||
|
(movie, show, season, episode, artist, album, track, photoalbum, photo).
|
||||||
sort (str or list, optional): Smart playlists only, a string of comma separated sort fields
|
sort (str or list, optional): Smart playlists only, a string of comma separated sort fields
|
||||||
or a list of sort fields in the format ``column:dir``.
|
or a list of sort fields in the format ``column:dir``.
|
||||||
See :func:`~plexapi.library.LibrarySection.search` for more info.
|
See :func:`~plexapi.library.LibrarySection.search` for more info.
|
||||||
|
@ -476,7 +478,7 @@ class PlexServer(PlexObject):
|
||||||
"""
|
"""
|
||||||
return Playlist.create(
|
return Playlist.create(
|
||||||
self, title, section=section, items=items, smart=smart, limit=limit,
|
self, title, section=section, items=items, smart=smart, limit=limit,
|
||||||
sort=sort, filters=filters, **kwargs)
|
libtype=libtype, sort=sort, filters=filters, **kwargs)
|
||||||
|
|
||||||
def createPlayQueue(self, item, **kwargs):
|
def createPlayQueue(self, item, **kwargs):
|
||||||
""" Creates and returns a new :class:`~plexapi.playqueue.PlayQueue`.
|
""" Creates and returns a new :class:`~plexapi.playqueue.PlayQueue`.
|
||||||
|
@ -575,17 +577,29 @@ class PlexServer(PlexObject):
|
||||||
args['X-Plex-Container-Start'] += args['X-Plex-Container-Size']
|
args['X-Plex-Container-Start'] += args['X-Plex-Container-Size']
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def playlists(self, playlistType=None):
|
def playlists(self, playlistType=None, sectionId=None, title=None, sort=None, **kwargs):
|
||||||
""" Returns a list of all :class:`~plexapi.playlist.Playlist` objects on the server.
|
""" Returns a list of all :class:`~plexapi.playlist.Playlist` objects on the server.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
playlistType (str, optional): The type of playlists to return (audio, video, photo).
|
playlistType (str, optional): The type of playlists to return (audio, video, photo).
|
||||||
Default returns all playlists.
|
Default returns all playlists.
|
||||||
|
sectionId (int, optional): The section ID (key) of the library to search within.
|
||||||
|
title (str, optional): General string query to search for. Partial string matches are allowed.
|
||||||
|
sort (str or list, optional): A string of comma separated sort fields in the format ``column:dir``.
|
||||||
"""
|
"""
|
||||||
key = '/playlists'
|
args = {}
|
||||||
if playlistType:
|
if playlistType is not None:
|
||||||
key = '%s?playlistType=%s' % (key, playlistType)
|
args['playlistType'] = playlistType
|
||||||
return self.fetchItems(key)
|
if sectionId is not None:
|
||||||
|
args['sectionID'] = sectionId
|
||||||
|
if title is not None:
|
||||||
|
args['title'] = title
|
||||||
|
if sort is not None:
|
||||||
|
# TODO: Automatically retrieve and validate sort field similar to LibrarySection.search()
|
||||||
|
args['sort'] = sort
|
||||||
|
|
||||||
|
key = '/playlists%s' % utils.joinArgs(args)
|
||||||
|
return self.fetchItems(key, **kwargs)
|
||||||
|
|
||||||
def playlist(self, title):
|
def playlist(self, title):
|
||||||
""" Returns the :class:`~plexapi.client.Playlist` that matches the specified title.
|
""" Returns the :class:`~plexapi.client.Playlist` that matches the specified title.
|
||||||
|
@ -594,9 +608,12 @@ class PlexServer(PlexObject):
|
||||||
title (str): Title of the playlist to return.
|
title (str): Title of the playlist to return.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
:exc:`~plexapi.exceptions.NotFound`: Invalid playlist title.
|
:exc:`~plexapi.exceptions.NotFound`: Unable to find playlist.
|
||||||
"""
|
"""
|
||||||
return self.fetchItem('/playlists', title=title)
|
try:
|
||||||
|
return self.playlists(title=title, title__iexact=title)[0]
|
||||||
|
except IndexError:
|
||||||
|
raise NotFound('Unable to find playlist with title "%s".' % title) from None
|
||||||
|
|
||||||
def optimizedItems(self, removeAll=None):
|
def optimizedItems(self, removeAll=None):
|
||||||
""" Returns list of all :class:`~plexapi.media.Optimized` objects connected to server. """
|
""" Returns list of all :class:`~plexapi.media.Optimized` objects connected to server. """
|
||||||
|
@ -873,6 +890,42 @@ class PlexServer(PlexObject):
|
||||||
key = '/statistics/resources?timespan=6'
|
key = '/statistics/resources?timespan=6'
|
||||||
return self.fetchItems(key, StatisticsResources)
|
return self.fetchItems(key, StatisticsResources)
|
||||||
|
|
||||||
|
def _buildWebURL(self, base=None, endpoint=None, **kwargs):
|
||||||
|
""" Build the Plex Web URL for the object.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
base (str): The base URL before the fragment (``#!``).
|
||||||
|
Default is https://app.plex.tv/desktop.
|
||||||
|
endpoint (str): The Plex Web URL endpoint.
|
||||||
|
None for server, 'playlist' for playlists, 'details' for all other media types.
|
||||||
|
**kwargs (dict): Dictionary of URL parameters.
|
||||||
|
"""
|
||||||
|
if base is None:
|
||||||
|
base = 'https://app.plex.tv/desktop/'
|
||||||
|
|
||||||
|
if endpoint:
|
||||||
|
return '%s#!/server/%s/%s%s' % (
|
||||||
|
base, self.machineIdentifier, endpoint, utils.joinArgs(kwargs)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return '%s#!/media/%s/com.plexapp.plugins.library%s' % (
|
||||||
|
base, self.machineIdentifier, utils.joinArgs(kwargs)
|
||||||
|
)
|
||||||
|
|
||||||
|
def getWebURL(self, base=None, playlistTab=None):
|
||||||
|
""" Returns the Plex Web URL for the server.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
base (str): The base URL before the fragment (``#!``).
|
||||||
|
Default is https://app.plex.tv/desktop.
|
||||||
|
playlistTab (str): The playlist tab (audio, video, photo). Only used for the playlist URL.
|
||||||
|
"""
|
||||||
|
if playlistTab is not None:
|
||||||
|
params = {'source': 'playlists', 'pivot': 'playlists.%s' % playlistTab}
|
||||||
|
else:
|
||||||
|
params = {'key': '/hubs', 'pageType': 'hub'}
|
||||||
|
return self._buildWebURL(base=base, **params)
|
||||||
|
|
||||||
|
|
||||||
class Account(PlexObject):
|
class Account(PlexObject):
|
||||||
""" Contains the locally cached MyPlex account information. The properties provided don't
|
""" Contains the locally cached MyPlex account information. The properties provided don't
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue