python-plexapi/plexapi/settings.py
JonnyWong16 9ede493f4c
Add LibrarySection methods to multi-edit items (#1184)
* Use generator for string join

* Group media type edit mixins

* Add UserRatingMixin

* Add LibrarySection methods to multi-edit items

* Remove deprecated banners

* Factor out resource lock/unlock mixins

* Update `fetchItems` to accept list of rating keys

* Add repr and helper methods to Common object

* Update tests
2023-07-27 14:45:23 -07:00

186 lines
7 KiB
Python

# -*- coding: utf-8 -*-
from collections import defaultdict
from urllib.parse import quote
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('_'):
try:
return self.__dict__[attr]
except KeyError:
raise AttributeError
return self.get(attr).value
def __setattr__(self, attr, value):
if not attr.startswith('_'):
return self.get(attr).set(value)
self.__dict__[attr] = value
def _loadData(self, data):
""" 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):
""" Returns a list of all :class:`~plexapi.settings.Setting` objects available. """
return [v for id, v in sorted(self._settings.items())]
def get(self, id):
""" 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(f'Invalid setting id: {id}')
def groups(self):
""" 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):
""" Return a list of all :class:`~plexapi.settings.Setting` objects in the specified group.
Parameters:
group (str): Group to return all settings.
"""
return self.groups().get(group, [])
def save(self):
""" Save any outstanding setting 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.')
querystr = '&'.join(f'{k}={v}' for k, v in params.items())
url = f'{self.key}?{querystr}'
self._server.query(url, self._server._session.put)
self.reload()
class Setting(PlexObject):
""" Represents a single Plex setting.
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 valid values for this setting.
"""
_bool_cast = lambda x: bool(x == 'true' or x == '1')
_bool_str = lambda x: str(x).lower()
TYPES = {
'bool': {'type': bool, 'cast': _bool_cast, 'tostr': _bool_str},
'double': {'type': float, 'cast': float, 'tostr': str},
'int': {'type': int, 'cast': int, 'tostr': str},
'text': {'type': str, 'cast': str, 'tostr': str},
}
def _loadData(self, data):
""" Load attribute values from Plex XML response. """
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.value = self._cast(data.attrib.get('value'))
self._setValue = None
def _cast(self, value):
""" Cast the specific value to the type of this setting. """
if self.type != 'enum':
value = utils.cast(self.TYPES.get(self.type)['cast'], value)
return value
def _getEnumValues(self, data):
""" 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:
d = {}
for kv in enumstr.split('|'):
try:
k, v = kv.split(':')
d[self._cast(k)] = v
except ValueError:
d[self._cast(kv)] = kv
return d
return enumstr.split('|')
def set(self, value):
""" Set a new value for this setting. 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(f'Invalid value for {self.id}: a {self.type} is required, not {badtype}')
if self.enumValues and value not in self.enumValues:
raise BadRequest(f'Invalid value for {self.id}: {value} not in {list(self.enumValues)}')
# store value off to the side until we call settings.save()
tostr = self.TYPES[self.type]['tostr']
self._setValue = tostr(value)
def toUrl(self):
"""Helper for urls"""
return f'{self.id}={self._value or self.value}'
@utils.registerPlexObject
class Preferences(Setting):
""" Represents a single Preferences.
Attributes:
TAG (str): 'Setting'
FILTER (str): 'preferences'
"""
TAG = 'Setting'
FILTER = 'preferences'
def _default(self):
""" Set the default value for this setting."""
key = f'{self._initpath}/prefs?'
url = key + f'{self.id}={self.default}'
self._server.query(url, method=self._server._session.put)