diff --git a/libs/plex/__init__.py b/libs/plex/__init__.py deleted file mode 100644 index ab2d8dcb3..000000000 --- a/libs/plex/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -import logging - -log = logging.getLogger(__name__) - -__version__ = '0.7.0' - - -try: - from plex.client import Plex -except Exception as ex: - log.warn('Unable to import submodules - %s', ex, exc_info=True) diff --git a/libs/plex/client.py b/libs/plex/client.py deleted file mode 100644 index 4664e61ca..000000000 --- a/libs/plex/client.py +++ /dev/null @@ -1,116 +0,0 @@ -from plex.core.configuration import ConfigurationManager -from plex.core.http import HttpClient -from plex.helpers import has_attribute -from plex.interfaces import construct_map -from plex.interfaces.core.base import InterfaceProxy -from plex.lib.six import add_metaclass -from plex.objects.core.manager import ObjectManager - -import logging -import socket - -log = logging.getLogger(__name__) - - -class PlexClient(object): - __interfaces = None - - def __init__(self): - # Construct interfaces - self.http = HttpClient(self) - self.configuration = ConfigurationManager() - - self.__interfaces = construct_map(self) - - # Discover modules - ObjectManager.construct() - - @property - def base_url(self): - host = self.configuration.get('server.host', '127.0.0.1') - port = self.configuration.get('server.port', 32400) - - return 'http://%s:%s' % (host, port) - - def __getitem__(self, path): - parts = path.strip('/').split('/') - - cur = self.__interfaces - parameters = [] - - while parts and type(cur) is dict: - key = parts.pop(0) - - if key == '*': - key = None - elif key not in cur: - if None in cur: - parameters.append(key) - - cur = cur[None] - continue - - return None - - cur = cur[key] - - while type(cur) is dict: - cur = cur.get(None) - - if parts: - parameters.extend(parts) - - if parameters: - return InterfaceProxy(cur, parameters) - - return cur - - def __getattr__(self, name): - interface = self.__interfaces.get(None) - - if not interface: - raise Exception("Root interface not found") - - return getattr(interface, name) - - -class PlexMeta(type): - @property - def client(cls): - if cls._client is None: - cls.construct() - - return cls._client - - def __getattr__(self, name): - if has_attribute(self, name): - return super(PlexMeta, self).__getattribute__(name) - - if self.client is None: - self.construct() - - return getattr(self.client, name) - - def __setattr__(self, name, value): - if has_attribute(self, name): - return super(PlexMeta, self).__setattr__(name, value) - - if self.client is None: - self.construct() - - setattr(self.client, name, value) - - def __getitem__(self, key): - if self.client is None: - self.construct() - - return self.client[key] - - -@add_metaclass(PlexMeta) -class Plex(object): - _client = None - - @classmethod - def construct(cls): - cls._client = PlexClient() diff --git a/libs/plex/core/__init__.py b/libs/plex/core/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/libs/plex/core/configuration.py b/libs/plex/core/configuration.py deleted file mode 100644 index e683debe6..000000000 --- a/libs/plex/core/configuration.py +++ /dev/null @@ -1,115 +0,0 @@ -class ConfigurationManager(object): - def __init__(self): - self.stack = [ - Configuration(self) - ] - - @property - def current(self): - return self.stack[-1] - - @property - def defaults(self): - return self.stack[0] - - def authentication(self, token): - return Configuration(self).authentication(token) - - def cache(self, **definitions): - return Configuration(self).cache(**definitions) - - def client(self, identifier, product, version): - return Configuration(self).client(identifier, product, version) - - def device(self, name, system): - return Configuration(self).device(name, system) - - def headers(self, headers): - return Configuration(self).headers(headers) - - def platform(self, name, version): - return Configuration(self).platform(name, version) - - def server(self, host='127.0.0.1', port=32400): - return Configuration(self).server(host, port) - - def get(self, key, default=None): - for x in range(len(self.stack) - 1, -1, -1): - value = self.stack[x].get(key) - - if value is not None: - return value - - return default - - def __getitem__(self, key): - return self.get(key) - - def __setitem__(self, key, value): - self.current[key] = value - - -class Configuration(object): - def __init__(self, manager): - self.manager = manager - - self.data = {} - - def authentication(self, token): - self.data['authentication.token'] = token - - return self - - def cache(self, **definitions): - for key, value in definitions.items(): - self.data['cache.%s' % key] = value - - return self - - def client(self, identifier, product, version): - self.data['client.identifier'] = identifier - - self.data['client.product'] = product - self.data['client.version'] = version - - return self - - def device(self, name, system): - self.data['device.name'] = name - self.data['device.system'] = system - - return self - - def headers(self, headers): - self.data['headers'] = headers - - return self - - def platform(self, name, version): - self.data['platform.name'] = name - self.data['platform.version'] = version - - return self - - def server(self, host='127.0.0.1', port=32400): - self.data['server.host'] = host - self.data['server.port'] = port - - return self - - def get(self, key, default=None): - return self.data.get(key, default) - - def __enter__(self): - self.manager.stack.append(self) - - def __exit__(self, exc_type, exc_val, exc_tb): - item = self.manager.stack.pop() - - assert item == self - - def __getitem__(self, key): - return self.data[key] - - def __setitem__(self, key, value): - self.data[key] = value diff --git a/libs/plex/core/context.py b/libs/plex/core/context.py deleted file mode 100644 index 21c430bb6..000000000 --- a/libs/plex/core/context.py +++ /dev/null @@ -1,26 +0,0 @@ -from threading import Lock - - -class Context(object): - def __init__(self, **kwargs): - self.kwargs = kwargs - - def __getattr__(self, key): - return self.kwargs.get(key) - - -class ContextStack(object): - def __init__(self): - self._list = [] - self._lock = Lock() - - def pop(self): - context = self._list.pop() - - self._lock.release() - return context - - def push(self, **kwargs): - self._lock.acquire() - - return self._list.append(Context(**kwargs)) diff --git a/libs/plex/core/extension.py b/libs/plex/core/extension.py deleted file mode 100644 index 4625e04c7..000000000 --- a/libs/plex/core/extension.py +++ /dev/null @@ -1,105 +0,0 @@ -# ExtensionImporter (```flask.exthook```) -# ---------------------------------- -# :copyright: (c) 2014 by Armin Ronacher. -# :license: BSD, see LICENSE for more details. - -from plex.lib.six import reraise - -import os -import sys - - -class ExtensionImporter(object): - """This importer redirects imports from this submodule to other locations. - This makes it possible to transition from the old flaskext.name to the - newer flask_name without people having a hard time. - """ - - def __init__(self, module_choices, wrapper_module): - self.module_choices = module_choices - self.wrapper_module = wrapper_module - self.prefix = wrapper_module + '.' - self.prefix_cutoff = wrapper_module.count('.') + 1 - - def __eq__(self, other): - return self.__class__.__module__ == other.__class__.__module__ and \ - self.__class__.__name__ == other.__class__.__name__ and \ - self.wrapper_module == other.wrapper_module and \ - self.module_choices == other.module_choices - - def __ne__(self, other): - return not self.__eq__(other) - - def install(self): - sys.meta_path[:] = [x for x in sys.meta_path if self != x] + [self] - - def find_module(self, fullname, path=None): - if fullname.startswith(self.prefix): - return self - - def load_module(self, fullname): - if fullname in sys.modules: - return sys.modules[fullname] - modname = fullname.split('.', self.prefix_cutoff)[self.prefix_cutoff] - for path in self.module_choices: - realname = path % modname - try: - __import__(realname) - except ImportError: - exc_type, exc_value, tb = sys.exc_info() - # since we only establish the entry in sys.modules at the - # very this seems to be redundant, but if recursive imports - # happen we will call into the move import a second time. - # On the second invocation we still don't have an entry for - # fullname in sys.modules, but we will end up with the same - # fake module name and that import will succeed since this - # one already has a temporary entry in the modules dict. - # Since this one "succeeded" temporarily that second - # invocation now will have created a fullname entry in - # sys.modules which we have to kill. - sys.modules.pop(fullname, None) - - # If it's an important traceback we reraise it, otherwise - # we swallow it and try the next choice. The skipped frame - # is the one from __import__ above which we don't care about - if self.is_important_traceback(realname, tb): - reraise(exc_type, exc_value, tb.tb_next) - continue - module = sys.modules[fullname] = sys.modules[realname] - if '.' not in modname: - setattr(sys.modules[self.wrapper_module], modname, module) - return module - raise ImportError('No module named %s' % fullname) - - def is_important_traceback(self, important_module, tb): - """Walks a traceback's frames and checks if any of the frames - originated in the given important module. If that is the case then we - were able to import the module itself but apparently something went - wrong when the module was imported. (Eg: import of an import failed). - """ - while tb is not None: - if self.is_important_frame(important_module, tb): - return True - tb = tb.tb_next - return False - - def is_important_frame(self, important_module, tb): - """Checks a single frame if it's important.""" - g = tb.tb_frame.f_globals - if '__name__' not in g: - return False - - module_name = g['__name__'] - - # Python 2.7 Behavior. Modules are cleaned up late so the - # name shows up properly here. Success! - if module_name == important_module: - return True - - # Some python versions will will clean up modules so early that the - # module name at that point is no longer set. Try guessing from - # the filename then. - filename = os.path.abspath(tb.tb_frame.f_code.co_filename) - test_string = os.path.sep + important_module.replace('.', os.path.sep) - return test_string + '.py' in filename or \ - test_string + os.path.sep + '__init__.py' in filename diff --git a/libs/plex/core/helpers.py b/libs/plex/core/helpers.py deleted file mode 100644 index 9315add55..000000000 --- a/libs/plex/core/helpers.py +++ /dev/null @@ -1,59 +0,0 @@ -from plex.lib import six - -import re -import unicodedata - -def flatten(text): - if text is None: - return None - - # Normalize `text` to ascii - text = normalize(text) - - # Remove special characters - text = re.sub('[^A-Za-z0-9\s]+', '', text) - - # Merge duplicate spaces - text = ' '.join(text.split()) - - # Convert to lower-case - return text.lower() - -def normalize(text): - if text is None: - return None - - # Normalize unicode characters - if type(text) is six.text_type: - text = unicodedata.normalize('NFKD', text) - - # Ensure text is ASCII, ignore unknown characters - text = text.encode('ascii', 'ignore') - - # Return decoded `text` - return text.decode('ascii') - -def to_iterable(value): - if value is None: - return None - - if isinstance(value, (list, tuple)): - return value - - return [value] - - -def synchronized(func): - def wrapper(self, *__args, **__kw): - self._lock.acquire() - - try: - return func(self, *__args, **__kw) - finally: - self._lock.release() - - wrapper.__name__ = func.__name__ - wrapper.__dict__ = func.__dict__ - wrapper.__doc__ = func.__doc__ - - return wrapper diff --git a/libs/plex/core/http.py b/libs/plex/core/http.py deleted file mode 100644 index f5f3348f5..000000000 --- a/libs/plex/core/http.py +++ /dev/null @@ -1,151 +0,0 @@ -from plex.core.context import ContextStack -from plex.core.helpers import synchronized -from plex.request import PlexRequest - -from threading import Condition -import hashlib -import logging -import requests -import socket - -log = logging.getLogger(__name__) - - -class HttpClient(object): - def __init__(self, client): - self.client = client - - self.configuration = ContextStack() - - self.session = None - - # Private - self._lock = Condition() - - # Build requests session - self._build() - - @property - def cache(self): - return self.client.configuration.get('cache.http') - - def configure(self, path=None): - self.configuration.push(base_path=path) - return self - - def request(self, method, path=None, params=None, query=None, data=None, credentials=None, **kwargs): - # retrieve configuration - ctx = self.configuration.pop() - - if path is not None and type(path) is not str: - # Convert `path` to string (excluding NoneType) - path = str(path) - - if ctx.base_path and path: - # Prepend `base_path` to relative `path`s - if not path.startswith('/'): - path = ctx.base_path + '/' + path - - elif ctx.base_path: - path = ctx.base_path - elif not path: - path = '' - - request = PlexRequest( - self.client, - method=method, - path=path, - - params=params, - query=query, - data=data, - - credentials=credentials, - **kwargs - ) - - prepared = request.prepare() - - # Try retrieve cached response - response = self._cache_lookup(prepared) - - if response: - return response - - # TODO retrying requests on 502, 503 errors? - # try: - # response = self.session.send(prepared) - # except socket.gaierror as e: - # code, _ = e -# - # if code != 8: - # raise e -# - # log.warn('Encountered socket.gaierror (code: 8)') -# - # response = self._build().send(prepared) - response = request.request.send() - - # Store response in cache - self._cache_store(prepared, response) - - return response - - def get(self, path=None, params=None, query=None, data=None, **kwargs): - return self.request('GET', path, params, query, data, **kwargs) - - def put(self, path=None, params=None, query=None, data=None, **kwargs): - return self.request('PUT', path, params, query, data, **kwargs) - - def post(self, path=None, params=None, query=None, data=None, **kwargs): - return self.request('POST', path, params, query, data, **kwargs) - - def delete(self, path=None, params=None, query=None, data=None, **kwargs): - return self.request('DELETE', path, params, query, data, **kwargs) - - def _build(self): - if self.session: - log.info('Rebuilding session and connection pools...') - - # Rebuild the connection pool (old pool has stale connections) - self.session = requests.Session() - - return self.session - - @synchronized - def _cache_lookup(self, request): - if self.cache is None: - return None - - if request.method not in ['GET']: - return None - - # Retrieve from cache - return self.cache.get(self._cache_key(request)) - - @synchronized - def _cache_store(self, request, response): - if self.cache is None: - return None - - if request.method not in ['GET']: - return None - - # Store in cache - self.cache[self._cache_key(request)] = response - - @staticmethod - def _cache_key(request): - raw = ','.join([request.method, request.url]) - - # Generate MD5 hash of key - m = hashlib.md5() - m.update(raw.encode('utf-8')) - - return m.hexdigest() - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - pass diff --git a/libs/plex/core/idict.py b/libs/plex/core/idict.py deleted file mode 100644 index 354d3bb8f..000000000 --- a/libs/plex/core/idict.py +++ /dev/null @@ -1,54 +0,0 @@ -from plex.lib.six import string_types - -class idict(dict): - def __init__(self, initial=None): - if initial: - self.update(initial) - - def get(self, k, d=None): - if isinstance(k, string_types): - k = k.lower() - - if super(idict, self).__contains__(k): - return self[k] - - return d - - def update(self, E=None, **F): - if E: - if hasattr(E, 'keys'): - # Update with `E` dictionary - for k in E: - self[k] = E[k] - else: - # Update with `E` items - for (k, v) in E: - self[k] = v - - # Update with `F` dictionary - for k in F: - self[k] = F[k] - - def __contains__(self, k): - if isinstance(k, string_types): - k = k.lower() - - return super(idict, self).__contains__(k) - - def __delitem__(self, k): - if isinstance(k, string_types): - k = k.lower() - - super(idict, self).__delitem__(k) - - def __getitem__(self, k): - if isinstance(k, string_types): - k = k.lower() - - return super(idict, self).__getitem__(k) - - def __setitem__(self, k, value): - if isinstance(k, string_types): - k = k.lower() - - super(idict, self).__setitem__(k, value) diff --git a/libs/plex/ext/__init__.py b/libs/plex/ext/__init__.py deleted file mode 100644 index 0b1ab5e02..000000000 --- a/libs/plex/ext/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from plex.core.extension import ExtensionImporter - -importer = ExtensionImporter(['plex_%s'], __name__) -importer.install() diff --git a/libs/plex/helpers.py b/libs/plex/helpers.py deleted file mode 100644 index 4bfdc3d98..000000000 --- a/libs/plex/helpers.py +++ /dev/null @@ -1,6 +0,0 @@ -def has_attribute(obj, name): - try: - object.__getattribute__(obj, name) - return True - except AttributeError: - return False diff --git a/libs/plex/interfaces/__init__.py b/libs/plex/interfaces/__init__.py deleted file mode 100644 index 7bb29b0eb..000000000 --- a/libs/plex/interfaces/__init__.py +++ /dev/null @@ -1,81 +0,0 @@ -from plex.interfaces.channel import ChannelInterface -from plex.interfaces.library import LibraryInterface -from plex.interfaces.library.metadata import LibraryMetadataInterface -from plex.interfaces.plugin import PluginInterface -from plex.interfaces.plugin.preferences import PluginPreferencesInterface -from plex.interfaces.preferences import PreferencesInterface -from plex.interfaces.root import RootInterface -from plex.interfaces.section import SectionInterface -from plex.interfaces.status import StatusInterface -from plex.interfaces.timeline import TimelineInterface - - -# TODO automatic interface discovery - -INTERFACES = [ - RootInterface, - - # / - ChannelInterface, - StatusInterface, - - # /library - LibraryInterface, - LibraryMetadataInterface, - SectionInterface, - - # /: - PreferencesInterface, - TimelineInterface, - - # /:/plugins - PluginInterface, - PluginPreferencesInterface -] - - -def get_interfaces(): - for interface in INTERFACES: - if interface.path: - path = interface.path.strip('/') - else: - path = '' - - if path: - path = path.split('/') - else: - path = [] - - yield path, interface - - -def construct_map(client, d=None, interfaces=None): - if d is None: - d = {} - - if interfaces is None: - interfaces = get_interfaces() - - for path, interface in interfaces: - if len(path) > 0: - key = path.pop(0) - else: - key = None - - if key == '*': - key = None - - if len(path) == 0: - d[key] = interface(client) - continue - - value = d.get(key, {}) - - if type(value) is not dict: - value = {None: value} - - construct_map(client, value, [(path, interface)]) - - d[key] = value - - return d diff --git a/libs/plex/interfaces/channel.py b/libs/plex/interfaces/channel.py deleted file mode 100644 index 0b3dbfc2a..000000000 --- a/libs/plex/interfaces/channel.py +++ /dev/null @@ -1,8 +0,0 @@ -from plex.interfaces.core.base import Interface - - -class ChannelInterface(Interface): - path = 'channels' - - def all(self): - raise NotImplementedError() diff --git a/libs/plex/interfaces/core/__init__.py b/libs/plex/interfaces/core/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/libs/plex/interfaces/core/base.py b/libs/plex/interfaces/core/base.py deleted file mode 100644 index f8164568c..000000000 --- a/libs/plex/interfaces/core/base.py +++ /dev/null @@ -1,216 +0,0 @@ -from plex.lib.six import string_types, StringIO -from plex.lib.six.moves.urllib_parse import urlparse - -from functools import wraps -import logging - -# Import available parser -PARSER = None - -try: - from lxml import etree - PARSER = 'etree.HTMLParser' -except ImportError: - from xml.etree import ElementTree as etree - PARSER = 'etree.XMLParser' - -log = logging.getLogger(__name__) - - -class Helpers(object): - @staticmethod - def get(node, attr): - if PARSER == 'etree.HTMLParser': - return node.get(attr.lower()) - - return node.get(attr) - - @staticmethod - def find(node, tag): - if PARSER == 'etree.HTMLParser': - return node.find(tag.lower()) - - return node.find(tag) - - @staticmethod - def findall(node, tag): - if PARSER == 'etree.HTMLParser': - return node.findall(tag.lower()) - - return node.findall(tag) - - -class Interface(object): - helpers = Helpers - - path = None - object_map = {} - - def __init__(self, client): - self.client = client - - def __getitem__(self, name): - if hasattr(self, name): - return getattr(self, name) - - raise ValueError('Unknown action "%s" on %s', name, self) - - @property - def http(self): - if not self.client: - return None - - return self.client.http.configure(self.path) - - def parse(self, response, schema): - if response.status_code < 200 or response.status_code >= 300: - return None - - try: - root = self.__parse_xml(response.content) - except SyntaxError as ex: - log.error('Unable to parse XML response: %s', ex, exc_info=True, extra={ - 'data': { - 'snippet': self.__error_snippet(response, ex) - } - }) - - return None - except Exception as ex: - log.error('Unable to parse XML response: %s', ex, exc_info=True) - - return None - - url = urlparse(response.url) - path = url.path - - return self.__construct(self.client, path, root, schema) - - @staticmethod - def __parse_xml(content): - if PARSER == 'etree.HTMLParser': - html = etree.fromstring(content, parser=etree.HTMLParser()) - assert html.tag == 'html' - - bodies = [e for e in html if e.tag == 'body'] - assert len(bodies) == 1 - - body = bodies[0] - assert len(body) == 1 - - return body[0] - - return etree.fromstring(content) - - @staticmethod - def __error_snippet(response, ex): - # Retrieve the error line - position = getattr(ex, 'position', None) - - if not position or len(position) != 2: - return None - - n_line, n_column = position - snippet = None - - # Create StringIO stream - stream = StringIO(response.text) - - # Iterate over `content` to find `n_line` - for x, l in enumerate(stream): - if x < n_line - 1: - continue - - # Line found - snippet = l - break - - # Close the stream - stream.close() - - if not snippet: - # Couldn't find the line - return None - - # Find an attribute value containing `n_column` - start = snippet.find('"', n_column) - end = snippet.find('"', start + 1) - - # Trim `snippet` (if attribute value was found) - if start >= 0 and end >= 0: - return snippet[start:end + 1] - - return snippet - - @classmethod - def __construct(cls, client, path, node, schema): - if not schema: - return None - - # Try retrieve schema for `tag` - item = schema.get(node.tag) - - if item is None: - raise ValueError('Unknown node with tag "%s"' % node.tag) - - if type(item) is dict: - value = cls.helpers.get(node, item.get('_', 'type')) - - if value is None: - return None - - item = item.get(value) - - if item is None: - raise ValueError('Unknown node type "%s"' % value) - - descriptor = None - child_schema = None - - if type(item) is tuple and len(item) == 2: - descriptor, child_schema = item - else: - descriptor = item - - if isinstance(descriptor, string_types): - if descriptor not in cls.object_map: - raise Exception('Unable to find descriptor by name "%s"' % descriptor) - - descriptor = cls.object_map.get(descriptor) - - if descriptor is None: - raise Exception('Unable to find descriptor') - - keys_used, obj = descriptor.construct(client, node, path=path) - - # Lazy-construct children - def iter_children(): - for child_node in node: - item = cls.__construct(client, path, child_node, child_schema) - - if item: - yield item - - obj._children = iter_children() - - return obj - - -class InterfaceProxy(object): - def __init__(self, interface, args): - self.interface = interface - self.args = list(args) - - def __getattr__(self, name): - value = getattr(self.interface, name) - - if not hasattr(value, '__call__'): - return value - - @wraps(value) - def wrap(*args, **kwargs): - args = self.args + list(args) - - return value(*args, **kwargs) - - return wrap diff --git a/libs/plex/interfaces/library/__init__.py b/libs/plex/interfaces/library/__init__.py deleted file mode 100644 index fb334038f..000000000 --- a/libs/plex/interfaces/library/__init__.py +++ /dev/null @@ -1,104 +0,0 @@ -from plex.core.idict import idict -from plex.interfaces.core.base import Interface - - -class LibraryInterface(Interface): - path = 'library' - - def metadata(self, rating_key): - response = self.http.get('metadata', rating_key) - - return self.parse(response, idict({ - 'MediaContainer': ('MediaContainer', idict({ - 'Directory': { - 'album': 'Album', - 'artist': 'Artist', - - 'season': 'Season', - 'show': 'Show' - }, - 'Video': { - 'episode': 'Episode', - 'clip': 'Clip', - 'movie': 'Movie' - }, - - 'Track': 'Track' - })) - })) - - def on_deck(self): - response = self.http.get('onDeck') - - return self.parse(response, idict({ - 'MediaContainer': ('MediaContainer', idict({ - 'Video': { - 'movie': 'Movie', - 'episode': 'Episode' - } - })) - })) - - def recently_added(self): - response = self.http.get('recentlyAdded') - - return self.parse(response, idict({ - 'MediaContainer': ('MediaContainer', idict({ - 'Directory': { - 'album': 'Album', - 'season': 'Season' - }, - 'Video': { - 'movie': 'Movie' - } - })) - })) - - def sections(self): - response = self.http.get('sections') - - return self.parse(response, idict({ - 'MediaContainer': ('SectionContainer', idict({ - 'Directory': ('Section', idict({ - 'Location': 'Location' - })) - })) - })) - - # - # Item actions - # - - def rate(self, key, rating): - response = self.http.get( - '/:/rate', - query={ - 'identifier': 'com.plexapp.plugins.library', - 'key': key, - 'rating': int(round(rating, 0)) - } - ) - - return response.status_code == 200 - - def scrobble(self, key): - response = self.http.get( - '/:/scrobble', - query={ - 'identifier': 'com.plexapp.plugins.library', - 'key': key - } - ) - - return response.status_code == 200 - - def unscrobble(self, key): - response = self.http.get( - '/:/unscrobble', - query={ - 'identifier': 'com.plexapp.plugins.library', - 'key': key - } - ) - - return response.status_code == 200 diff --git a/libs/plex/interfaces/library/metadata.py b/libs/plex/interfaces/library/metadata.py deleted file mode 100644 index b63429755..000000000 --- a/libs/plex/interfaces/library/metadata.py +++ /dev/null @@ -1,65 +0,0 @@ -from plex.core.idict import idict -from plex.interfaces.core.base import Interface - - -class LibraryMetadataInterface(Interface): - path = 'library/metadata' - - def refresh(self, key): - response = self.http.put(str(key) + "/refresh") - - def all_leaves(self, key): - response = self.http.get(key, 'allLeaves') - - return self.parse(response, idict({ - 'MediaContainer': { - '_': 'viewGroup', - - 'episode': ('ShowLeavesContainer', idict({ - 'Video': { - 'episode': 'Episode' - } - })), - - 'track': ('ArtistLeavesContainer', idict({ - 'Track': 'Track' - })) - } - })) - - def children(self, key): - response = self.http.get(key, 'children') - - return self.parse(response, idict({ - 'MediaContainer': { - '_': 'viewGroup', - - # --------------------------------------- - # Music - # --------------------------------------- - 'album': ('ArtistChildrenContainer', idict({ - 'Directory': { - 'album': 'Album' - } - })), - - 'track': ('AlbumChildrenContainer', idict({ - 'Track': 'Track' - })), - - # --------------------------------------- - # TV - # --------------------------------------- - 'season': ('ShowChildrenContainer', idict({ - 'Directory': { - 'season': 'Season' - } - })), - - 'episode': ('SeasonChildrenContainer', idict({ - 'Video': { - 'episode': 'Episode' - } - })) - } - })) diff --git a/libs/plex/interfaces/plugin/__init__.py b/libs/plex/interfaces/plugin/__init__.py deleted file mode 100644 index a51dcde75..000000000 --- a/libs/plex/interfaces/plugin/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -from plex.interfaces.core.base import Interface - - -class PluginInterface(Interface): - path = ':/plugins' - - def reload_services(self, plugin_id): - response = self.http.get(plugin_id, 'services/reload') - return response.status_code == 200 - - def restart(self, plugin_id): - response = self.http.get(plugin_id, 'restart') - return response.status_code == 200 diff --git a/libs/plex/interfaces/plugin/preferences.py b/libs/plex/interfaces/plugin/preferences.py deleted file mode 100644 index 46e5f2fcc..000000000 --- a/libs/plex/interfaces/plugin/preferences.py +++ /dev/null @@ -1,40 +0,0 @@ -from plex.core.idict import idict -from plex.interfaces.core.base import Interface - - -class PluginPreferencesInterface(Interface): - path = ':/plugins/*/prefs' - - def get(self, plugin_id, id=None): - response = self.http.get('/:/plugins/%s/prefs' % plugin_id) - - container = self.parse(response, idict({ - 'MediaContainer': ('MediaContainer', idict({ - 'Setting': 'Setting' - })) - })) - - if container is None or id is None: - return container - - for setting in container: - if setting.id == id: - return setting - - return None - - def set(self, plugin_id, id, value): - response = self.http.get('/:/plugins/%s/prefs/set' % plugin_id, query={ - id: self.to_setting_value(value, type(value)) - }) - - return response.status_code == 200 - - def to_setting_value(self, value, value_type=None): - if value is None: - return None - - if value_type is bool: - return str(value).lower() - - return str(value) diff --git a/libs/plex/interfaces/preferences.py b/libs/plex/interfaces/preferences.py deleted file mode 100644 index 240609c60..000000000 --- a/libs/plex/interfaces/preferences.py +++ /dev/null @@ -1,40 +0,0 @@ -from plex.core.idict import idict -from plex.interfaces.core.base import Interface - - -class PreferencesInterface(Interface): - path = ':/prefs' - - def get(self, id=None): - response = self.http.get() - - container = self.parse(response, idict({ - 'MediaContainer': ('MediaContainer', idict({ - 'Setting': 'Setting' - })) - })) - - if container is None or id is None: - return container - - for setting in container: - if setting.id == id: - return setting - - return None - - def set(self, id, value): - response = self.http.put(query={ - id: self.to_setting_value(value, type(value)) - }) - - return response.status_code == 200 - - def to_setting_value(self, value, value_type=None): - if value is None: - return None - - if value_type is bool: - return str(value).lower() - - return str(value) diff --git a/libs/plex/interfaces/root.py b/libs/plex/interfaces/root.py deleted file mode 100644 index dcc83880a..000000000 --- a/libs/plex/interfaces/root.py +++ /dev/null @@ -1,59 +0,0 @@ -from plex.core.idict import idict -from plex.interfaces.core.base import Interface - - -class RootInterface(Interface): - def detail(self): - response = self.http.get() - - return self.parse(response, idict({ - 'MediaContainer': ('Detail', idict({ - 'Directory': 'Directory' - })) - })) - - def version(self): - detail = self.detail() - - if not detail: - return None - - return detail.version - - def clients(self): - response = self.http.get('clients') - - return self.parse(response, idict({ - 'MediaContainer': ('ClientContainer', idict({ - 'Server': 'Client' - })) - })) - - def players(self): - pass - - def servers(self): - response = self.http.get('servers') - - return self.parse(response, idict({ - 'MediaContainer': ('Container', idict({ - 'Server': 'Server' - })) - })) - - def agents(self): - response = self.http.get('system/agents') - - return self.parse(response, idict({ - 'MediaContainer': ('Container', idict({ - 'Agent': 'Agent' - })) - })) - - def primary_agent(self, guid, media_type): - response = self.http.get('/system/agents/%s/config/%s' % (guid, media_type)) - return self.parse(response, idict({ - 'MediaContainer': ('Container', idict({ - 'Agent': 'Agent' - })) - })) diff --git a/libs/plex/interfaces/section.py b/libs/plex/interfaces/section.py deleted file mode 100644 index a5c799ce3..000000000 --- a/libs/plex/interfaces/section.py +++ /dev/null @@ -1,69 +0,0 @@ -from plex.core.idict import idict -from plex.interfaces.core.base import Interface - - -class SectionInterface(Interface): - path = 'library/sections' - - def all(self, key): - response = self.http.get(key, 'all') - - return self.parse(response, idict({ - 'MediaContainer': ('MediaContainer', idict({ - 'Directory': { - 'artist': 'Artist', - 'show': 'Show' - }, - 'Video': { - 'movie': 'Movie' - } - })) - })) - - def recently_added(self, key): - response = self.http.get(key, 'recentlyAdded') - - return self.parse(response, idict({ - 'MediaContainer': ('MediaContainer', idict({ - 'Directory': { - 'artist': 'Artist', - 'show': 'Show' - }, - 'Video': { - 'movie': 'Movie', - 'episode': 'Episode', - 'clip': 'Clip', - } - })) - })) - - def first_character(self, key, character=None): - if character: - response = self.http.get(key, ['firstCharacter', character]) - - # somehow plex wrongly returns items of other libraries when character is # - return self.parse(response, idict({ - 'MediaContainer': ('MediaContainer', idict({ - 'Directory': { - 'album': 'Album', - 'artist': 'Artist', - - 'season': 'Season', - 'show': 'Show' - }, - 'Video': { - 'episode': 'Episode', - 'clip': 'Clip', - 'movie': 'Movie' - }, - 'Track': 'Track' - })) - })) - - response = self.http.get(key, 'firstCharacter') - - return self.parse(response, idict({ - 'MediaContainer': ('MediaContainer', idict({ - 'Directory': 'Directory' - })) - })) diff --git a/libs/plex/interfaces/status.py b/libs/plex/interfaces/status.py deleted file mode 100644 index 97d3c3b85..000000000 --- a/libs/plex/interfaces/status.py +++ /dev/null @@ -1,21 +0,0 @@ -from plex.core.idict import idict -from plex.interfaces.core.base import Interface - - -class StatusInterface(Interface): - path = 'status' - - def sessions(self): - response = self.http.get('sessions') - - return self.parse(response, idict({ - 'MediaContainer': ('SessionContainer', idict({ - 'Track': 'Track', - - 'Video': { - 'episode': 'Episode', - 'clip': 'Clip', - 'movie': 'Movie' - } - })) - })) diff --git a/libs/plex/interfaces/timeline.py b/libs/plex/interfaces/timeline.py deleted file mode 100644 index b92709e44..000000000 --- a/libs/plex/interfaces/timeline.py +++ /dev/null @@ -1,36 +0,0 @@ -from plex.interfaces.core.base import Interface - -TIMELINE_STATES = [ - 'buffering', - 'paused', - 'playing', - 'stopped' -] - - -class TimelineInterface(Interface): - path = ':/timeline' - - def update(self, rating_key, state, time, duration, key=None, play_queue_item_id=None): - if not rating_key: - raise ValueError('Invalid "rating_key" parameter') - - if time is None or duration is None: - raise ValueError('"time" and "duration" parameters are required') - - if state not in TIMELINE_STATES: - raise ValueError('Unknown "state"') - - response = self.http.get(query=[ - ('ratingKey', rating_key), - ('state', state), - - ('time', time), - ('duration', duration), - - # Optional parameters - ('key', key), - ('playQueueItemID', play_queue_item_id) - ]) - - return response and response.status_code == 200 diff --git a/libs/plex/lib/__init__.py b/libs/plex/lib/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/libs/plex/lib/six.py b/libs/plex/lib/six.py deleted file mode 100644 index 21b0e8032..000000000 --- a/libs/plex/lib/six.py +++ /dev/null @@ -1,762 +0,0 @@ -"""Utilities for writing code that runs on Python 2 and 3""" - -# Copyright (c) 2010-2014 Benjamin Peterson -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -from __future__ import absolute_import - -import functools -import operator -import sys -import types - -__author__ = "Benjamin Peterson " -__version__ = "1.8.0" - - -# Useful for very coarse version differentiation. -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 - -if PY3: - string_types = str, - integer_types = int, - class_types = type, - text_type = str - binary_type = bytes - - MAXSIZE = sys.maxsize -else: - string_types = basestring, - integer_types = (int, long) - class_types = (type, types.ClassType) - text_type = unicode - binary_type = str - - if sys.platform.startswith("java"): - # Jython always uses 32 bits. - MAXSIZE = int((1 << 31) - 1) - else: - # It's possible to have sizeof(long) != sizeof(Py_ssize_t). - class X(object): - def __len__(self): - return 1 << 31 - try: - len(X()) - except OverflowError: - # 32-bit - MAXSIZE = int((1 << 31) - 1) - else: - # 64-bit - MAXSIZE = int((1 << 63) - 1) - del X - - -def _add_doc(func, doc): - """Add documentation to a function.""" - func.__doc__ = doc - - -def _import_module(name): - """Import module, returning the module after the last dot.""" - __import__(name) - return sys.modules[name] - - -class _LazyDescr(object): - - def __init__(self, name): - self.name = name - - def __get__(self, obj, tp): - result = self._resolve() - setattr(obj, self.name, result) # Invokes __set__. - # This is a bit ugly, but it avoids running this again. - delattr(obj.__class__, self.name) - return result - - -class MovedModule(_LazyDescr): - - def __init__(self, name, old, new=None): - super(MovedModule, self).__init__(name) - if PY3: - if new is None: - new = name - self.mod = new - else: - self.mod = old - - def _resolve(self): - return _import_module(self.mod) - - def __getattr__(self, attr): - _module = self._resolve() - value = getattr(_module, attr) - setattr(self, attr, value) - return value - - -class _LazyModule(types.ModuleType): - - def __init__(self, name): - super(_LazyModule, self).__init__(name) - self.__doc__ = self.__class__.__doc__ - - def __dir__(self): - attrs = ["__doc__", "__name__"] - attrs += [attr.name for attr in self._moved_attributes] - return attrs - - # Subclasses should override this - _moved_attributes = [] - - -class MovedAttribute(_LazyDescr): - - def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): - super(MovedAttribute, self).__init__(name) - if PY3: - if new_mod is None: - new_mod = name - self.mod = new_mod - if new_attr is None: - if old_attr is None: - new_attr = name - else: - new_attr = old_attr - self.attr = new_attr - else: - self.mod = old_mod - if old_attr is None: - old_attr = name - self.attr = old_attr - - def _resolve(self): - module = _import_module(self.mod) - return getattr(module, self.attr) - - -class _SixMetaPathImporter(object): - """ - A meta path importer to import six.moves and its submodules. - - This class implements a PEP302 finder and loader. It should be compatible - with Python 2.5 and all existing versions of Python3 - """ - def __init__(self, six_module_name): - self.name = six_module_name - self.known_modules = {} - - def _add_module(self, mod, *fullnames): - for fullname in fullnames: - self.known_modules[self.name + "." + fullname] = mod - - def _get_module(self, fullname): - return self.known_modules[self.name + "." + fullname] - - def find_module(self, fullname, path=None): - if fullname in self.known_modules: - return self - return None - - def __get_module(self, fullname): - try: - return self.known_modules[fullname] - except KeyError: - raise ImportError("This loader does not know module " + fullname) - - def load_module(self, fullname): - try: - # in case of a reload - return sys.modules[fullname] - except KeyError: - pass - mod = self.__get_module(fullname) - if isinstance(mod, MovedModule): - mod = mod._resolve() - else: - mod.__loader__ = self - sys.modules[fullname] = mod - return mod - - def is_package(self, fullname): - """ - Return true, if the named module is a package. - - We need this method to get correct spec objects with - Python 3.4 (see PEP451) - """ - return hasattr(self.__get_module(fullname), "__path__") - - def get_code(self, fullname): - """Return None - - Required, if is_package is implemented""" - self.__get_module(fullname) # eventually raises ImportError - return None - get_source = get_code # same as get_code - -_importer = _SixMetaPathImporter(__name__) - - -class _MovedItems(_LazyModule): - """Lazy loading of moved objects""" - __path__ = [] # mark as package - - -_moved_attributes = [ - MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), - MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), - MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), - MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), - MovedAttribute("intern", "__builtin__", "sys"), - MovedAttribute("map", "itertools", "builtins", "imap", "map"), - MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("reload_module", "__builtin__", "imp", "reload"), - MovedAttribute("reduce", "__builtin__", "functools"), - MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), - MovedAttribute("StringIO", "StringIO", "io"), - MovedAttribute("UserDict", "UserDict", "collections"), - MovedAttribute("UserList", "UserList", "collections"), - MovedAttribute("UserString", "UserString", "collections"), - MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), - MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), - - MovedModule("builtins", "__builtin__"), - MovedModule("configparser", "ConfigParser"), - MovedModule("copyreg", "copy_reg"), - MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), - MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), - MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), - MovedModule("http_cookies", "Cookie", "http.cookies"), - MovedModule("html_entities", "htmlentitydefs", "html.entities"), - MovedModule("html_parser", "HTMLParser", "html.parser"), - MovedModule("http_client", "httplib", "http.client"), - MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), - MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), - MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), - MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), - MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), - MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), - MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), - MovedModule("cPickle", "cPickle", "pickle"), - MovedModule("queue", "Queue"), - MovedModule("reprlib", "repr"), - MovedModule("socketserver", "SocketServer"), - MovedModule("_thread", "thread", "_thread"), - MovedModule("tkinter", "Tkinter"), - MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), - MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), - MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), - MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), - MovedModule("tkinter_tix", "Tix", "tkinter.tix"), - MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), - MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), - MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), - MovedModule("tkinter_colorchooser", "tkColorChooser", - "tkinter.colorchooser"), - MovedModule("tkinter_commondialog", "tkCommonDialog", - "tkinter.commondialog"), - MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), - MovedModule("tkinter_font", "tkFont", "tkinter.font"), - MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), - MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", - "tkinter.simpledialog"), - MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), - MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), - MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), - MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), - MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), - MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), - MovedModule("winreg", "_winreg"), -] -for attr in _moved_attributes: - setattr(_MovedItems, attr.name, attr) - if isinstance(attr, MovedModule): - _importer._add_module(attr, "moves." + attr.name) -del attr - -_MovedItems._moved_attributes = _moved_attributes - -moves = _MovedItems(__name__ + ".moves") -_importer._add_module(moves, "moves") - - -class Module_six_moves_urllib_parse(_LazyModule): - """Lazy loading of moved objects in six.moves.urllib_parse""" - - -_urllib_parse_moved_attributes = [ - MovedAttribute("ParseResult", "urlparse", "urllib.parse"), - MovedAttribute("SplitResult", "urlparse", "urllib.parse"), - MovedAttribute("parse_qs", "urlparse", "urllib.parse"), - MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), - MovedAttribute("urldefrag", "urlparse", "urllib.parse"), - MovedAttribute("urljoin", "urlparse", "urllib.parse"), - MovedAttribute("urlparse", "urlparse", "urllib.parse"), - MovedAttribute("urlsplit", "urlparse", "urllib.parse"), - MovedAttribute("urlunparse", "urlparse", "urllib.parse"), - MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), - MovedAttribute("quote", "urllib", "urllib.parse"), - MovedAttribute("quote_plus", "urllib", "urllib.parse"), - MovedAttribute("unquote", "urllib", "urllib.parse"), - MovedAttribute("unquote_plus", "urllib", "urllib.parse"), - MovedAttribute("urlencode", "urllib", "urllib.parse"), - MovedAttribute("splitquery", "urllib", "urllib.parse"), - MovedAttribute("splittag", "urllib", "urllib.parse"), - MovedAttribute("splituser", "urllib", "urllib.parse"), - MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), - MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), - MovedAttribute("uses_params", "urlparse", "urllib.parse"), - MovedAttribute("uses_query", "urlparse", "urllib.parse"), - MovedAttribute("uses_relative", "urlparse", "urllib.parse"), -] -for attr in _urllib_parse_moved_attributes: - setattr(Module_six_moves_urllib_parse, attr.name, attr) -del attr - -Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes - -_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), - "moves.urllib_parse", "moves.urllib.parse") - - -class Module_six_moves_urllib_error(_LazyModule): - """Lazy loading of moved objects in six.moves.urllib_error""" - - -_urllib_error_moved_attributes = [ - MovedAttribute("URLError", "urllib2", "urllib.error"), - MovedAttribute("HTTPError", "urllib2", "urllib.error"), - MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), -] -for attr in _urllib_error_moved_attributes: - setattr(Module_six_moves_urllib_error, attr.name, attr) -del attr - -Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes - -_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), - "moves.urllib_error", "moves.urllib.error") - - -class Module_six_moves_urllib_request(_LazyModule): - """Lazy loading of moved objects in six.moves.urllib_request""" - - -_urllib_request_moved_attributes = [ - MovedAttribute("urlopen", "urllib2", "urllib.request"), - MovedAttribute("install_opener", "urllib2", "urllib.request"), - MovedAttribute("build_opener", "urllib2", "urllib.request"), - MovedAttribute("pathname2url", "urllib", "urllib.request"), - MovedAttribute("url2pathname", "urllib", "urllib.request"), - MovedAttribute("getproxies", "urllib", "urllib.request"), - MovedAttribute("Request", "urllib2", "urllib.request"), - MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), - MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), - MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), - MovedAttribute("BaseHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), - MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), - MovedAttribute("FileHandler", "urllib2", "urllib.request"), - MovedAttribute("FTPHandler", "urllib2", "urllib.request"), - MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), - MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), - MovedAttribute("urlretrieve", "urllib", "urllib.request"), - MovedAttribute("urlcleanup", "urllib", "urllib.request"), - MovedAttribute("URLopener", "urllib", "urllib.request"), - MovedAttribute("FancyURLopener", "urllib", "urllib.request"), - MovedAttribute("proxy_bypass", "urllib", "urllib.request"), -] -for attr in _urllib_request_moved_attributes: - setattr(Module_six_moves_urllib_request, attr.name, attr) -del attr - -Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes - -_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), - "moves.urllib_request", "moves.urllib.request") - - -class Module_six_moves_urllib_response(_LazyModule): - """Lazy loading of moved objects in six.moves.urllib_response""" - - -_urllib_response_moved_attributes = [ - MovedAttribute("addbase", "urllib", "urllib.response"), - MovedAttribute("addclosehook", "urllib", "urllib.response"), - MovedAttribute("addinfo", "urllib", "urllib.response"), - MovedAttribute("addinfourl", "urllib", "urllib.response"), -] -for attr in _urllib_response_moved_attributes: - setattr(Module_six_moves_urllib_response, attr.name, attr) -del attr - -Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes - -_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), - "moves.urllib_response", "moves.urllib.response") - - -class Module_six_moves_urllib_robotparser(_LazyModule): - """Lazy loading of moved objects in six.moves.urllib_robotparser""" - - -_urllib_robotparser_moved_attributes = [ - MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), -] -for attr in _urllib_robotparser_moved_attributes: - setattr(Module_six_moves_urllib_robotparser, attr.name, attr) -del attr - -Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes - -_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), - "moves.urllib_robotparser", "moves.urllib.robotparser") - - -class Module_six_moves_urllib(types.ModuleType): - """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" - __path__ = [] # mark as package - parse = _importer._get_module("moves.urllib_parse") - error = _importer._get_module("moves.urllib_error") - request = _importer._get_module("moves.urllib_request") - response = _importer._get_module("moves.urllib_response") - robotparser = _importer._get_module("moves.urllib_robotparser") - - def __dir__(self): - return ['parse', 'error', 'request', 'response', 'robotparser'] - -_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), - "moves.urllib") - - -def add_move(move): - """Add an item to six.moves.""" - setattr(_MovedItems, move.name, move) - - -def remove_move(name): - """Remove item from six.moves.""" - try: - delattr(_MovedItems, name) - except AttributeError: - try: - del moves.__dict__[name] - except KeyError: - raise AttributeError("no such move, %r" % (name,)) - - -if PY3: - _meth_func = "__func__" - _meth_self = "__self__" - - _func_closure = "__closure__" - _func_code = "__code__" - _func_defaults = "__defaults__" - _func_globals = "__globals__" -else: - _meth_func = "im_func" - _meth_self = "im_self" - - _func_closure = "func_closure" - _func_code = "func_code" - _func_defaults = "func_defaults" - _func_globals = "func_globals" - - -try: - advance_iterator = next -except NameError: - def advance_iterator(it): - return it.next() -next = advance_iterator - - -try: - callable = callable -except NameError: - def callable(obj): - return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) - - -if PY3: - def get_unbound_function(unbound): - return unbound - - create_bound_method = types.MethodType - - Iterator = object -else: - def get_unbound_function(unbound): - return unbound.im_func - - def create_bound_method(func, obj): - return types.MethodType(func, obj, obj.__class__) - - class Iterator(object): - - def next(self): - return type(self).__next__(self) - - callable = callable -_add_doc(get_unbound_function, - """Get the function out of a possibly unbound function""") - - -get_method_function = operator.attrgetter(_meth_func) -get_method_self = operator.attrgetter(_meth_self) -get_function_closure = operator.attrgetter(_func_closure) -get_function_code = operator.attrgetter(_func_code) -get_function_defaults = operator.attrgetter(_func_defaults) -get_function_globals = operator.attrgetter(_func_globals) - - -if PY3: - def iterkeys(d, **kw): - return iter(d.keys(**kw)) - - def itervalues(d, **kw): - return iter(d.values(**kw)) - - def iteritems(d, **kw): - return iter(d.items(**kw)) - - def iterlists(d, **kw): - return iter(d.lists(**kw)) -else: - def iterkeys(d, **kw): - return iter(d.iterkeys(**kw)) - - def itervalues(d, **kw): - return iter(d.itervalues(**kw)) - - def iteritems(d, **kw): - return iter(d.iteritems(**kw)) - - def iterlists(d, **kw): - return iter(d.iterlists(**kw)) - -_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") -_add_doc(itervalues, "Return an iterator over the values of a dictionary.") -_add_doc(iteritems, - "Return an iterator over the (key, value) pairs of a dictionary.") -_add_doc(iterlists, - "Return an iterator over the (key, [values]) pairs of a dictionary.") - - -if PY3: - def b(s): - return s.encode("latin-1") - def u(s): - return s - unichr = chr - if sys.version_info[1] <= 1: - def int2byte(i): - return bytes((i,)) - else: - # This is about 2x faster than the implementation above on 3.2+ - int2byte = operator.methodcaller("to_bytes", 1, "big") - byte2int = operator.itemgetter(0) - indexbytes = operator.getitem - iterbytes = iter - import io - StringIO = io.StringIO - BytesIO = io.BytesIO -else: - def b(s): - return s - # Workaround for standalone backslash - def u(s): - return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") - unichr = unichr - int2byte = chr - def byte2int(bs): - return ord(bs[0]) - def indexbytes(buf, i): - return ord(buf[i]) - def iterbytes(buf): - return (ord(byte) for byte in buf) - import StringIO - StringIO = BytesIO = StringIO.StringIO -_add_doc(b, """Byte literal""") -_add_doc(u, """Text literal""") - - -if PY3: - exec_ = getattr(moves.builtins, "exec") - - - def reraise(tp, value, tb=None): - if value is None: - value = tp() - if value.__traceback__ is not tb: - raise value.with_traceback(tb) - raise value - -else: - def exec_(_code_, _globs_=None, _locs_=None): - """Execute code in a namespace.""" - if _globs_ is None: - frame = sys._getframe(1) - _globs_ = frame.f_globals - if _locs_ is None: - _locs_ = frame.f_locals - del frame - elif _locs_ is None: - _locs_ = _globs_ - exec("""exec _code_ in _globs_, _locs_""") - - - exec_("""def reraise(tp, value, tb=None): - raise tp, value, tb -""") - - -print_ = getattr(moves.builtins, "print", None) -if print_ is None: - def print_(*args, **kwargs): - """The new-style print function for Python 2.4 and 2.5.""" - fp = kwargs.pop("file", sys.stdout) - if fp is None: - return - def write(data): - if not isinstance(data, basestring): - data = str(data) - # If the file has an encoding, encode unicode with it. - if (isinstance(fp, file) and - isinstance(data, unicode) and - fp.encoding is not None): - errors = getattr(fp, "errors", None) - if errors is None: - errors = "strict" - data = data.encode(fp.encoding, errors) - fp.write(data) - want_unicode = False - sep = kwargs.pop("sep", None) - if sep is not None: - if isinstance(sep, unicode): - want_unicode = True - elif not isinstance(sep, str): - raise TypeError("sep must be None or a string") - end = kwargs.pop("end", None) - if end is not None: - if isinstance(end, unicode): - want_unicode = True - elif not isinstance(end, str): - raise TypeError("end must be None or a string") - if kwargs: - raise TypeError("invalid keyword arguments to print()") - if not want_unicode: - for arg in args: - if isinstance(arg, unicode): - want_unicode = True - break - if want_unicode: - newline = unicode("\n") - space = unicode(" ") - else: - newline = "\n" - space = " " - if sep is None: - sep = space - if end is None: - end = newline - for i, arg in enumerate(args): - if i: - write(sep) - write(arg) - write(end) - -_add_doc(reraise, """Reraise an exception.""") - -if sys.version_info[0:2] < (3, 4): - def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, - updated=functools.WRAPPER_UPDATES): - def wrapper(f): - f = functools.wraps(wrapped)(f) - f.__wrapped__ = wrapped - return f - return wrapper -else: - wraps = functools.wraps - -def with_metaclass(meta, *bases): - """Create a base class with a metaclass.""" - # This requires a bit of explanation: the basic idea is to make a dummy - # metaclass for one level of class instantiation that replaces itself with - # the actual metaclass. - class metaclass(meta): - def __new__(cls, name, this_bases, d): - return meta(name, bases, d) - return type.__new__(metaclass, 'temporary_class', (), {}) - - -def add_metaclass(metaclass): - """Class decorator for creating a class with a metaclass.""" - def wrapper(cls): - orig_vars = cls.__dict__.copy() - slots = orig_vars.get('__slots__') - if slots is not None: - if isinstance(slots, str): - slots = [slots] - for slots_var in slots: - orig_vars.pop(slots_var) - orig_vars.pop('__dict__', None) - orig_vars.pop('__weakref__', None) - return metaclass(cls.__name__, cls.__bases__, orig_vars) - return wrapper - -# Complete the moves implementation. -# This code is at the end of this module to speed up module loading. -# Turn this module into a package. -__path__ = [] # required for PEP 302 and PEP 451 -__package__ = __name__ # see PEP 366 @ReservedAssignment -if globals().get("__spec__") is not None: - __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable -# Remove other six meta path importers, since they cause problems. This can -# happen if six is removed from sys.modules and then reloaded. (Setuptools does -# this for some reason.) -if sys.meta_path: - for i, importer in enumerate(sys.meta_path): - # Here's some real nastiness: Another "instance" of the six module might - # be floating around. Therefore, we can't use isinstance() to check for - # the six meta path importer, since the other six instance will have - # inserted an importer with different class. - if (type(importer).__name__ == "_SixMetaPathImporter" and - importer.name == __name__): - del sys.meta_path[i] - break - del i, importer -# Finally, add the importer to the meta path import hook. -sys.meta_path.append(_importer) diff --git a/libs/plex/objects/__init__.py b/libs/plex/objects/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/libs/plex/objects/agent.py b/libs/plex/objects/agent.py deleted file mode 100644 index ae3706090..000000000 --- a/libs/plex/objects/agent.py +++ /dev/null @@ -1,29 +0,0 @@ -from plex.objects.core.base import Descriptor, Property - - -class MediaType(Descriptor): - name = Property - media_type = Property("mediaType", type=int) - - @classmethod - def from_node(cls, client, node): - items = [] - - for t in cls.helpers.findall(node, 'MediaType'): - _, obj = MediaType.construct(client, t, child=True) - - items.append(obj) - - return [], items - - -class Agent(Descriptor): - name = Property - enabled = Property(type=int) - identifier = Property - primary = Property(type=int) - has_prefs = Property("hasPrefs", type=int) - has_attribution = Property("hasAttribution", type=int) - - media_types = Property(resolver=lambda: MediaType.from_node) - diff --git a/libs/plex/objects/client.py b/libs/plex/objects/client.py deleted file mode 100644 index 2dbca4adc..000000000 --- a/libs/plex/objects/client.py +++ /dev/null @@ -1,32 +0,0 @@ -from plex.core.helpers import to_iterable -from plex.objects.container import Container -from plex.objects.core.base import Property -from plex.objects.server import Server - - -class Client(Server): - product = Property - device_class = Property('deviceClass') - - protocol = Property - protocol_version = Property('protocolVersion', type=int) - protocol_capabilities = Property('protocolCapabilities') - - -class ClientContainer(Container): - filter_passes = lambda _, allowed, value: allowed is None or value in allowed - - def filter(self, identifiers=None): - identifiers = to_iterable(identifiers) - - for client in self: - if not self.filter_passes(identifiers, client.machine_identifier): - continue - - yield client - - def get(self, identifier): - for item in self.filter(identifier): - return item - - return None diff --git a/libs/plex/objects/container.py b/libs/plex/objects/container.py deleted file mode 100644 index 9123cef27..000000000 --- a/libs/plex/objects/container.py +++ /dev/null @@ -1,7 +0,0 @@ -from plex.objects.core.base import Descriptor, Property - - -class Container(Descriptor): - size = Property(type=int) - - updated_at = Property('updatedAt', int) diff --git a/libs/plex/objects/core/__init__.py b/libs/plex/objects/core/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/libs/plex/objects/core/base.py b/libs/plex/objects/core/base.py deleted file mode 100644 index deb527036..000000000 --- a/libs/plex/objects/core/base.py +++ /dev/null @@ -1,168 +0,0 @@ -from plex.lib.six import add_metaclass -from plex.interfaces.core.base import Interface - -import logging -import traceback -import types - -log = logging.getLogger(__name__) - - -class Property(object): - helpers = Interface.helpers - - def __init__(self, name=None, type=None, resolver=None): - self.name = name - self.type = type - self.resolver = resolver - - def value(self, client, key, node, keys_used): - if self.resolver is not None: - return self.value_func(client, node, keys_used) - - return self.value_node(key, node, keys_used) - - def value_node(self, key, node, keys_used): - value = self.helpers.get(node, key) - keys_used.append(key.lower()) - - if value is None: - return None - - return self.value_convert(value) - - def value_convert(self, value): - if not self.type: - return value - - types = self.type if type(self.type) is list else [self.type] - result = value - - for target_type in types: - try: - result = target_type(result) - except: - return None - - return result - - def value_func(self, client, node, keys_used): - func = self.resolver() - - try: - keys, value = func(client, node) - - keys_used.extend([k.lower() for k in keys]) - return value - except Exception as ex: - log.warn('Exception in value function (%s): %s - %s', func, ex, traceback.format_exc()) - return None - - -class DescriptorMeta(type): - def __init__(self, name, bases, attrs): - super(DescriptorMeta, self).__init__(name, bases, attrs) - - Interface.object_map[self.__name__] = self - - -@add_metaclass(DescriptorMeta) -class Descriptor(Interface): - attribute_map = None - - def __init__(self, client, path): - super(Descriptor, self).__init__(client) - self.path = path - - self._children = None - - @classmethod - def properties(cls): - keys = [k for k in dir(cls) if not k.startswith('_')] - - #log.debug('%s - keys: %s', self, keys) - - for key in keys: - if key.startswith('_'): - continue - - value = getattr(cls, key) - - if value is Property: - yield key, Property(key) - elif isinstance(value, Property): - yield key, value - - @classmethod - def construct(cls, client, node, attribute_map=None, path=None, child=False): - if node is None: - return [], None - - keys_available = [k.lower() for k in node.keys()] - keys_used = [] - - if attribute_map is None: - attribute_map = cls.attribute_map or {} - - require_map = attribute_map.get('*') != '*' - - # Determine path from object "key" - key = cls.helpers.get(node, 'key') - - if key is not None: - path = key[:key.rfind('/')] - - # Construct object - obj = cls(client, path) - - #log.debug('%s - Properties: %s', cls.__name__, list(obj.properties())) - - for key, prop in cls.properties(): - node_key = prop.name or key - - if attribute_map: - if node_key in attribute_map: - node_key = attribute_map.get(node_key) - elif require_map: - setattr(obj, key, None) - continue - - #log.debug('%s - Found property "%s"', cls.__name__, key) - setattr(obj, key, prop.value(client, node_key, node, keys_used)) - - # Post-fill transformation - obj.__transform__() - - # Look for omitted keys - omitted = list(set(keys_available) - set(keys_used)) - omitted.sort() - - if omitted and not child: - log.warn('%s - Omitted attributes: %s', cls.__name__, ', '.join(omitted)) - - return keys_used, obj - - def __transform__(self): - pass - - def __iter__(self): - return self._children or [] - - def __getstate__(self): - data = self.__dict__ - - def build(): - for key, value in data.items(): - if isinstance(value, types.GeneratorType): - value = list(value) - - if key in ['client']: - continue - - yield key, value - - return dict(build()) - - -class DescriptorMixin(Descriptor): - pass diff --git a/libs/plex/objects/core/manager.py b/libs/plex/objects/core/manager.py deleted file mode 100644 index d694926f0..000000000 --- a/libs/plex/objects/core/manager.py +++ /dev/null @@ -1,89 +0,0 @@ -import inspect -import logging -import os - -log = logging.getLogger(__name__) - -UNC_PREFIX = '\\\\?\\' - - -class ObjectManager(object): - base_dir = None - objects_dir = None - objects_map = {} - - ignore_files = [ - '__init__.py' - ] - ignore_paths = [ - 'plex\\objects\\core\\base.py', - 'plex\\objects\\core\\manager.py' - ] - - @classmethod - def discover(cls): - cls.objects_dir = os.path.join(cls.base_dir, 'plex', 'objects') - - # Walk plex/objects directory - for current, directories, files in os.walk(cls.objects_dir): - - # Iterate files, yield valid paths - for filename in files: - if not filename.endswith('.py'): - continue - - # Ensure filename is not in ignore list - if filename in cls.ignore_files: - continue - - path = os.path.join(current, filename) - - # Ensure path is not in ignore list - if not all([not path.endswith(p) for p in cls.ignore_paths]): - continue - - # Remove UNC prefix (if it exists) - if path.startswith(UNC_PREFIX): - path = path[len(UNC_PREFIX):] - - path = os.path.relpath(path, cls.base_dir) - name = os.path.splitext(path)[0].replace(os.path.sep, '.') - - yield path, name - - @classmethod - def load(cls): - for path, name in cls.discover(): - try: - mod = __import__(name, fromlist=['*']) - except Exception as ex: - log.warn('Unable to import "%s" - %s', name, ex) - continue - - # Get classes in module - classes = [ - (key, getattr(mod, key)) for key in dir(mod) - if not key.startswith('_') - ] - - # Filter to module-specific classes - classes = [ - (key, value) for (key, value) in classes - if inspect.isclass(value) and value.__module__ == name - ] - - yield classes - - @classmethod - def construct(cls): - log.debug('Loading descriptors...') - - cls.base_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../', '..')) - - # Load modules, find descriptor classes - for classes in cls.load(): - # Update object map - for key, value in classes: - cls.objects_map[key] = value - - log.debug('Loaded %s descriptors (%s)', len(cls.objects_map), ', '.join(sorted(cls.objects_map.keys()))) diff --git a/libs/plex/objects/detail.py b/libs/plex/objects/detail.py deleted file mode 100644 index f16c2c13d..000000000 --- a/libs/plex/objects/detail.py +++ /dev/null @@ -1,62 +0,0 @@ -from plex.objects.core.base import Descriptor, Property -from plex.objects.container import Container - - -class Detail(Container): - myplex = Property(resolver=lambda: Detail.construct_myplex) - transcoder = Property(resolver=lambda: Detail.construct_transcoder) - - friendly_name = Property('friendlyName') - - machine_identifier = Property('machineIdentifier') - version = Property - - platform = Property - platform_version = Property('platformVersion') - - allow_camera_upload = Property('allowCameraUpload', [int, bool]) - allow_channel_access = Property('allowChannelAccess', [int, bool]) - allow_sync = Property('allowSync', [int, bool]) - - certificate = Property(type=[int, bool]) - multiuser = Property(type=[int, bool]) - sync = Property(type=[int, bool]) - - start_state = Property('startState') - - silverlight = Property('silverlightInstalled', [int, bool]) - soundflower = Property('soundflowerInstalled', [int, bool]) - flash = Property('flashInstalled', [int, bool]) - webkit = Property(type=[int, bool]) - - cookie_parameters = Property('requestParametersInCookie', [int, bool]) - - @staticmethod - def construct_myplex(client, node): - return MyPlexDetail.construct(client, node, child=True) - - @staticmethod - def construct_transcoder(client, node): - return TranscoderDetail.construct(client, node, child=True) - - -class MyPlexDetail(Descriptor): - enabled = Property('myPlex', type=bool) - - username = Property('myPlexUsername') - - mapping_state = Property('myPlexMappingState') - signin_state = Property('myPlexSigninState') - - subscription = Property('myPlexSubscription', [int, bool]) - - -class TranscoderDetail(Descriptor): - audio = Property('transcoderAudio', [int, bool]) - video = Property('transcoderVideo', [int, bool]) - - video_bitrates = Property('transcoderVideoBitrates') - video_qualities = Property('transcoderVideoQualities') - video_resolutions = Property('transcoderVideoResolutions') - - active_video_sessions = Property('transcoderActiveVideoSessions', int) diff --git a/libs/plex/objects/directory.py b/libs/plex/objects/directory.py deleted file mode 100644 index 27ebb75aa..000000000 --- a/libs/plex/objects/directory.py +++ /dev/null @@ -1,16 +0,0 @@ -from plex.objects.core.base import Descriptor, Property - - -class Directory(Descriptor): - key = Property - type = Property - - title = Property - - size = Property - - art = Property - thumb = Property - - allow_sync = Property('allowSync', bool) - updated_at = Property('updatedAt', int) diff --git a/libs/plex/objects/library/__init__.py b/libs/plex/objects/library/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/libs/plex/objects/library/container.py b/libs/plex/objects/library/container.py deleted file mode 100644 index c7a7a676d..000000000 --- a/libs/plex/objects/library/container.py +++ /dev/null @@ -1,80 +0,0 @@ -from plex.core.helpers import flatten, to_iterable -from plex.objects.core.base import Property -from plex.objects.container import Container -from plex.objects.library.section import Section - - -class MediaContainer(Container): - section = Property(resolver=lambda: MediaContainer.construct_section) - - title1 = Property - title2 = Property - - identifier = Property - - art = Property - thumb = Property - - view_group = Property('viewGroup') - view_mode = Property('viewMode', int) - - media_tag_prefix = Property('mediaTagPrefix') - media_tag_version = Property('mediaTagVersion') - - size = Property('size', int) - total_size = Property('totalSize', int) - - allow_sync = Property('allowSync', bool) - mixed_parents = Property('mixedParents', bool) - no_cache = Property('nocache', bool) - sort_asc = Property('sortAsc', bool) - - @staticmethod - def construct_section(client, node): - attribute_map = { - 'key': 'librarySectionID', - 'uuid': 'librarySectionUUID', - 'title': 'librarySectionTitle' - } - - return Section.construct(client, node, attribute_map, child=True) - - def __iter__(self): - for item in super(MediaContainer, self).__iter__(): - item.section = self.section - - yield item - - -class ChildrenContainer(MediaContainer): - pass - - -class LeavesContainer(MediaContainer): - pass - - -class SectionContainer(MediaContainer): - filter_passes = lambda _, allowed, value: allowed is None or value in allowed - - def filter(self, types=None, keys=None, titles=None): - types = to_iterable(types) - keys = to_iterable(keys) - - titles = to_iterable(titles) - - if titles: - # Flatten titles - titles = [flatten(x) for x in titles] - - for section in self: - if not self.filter_passes(types, section.type): - continue - - if not self.filter_passes(keys, section.key): - continue - - if not self.filter_passes(titles, flatten(section.title)): - continue - - yield section diff --git a/libs/plex/objects/library/extra/__init__.py b/libs/plex/objects/library/extra/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/libs/plex/objects/library/extra/country.py b/libs/plex/objects/library/extra/country.py deleted file mode 100644 index 560a290bd..000000000 --- a/libs/plex/objects/library/extra/country.py +++ /dev/null @@ -1,10 +0,0 @@ -from plex.objects.core.base import Descriptor, Property - - -class Country(Descriptor): - id = Property(type=int) - tag = Property - - @classmethod - def from_node(cls, client, node): - return cls.construct(client, cls.helpers.find(node, 'Country'), child=True) diff --git a/libs/plex/objects/library/extra/director.py b/libs/plex/objects/library/extra/director.py deleted file mode 100644 index ea01fd564..000000000 --- a/libs/plex/objects/library/extra/director.py +++ /dev/null @@ -1,10 +0,0 @@ -from plex.objects.core.base import Descriptor, Property - - -class Director(Descriptor): - id = Property(type=int) - tag = Property - - @classmethod - def from_node(cls, client, node): - return cls.construct(client, cls.helpers.find(node, 'Director'), child=True) diff --git a/libs/plex/objects/library/extra/genre.py b/libs/plex/objects/library/extra/genre.py deleted file mode 100644 index 37dcf716a..000000000 --- a/libs/plex/objects/library/extra/genre.py +++ /dev/null @@ -1,17 +0,0 @@ -from plex.objects.core.base import Descriptor, Property - - -class Genre(Descriptor): - id = Property(type=int) - tag = Property - - @classmethod - def from_node(cls, client, node): - items = [] - - for genre in cls.helpers.findall(node, 'Genre'): - _, obj = Genre.construct(client, genre, child=True) - - items.append(obj) - - return [], items diff --git a/libs/plex/objects/library/extra/role.py b/libs/plex/objects/library/extra/role.py deleted file mode 100644 index 0dc951f7c..000000000 --- a/libs/plex/objects/library/extra/role.py +++ /dev/null @@ -1,20 +0,0 @@ -from plex.objects.core.base import Descriptor, Property - - -class Role(Descriptor): - id = Property(type=int) - tag = Property - - role = Property - thumb = Property - - @classmethod - def from_node(cls, client, node): - items = [] - - for genre in cls.helpers.findall(node, 'Role'): - _, obj = Role.construct(client, genre, child=True) - - items.append(obj) - - return [], items diff --git a/libs/plex/objects/library/extra/writer.py b/libs/plex/objects/library/extra/writer.py deleted file mode 100644 index 6877aa33f..000000000 --- a/libs/plex/objects/library/extra/writer.py +++ /dev/null @@ -1,17 +0,0 @@ -from plex.objects.core.base import Descriptor, Property - - -class Writer(Descriptor): - id = Property(type=int) - tag = Property - - @classmethod - def from_node(cls, client, node): - items = [] - - for genre in cls.helpers.findall(node, 'Writer'): - _, obj = Writer.construct(client, genre, child=True) - - items.append(obj) - - return [], items diff --git a/libs/plex/objects/library/location.py b/libs/plex/objects/library/location.py deleted file mode 100644 index 6316fd8dc..000000000 --- a/libs/plex/objects/library/location.py +++ /dev/null @@ -1,6 +0,0 @@ -from plex.objects.core.base import Descriptor, Property - - -class Location(Descriptor): - id = Property - path = Property diff --git a/libs/plex/objects/library/media.py b/libs/plex/objects/library/media.py deleted file mode 100644 index 3749ad849..000000000 --- a/libs/plex/objects/library/media.py +++ /dev/null @@ -1,39 +0,0 @@ -from plex.objects.core.base import Descriptor, Property -from plex.objects.library.part import Part - - -class Media(Descriptor): - parts = Property(resolver=lambda: Part.from_node) - - id = Property(type=int) - - video_codec = Property('videoCodec') - video_frame_rate = Property('videoFrameRate') - video_resolution = Property('videoResolution') - - audio_channels = Property('audioChannels', type=int) - audio_codec = Property('audioCodec') - - container = Property - - width = Property(type=int) - height = Property(type=int) - - aspect_ratio = Property('aspectRatio', type=float) - bitrate = Property(type=int) - duration = Property(type=int) - - #@classmethod - #def from_node(cls, client, node): - # return cls.construct(client, cls.helpers.find(node, 'Media'), child=True) - - @classmethod - def from_node(cls, client, node): - items = [] - - for genre in cls.helpers.findall(node, 'Media'): - _, obj = Media.construct(client, genre, child=True) - - items.append(obj) - - return [], items diff --git a/libs/plex/objects/library/metadata/__init__.py b/libs/plex/objects/library/metadata/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/libs/plex/objects/library/metadata/album.py b/libs/plex/objects/library/metadata/album.py deleted file mode 100644 index db0651010..000000000 --- a/libs/plex/objects/library/metadata/album.py +++ /dev/null @@ -1,68 +0,0 @@ -from plex.objects.core.base import Property -from plex.objects.directory import Directory -from plex.objects.library.container import ChildrenContainer -from plex.objects.library.extra.genre import Genre -from plex.objects.library.metadata.base import Metadata -from plex.objects.library.metadata.artist import Artist -from plex.objects.mixins.rate import RateMixin - - -class Album(Directory, Metadata, RateMixin): - artist = Property(resolver=lambda: Album.construct_artist) - genres = Property(resolver=lambda: Genre.from_node) - - index = Property(type=int) - - year = Property(type=int) - originally_available_at = Property('originallyAvailableAt') - - track_count = Property('leafCount', int) - viewed_track_count = Property('viewedLeafCount', int) - - def children(self): - return self.client['library/metadata'].children(self.rating_key) - - @staticmethod - def construct_artist(client, node): - attribute_map = { - 'key': 'parentKey', - 'ratingKey': 'parentRatingKey', - - 'title': 'parentTitle', - 'thumb': 'parentThumb' - } - - return Artist.construct(client, node, attribute_map, child=True) - - -class AlbumChildrenContainer(ChildrenContainer): - artist = Property(resolver=lambda: AlbumChildrenContainer.construct_artist) - album = Property(resolver=lambda: AlbumChildrenContainer.construct_album) - - key = Property - - @staticmethod - def construct_artist(client, node): - attribute_map = { - 'title': 'grandparentTitle' - } - - return Artist.construct(client, node, attribute_map, child=True) - - @staticmethod - def construct_album(client, node): - attribute_map = { - 'index': 'parentIndex', - - 'title': 'parentTitle', - 'year' : 'parentYear' - } - - return Album.construct(client, node, attribute_map, child=True) - - def __iter__(self): - for item in super(ChildrenContainer, self).__iter__(): - item.artist = self.artist - item.album = self.album - - yield item diff --git a/libs/plex/objects/library/metadata/artist.py b/libs/plex/objects/library/metadata/artist.py deleted file mode 100644 index e7ffbe5ae..000000000 --- a/libs/plex/objects/library/metadata/artist.py +++ /dev/null @@ -1,58 +0,0 @@ -from plex.objects.core.base import Property -from plex.objects.directory import Directory -from plex.objects.library.container import LeavesContainer, ChildrenContainer -from plex.objects.library.metadata.base import Metadata -from plex.objects.mixins.rate import RateMixin - - -class Artist(Directory, Metadata, RateMixin): - index = Property(type=int) - - def all_leaves(self): - return self.client['library/metadata'].all_leaves(self.rating_key) - - def children(self): - return self.client['library/metadata'].children(self.rating_key) - - -class ArtistChildrenContainer(ChildrenContainer): - artist = Property(resolver=lambda: ArtistChildrenContainer.construct_artist) - - key = Property - summary = Property - - @staticmethod - def construct_artist(client, node): - attribute_map = { - 'index': 'parentIndex', - 'title': 'parentTitle' - } - - return Artist.construct(client, node, attribute_map, child=True) - - def __iter__(self): - for item in super(ChildrenContainer, self).__iter__(): - item.artist = self.artist - - yield item - - -class ArtistLeavesContainer(LeavesContainer): - artist = Property(resolver=lambda: ArtistLeavesContainer.construct_artist) - - key = Property - - @staticmethod - def construct_artist(client, node): - attribute_map = { - 'index': 'parentIndex', - 'title': 'parentTitle' - } - - return Artist.construct(client, node, attribute_map, child=True) - - def __iter__(self): - for item in super(LeavesContainer, self).__iter__(): - item.artist = self.artist - - yield item diff --git a/libs/plex/objects/library/metadata/base.py b/libs/plex/objects/library/metadata/base.py deleted file mode 100644 index 71fcb167a..000000000 --- a/libs/plex/objects/library/metadata/base.py +++ /dev/null @@ -1,38 +0,0 @@ -from plex.objects.core.base import Descriptor, Property -from plex.objects.library.section import Section - - -class Metadata(Descriptor): - section = Property(resolver=lambda: Metadata.construct_section) - - # somehow section doesn't resolve on onDeck, add key manually - section_key = Property('librarySectionID') - - key = Property - guid = Property - rating_key = Property('ratingKey') - extra_key = Property('primaryExtraKey') - - title = Property - title_sort = Property('titleSort') - title_original = Property('originalTitle') - - summary = Property - - thumb = Property - - source_title = Property('sourceTitle') - - added_at = Property('addedAt', int) - last_viewed_at = Property('lastViewedAt', int) - - @staticmethod - def construct_section(client, node): - attribute_map = { - 'key': 'librarySectionID', - 'uuid': 'librarySectionUUID', - 'title': 'librarySectionTitle' - } - - return Section.construct(client, node, attribute_map, child=True) - diff --git a/libs/plex/objects/library/metadata/clip.py b/libs/plex/objects/library/metadata/clip.py deleted file mode 100644 index 8e94dbdb3..000000000 --- a/libs/plex/objects/library/metadata/clip.py +++ /dev/null @@ -1,9 +0,0 @@ -from plex.objects.core.base import Property -from plex.objects.library.metadata.base import Metadata -from plex.objects.library.video import Video - - -class Clip(Video, Metadata): - extra_type = Property('extraType', type=int) - - index = Property(type=int) diff --git a/libs/plex/objects/library/metadata/episode.py b/libs/plex/objects/library/metadata/episode.py deleted file mode 100644 index e7cce4688..000000000 --- a/libs/plex/objects/library/metadata/episode.py +++ /dev/null @@ -1,48 +0,0 @@ -from plex.objects.core.base import Property -from plex.objects.library.metadata.season import Season -from plex.objects.library.metadata.show import Show -from plex.objects.library.metadata.base import Metadata -from plex.objects.library.video import Video -from plex.objects.mixins.rate import RateMixin -from plex.objects.mixins.scrobble import ScrobbleMixin - - -class Episode(Video, Metadata, RateMixin, ScrobbleMixin): - show = Property(resolver=lambda: Episode.construct_show) - season = Property(resolver=lambda: Episode.construct_season) - - index = Property(type=int) - - studio = Property - audience_rating = Property('audienceRating', float) - content_rating = Property('contentRating') - - year = Property(type=int) - originally_available_at = Property('originallyAvailableAt') - - @staticmethod - def construct_show(client, node): - attribute_map = { - 'key': 'grandparentKey', - 'ratingKey': 'grandparentRatingKey', - - 'title': 'grandparentTitle', - - 'art': 'grandparentArt', - 'theme': 'grandparentTheme', - 'thumb': 'grandparentThumb' - } - - return Show.construct(client, node, attribute_map, child=True) - - @staticmethod - def construct_season(client, node): - attribute_map = { - 'index': 'parentIndex', - 'key': 'parentKey', - 'ratingKey': 'parentRatingKey', - - 'thumb': 'parentThumb' - } - - return Season.construct(client, node, attribute_map, child=True) diff --git a/libs/plex/objects/library/metadata/movie.py b/libs/plex/objects/library/metadata/movie.py deleted file mode 100644 index fb807f243..000000000 --- a/libs/plex/objects/library/metadata/movie.py +++ /dev/null @@ -1,22 +0,0 @@ -from plex.objects.core.base import Property -from plex.objects.library.extra.country import Country -from plex.objects.library.extra.genre import Genre -from plex.objects.library.extra.role import Role -from plex.objects.library.metadata.base import Metadata -from plex.objects.library.video import Video -from plex.objects.mixins.rate import RateMixin -from plex.objects.mixins.scrobble import ScrobbleMixin - - -class Movie(Video, Metadata, RateMixin, ScrobbleMixin): - country = Property(resolver=lambda: Country.from_node) - genres = Property(resolver=lambda: Genre.from_node) - roles = Property(resolver=lambda: Role.from_node) - - studio = Property - content_rating = Property('contentRating') - - year = Property(type=int) - originally_available_at = Property('originallyAvailableAt') - - tagline = Property diff --git a/libs/plex/objects/library/metadata/season.py b/libs/plex/objects/library/metadata/season.py deleted file mode 100644 index f309027ea..000000000 --- a/libs/plex/objects/library/metadata/season.py +++ /dev/null @@ -1,78 +0,0 @@ -from plex.objects.core.base import Property -from plex.objects.library.container import ChildrenContainer -from plex.objects.library.metadata.show import Show -from plex.objects.library.metadata.base import Metadata -from plex.objects.library.video import Directory - - -class Season(Directory, Metadata): - show = Property(resolver=lambda: Season.construct_show) - - index = Property(type=int) - - banner = Property - theme = Property - - year = Property(type=int) - - episode_count = Property('leafCount', int) - viewed_episode_count = Property('viewedLeafCount', int) - - view_count = Property('viewCount', type=int) - - def children(self): - return self.client['library/metadata'].children(self.rating_key) - - @staticmethod - def construct_show(client, node): - attribute_map = { - 'index' : 'parentIndex', - 'key' : 'parentKey', - 'ratingKey': 'parentRatingKey', - - 'title' : 'parentTitle', - 'summary' : 'parentSummary', - 'thumb' : 'parentThumb', - - 'theme' : 'parentTheme' - } - - return Show.construct(client, node, attribute_map, child=True) - - -class SeasonChildrenContainer(ChildrenContainer): - show = Property(resolver=lambda: SeasonChildrenContainer.construct_show) - season = Property(resolver=lambda: SeasonChildrenContainer.construct_season) - - key = Property - - banner = Property - theme = Property - - @staticmethod - def construct_show(client, node): - attribute_map = { - 'title' : 'grandparentTitle', - - 'contentRating': 'grandparentContentRating', - 'studio' : 'grandparentStudio', - 'theme' : 'grandparentTheme' - } - - return Show.construct(client, node, attribute_map, child=True) - - @staticmethod - def construct_season(client, node): - attribute_map = { - 'index': 'parentIndex', - 'title': 'parentTitle' - } - - return Season.construct(client, node, attribute_map, child=True) - - def __iter__(self): - for item in super(ChildrenContainer, self).__iter__(): - item.show = self.show - item.season = self.season - - yield item diff --git a/libs/plex/objects/library/metadata/show.py b/libs/plex/objects/library/metadata/show.py deleted file mode 100644 index 39640583a..000000000 --- a/libs/plex/objects/library/metadata/show.py +++ /dev/null @@ -1,85 +0,0 @@ -from plex.objects.core.base import Property -from plex.objects.directory import Directory -from plex.objects.library.container import LeavesContainer, ChildrenContainer -from plex.objects.library.metadata.base import Metadata -from plex.objects.mixins.rate import RateMixin - - -class Show(Directory, Metadata, RateMixin): - index = Property(type=int) - duration = Property(type=int) - - studio = Property - content_rating = Property('contentRating') - - banner = Property - theme = Property - - year = Property(type=int) - originally_available_at = Property('originallyAvailableAt') - - season_count = Property('childCount', int) - - episode_count = Property('leafCount', int) - viewed_episode_count = Property('viewedLeafCount', int) - - view_count = Property('viewCount', int) - - def all_leaves(self): - return self.client['library/metadata'].all_leaves(self.rating_key) - - def children(self): - return self.client['library/metadata'].children(self.rating_key) - - -class ShowChildrenContainer(ChildrenContainer): - show = Property(resolver=lambda: ShowLeavesContainer.construct_show) - - key = Property - summary = Property - - banner = Property - theme = Property - - @staticmethod - def construct_show(client, node): - attribute_map = { - 'index': 'parentIndex', - - 'title': 'parentTitle', - 'year' : 'parentYear' - } - - return Show.construct(client, node, attribute_map, child=True) - - def __iter__(self): - for item in super(ChildrenContainer, self).__iter__(): - item.show = self.show - - yield item - - -class ShowLeavesContainer(LeavesContainer): - show = Property(resolver=lambda: ShowLeavesContainer.construct_show) - - key = Property - - banner = Property - theme = Property - - @staticmethod - def construct_show(client, node): - attribute_map = { - 'index': 'parentIndex', - - 'title': 'parentTitle', - 'year' : 'parentYear' - } - - return Show.construct(client, node, attribute_map, child=True) - - def __iter__(self): - for item in super(LeavesContainer, self).__iter__(): - item.show = self.show - - yield item diff --git a/libs/plex/objects/library/metadata/track.py b/libs/plex/objects/library/metadata/track.py deleted file mode 100644 index 8d380375f..000000000 --- a/libs/plex/objects/library/metadata/track.py +++ /dev/null @@ -1,47 +0,0 @@ -from plex.objects.core.base import Property -from plex.objects.directory import Directory -from plex.objects.library.metadata.album import Album -from plex.objects.library.metadata.artist import Artist -from plex.objects.library.metadata.base import Metadata -from plex.objects.mixins.scrobble import ScrobbleMixin -from plex.objects.mixins.session import SessionMixin - - -class Track(Directory, Metadata, SessionMixin, ScrobbleMixin): - artist = Property(resolver=lambda: Track.construct_artist) - album = Property(resolver=lambda: Track.construct_album) - - index = Property(type=int) - - view_count = Property('viewCount', type=int) - view_offset = Property('viewOffset', type=int) - - duration = Property(type=int) - - @staticmethod - def construct_artist(client, node): - attribute_map = { - 'key': 'grandparentKey', - 'ratingKey': 'grandparentRatingKey', - - 'title': 'grandparentTitle', - - 'thumb': 'grandparentThumb' - } - - return Artist.construct(client, node, attribute_map, child=True) - - @staticmethod - def construct_album(client, node): - attribute_map = { - 'index': 'parentIndex', - 'key': 'parentKey', - 'ratingKey': 'parentRatingKey', - - 'title': 'parentTitle', - 'year': 'parentYear', - - 'thumb': 'parentThumb' - } - - return Album.construct(client, node, attribute_map, child=True) diff --git a/libs/plex/objects/library/part.py b/libs/plex/objects/library/part.py deleted file mode 100644 index fe1fc058a..000000000 --- a/libs/plex/objects/library/part.py +++ /dev/null @@ -1,26 +0,0 @@ -from plex.objects.core.base import Descriptor, Property -from plex.objects.library.stream import Stream - - -class Part(Descriptor): - streams = Property(resolver=lambda: Stream.from_node) - - id = Property(type=int) - key = Property - - file = Property - container = Property - - duration = Property(type=int) - size = Property(type=int) - - @classmethod - def from_node(cls, client, node): - items = [] - - for genre in cls.helpers.findall(node, 'Part'): - _, obj = Part.construct(client, genre, child=True) - - items.append(obj) - - return [], items diff --git a/libs/plex/objects/library/section.py b/libs/plex/objects/library/section.py deleted file mode 100644 index 7339ff0e3..000000000 --- a/libs/plex/objects/library/section.py +++ /dev/null @@ -1,37 +0,0 @@ -from plex.core.idict import idict -from plex.objects.core.base import Property -from plex.objects.directory import Directory - - -class Section(Directory): - uuid = Property - - filters = Property(type=bool) - refreshing = Property(type=bool) - - agent = Property - scanner = Property - language = Property - - composite = Property - type = Property - - created_at = Property('createdAt', int) - - def __transform__(self): - self.path = '/library/sections/%s' % self.key - - def all(self): - response = self.http.get('all') - - return self.parse(response, idict({ - 'MediaContainer': ('MediaContainer', idict({ - 'Directory': { - 'artist': 'Artist', - 'show': 'Show' - }, - 'Video': { - 'movie': 'Movie' - } - })) - })) diff --git a/libs/plex/objects/library/stream.py b/libs/plex/objects/library/stream.py deleted file mode 100644 index 1acd55818..000000000 --- a/libs/plex/objects/library/stream.py +++ /dev/null @@ -1,56 +0,0 @@ -from plex.objects.core.base import Descriptor, Property - - -class Stream(Descriptor): - id = Property(type=int) - index = Property(type=int) - - stream_key = Property('key') - - stream_type = Property('streamType', type=int) - selected = Property(type=bool) - - forced = Property(type=bool) - default = Property(type=bool) - - title = Property - duration = Property(type=int) - - codec = Property - codec_id = Property('codecID') - - bit_depth = Property('bitDepth', type=int) - chroma_subsampling = Property('chromaSubsampling') - color_space = Property('colorSpace') - - width = Property(type=int) - height = Property(type=int) - - bitrate = Property(type=int) - bitrate_mode = Property('bitrateMode') - - channels = Property(type=int) - sampling_rate = Property('samplingRate', type=int) - - frame_rate = Property('frameRate') - profile = Property - scan_type = Property('scanType') - - language = Property('language') - language_code = Property('languageCode') - - bvop = Property(type=int) - gmc = Property(type=int) - level = Property(type=int) - qpel = Property(type=int) - - @classmethod - def from_node(cls, client, node): - items = [] - - for genre in cls.helpers.findall(node, 'Stream'): - _, obj = Stream.construct(client, genre, child=True) - - items.append(obj) - - return [], items diff --git a/libs/plex/objects/library/video.py b/libs/plex/objects/library/video.py deleted file mode 100644 index 123375364..000000000 --- a/libs/plex/objects/library/video.py +++ /dev/null @@ -1,22 +0,0 @@ -from plex.objects.core.base import Property -from plex.objects.directory import Directory -from plex.objects.library.extra.director import Director -from plex.objects.library.extra.writer import Writer -from plex.objects.library.media import Media -from plex.objects.mixins.session import SessionMixin - - -class Video(Directory, SessionMixin): - director = Property(resolver=lambda: Director.from_node) - media = Property(resolver=lambda: Media.from_node) - writers = Property(resolver=lambda: Writer.from_node) - - view_count = Property('viewCount', type=int) - view_offset = Property('viewOffset', type=int) - - chapter_source = Property('chapterSource') - duration = Property(type=int) - - @property - def seen(self): - return self.view_count and self.view_count >= 1 diff --git a/libs/plex/objects/mixins/__init__.py b/libs/plex/objects/mixins/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/libs/plex/objects/mixins/rate.py b/libs/plex/objects/mixins/rate.py deleted file mode 100644 index b14ecb2a5..000000000 --- a/libs/plex/objects/mixins/rate.py +++ /dev/null @@ -1,10 +0,0 @@ -from plex import Plex -from plex.objects.core.base import Property, DescriptorMixin - - -class RateMixin(DescriptorMixin): - rating = Property(type=float) - user_rating = Property('userRating', type=float) - - def rate(self, value): - return Plex['library'].rate(self.rating_key, value) diff --git a/libs/plex/objects/mixins/scrobble.py b/libs/plex/objects/mixins/scrobble.py deleted file mode 100644 index bfdfad118..000000000 --- a/libs/plex/objects/mixins/scrobble.py +++ /dev/null @@ -1,7 +0,0 @@ -from plex import Plex -from plex.objects.core.base import DescriptorMixin - - -class ScrobbleMixin(DescriptorMixin): - def scrobble(self): - return Plex['library'].scrobble(self.rating_key) diff --git a/libs/plex/objects/mixins/session.py b/libs/plex/objects/mixins/session.py deleted file mode 100644 index 9f2038d6e..000000000 --- a/libs/plex/objects/mixins/session.py +++ /dev/null @@ -1,32 +0,0 @@ -from plex.objects.core.base import Descriptor, Property, DescriptorMixin -from plex.objects.player import Player -from plex.objects.transcode_session import TranscodeSession -from plex.objects.user import User - - -class SessionMixin(DescriptorMixin): - session = Property(resolver=lambda: SessionMixin.construct_session) - - @staticmethod - def construct_session(client, node): - return Session.construct(client, node, child=True) - - -class Session(Descriptor): - key = Property('sessionKey', int) - - user = Property(resolver=lambda: Session.construct_user) - player = Property(resolver=lambda: Session.construct_player) - transcode_session = Property(resolver=lambda: Session.construct_transcode_session) - - @classmethod - def construct_user(cls, client, node): - return User.construct(client, cls.helpers.find(node, 'User'), child=True) - - @classmethod - def construct_player(cls, client, node): - return Player.construct(client, cls.helpers.find(node, 'Player'), child=True) - - @classmethod - def construct_transcode_session(cls, client, node): - return TranscodeSession.construct(client, cls.helpers.find(node, 'TranscodeSession'), child=True) diff --git a/libs/plex/objects/player.py b/libs/plex/objects/player.py deleted file mode 100644 index 891ccb6e0..000000000 --- a/libs/plex/objects/player.py +++ /dev/null @@ -1,11 +0,0 @@ -from plex.objects.core.base import Descriptor, Property - - -class Player(Descriptor): - title = Property - machine_identifier = Property('machineIdentifier') - - state = Property - - platform = Property - product = Property diff --git a/libs/plex/objects/server.py b/libs/plex/objects/server.py deleted file mode 100644 index 0e8dda99f..000000000 --- a/libs/plex/objects/server.py +++ /dev/null @@ -1,12 +0,0 @@ -from plex.objects.core.base import Descriptor, Property - - -class Server(Descriptor): - name = Property - host = Property - - address = Property - port = Property(type=int) - - machine_identifier = Property('machineIdentifier') - version = Property diff --git a/libs/plex/objects/session.py b/libs/plex/objects/session.py deleted file mode 100644 index af6a7a5af..000000000 --- a/libs/plex/objects/session.py +++ /dev/null @@ -1,21 +0,0 @@ -from plex.core.helpers import to_iterable -from plex.objects.library.container import MediaContainer - - -class SessionContainer(MediaContainer): - filter_passes = lambda _, allowed, value: allowed is None or value in allowed - - def filter(self, keys=None): - keys = to_iterable(keys) - - for item in self: - if not self.filter_passes(keys, item.session.key): - continue - - yield item - - def get(self, key): - for item in self.filter(key): - return item - - return None diff --git a/libs/plex/objects/setting.py b/libs/plex/objects/setting.py deleted file mode 100644 index a2b7cc46b..000000000 --- a/libs/plex/objects/setting.py +++ /dev/null @@ -1,53 +0,0 @@ -from plex.objects.core.base import Descriptor, Property - - -class Setting(Descriptor): - id = Property - - label = Property - summary = Property - - type = Property - group = Property - - value = Property(resolver=lambda: Setting.parse_value) - default = Property(resolver=lambda: Setting.parse_default) - options = Property('enumValues', resolver=lambda: Setting.parse_options) - - hidden = Property(type=[int, bool]) - advanced = Property(type=[int, bool]) - - @classmethod - def parse_value(cls, client, node): - type = cls.helpers.get(node, 'type') - value = cls.helpers.get(node, 'value') - - return ['value'], Setting.convert(type, value) - - @classmethod - def parse_default(cls, client, node): - type = cls.helpers.get(node, 'type') - default = cls.helpers.get(node, 'default') - - return ['default'], Setting.convert(type, default) - - @classmethod - def parse_options(cls, client, node): - value = cls.helpers.get(node, 'enumValues') - - if not value: - return [], None - - return ['enumValues'], [ - tuple(option.split(':', 2)) for option in value.split('|') - ] - - @staticmethod - def convert(type, value): - if type == 'bool': - value = value.lower() - value = value == 'true' - elif type == 'int': - value = int(value) - - return value diff --git a/libs/plex/objects/transcode_session.py b/libs/plex/objects/transcode_session.py deleted file mode 100644 index c3ea4a5ab..000000000 --- a/libs/plex/objects/transcode_session.py +++ /dev/null @@ -1,24 +0,0 @@ -from plex.objects.core.base import Descriptor, Property - - -class TranscodeSession(Descriptor): - key = Property - - progress = Property(type=float) - speed = Property(type=float) - duration = Property(type=int) - - protocol = Property - throttled = Property(type=int) # TODO this needs to cast: str -> int -> bool - - container = Property('container') - - video_codec = Property('videoCodec') - video_decision = Property('videoDecision') - - audio_codec = Property('audioCodec') - audio_channels = Property('audioChannels', int) - audio_decision = Property('audioDecision') - - width = Property(type=int) - height = Property(type=int) diff --git a/libs/plex/objects/user.py b/libs/plex/objects/user.py deleted file mode 100644 index 5718bd8fb..000000000 --- a/libs/plex/objects/user.py +++ /dev/null @@ -1,8 +0,0 @@ -from plex.objects.core.base import Descriptor, Property - - -class User(Descriptor): - id = Property(type=int) - - title = Property - thumb = Property diff --git a/libs/plex/request.py b/libs/plex/request.py deleted file mode 100644 index 5eb24361d..000000000 --- a/libs/plex/request.py +++ /dev/null @@ -1,121 +0,0 @@ -from plex.lib.six.moves.urllib_parse import urlencode - -from requests import Request -import json - - -class PlexRequest(object): - def __init__(self, client, **kwargs): - self.client = client - self.kwargs = kwargs - - self.request = None - - # Parsed Attributes - self.path = None - self.params = None - - self.data = None - self.headers = None - self.method = None - - def prepare(self): - self.request = Request() - - self.transform_parameters() - self.request.url = self.construct_url() - - self.request.data = self.transform_data() - self.request.headers = self.transform_headers() - self.request.method = self.transform_method() - - return self.request.prepare() - - def construct_url(self): - """Construct a full plex request URI, with `params`.""" - path = [self.path] - path.extend([str(x) for x in self.params]) - - url = self.client.base_url + '/'.join(x for x in path if x) - query = self.kwargs.get('query') - - if query: - # Dict -> List - if type(query) is dict: - query = query.items() - - # Remove items with `None` value - query = [ - (k, v) for (k, v) in query - if v is not None - ] - - # Encode query, append to URL - url += '?' + urlencode(query) - - return url - - def transform_parameters(self): - # Transform `path` - self.path = self.kwargs.get('path') - - if not self.path.startswith('/'): - self.path = '/' + self.path - - if self.path.endswith('/'): - self.path = self.path[:-1] - - # Transform `params` into list - self.params = self.kwargs.get('params') or [] - - if type(self.params) is not list: - self.params = [self.params] - - def transform_data(self): - self.data = self.kwargs.get('data') - - if self.data is None: - return None - - return json.dumps(self.data) - - def transform_headers(self): - self.headers = self.kwargs.get('headers') or {} - - # Authentication - self.headers['X-Plex-Token'] = self.client.configuration['authentication.token'] - - # Client - self.headers['X-Plex-Client-Identifier'] = self.client.configuration['client.identifier'] - - self.headers['X-Plex-Product'] = self.client.configuration['client.product'] - self.headers['X-Plex-Version'] = self.client.configuration['client.version'] - - # Device - self.headers['X-Device'] = self.client.configuration['device.system'] - self.headers['X-Device-Name'] = self.client.configuration['device.name'] - - # Platform - self.headers['X-Platform'] = self.client.configuration['platform.name'] - self.headers['X-Platform-Version'] = self.client.configuration['platform.version'] - - # Update with extra headers from configuration - c_headers = self.client.configuration['headers'] - - if c_headers: - self.headers.update(c_headers) - - # Only return headers with valid values - return dict([ - (k, v) for (k, v) in self.headers.items() - if v is not None - ]) - - def transform_method(self): - self.method = self.kwargs.get('method') - - # Pick `method` (if not provided) - if not self.method: - self.method = 'POST' if self.data else 'GET' - - return self.method diff --git a/libs/plex/serializer.py b/libs/plex/serializer.py deleted file mode 100644 index 4e58e0acc..000000000 --- a/libs/plex/serializer.py +++ /dev/null @@ -1,17 +0,0 @@ -import jsonpickle - - -class Serializer(object): - @classmethod - def encode(cls, value): - return jsonpickle.encode(value) - - @classmethod - def decode(cls, value, client=None): - try: - result = jsonpickle.decode(value) - result.client = client - - return result - except: - return None