Bump plexapi from 4.12.1 to 4.13.1 (#1888)

Bumps [plexapi](https://github.com/pkkid/python-plexapi) from 4.12.1 to 4.13.1.
- [Release notes](https://github.com/pkkid/python-plexapi/releases)
- [Commits](https://github.com/pkkid/python-plexapi/compare/4.12.1...4.13.1)

---
updated-dependencies:
- dependency-name: plexapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
This commit is contained in:
dependabot[bot] 2022-11-12 17:29:35 -08:00 committed by GitHub
parent 3af08f0d07
commit e79da07973
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 1791 additions and 724 deletions

View file

@ -171,7 +171,7 @@ class PlexServer(PlexObject):
return headers
def _uriRoot(self):
return 'server://%s/com.plexapp.plugins.library' % self.machineIdentifier
return f'server://{self.machineIdentifier}/com.plexapp.plugins.library'
@property
def library(self):
@ -232,7 +232,7 @@ class PlexServer(PlexObject):
""" Returns a list of :class:`~plexapi.media.Agent` objects this server has available. """
key = '/system/agents'
if mediaType:
key += '?mediaType=%s' % utils.searchType(mediaType)
key += f'?mediaType={utils.searchType(mediaType)}'
return self.fetchItems(key)
def createToken(self, type='delegation', scope='all'):
@ -240,7 +240,7 @@ class PlexServer(PlexObject):
if not self._token:
# Handle unclaimed servers
return None
q = self.query('/security/token?type=%s&scope=%s' % (type, scope))
q = self.query(f'/security/token?type={type}&scope={scope}')
return q.attrib.get('token')
def switchUser(self, username, session=None, timeout=None):
@ -291,7 +291,7 @@ class PlexServer(PlexObject):
try:
return next(account for account in self.systemAccounts() if account.id == accountID)
except StopIteration:
raise NotFound('Unknown account with accountID=%s' % accountID) from None
raise NotFound(f'Unknown account with accountID={accountID}') from None
def systemDevices(self):
""" Returns a list of :class:`~plexapi.server.SystemDevice` objects this server contains. """
@ -309,7 +309,7 @@ class PlexServer(PlexObject):
try:
return next(device for device in self.systemDevices() if device.id == deviceID)
except StopIteration:
raise NotFound('Unknown device with deviceID=%s' % deviceID) from None
raise NotFound(f'Unknown device with deviceID={deviceID}') from None
def myPlexAccount(self):
""" Returns a :class:`~plexapi.myplex.MyPlexAccount` object using the same
@ -351,7 +351,7 @@ class PlexServer(PlexObject):
key = path.key
elif path is not None:
base64path = utils.base64str(path)
key = '/services/browse/%s' % base64path
key = f'/services/browse/{base64path}'
else:
key = '/services/browse'
if includeFiles:
@ -406,7 +406,7 @@ class PlexServer(PlexObject):
log.warning('%s did not advertise a port, checking plex.tv.', elem.attrib.get('name'))
ports = self._myPlexClientPorts() if ports is None else ports
port = ports.get(elem.attrib.get('machineIdentifier'))
baseurl = 'http://%s:%s' % (elem.attrib['host'], port)
baseurl = f"http://{elem.attrib['host']}:{port}"
items.append(PlexClient(baseurl=baseurl, server=self,
token=self._token, data=elem, connect=False))
@ -425,7 +425,7 @@ class PlexServer(PlexObject):
if client and client.title == name:
return client
raise NotFound('Unknown client name: %s' % name)
raise NotFound(f'Unknown client name: {name}')
def createCollection(self, title, section, items=None, smart=False, limit=None,
libtype=None, sort=None, filters=None, **kwargs):
@ -547,6 +547,7 @@ class PlexServer(PlexObject):
f'Invalid butler task: {task}. Available tasks are: {validTasks}'
)
self.query(f'/butler/{task}', method=self._session.post)
return self
@deprecated('use "checkForUpdate" instead')
def check_for_update(self, force=True, download=False):
@ -559,7 +560,7 @@ class PlexServer(PlexObject):
force (bool): Force server to check for new releases
download (bool): Download if a update is available.
"""
part = '/updater/check?download=%s' % (1 if download else 0)
part = f'/updater/check?download={1 if download else 0}'
if force:
self.query(part, method=self._session.put)
releases = self.fetchItems('/updater/status')
@ -608,7 +609,7 @@ class PlexServer(PlexObject):
args['X-Plex-Container-Start'] = 0
args['X-Plex-Container-Size'] = min(X_PLEX_CONTAINER_SIZE, maxresults)
while subresults and maxresults > len(results):
key = '/status/sessions/history/all%s' % utils.joinArgs(args)
key = f'/status/sessions/history/all{utils.joinArgs(args)}'
subresults = self.fetchItems(key)
results += subresults[:maxresults - len(results)]
args['X-Plex-Container-Start'] += args['X-Plex-Container-Size']
@ -635,7 +636,7 @@ class PlexServer(PlexObject):
# TODO: Automatically retrieve and validate sort field similar to LibrarySection.search()
args['sort'] = sort
key = '/playlists%s' % utils.joinArgs(args)
key = f'/playlists{utils.joinArgs(args)}'
return self.fetchItems(key, **kwargs)
def playlist(self, title):
@ -650,7 +651,7 @@ class PlexServer(PlexObject):
try:
return self.playlists(title=title, title__iexact=title)[0]
except IndexError:
raise NotFound('Unable to find playlist with title "%s".' % title) from None
raise NotFound(f'Unable to find playlist with title "{title}".') from None
def optimizedItems(self, removeAll=None):
""" Returns list of all :class:`~plexapi.media.Optimized` objects connected to server. """
@ -659,7 +660,7 @@ class PlexServer(PlexObject):
self.query(key, method=self._server._session.delete)
else:
backgroundProcessing = self.fetchItem('/playlists?type=42')
return self.fetchItems('%s/items' % backgroundProcessing.key, cls=Optimized)
return self.fetchItems(f'{backgroundProcessing.key}/items', cls=Optimized)
@deprecated('use "plexapi.media.Optimized.items()" instead')
def optimizedItem(self, optimizedID):
@ -668,7 +669,7 @@ class PlexServer(PlexObject):
"""
backgroundProcessing = self.fetchItem('/playlists?type=42')
return self.fetchItem('%s/items/%s/items' % (backgroundProcessing.key, optimizedID))
return self.fetchItem(f'{backgroundProcessing.key}/items/{optimizedID}/items')
def conversions(self, pause=None):
""" Returns list of all :class:`~plexapi.media.Conversion` objects connected to server. """
@ -697,7 +698,7 @@ class PlexServer(PlexObject):
if response.status_code not in (200, 201, 204):
codename = codes.get(response.status_code)[0]
errtext = response.text.replace('\n', ' ')
message = '(%s) %s; %s %s' % (response.status_code, codename, response.url, errtext)
message = f'({response.status_code}) {codename}; {response.url} {errtext}'
if response.status_code == 401:
raise Unauthorized(message)
elif response.status_code == 404:
@ -736,7 +737,7 @@ class PlexServer(PlexObject):
params['limit'] = limit
if sectionId:
params['sectionId'] = sectionId
key = '/hubs/search?%s' % urlencode(params)
key = f'/hubs/search?{urlencode(params)}'
for hub in self.fetchItems(key, Hub):
if mediatype:
if hub.type == mediatype:
@ -753,21 +754,27 @@ class PlexServer(PlexObject):
""" Returns a list of all active :class:`~plexapi.media.TranscodeSession` objects. """
return self.fetchItems('/transcode/sessions')
def startAlertListener(self, callback=None):
def startAlertListener(self, callback=None, callbackError=None):
""" Creates a websocket connection to the Plex Server to optionally receive
notifications. These often include messages from Plex about media scans
as well as updates to currently running Transcode Sessions.
NOTE: You need websocket-client installed in order to use this feature.
>> pip install websocket-client
Returns a new :class:`~plexapi.alert.AlertListener` object.
Note: ``websocket-client`` must be installed in order to use this feature.
.. code-block:: python
>> pip install websocket-client
Parameters:
callback (func): Callback function to call on received messages.
callbackError (func): Callback function to call on errors.
Raises:
:exc:`~plexapi.exception.Unsupported`: Websocket-client not installed.
"""
notifier = AlertListener(self, callback)
notifier = AlertListener(self, callback, callbackError)
notifier.start()
return notifier
@ -809,7 +816,7 @@ class PlexServer(PlexObject):
if imageFormat is not None:
params['format'] = imageFormat.lower()
key = '/photo/:/transcode%s' % utils.joinArgs(params)
key = f'/photo/:/transcode{utils.joinArgs(params)}'
return self.url(key, includeToken=True)
def url(self, key, includeToken=None):
@ -818,8 +825,8 @@ class PlexServer(PlexObject):
"""
if self._token and (includeToken or self._showSecrets):
delim = '&' if '?' in key else '?'
return '%s%s%sX-Plex-Token=%s' % (self._baseurl, key, delim, self._token)
return '%s%s' % (self._baseurl, key)
return f'{self._baseurl}{key}{delim}X-Plex-Token={self._token}'
return f'{self._baseurl}{key}'
def refreshSynclist(self):
""" Force PMS to download new SyncList from Plex.tv. """
@ -853,7 +860,7 @@ class PlexServer(PlexObject):
log.debug('Plex is currently not allowed to delete media. Toggle set to not allow, exiting.')
raise BadRequest('Plex is currently not allowed to delete media. Toggle set to not allow, exiting.')
value = 1 if toggle is True else 0
return self.query('/:/prefs?allowMediaDeletion=%s' % value, self._session.put)
return self.query(f'/:/prefs?allowMediaDeletion={value}', self._session.put)
def bandwidth(self, timespan=None, **kwargs):
""" Returns a list of :class:`~plexapi.server.StatisticsBandwidth` objects
@ -904,8 +911,7 @@ class PlexServer(PlexObject):
gigabytes = round(bandwidth.bytes / 1024**3, 3)
local = 'local' if bandwidth.lan else 'remote'
date = bandwidth.at.strftime('%Y-%m-%d')
print('%s used %s GB of %s bandwidth on %s from %s'
% (account.name, gigabytes, local, date, device.name))
print(f'{account.name} used {gigabytes} GB of {local} bandwidth on {date} from {device.name}')
"""
params = {}
@ -923,19 +929,19 @@ class PlexServer(PlexObject):
try:
params['timespan'] = timespans[timespan]
except KeyError:
raise BadRequest('Invalid timespan specified: %s. '
'Available timespans: %s' % (timespan, ', '.join(timespans.keys())))
raise BadRequest(f"Invalid timespan specified: {timespan}. "
f"Available timespans: {', '.join(timespans.keys())}")
filters = {'accountID', 'at', 'at<', 'at>', 'bytes', 'bytes<', 'bytes>', 'deviceID', 'lan'}
for key, value in kwargs.items():
if key not in filters:
raise BadRequest('Unknown filter: %s=%s' % (key, value))
raise BadRequest(f'Unknown filter: {key}={value}')
if key.startswith('at'):
try:
value = utils.cast(int, value.timestamp())
except AttributeError:
raise BadRequest('Time frame filter must be a datetime object: %s=%s' % (key, value))
raise BadRequest(f'Time frame filter must be a datetime object: {key}={value}')
elif key.startswith('bytes') or key == 'lan':
value = utils.cast(int, value)
elif key == 'accountID':
@ -943,7 +949,7 @@ class PlexServer(PlexObject):
value = 1 # The admin account is accountID=1
params[key] = value
key = '/statistics/bandwidth?%s' % urlencode(params)
key = f'/statistics/bandwidth?{urlencode(params)}'
return self.fetchItems(key, StatisticsBandwidth)
def resources(self):
@ -966,13 +972,9 @@ class PlexServer(PlexObject):
base = 'https://app.plex.tv/desktop/'
if endpoint:
return '%s#!/server/%s/%s%s' % (
base, self.machineIdentifier, endpoint, utils.joinArgs(kwargs)
)
return f'{base}#!/server/{self.machineIdentifier}/{endpoint}{utils.joinArgs(kwargs)}'
else:
return '%s#!/media/%s/com.plexapp.plugins.library%s' % (
base, self.machineIdentifier, utils.joinArgs(kwargs)
)
return f'{base}#!/media/{self.machineIdentifier}/com.plexapp.plugins.library{utils.joinArgs(kwargs)}'
def getWebURL(self, base=None, playlistTab=None):
""" Returns the Plex Web URL for the server.
@ -983,7 +985,7 @@ class PlexServer(PlexObject):
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}
params = {'source': 'playlists', 'pivot': f'playlists.{playlistTab}'}
else:
params = {'key': '/hubs', 'pageType': 'hub'}
return self._buildWebURL(base=base, **params)
@ -1115,7 +1117,7 @@ class SystemDevice(PlexObject):
self.clientIdentifier = data.attrib.get('clientIdentifier')
self.createdAt = utils.toDatetime(data.attrib.get('createdAt'))
self.id = utils.cast(int, data.attrib.get('id'))
self.key = '/devices/%s' % self.id
self.key = f'/devices/{self.id}'
self.name = data.attrib.get('name')
self.platform = data.attrib.get('platform')
@ -1146,12 +1148,14 @@ class StatisticsBandwidth(PlexObject):
self.timespan = utils.cast(int, data.attrib.get('timespan'))
def __repr__(self):
return '<%s>' % ':'.join([p for p in [
self.__class__.__name__,
self._clean(self.accountID),
self._clean(self.deviceID),
self._clean(int(self.at.timestamp()))
] if p])
return '<{}>'.format(
':'.join([p for p in [
self.__class__.__name__,
self._clean(self.accountID),
self._clean(self.deviceID),
self._clean(int(self.at.timestamp()))
] if p])
)
def account(self):
""" Returns the :class:`~plexapi.server.SystemAccount` associated with the bandwidth data. """
@ -1186,10 +1190,7 @@ class StatisticsResources(PlexObject):
self.timespan = utils.cast(int, data.attrib.get('timespan'))
def __repr__(self):
return '<%s>' % ':'.join([p for p in [
self.__class__.__name__,
self._clean(int(self.at.timestamp()))
] if p])
return f"<{':'.join([p for p in [self.__class__.__name__, self._clean(int(self.at.timestamp()))] if p])}>"
@utils.registerPlexObject