From 5e77480611ad11eca6872dc943982de2b5a8decb Mon Sep 17 00:00:00 2001 From: Adrien Brault Date: Tue, 29 Sep 2015 20:14:30 +0200 Subject: [PATCH] Get devices and implement play/navigation control --- plexapi/myplex.py | 97 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/plexapi/myplex.py b/plexapi/myplex.py index cdef2c19..7f7eb3ae 100644 --- a/plexapi/myplex.py +++ b/plexapi/myplex.py @@ -2,7 +2,7 @@ PlexAPI MyPlex """ import plexapi, requests -from plexapi import TIMEOUT, log +from plexapi import TIMEOUT, log, utils from plexapi.exceptions import BadRequest, NotFound, Unauthorized from plexapi.utils import cast, toDatetime from requests.status_codes import _codes as codes @@ -37,6 +37,9 @@ class MyPlexUser: """ return _findResource(self.resources(), search, port) + def devices(self): + return MyPlexDevice.fetch_resources(self.authenticationToken) + @classmethod def signin(cls, username, password): if 'X-Plex-Token' in plexapi.BASE_HEADERS: @@ -178,3 +181,95 @@ def _findResource(resources, search, port=32400): return server log.info('Unable to find server: %s', search) raise NotFound('Unable to find server: %s' % search) + +class MyPlexDevice: + DEVICES = 'https://plex.tv/devices.xml' + + def __init__(self, data): + self.name = data.attrib.get('name') + self.publicAddress = data.attrib.get('publicAddress') + self.product = data.attrib.get('product') + self.productVersion = data.attrib.get('productVersion') + self.platform = data.attrib.get('platform') + self.platformVersion = data.attrib.get('platformVersion') + self.device = data.attrib.get('device') + self.model = data.attrib.get('model') + self.vendor = data.attrib.get('vendor') + self.provides = data.attrib.get('provides').split(',') + self.clientIdentifier = data.attrib.get('clientIdentifier') + self.version = data.attrib.get('version') + self.id = data.attrib.get('id') + self.token = data.attrib.get('token') + self.screenResolution = data.attrib.get('screenResolution') + self.screenDensity = data.attrib.get('screenDensity') + self.connectionsUris = [ connection.attrib.get('uri') for connection in data.iter('Connection') ] + + def __repr__(self): + return '<%s:%s:%s>' % (self.__class__.__name__, self.name.encode('utf8'), self.product.encode('utf8')) + + @property + def isReachable(self): + return len(self.connectionsUris) + + @property + def baseUrl(self): + if not self.isReachable: + raise Exception('This device is not reachable') + return self.connectionsUris[0] + + @classmethod + def fetch_resources(cls, token): + headers = plexapi.BASE_HEADERS + headers['X-Plex-Token'] = token + log.info('GET %s?X-Plex-Token=%s', cls.DEVICES, token) + response = requests.get(cls.DEVICES, headers=headers, timeout=TIMEOUT) + data = ElementTree.fromstring(response.text.encode('utf8')) + return [MyPlexDevice(elem) for elem in data] + + def sendCommand(self, command, args=None): + url = '%s%s' % (self.url(command), utils.joinArgs(args)) + log.info('GET %s', url) + headers = plexapi.BASE_HEADERS + headers['X-Plex-Target-Client-Identifier'] = self.clientIdentifier + response = requests.get(url, headers=headers, timeout=TIMEOUT) + if response.status_code != requests.codes.ok: + codename = codes.get(response.status_code)[0] + raise BadRequest('(%s) %s' % (response.status_code, codename)) + data = response.text.encode('utf8') + + if data: + try: + return ElementTree.fromstring(data) + except: + pass + + return None + + def url(self, path): + return '%s/player/%s' % (self.baseUrl, path.lstrip('/')) + + # Navigation Commands + def moveUp(self, args=None): self.sendCommand('navigation/moveUp', args) # noqa + def moveDown(self, args=None): self.sendCommand('navigation/moveDown', args) # noqa + def moveLeft(self, args=None): self.sendCommand('navigation/moveLeft', args) # noqa + def moveRight(self, args=None): self.sendCommand('navigation/moveRight', args) # noqa + def pageUp(self, args=None): self.sendCommand('navigation/pageUp', args) # noqa + def pageDown(self, args=None): self.sendCommand('navigation/pageDown', args) # noqa + def nextLetter(self, args=None): self.sendCommand('navigation/nextLetter', args) # noqa + def previousLetter(self, args=None): self.sendCommand('navigation/previousLetter', args) # noqa + def select(self, args=None): self.sendCommand('navigation/select', args) # noqa + def back(self, args=None): self.sendCommand('navigation/back', args) # noqa + def contextMenu(self, args=None): self.sendCommand('navigation/contextMenu', args) # noqa + def toggleOSD(self, args=None): self.sendCommand('navigation/toggleOSD', args) # noqa + + # Playback Commands + def play(self, args=None): self.sendCommand('playback/play', args) # noqa + def pause(self, args=None): self.sendCommand('playback/pause', args) # noqa + def stop(self, args=None): self.sendCommand('playback/stop', args) # noqa + def stepForward(self, args=None): self.sendCommand('playback/stepForward', args) # noqa + def bigStepForward(self, args=None): self.sendCommand('playback/bigStepForward', args) # noqa + def stepBack(self, args=None): self.sendCommand('playback/stepBack', args) # noqa + def bigStepBack(self, args=None): self.sendCommand('playback/bigStepBack', args) # noqa + def skipNext(self, args=None): self.sendCommand('playback/skipNext', args) # noqa + def skipPrevious(self, args=None): self.sendCommand('playback/skipPrevious', args) # noqa +