def _cleanSearchFilter(self, category, value, libtype=None): # check a few things before we begin if category not in self.ALLOWED_FILTERS: raise BadRequest('Unknown filter category: %s' % category) if category in self.BOOLEAN_FILTERS: return '1' if value else '0' if not isinstance(value, (list, tuple)): value = [value] # convert list of values to list of keys or ids result = set() choices = self.listChoices(category, libtype) lookup = {c.title.lower(): unquote(unquote(c.key)) for c in choices} allowed = set(c.key for c in choices) for item in value: item = str((item.id or item.tag ) if isinstance(item, MediaTag) else item).lower() # find most logical choice(s) to use in url if item in allowed: result.add(item) continue if item in lookup: result.add(lookup[item]) continue matches = [k for t, k in lookup.items() if item in t] if matches: map(result.add, matches) continue # nothing matched; use raw item value log.warning('Filter value not listed, using raw item value: %s' % item) result.add(item) return ','.join(result)
def claimToken(self): """ Returns a str, a new "claim-token", which you can use to register your new Plex Server instance to your account. See: https://hub.docker.com/r/plexinc/pms-docker/, https://www.plex.tv/claim/ """ response = self._session.get('https://plex.tv/api/claim/token.json', headers=self._headers(), timeout=TIMEOUT) if response.status_code not in (200, 201, 204): # pragma: no cover codename = codes.get(response.status_code)[0] errtext = response.text.replace('\n', ' ') log.warning('BadRequest (%s) %s %s; %s' % (response.status_code, codename, response.url, errtext)) raise BadRequest('(%s) %s %s; %s' % (response.status_code, codename, response.url, errtext)) return response.json()['token']
def query(self, url, method=None, headers=None, timeout=None, **kwargs): method = method or self._session.get timeout = timeout or TIMEOUT log.debug('%s %s %s', method.__name__.upper(), url, kwargs.get('json', '')) headers = self._headers(**headers or {}) response = method(url, headers=headers, timeout=timeout, **kwargs) if response.status_code not in (200, 201, 204): # pragma: no cover codename = codes.get(response.status_code)[0] errtext = response.text.replace('\n', ' ') log.warning('BadRequest (%s) %s %s; %s' % (response.status_code, codename, response.url, errtext)) raise BadRequest('(%s) %s %s; %s' % (response.status_code, codename, response.url, errtext)) data = response.text.encode('utf8') return ElementTree.fromstring(data) if data.strip() else None
def run(self): try: import websocket except ImportError: log.warning("Can't use the AlertListener without websocket") return # create the websocket connection url = self._server.url(self.key, includeToken=True).replace('http', 'ws') log.info('Starting AlertListener: %s', url) self._ws = websocket.WebSocketApp(url, on_message=self._onMessage, on_error=self._onError) self._ws.run_forever()
def __getattribute__(self, attr): # Dragons inside.. :-/ value = utils.getattributeOrNone(PlexPartialObject, self, attr) # Check a few cases where we dont want to reload if attr == 'key' or attr.startswith('_'): return value if value not in (None, []): return value if self.isFullObject(): return value # Log warning that were reloading the object clsname = self.__class__.__name__ title = self.__dict__.get('title', self.__dict__.get('name')) objname = "%s '%s'" % (clsname, title) if title else clsname log.warning("Reloading %s for attr '%s'" % (objname, attr)) # Reload and return the value self.reload() return utils.getattributeOrNone(PlexPartialObject, self, attr)
def clients(self): """ Returns list of all :class:`~plexapi.client.PlexClient` objects connected to server. """ items = [] ports = None for elem in self.query('/clients'): port = elem.attrib.get('port') if not port: log.warning('%s did not advertise a port, checking plex.tv.', elem.attrib.get('name')) ports = self._myPlexClientPorts() if ports is None else ports port = ports.get(elem.attrib.get('machineIdentifier')) baseurl = 'http://%s:%s' % (elem.attrib['host'], port) items.append(PlexClient(baseurl=baseurl, server=self, token=self._token, data=elem, connect=False)) return items
def _myPlexClientPorts(self): """ Sometimes the PlexServer does not properly advertise port numbers required to connect. This attemps to look up device port number from plex.tv. See issue #126: Make PlexServer.clients() more user friendly. https://github.com/pkkid/python-plexapi/issues/126 """ try: ports = {} account = self.myPlexAccount() for device in account.devices(): if device.connections and ':' in device.connections[0][6:]: ports[device.clientIdentifier] = device.connections[0].split(':')[-1] return ports except Exception as err: log.warning('Unable to fetch client ports from myPlex: %s', err) return ports
def query(self, path, method=None, headers=None, timeout=None, **kwargs): """ Main method used to handle HTTPS requests to the Plex client. This method helps by encoding the response to utf-8 and parsing the returned XML into and ElementTree object. Returns None if no data exists in the response. """ url = self.url(path) method = method or self._session.get timeout = timeout or TIMEOUT log.debug('%s %s', method.__name__.upper(), url) headers = self._headers(**headers or {}) response = method(url, headers=headers, timeout=timeout, **kwargs) if response.status_code not in (200, 201): codename = codes.get(response.status_code)[0] errtext = response.text.replace('\n', ' ') log.warning('BadRequest (%s) %s %s; %s' % (response.status_code, codename, response.url, errtext)) raise BadRequest('(%s) %s; %s %s' % (response.status_code, codename, response.url, errtext)) data = response.text.encode('utf8') return ElementTree.fromstring(data) if data.strip() else None
def query(self, key, method=None, headers=None, timeout=None, **kwargs): """ Main method used to handle HTTPS requests to the Plex server. This method helps by encoding the response to utf-8 and parsing the returned XML into and ElementTree object. Returns None if no data exists in the response. """ url = self.url(key) method = method or self._session.get timeout = timeout or TIMEOUT log.debug('%s %s', method.__name__.upper(), url) headers = self._headers(**headers or {}) response = method(url, headers=headers, timeout=timeout, **kwargs) if response.status_code not in (200, 201): codename = codes.get(response.status_code)[0] errtext = response.text.replace('\n', ' ') log.warning('BadRequest (%s) %s %s; %s' % (response.status_code, codename, response.url, errtext)) raise BadRequest('(%s) %s; %s %s' % (response.status_code, codename, response.url, errtext)) data = response.text.encode('utf8') return ElementTree.fromstring(data) if data.strip() else None
def _cleanSearchFilter(self, category, value, libtype=None): # check a few things before we begin if category not in self.ALLOWED_FILTERS: raise BadRequest('Unknown filter category: %s' % category) if category in self.BOOLEAN_FILTERS: return '1' if value else '0' if not isinstance(value, (list, tuple)): value = [value] # convert list of values to list of keys or ids result = set() choices = self.listChoices(category, libtype) lookup = {c.title.lower():c.key for c in choices} allowed = set(c.key for c in choices) for item in value: item = str(item.id if isinstance(item, MediaTag) else item).lower() # find most logical choice(s) to use in url if item in allowed: result.add(item); continue if item in lookup: result.add(lookup[item]); continue matches = [k for t,k in lookup.items() if item in t] if matches: map(result.add, matches); continue # nothing matched; use raw item value log.warning('Filter value not listed, using raw item value: %s' % item) result.add(item) return ','.join(result)
def updateFriend(self, user, server, sections=None, removeSections=False, allowSync=None, allowCameraUpload=None, allowChannels=None, filterMovies=None, filterTelevision=None, filterMusic=None): """ Update the specified user's share settings. Parameters: user (str): MyPlexUser, username, email of the user to be added. server (PlexServer): PlexServer object or machineIdentifier containing the library sections to share. sections: ([Section]): Library sections, names or ids to be shared (default None shares all sections). removeSections (Bool): Set True to remove all shares. Supersedes sections. allowSync (Bool): Set True to allow user to sync content. allowCameraUpload (Bool): Set True to allow user to upload photos. allowChannels (Bool): Set True to allow user to utilize installed channels. filterMovies (Dict): Dict containing key 'contentRating' and/or 'label' each set to a list of values to be filtered. ex: {'contentRating':['G'], 'label':['foo']} filterTelevision (Dict): Dict containing key 'contentRating' and/or 'label' each set to a list of values to be filtered. ex: {'contentRating':['G'], 'label':['foo']} filterMusic (Dict): Dict containing key 'label' set to a list of values to be filtered. ex: {'label':['foo']} """ # Update friend servers response_filters = '' response_servers = '' user = self.user( user.username if isinstance(user, MyPlexUser) else user) machineId = server.machineIdentifier if isinstance( server, PlexServer) else server sectionIds = self._getSectionIds(machineId, sections) headers = {'Content-Type': 'application/json'} # Determine whether user has access to the shared server. user_servers = [ s for s in user.servers if s.machineIdentifier == machineId ] if user_servers and sectionIds: serverId = user_servers[0].id params = { 'server_id': machineId, 'shared_server': { 'library_section_ids': sectionIds } } url = self.FRIENDSERVERS.format(machineId=machineId, serverId=serverId) else: params = { 'server_id': machineId, 'shared_server': { 'library_section_ids': sectionIds, "invited_id": user.id } } url = self.FRIENDINVITE.format(machineId=machineId) # Remove share sections, add shares to user without shares, or update shares if sectionIds: if removeSections is True: response_servers = self.query(url, self._session.delete, json=params, headers=headers) elif 'invited_id' in params.get('shared_server', ''): response_servers = self.query(url, self._session.post, json=params, headers=headers) else: response_servers = self.query(url, self._session.put, json=params, headers=headers) else: log.warning( 'Section name, number of section object is required changing library sections' ) # Update friend filters url = self.FRIENDUPDATE.format(userId=user.id) params = {} if isinstance(allowSync, bool): params['allowSync'] = '1' if allowSync else '0' if isinstance(allowCameraUpload, bool): params['allowCameraUpload'] = '1' if allowCameraUpload else '0' if isinstance(allowChannels, bool): params['allowChannels'] = '1' if allowChannels else '0' if isinstance(filterMovies, dict): params['filterMovies'] = self._filterDictToStr( filterMovies or {}) # '1' if allowChannels else '0' if isinstance(filterTelevision, dict): params['filterTelevision'] = self._filterDictToStr(filterTelevision or {}) if isinstance(allowChannels, dict): params['filterMusic'] = self._filterDictToStr(filterMusic or {}) if params: url += joinArgs(params) response_filters = self.query(url, self._session.put) return response_servers, response_filters
def updateFriend(self, user, server, sections=None, removeSections=False, allowSync=None, allowCameraUpload=None, allowChannels=None, filterMovies=None, filterTelevision=None, filterMusic=None): """ Update the specified user's share settings. Parameters: user (str): MyPlexUser, username, email of the user to be added. server (PlexServer): PlexServer object or machineIdentifier containing the library sections to share. sections: ([Section]): Library sections, names or ids to be shared (default None shares all sections). removeSections (Bool): Set True to remove all shares. Supersedes sections. allowSync (Bool): Set True to allow user to sync content. allowCameraUpload (Bool): Set True to allow user to upload photos. allowChannels (Bool): Set True to allow user to utilize installed channels. filterMovies (Dict): Dict containing key 'contentRating' and/or 'label' each set to a list of values to be filtered. ex: {'contentRating':['G'], 'label':['foo']} filterTelevision (Dict): Dict containing key 'contentRating' and/or 'label' each set to a list of values to be filtered. ex: {'contentRating':['G'], 'label':['foo']} filterMusic (Dict): Dict containing key 'label' set to a list of values to be filtered. ex: {'label':['foo']} """ # Update friend servers response_filters = '' response_servers = '' user = self.user(user.username if isinstance(user, MyPlexUser) else user) machineId = server.machineIdentifier if isinstance(server, PlexServer) else server sectionIds = self._getSectionIds(machineId, sections) headers = {'Content-Type': 'application/json'} # Determine whether user has access to the shared server. user_servers = [s for s in user.servers if s.machineIdentifier == machineId] if user_servers and sectionIds: serverId = user_servers[0].id params = {'server_id': machineId, 'shared_server': {'library_section_ids': sectionIds}} url = self.FRIENDSERVERS.format(machineId=machineId, serverId=serverId) else: params = {'server_id': machineId, 'shared_server': {'library_section_ids': sectionIds, "invited_id": user.id}} url = self.FRIENDINVITE.format(machineId=machineId) # Remove share sections, add shares to user without shares, or update shares if sectionIds: if removeSections is True: response_servers = self.query(url, self._session.delete, json=params, headers=headers) elif 'invited_id' in params.get('shared_server', ''): response_servers = self.query(url, self._session.post, json=params, headers=headers) else: response_servers = self.query(url, self._session.put, json=params, headers=headers) else: log.warning('Section name, number of section object is required changing library sections') # Update friend filters url = self.FRIENDUPDATE.format(userId=user.id) params = {} if isinstance(allowSync, bool): params['allowSync'] = '1' if allowSync else '0' if isinstance(allowCameraUpload, bool): params['allowCameraUpload'] = '1' if allowCameraUpload else '0' if isinstance(allowChannels, bool): params['allowChannels'] = '1' if allowChannels else '0' if isinstance(filterMovies, dict): params['filterMovies'] = self._filterDictToStr(filterMovies or {}) # '1' if allowChannels else '0' if isinstance(filterTelevision, dict): params['filterTelevision'] = self._filterDictToStr(filterTelevision or {}) if isinstance(allowChannels, dict): params['filterMusic'] = self._filterDictToStr(filterMusic or {}) if params: url += joinArgs(params) response_filters = self.query(url, self._session.put) return response_servers, response_filters