python-plexapi/plexapi/settings.py

170 lines
6.5 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
from collections import defaultdict
2020-05-12 21:15:16 +00:00
from urllib.parse import quote
2017-02-28 21:49:49 +00:00
from plexapi import log, utils
from plexapi.base import PlexObject
from plexapi.exceptions import BadRequest, NotFound
class Settings(PlexObject):
""" Container class for all settings. Allows getting and setting PlexServer settings.
Attributes:
key (str): '/:/prefs'
"""
key = '/:/prefs'
def __init__(self, server, data, initpath=None):
self._settings = {}
super(Settings, self).__init__(server, data, initpath)
def __getattr__(self, attr):
if attr.startswith('_'):
return self.__dict__[attr]
return self.get(attr).value
def __setattr__(self, attr, value):
if not attr.startswith('_'):
return self.get(attr).set(value)
self.__dict__[attr] = value
2017-02-23 07:08:04 +00:00
def _loadData(self, data):
2017-02-24 16:18:54 +00:00
""" Load attribute values from Plex XML response. """
self._data = data
for elem in data:
id = utils.lowerFirst(elem.attrib['id'])
if id in self._settings:
self._settings[id]._loadData(elem)
continue
self._settings[id] = Setting(self._server, elem, self._initpath)
def all(self):
2017-02-24 16:18:54 +00:00
""" Returns a list of all :class:`~plexapi.settings.Setting` objects available. """
return list(v for id, v in sorted(self._settings.items()))
def get(self, id):
2017-02-24 16:18:54 +00:00
""" Return the :class:`~plexapi.settings.Setting` object with the specified id. """
id = utils.lowerFirst(id)
if id in self._settings:
return self._settings[id]
raise NotFound('Invalid setting id: %s' % id)
def groups(self):
2017-02-24 16:18:54 +00:00
""" Returns a dict of lists for all :class:`~plexapi.settings.Setting`
objects grouped by setting group.
"""
groups = defaultdict(list)
for setting in self.all():
groups[setting.group].append(setting)
return dict(groups)
def group(self, group):
2017-02-24 16:18:54 +00:00
""" Return a list of all :class:`~plexapi.settings.Setting` objects in the specified group.
2017-02-25 04:14:52 +00:00
2017-02-24 16:18:54 +00:00
Parameters:
group (str): Group to return all settings.
"""
return self.groups().get(group, [])
def save(self):
2017-02-24 16:18:54 +00:00
""" Save any outstanding settnig changes to the :class:`~plexapi.server.PlexServer`. This
performs a full reload() of Settings after complete.
"""
params = {}
for setting in self.all():
if setting._setValue:
log.info('Saving PlexServer setting %s = %s' % (setting.id, setting._setValue))
params[setting.id] = quote(setting._setValue)
if not params:
raise BadRequest('No setting have been modified.')
2017-02-23 07:08:04 +00:00
querystr = '&'.join(['%s=%s' % (k, v) for k, v in params.items()])
url = '%s?%s' % (self.key, querystr)
self._server.query(url, self._server._session.put)
self.reload()
class Setting(PlexObject):
2017-02-24 16:18:54 +00:00
""" Represents a single Plex setting.
2017-02-25 04:14:52 +00:00
2017-02-24 16:18:54 +00:00
Attributes:
id (str): Setting id (or name).
label (str): Short description of what this setting is.
summary (str): Long description of what this setting is.
type (str): Setting type (text, int, double, bool).
default (str): Default value for this setting.
value (str,bool,int,float): Current value for this setting.
hidden (bool): True if this is a hidden setting.
advanced (bool): True if this is an advanced setting.
group (str): Group name this setting is categorized as.
enumValues (list,dict): List or dictionary of valis values for this setting.
"""
Improvements in tests process (#297) * lets begin * skip plexpass tests if there is not plexpass on account * test new myplex attrubutes * bootstrap: proper photos organisation * fix rest of photos tests * fix myplex new attributes test * fix music bootstrap by setting agent to lastfm * fix sync tests * increase bootstrap timeout * remove timeout from .travis.yml * do not create playlist-style photoalbums in plex-bootstraptest.py * allow negative filtering in LibrarySection.search() * fix sync tests once again * use sendCrashReports in test_settings * fix test_settings * fix test_video * do not accept eula in bootstrap * fix PlexServer.isLatest() * add test against old version of PlexServer * fix MyPlexAccount.OutOut * add flag for one-time testing in Travis * fix test_library onDeck tests * fix more tests * use tqdm in plex-bootstraptest for media scanning progress * create sections one-by-one * update docs on AlertListener for timeline entries * fix plex-bootstraptest for server version 1.3.2 * display skip/xpass/xfail reasons * fix tests on 1.3 * wait for music to be fully processed in plex-bootstraptest * fix misplaced TEST_ACCOUNT_ONCE * fix test_myplex_users, not sure if in proper-way * add pytest-rerunfailures; mark test_myplex_optout as flaky * fix comment * Revert "add pytest-rerunfailures; mark test_myplex_optout as flaky" This reverts commit 580e4c95a758c92329d757eb2f3fc3bf44b26f09. * restart plex container on failure * add conftest.wait_until() and used where some retries are required * add more wait_until() usage in test_sync * fix managed user search * fix updating managed users in myplex * allow to add new servers to existent users * add new server to a shared user while bootstrapping * add some docs on testing process * perform few attemps when unable to get the claim token * unlock websocket-client in requirements_dev * fix docblock in tools/plex-teardowntest * do not hardcode mediapart size in test_video * remove cache:pip from travis * Revert "unlock websocket-client in requirements_dev" This reverts commit 0d536bd06dbdc4a4b869a1686f8cd008898859fe. * remove debug from server.py * improve webhook tests * fix type() check to isinstance() * remove excessive `else` branch due to Hellowlol advice * add `unknown` as allowed `myPlexMappingState` in test_server
2018-09-14 18:03:23 +00:00
_bool_cast = lambda x: True if x == 'true' or x == '1' else False
_bool_str = lambda x: str(x).lower()
2018-11-17 19:42:03 +00:00
_str = lambda x: str(x).encode('utf-8')
TYPES = {
'bool': {'type': bool, 'cast': _bool_cast, 'tostr': _bool_str},
2018-11-17 19:42:03 +00:00
'double': {'type': float, 'cast': float, 'tostr': _str},
'int': {'type': int, 'cast': int, 'tostr': _str},
2020-05-12 21:15:16 +00:00
'text': {'type': str, 'cast': _str, 'tostr': _str},
}
def _loadData(self, data):
2017-02-24 16:18:54 +00:00
""" Load attribute values from Plex XML response. """
self._setValue = None
self.id = data.attrib.get('id')
self.label = data.attrib.get('label')
self.summary = data.attrib.get('summary')
self.type = data.attrib.get('type')
2017-02-24 16:18:54 +00:00
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)
2017-02-24 16:18:54 +00:00
def _cast(self, value):
""" Cast the specific value to the type of this setting. """
if self.type != 'enum':
2017-02-24 16:18:54 +00:00
value = utils.cast(self.TYPES.get(self.type)['cast'], value)
return value
def _getEnumValues(self, data):
2017-02-24 16:18:54 +00:00
""" Returns a list of dictionary of valis value for this setting. """
enumstr = data.attrib.get('enumValues')
if not enumstr:
return None
if ':' in enumstr:
2017-02-24 16:18:54 +00:00
return {self._cast(k): v for k, v in [kv.split(':') for kv in enumstr.split('|')]}
return enumstr.split('|')
def set(self, value):
2017-02-24 16:18:54 +00:00
""" Set a new value for this setitng. NOTE: You must call plex.settings.save() for before
any changes to setting values are persisted to the :class:`~plexapi.server.PlexServer`.
"""
# check a few things up front
if not isinstance(value, self.TYPES[self.type]['type']):
badtype = type(value).__name__
raise BadRequest('Invalid value for %s: a %s is required, not %s' % (self.id, self.type, badtype))
if self.enumValues and value not in self.enumValues:
raise BadRequest('Invalid value for %s: %s not in %s' % (self.id, value, list(self.enumValues)))
# store value off to the side until we call settings.save()
tostr = self.TYPES[self.type]['tostr']
self._setValue = tostr(value)
2017-02-28 21:49:49 +00:00
def toUrl(self):
"""Helper for urls"""
return '%s=%s' % (self.id, self._value or self.value)
2020-05-24 03:04:55 +00:00
@utils.registerPlexObject
class Preferences(Setting):
""" Represents a single Preferences.
Attributes:
TAG (str): 'Preferences'
FILTER (str): 'preferences'
"""
TAG = 'Preferences'
FILTER = 'preferences'