mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-11 15:56:07 -07:00
Bump plexapi from 4.8.0 to 4.9.1 (#1627)
* Bump plexapi from 4.8.0 to 4.9.1 Bumps [plexapi](https://github.com/pkkid/python-plexapi) from 4.8.0 to 4.9.1. - [Release notes](https://github.com/pkkid/python-plexapi/releases) - [Commits](https://github.com/pkkid/python-plexapi/compare/4.8.0...4.9.1) --- updated-dependencies: - dependency-name: plexapi dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * Update plexapi==4.9.1 Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>
This commit is contained in:
parent
a4ab5ab9be
commit
7f0abe0fe6
9 changed files with 254 additions and 76 deletions
|
@ -8,6 +8,7 @@ from plexapi.exceptions import BadRequest
|
|||
from plexapi.mixins import AdvancedSettingsMixin, ArtUrlMixin, ArtMixin, PosterUrlMixin, PosterMixin
|
||||
from plexapi.mixins import RatingMixin, SplitMergeMixin, UnmatchMatchMixin
|
||||
from plexapi.mixins import CollectionMixin, CountryMixin, GenreMixin, LabelMixin, MoodMixin, SimilarArtistMixin, StyleMixin
|
||||
from plexapi.playlist import Playlist
|
||||
|
||||
|
||||
class Audio(PlexPartialObject):
|
||||
|
@ -222,6 +223,11 @@ class Artist(Audio, AdvancedSettingsMixin, ArtMixin, PosterMixin, RatingMixin, S
|
|||
filepaths += track.download(_savepath, keep_original_name, **kwargs)
|
||||
return filepaths
|
||||
|
||||
def station(self):
|
||||
""" Returns a :class:`~plexapi.playlist.Playlist` artist radio station or `None`. """
|
||||
key = '%s?includeStations=1' % self.key
|
||||
return next(iter(self.fetchItems(key, cls=Playlist, rtag="Stations")), None)
|
||||
|
||||
|
||||
@utils.registerPlexObject
|
||||
class Album(Audio, ArtMixin, PosterMixin, RatingMixin, UnmatchMatchMixin,
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
# Library version
|
||||
MAJOR_VERSION = 4
|
||||
MINOR_VERSION = 8
|
||||
PATCH_VERSION = 0
|
||||
MINOR_VERSION = 9
|
||||
PATCH_VERSION = 1
|
||||
__short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||
__version__ = f"{__short_version__}.{PATCH_VERSION}"
|
||||
|
|
|
@ -82,6 +82,28 @@ class Library(PlexObject):
|
|||
except KeyError:
|
||||
raise NotFound('Invalid library sectionID: %s' % sectionID) from None
|
||||
|
||||
def hubs(self, sectionID=None, identifier=None, **kwargs):
|
||||
""" Returns a list of :class:`~plexapi.library.Hub` across all library sections.
|
||||
|
||||
Parameters:
|
||||
sectionID (int or str or list, optional):
|
||||
IDs of the sections to limit results or "playlists".
|
||||
identifier (str or list, optional):
|
||||
Names of identifiers to limit results.
|
||||
Available on `Hub` instances as the `hubIdentifier` attribute.
|
||||
Examples: 'home.continue' or 'home.ondeck'
|
||||
"""
|
||||
if sectionID:
|
||||
if not isinstance(sectionID, list):
|
||||
sectionID = [sectionID]
|
||||
kwargs['contentDirectoryID'] = ",".join(map(str, sectionID))
|
||||
if identifier:
|
||||
if not isinstance(identifier, list):
|
||||
identifier = [identifier]
|
||||
kwargs['identifier'] = ",".join(identifier)
|
||||
key = '/hubs%s' % utils.joinArgs(kwargs)
|
||||
return self.fetchItems(key)
|
||||
|
||||
def all(self, **kwargs):
|
||||
""" Returns a list of all media from all library sections.
|
||||
This may be a very large dataset to retrieve.
|
||||
|
@ -169,7 +191,7 @@ class Library(PlexObject):
|
|||
name (str): Name of the library
|
||||
agent (str): Example com.plexapp.agents.imdb
|
||||
type (str): movie, show, # check me
|
||||
location (str): /path/to/files
|
||||
location (str or list): /path/to/files, ["/path/to/files", "/path/to/morefiles"]
|
||||
language (str): Two letter language fx en
|
||||
kwargs (dict): Advanced options should be passed as a dict. where the id is the key.
|
||||
|
||||
|
@ -308,8 +330,16 @@ class Library(PlexObject):
|
|||
40:South Africa, 41:Spain, 42:Sweden, 43:Switzerland, 44:Taiwan, 45:Trinidad,
|
||||
46:United Kingdom, 47:United States, 48:Uruguay, 49:Venezuela.
|
||||
"""
|
||||
part = '/library/sections?name=%s&type=%s&agent=%s&scanner=%s&language=%s&location=%s' % (
|
||||
quote_plus(name), type, agent, quote_plus(scanner), language, quote_plus(location)) # noqa E126
|
||||
if isinstance(location, str):
|
||||
location = [location]
|
||||
locations = []
|
||||
for path in location:
|
||||
if not self._server.isBrowsable(path):
|
||||
raise BadRequest('Path: %s does not exist.' % path)
|
||||
locations.append(('location', path))
|
||||
|
||||
part = '/library/sections?name=%s&type=%s&agent=%s&scanner=%s&language=%s&%s' % (
|
||||
quote_plus(name), type, agent, quote_plus(scanner), language, urlencode(locations, doseq=True)) # noqa E126
|
||||
if kwargs:
|
||||
part += urlencode(kwargs)
|
||||
return self._server.query(part, method=self._server._session.post)
|
||||
|
@ -486,16 +516,76 @@ class LibrarySection(PlexObject):
|
|||
return self
|
||||
|
||||
def edit(self, agent=None, **kwargs):
|
||||
""" Edit a library (Note: agent is required). See :class:`~plexapi.library.Library` for example usage.
|
||||
""" Edit a library. See :class:`~plexapi.library.Library` for example usage.
|
||||
|
||||
Parameters:
|
||||
agent (str, optional): The library agent.
|
||||
kwargs (dict): Dict of settings to edit.
|
||||
"""
|
||||
if not agent:
|
||||
agent = self.agent
|
||||
part = '/library/sections/%s?agent=%s&%s' % (self.key, agent, urlencode(kwargs))
|
||||
|
||||
locations = []
|
||||
if kwargs.get('location'):
|
||||
if isinstance(kwargs['location'], str):
|
||||
kwargs['location'] = [kwargs['location']]
|
||||
for path in kwargs.pop('location'):
|
||||
if not self._server.isBrowsable(path):
|
||||
raise BadRequest('Path: %s does not exist.' % path)
|
||||
locations.append(('location', path))
|
||||
|
||||
params = list(kwargs.items()) + locations
|
||||
|
||||
part = '/library/sections/%s?agent=%s&%s' % (self.key, agent, urlencode(params, doseq=True))
|
||||
self._server.query(part, method=self._server._session.put)
|
||||
|
||||
def addLocations(self, location):
|
||||
""" Add a location to a library.
|
||||
|
||||
Parameters:
|
||||
location (str or list): A single folder path, list of paths.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
LibrarySection.addLocations('/path/1')
|
||||
LibrarySection.addLocations(['/path/1', 'path/2', '/path/3'])
|
||||
"""
|
||||
locations = self.locations
|
||||
if isinstance(location, str):
|
||||
location = [location]
|
||||
for path in location:
|
||||
if not self._server.isBrowsable(path):
|
||||
raise BadRequest('Path: %s does not exist.' % path)
|
||||
locations.append(path)
|
||||
self.edit(location=locations)
|
||||
|
||||
def removeLocations(self, location):
|
||||
""" Remove a location from a library.
|
||||
|
||||
Parameters:
|
||||
location (str or list): A single folder path, list of paths.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
LibrarySection.removeLocations('/path/1')
|
||||
LibrarySection.removeLocations(['/path/1', 'path/2', '/path/3'])
|
||||
"""
|
||||
locations = self.locations
|
||||
if isinstance(location, str):
|
||||
location = [location]
|
||||
for path in location:
|
||||
if path in locations:
|
||||
locations.remove(path)
|
||||
else:
|
||||
raise BadRequest('Path: %s does not exist in the library.' % location)
|
||||
if len(locations) == 0:
|
||||
raise BadRequest('You are unable to remove all locations from a library.')
|
||||
self.edit(location=locations)
|
||||
|
||||
def get(self, title):
|
||||
""" Returns the media item with the specified title.
|
||||
|
||||
|
@ -510,9 +600,7 @@ class LibrarySection(PlexObject):
|
|||
|
||||
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.
|
||||
Note: Only available for the Plex Movie and Plex TV Series agents.
|
||||
|
||||
Parameters:
|
||||
guid (str): The external guid of the item to return.
|
||||
|
@ -525,20 +613,23 @@ class LibrarySection(PlexObject):
|
|||
|
||||
.. 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
|
||||
# Alternatively, create your own guid lookup dictionary for faster performance
|
||||
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)
|
||||
try:
|
||||
dummy = self.search(maxresults=1)[0]
|
||||
match = dummy.matches(agent=self.agent, title=guid.replace('://', '-'))
|
||||
return self.search(guid=match[0].guid)[0]
|
||||
except IndexError:
|
||||
raise NotFound("Guid '%s' is not found in the library" % guid) from None
|
||||
|
||||
def all(self, libtype=None, **kwargs):
|
||||
""" Returns a list of all items from this library section.
|
||||
|
@ -556,13 +647,13 @@ class LibrarySection(PlexObject):
|
|||
def hubs(self):
|
||||
""" Returns a list of available :class:`~plexapi.library.Hub` for this library section.
|
||||
"""
|
||||
key = '/hubs/sections/%s' % self.key
|
||||
key = '/hubs/sections/%s?includeStations=1' % self.key
|
||||
return self.fetchItems(key)
|
||||
|
||||
def agents(self):
|
||||
""" Returns a list of available :class:`~plexapi.media.Agent` for this library section.
|
||||
"""
|
||||
return self._server.agents(utils.searchType(self.type))
|
||||
return self._server.agents(self.type)
|
||||
|
||||
def settings(self):
|
||||
""" Returns a list of all library settings. """
|
||||
|
@ -606,6 +697,36 @@ class LibrarySection(PlexObject):
|
|||
|
||||
self.edit(**data)
|
||||
|
||||
def _lockUnlockAllField(self, field, libtype=None, locked=True):
|
||||
""" Lock or unlock a field for all items in the library. """
|
||||
libtype = libtype or self.TYPE
|
||||
args = {
|
||||
'type': utils.searchType(libtype),
|
||||
'%s.locked' % field: int(locked)
|
||||
}
|
||||
key = '/library/sections/%s/all%s' % (self.key, utils.joinArgs(args))
|
||||
self._server.query(key, method=self._server._session.put)
|
||||
|
||||
def lockAllField(self, field, libtype=None):
|
||||
""" Lock a field for all items in the library.
|
||||
|
||||
Parameters:
|
||||
field (str): The field to lock (e.g. thumb, rating, collection).
|
||||
libtype (str, optional): The library type to lock (movie, show, season, episode,
|
||||
artist, album, track, photoalbum, photo). Default is the main library type.
|
||||
"""
|
||||
self._lockUnlockAllField(field, libtype=libtype, locked=True)
|
||||
|
||||
def unlockAllField(self, field, libtype=None):
|
||||
""" Unlock a field for all items in the library.
|
||||
|
||||
Parameters:
|
||||
field (str): The field to unlock (e.g. thumb, rating, collection).
|
||||
libtype (str, optional): The library type to lock (movie, show, season, episode,
|
||||
artist, album, track, photoalbum, photo). Default is the main library type.
|
||||
"""
|
||||
self._lockUnlockAllField(field, libtype=libtype, locked=False)
|
||||
|
||||
def timeline(self):
|
||||
""" Returns a timeline query for this library section. """
|
||||
key = '/library/sections/%s/timeline' % self.key
|
||||
|
@ -1718,9 +1839,8 @@ class MusicSection(LibrarySection):
|
|||
return self.fetchItems(key)
|
||||
|
||||
def stations(self):
|
||||
""" Returns a list of :class:`~plexapi.audio.Album` objects in this section. """
|
||||
key = '/hubs/sections/%s?includeStations=1' % self.key
|
||||
return self.fetchItems(key, cls=Station)
|
||||
""" Returns a list of :class:`~plexapi.playlist.Playlist` stations in this section. """
|
||||
return next((hub.items for hub in self.hubs() if hub.context == 'hub.music.stations'), None)
|
||||
|
||||
def searchArtists(self, **kwargs):
|
||||
""" Search for an artist. See :func:`~plexapi.library.LibrarySection.search` for usage. """
|
||||
|
@ -1934,6 +2054,7 @@ class Hub(PlexObject):
|
|||
context (str): The context of the hub.
|
||||
hubKey (str): API URL for these specific hub items.
|
||||
hubIdentifier (str): The identifier of the hub.
|
||||
items (list): List of items in the hub.
|
||||
key (str): API URL for the hub.
|
||||
more (bool): True if there are more items to load (call reload() to fetch all items).
|
||||
size (int): The number of items in the hub.
|
||||
|
@ -2086,39 +2207,6 @@ class Place(HubMediaTag):
|
|||
TAGTYPE = 400
|
||||
|
||||
|
||||
@utils.registerPlexObject
|
||||
class Station(PlexObject):
|
||||
""" Represents the Station area in the MusicSection.
|
||||
|
||||
Attributes:
|
||||
TITLE (str): 'Stations'
|
||||
TYPE (str): 'station'
|
||||
hubIdentifier (str): Unknown.
|
||||
size (int): Number of items found.
|
||||
title (str): Title of this Hub.
|
||||
type (str): Type of items in the Hub.
|
||||
more (str): Unknown.
|
||||
style (str): Unknown
|
||||
items (str): List of items in the Hub.
|
||||
"""
|
||||
TITLE = 'Stations'
|
||||
TYPE = 'station'
|
||||
|
||||
def _loadData(self, data):
|
||||
""" Load attribute values from Plex XML response. """
|
||||
self._data = data
|
||||
self.hubIdentifier = data.attrib.get('hubIdentifier')
|
||||
self.size = utils.cast(int, data.attrib.get('size'))
|
||||
self.title = data.attrib.get('title')
|
||||
self.type = data.attrib.get('type')
|
||||
self.more = data.attrib.get('more')
|
||||
self.style = data.attrib.get('style')
|
||||
self.items = self.findItems(data)
|
||||
|
||||
def __len__(self):
|
||||
return self.size
|
||||
|
||||
|
||||
class FilteringType(PlexObject):
|
||||
""" Represents a single filtering Type object for a library.
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ 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 deprecated
|
||||
|
||||
|
||||
@utils.registerPlexObject
|
||||
|
@ -1058,31 +1059,50 @@ class Agent(PlexObject):
|
|||
self.hasAttribution = data.attrib.get('hasAttribution')
|
||||
self.hasPrefs = data.attrib.get('hasPrefs')
|
||||
self.identifier = data.attrib.get('identifier')
|
||||
self.name = data.attrib.get('name')
|
||||
self.primary = data.attrib.get('primary')
|
||||
self.shortIdentifier = self.identifier.rsplit('.', 1)[1]
|
||||
if 'mediaType' in self._initpath:
|
||||
self.name = data.attrib.get('name')
|
||||
self.languageCode = []
|
||||
for code in data:
|
||||
self.languageCode += [code.attrib.get('code')]
|
||||
else:
|
||||
self.mediaTypes = [AgentMediaType(server=self._server, data=d) for d in data]
|
||||
|
||||
def _settings(self):
|
||||
if 'mediaType' in self._initpath:
|
||||
self.languageCodes = self.listAttrs(data, 'code', etag='Language')
|
||||
self.mediaTypes = []
|
||||
else:
|
||||
self.languageCodes = []
|
||||
self.mediaTypes = self.findItems(data, cls=AgentMediaType)
|
||||
|
||||
@property
|
||||
@deprecated('use "languageCodes" instead')
|
||||
def languageCode(self):
|
||||
return self.languageCodes
|
||||
|
||||
def settings(self):
|
||||
key = '/:/plugins/%s/prefs' % self.identifier
|
||||
data = self._server.query(key)
|
||||
return self.findItems(data, cls=settings.Setting)
|
||||
|
||||
@deprecated('use "settings" instead')
|
||||
def _settings(self):
|
||||
return self.settings()
|
||||
|
||||
|
||||
class AgentMediaType(Agent):
|
||||
""" Represents a single Agent MediaType.
|
||||
|
||||
Attributes:
|
||||
TAG (str): 'MediaType'
|
||||
"""
|
||||
TAG = 'MediaType'
|
||||
|
||||
def __repr__(self):
|
||||
uid = self._clean(self.firstAttr('name'))
|
||||
return '<%s>' % ':'.join([p for p in [self.__class__.__name__, uid] if p])
|
||||
|
||||
def _loadData(self, data):
|
||||
self.languageCodes = self.listAttrs(data, 'code', etag='Language')
|
||||
self.mediaType = utils.cast(int, data.attrib.get('mediaType'))
|
||||
self.name = data.attrib.get('name')
|
||||
self.languageCode = []
|
||||
for code in data:
|
||||
self.languageCode += [code.attrib.get('code')]
|
||||
|
||||
@property
|
||||
@deprecated('use "languageCodes" instead')
|
||||
def languageCode(self):
|
||||
return self.languageCodes
|
||||
|
|
|
@ -29,7 +29,11 @@ class Playlist(PlexPartialObject, Playable, ArtMixin, PosterMixin, SmartFilterMi
|
|||
icon (str): Icon URI string for smart playlists.
|
||||
key (str): API URL (/playlist/<ratingkey>).
|
||||
leafCount (int): Number of items in the playlist view.
|
||||
librarySectionID (int): Library section identifier (radio only)
|
||||
librarySectionKey (str): Library section key (radio only)
|
||||
librarySectionTitle (str): Library section title (radio only)
|
||||
playlistType (str): 'audio', 'video', or 'photo'
|
||||
radio (bool): If this playlist represents a radio station
|
||||
ratingKey (int): Unique key identifying the playlist.
|
||||
smart (bool): True if the playlist is a smart playlist.
|
||||
summary (str): Summary of the playlist.
|
||||
|
@ -54,7 +58,11 @@ class Playlist(PlexPartialObject, Playable, ArtMixin, PosterMixin, SmartFilterMi
|
|||
self.icon = data.attrib.get('icon')
|
||||
self.key = data.attrib.get('key', '').replace('/items', '') # FIX_BUG_50
|
||||
self.leafCount = utils.cast(int, data.attrib.get('leafCount'))
|
||||
self.librarySectionID = utils.cast(int, data.attrib.get('librarySectionID'))
|
||||
self.librarySectionKey = data.attrib.get('librarySectionKey')
|
||||
self.librarySectionTitle = data.attrib.get('librarySectionTitle')
|
||||
self.playlistType = data.attrib.get('playlistType')
|
||||
self.radio = utils.cast(bool, data.attrib.get('radio', 0))
|
||||
self.ratingKey = utils.cast(int, data.attrib.get('ratingKey'))
|
||||
self.smart = utils.cast(bool, data.attrib.get('smart'))
|
||||
self.summary = data.attrib.get('summary')
|
||||
|
@ -169,6 +177,8 @@ class Playlist(PlexPartialObject, Playable, ArtMixin, PosterMixin, SmartFilterMi
|
|||
|
||||
def items(self):
|
||||
""" Returns a list of all items in the playlist. """
|
||||
if self.radio:
|
||||
return []
|
||||
if self._items is None:
|
||||
key = '%s/items' % self.key
|
||||
items = self.fetchItems(key)
|
||||
|
|
|
@ -175,8 +175,11 @@ class PlayQueue(PlexObject):
|
|||
args["uri"] = "library:///directory/{uri_args}".format(uri_args=uri_args)
|
||||
args["type"] = items[0].listType
|
||||
elif items.type == "playlist":
|
||||
args["playlistID"] = items.ratingKey
|
||||
args["type"] = items.playlistType
|
||||
if items.radio:
|
||||
args["uri"] = f"server://{server.machineIdentifier}/{server.library.identifier}{items.key}"
|
||||
else:
|
||||
args["playlistID"] = items.ratingKey
|
||||
else:
|
||||
uuid = items.section().uuid
|
||||
args["type"] = items.listType
|
||||
|
@ -192,6 +195,42 @@ class PlayQueue(PlexObject):
|
|||
c._server = server
|
||||
return c
|
||||
|
||||
@classmethod
|
||||
def fromStationKey(cls, server, key):
|
||||
"""Create and return a new :class:`~plexapi.playqueue.PlayQueue`.
|
||||
|
||||
This is a convenience method to create a `PlayQueue` for
|
||||
radio stations when only the `key` string is available.
|
||||
|
||||
Parameters:
|
||||
server (:class:`~plexapi.server.PlexServer`): Server you are connected to.
|
||||
key (str): A station key as provided by :func:`~plexapi.library.LibrarySection.hubs()`
|
||||
or :func:`~plexapi.audio.Artist.station()`
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from plexapi.playqueue import PlayQueue
|
||||
music = server.library.section("Music")
|
||||
artist = music.get("Artist Name")
|
||||
station = artist.station()
|
||||
key = station.key # "/library/metadata/12855/station/8bd39616-dbdb-459e-b8da-f46d0b170af4?type=10"
|
||||
pq = PlayQueue.fromStationKey(server, key)
|
||||
client = server.clients()[0]
|
||||
client.playMedia(pq)
|
||||
"""
|
||||
args = {
|
||||
"type": "audio",
|
||||
"uri": f"server://{server.machineIdentifier}/{server.library.identifier}{key}"
|
||||
}
|
||||
path = f"/playQueues{utils.joinArgs(args)}"
|
||||
data = server.query(path, method=server._session.post)
|
||||
c = cls(server, data, initpath=path)
|
||||
c.playQueueType = args["type"]
|
||||
c._server = server
|
||||
return c
|
||||
|
||||
def addItem(self, item, playNext=False, refresh=True):
|
||||
"""
|
||||
Append the provided item to the "Up Next" section of the PlayQueue.
|
||||
|
|
|
@ -3,6 +3,7 @@ from urllib.parse import urlencode
|
|||
from xml.etree import ElementTree
|
||||
|
||||
import requests
|
||||
import os
|
||||
from plexapi import (BASE_HEADERS, CONFIG, TIMEOUT, X_PLEX_CONTAINER_SIZE, log,
|
||||
logfilter)
|
||||
from plexapi import utils
|
||||
|
@ -228,10 +229,10 @@ class PlexServer(PlexObject):
|
|||
return activities
|
||||
|
||||
def agents(self, mediaType=None):
|
||||
""" Returns the :class:`~plexapi.media.Agent` objects this server has available. """
|
||||
""" Returns a list of :class:`~plexapi.media.Agent` objects this server has available. """
|
||||
key = '/system/agents'
|
||||
if mediaType:
|
||||
key += '?mediaType=%s' % mediaType
|
||||
key += '?mediaType=%s' % utils.searchType(mediaType)
|
||||
return self.fetchItems(key)
|
||||
|
||||
def createToken(self, type='delegation', scope='all'):
|
||||
|
@ -384,6 +385,18 @@ class PlexServer(PlexObject):
|
|||
for path, paths, files in self.walk(_path):
|
||||
yield path, paths, files
|
||||
|
||||
def isBrowsable(self, path):
|
||||
""" Returns True if the Plex server can browse the given path.
|
||||
|
||||
Parameters:
|
||||
path (:class:`~plexapi.library.Path` or str): Full path to browse.
|
||||
"""
|
||||
if isinstance(path, Path):
|
||||
path = path.path
|
||||
path = os.path.normpath(path)
|
||||
paths = [p.path for p in self.browse(os.path.dirname(path), includeFiles=False)]
|
||||
return path in paths
|
||||
|
||||
def clients(self):
|
||||
""" Returns list of all :class:`~plexapi.client.PlexClient` objects connected to server. """
|
||||
items = []
|
||||
|
|
|
@ -113,17 +113,19 @@ class Setting(PlexObject):
|
|||
|
||||
def _loadData(self, data):
|
||||
""" Load attribute values from Plex XML response. """
|
||||
self._setValue = None
|
||||
self.type = data.attrib.get('type')
|
||||
self.advanced = utils.cast(bool, data.attrib.get('advanced'))
|
||||
self.default = self._cast(data.attrib.get('default'))
|
||||
self.enumValues = self._getEnumValues(data)
|
||||
self.group = data.attrib.get('group')
|
||||
self.hidden = utils.cast(bool, data.attrib.get('hidden'))
|
||||
self.id = data.attrib.get('id')
|
||||
self.label = data.attrib.get('label')
|
||||
self.option = data.attrib.get('option')
|
||||
self.secure = utils.cast(bool, data.attrib.get('secure'))
|
||||
self.summary = data.attrib.get('summary')
|
||||
self.type = data.attrib.get('type')
|
||||
self.default = self._cast(data.attrib.get('default'))
|
||||
self.value = self._cast(data.attrib.get('value'))
|
||||
self.hidden = utils.cast(bool, data.attrib.get('hidden'))
|
||||
self.advanced = utils.cast(bool, data.attrib.get('advanced'))
|
||||
self.group = data.attrib.get('group')
|
||||
self.enumValues = self._getEnumValues(data)
|
||||
self._setValue = None
|
||||
|
||||
def _cast(self, value):
|
||||
""" Cast the specific value to the type of this setting. """
|
||||
|
@ -132,8 +134,8 @@ class Setting(PlexObject):
|
|||
return value
|
||||
|
||||
def _getEnumValues(self, data):
|
||||
""" Returns a list of dictionary of valis value for this setting. """
|
||||
enumstr = data.attrib.get('enumValues')
|
||||
""" Returns a list or dictionary of values for this setting. """
|
||||
enumstr = data.attrib.get('enumValues') or data.attrib.get('values')
|
||||
if not enumstr:
|
||||
return None
|
||||
if ':' in enumstr:
|
||||
|
|
|
@ -27,7 +27,7 @@ musicbrainzngs==0.7.1
|
|||
oauthlib==3.1.1
|
||||
packaging==21.3
|
||||
paho-mqtt==1.6.1
|
||||
plexapi==4.8.0
|
||||
plexapi==4.9.1
|
||||
portend==3.1.0
|
||||
profilehooks==1.12.0
|
||||
PyJWT==2.3.0
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue