Esempio n. 1
0
 def connect(self, timeout=None):
     """ Alias of reload as any subsequent requests to this client will be
         made directly to the device even if the object attributes were initially
         populated from a PlexServer.
     """
     if not self.key:
         raise Unsupported('Cannot reload an object not built from a URL.')
     self._initpath = self.key
     data = self.query(self.key, timeout=timeout)
     if not data:
         raise NotFound("Client not found at %s" % self._baseurl)
     if self._clientIdentifier:
         client = next(
             (x for x in data
              if x.attrib.get("machineIdentifier") == self._clientIdentifier
              ),
             None,
         )
         if client is None:
             raise NotFound("Client with identifier %s not found at %s" %
                            (self._clientIdentifier, self._baseurl))
     else:
         client = data[0]
     self._loadData(client)
     return self
Esempio n. 2
0
    def fixMatch(self, searchResult=None, auto=False, agent=None):
        """ Use match result to update show metadata.

            Parameters:
                auto (bool): True uses first match from matches
                    False allows user to provide the match
                searchResult (:class:`~plexapi.media.SearchResult`): Search result from
                    ~plexapi.base.matches()
                agent (str): Agent name to be used (imdb, thetvdb, themoviedb, etc.)
        """
        key = '/library/metadata/%s/match' % self.ratingKey
        if auto:
            autoMatch = self.matches(agent=agent)
            if autoMatch:
                searchResult = autoMatch[0]
            else:
                raise NotFound('No matches found using this agent: (%s:%s)' % (agent, autoMatch))
        elif not searchResult:
            raise NotFound('fixMatch() requires either auto=True or '
                           'searchResult=:class:`~plexapi.media.SearchResult`.')

        params = {'guid': searchResult.guid,
                  'name': searchResult.name}

        data = key + '?' + urlencode(params)
        self._server.query(data, method=self._server._session.put)
Esempio n. 3
0
 def _connect(self):
     """Used for fetching the attributes for __init__."""
     try:
         return self.query('/')
     except Exception as err:
         log.error('%s: %s', self.baseurl, err)
         raise NotFound('No server found at: %s' % self.baseurl)
Esempio n. 4
0
 def _connect(self):
     try:
         return self.query('/')
     except Exception as err:
         log.error('%s:%s: %s', self.address, self.port, err)
         raise NotFound('No server found at: %s:%s' %
                        (self.address, self.port))
Esempio n. 5
0
    def fetchItem(self, ekey, cls=None, **kwargs):
        """ Load the specified key to find and build the first item with the
            specified tag and attrs. If no tag or attrs are specified then
            the first item in the result set is returned.

            Parameters:
                ekey (str or int): Path in Plex to fetch items from. If an int is passed
                    in, the key will be translated to /library/metadata/<key>. This allows
                    fetching an item only knowing its key-id.
                cls (:class:`~plexapi.base.PlexObject`): If you know the class of the
                    items to be fetched, passing this in will help the parser ensure
                    it only returns those items. By default we convert the xml elements
                    with the best guess PlexObjects based on tag and type attrs.
                etag (str): Only fetch items with the specified tag.
                **kwargs (dict): Optionally add XML attribute to filter the items.
                    See :func:`~plexapi.base.PlexObject.fetchItems` for more details
                    on how this is used.
        """
        if ekey is None:
            raise BadRequest('ekey was not provided')
        if isinstance(ekey, int):
            ekey = '/library/metadata/%s' % ekey
        data = self._server.query(ekey)
        librarySectionID = utils.cast(int, data.attrib.get('librarySectionID'))
        for elem in data:
            if self._checkAttrs(elem, **kwargs):
                item = self._buildItem(elem, cls, ekey)
                if librarySectionID:
                    item.librarySectionID = librarySectionID
                return item
        clsname = cls.__name__ if cls else 'None'
        raise NotFound('Unable to find elem: cls=%s, attrs=%s' %
                       (clsname, kwargs))
Esempio n. 6
0
 def connect(self, ssl=None):
     # Only check non-local connections unless we own the resource
     connections = sorted(self.connections,
                          key=lambda c: c.local,
                          reverse=True)
     if not self.owned:
         connections = [c for c in connections if c.local is False]
     # Try connecting to all known resource connections in parellel, but
     # only return the first server (in order) that provides a response.
     threads, results = [], []
     for testssl, attr in self.SSLTESTS:
         if ssl in [None, testssl]:
             for i in range(len(connections)):
                 uri = getattr(connections[i], attr)
                 args = (uri, results, len(results))
                 results.append(None)
                 threads.append(Thread(target=self._connect, args=args))
                 threads[-1].start()
     for thread in threads:
         thread.join()
     # At this point we have a list of result tuples containing (uri, PlexServer)
     # or (uri, None) in the case a connection could not be established.
     for uri, result in results:
         log.info('Testing connection: %s %s', uri,
                  'OK' if result else 'ERR')
     results = list(filter(None, [r[1] for r in results if r]))
     if not results:
         raise NotFound('Unable to connect to resource: %s' % self.name)
     log.info('Connecting to server: %s', results[0])
     return results[0]
Esempio n. 7
0
    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, 204):
            codename = codes.get(response.status_code)[0]
            errtext = response.text.replace('\n', ' ')
            message = '(%s) %s; %s %s' % (response.status_code, codename,
                                          response.url, errtext)
            if response.status_code == 401:
                raise Unauthorized(message)
            elif response.status_code == 404:
                raise NotFound(message)
            else:
                raise BadRequest(message)
        data = response.text.encode('utf8')
        return ElementTree.fromstring(data) if data.strip() else None
Esempio n. 8
0
    def connect(self, ssl=None):
        """Connect to the first server.

        Args:
            ssl (None, optional): Use SSL?

        Returns:
            TYPE: Plexserver

        Raises:
            NotFound: Unable to connect to resource: name
        """
        # Try connecting to all known resource connections in parellel, but
        # only return the first server (in order) that provides a response.
        listargs = [[c] for c in self.connections]
        results = utils.threaded(self._connect, listargs)
        # At this point we have a list of result tuples containing (url, token, PlexServer)
        # or (url, token, None) in the case a connection could not be
        # established.
        for url, token, result in results:
            okerr = 'OK' if result else 'ERR'
            log.info('Testing device connection: %s?X-Plex-Token=%s %s', url,
                     token, okerr)
        results = [r[2] for r in results if r and r[2] is not None]
        if not results:
            raise NotFound('Unable to connect to resource: %s' % self.name)
        log.info('Connecting to server: %s?X-Plex-Token=%s',
                 results[0].baseurl, results[0].token)

        return results[0]
Esempio n. 9
0
 def _getPlaylistItemID(self, item):
     """ Match an item to a playlist item and return the item playlistItemID. """
     for _item in self.items():
         if _item.ratingKey == item.ratingKey:
             return _item.playlistItemID
     raise NotFound('Item with title "%s" not found in the playlist' %
                    item.title)
Esempio n. 10
0
    def episode(self, title=None, season=None, episode=None):
        """ Find a episode using a title or season and episode.

           Parameters:
                title (str): Title of the episode to return
                season (int): Season number (default:None; required if title not specified).
                episode (int): Episode number (default:None; required if title not specified).

           Raises:
                :class:`plexapi.exceptions.BadRequest`: If season and episode is missing.
                :class:`plexapi.exceptions.NotFound`: If the episode is missing.
        """
        if title:
            key = '/library/metadata/%s/allLeaves' % self.ratingKey
            return self.fetchItem(key, title__iexact=title)
        elif season is not None and episode:
            results = [
                i for i in self.episodes()
                if i.seasonNumber == season and i.index == episode
            ]
            if results:
                return results[0]
            raise NotFound('Couldnt find %s S%s E%s' %
                           (self.title, season, episode))
        raise BadRequest(
            'Missing argument: title or season and episode are required')
Esempio n. 11
0
 def device(self):
     """ Returns the :class:`~plexapi.server.SystemDevice` associated with the bandwidth data. """
     devices = self._server.systemDevices()
     try:
         return next(device for device in devices if device.id == self.deviceID)
     except StopIteration:
         raise NotFound('Unknown device for this bandwidth data: deviceID=%s' % self.deviceID)
Esempio n. 12
0
 def client(self, name):
     for elem in self.query('/clients'):
         if elem.attrib.get('name').lower() == name.lower():
             baseurl = 'http://%s:%s' % (elem.attrib['address'],
                                         elem.attrib['port'])
             return PlexClient(baseurl, server=self, data=elem)
     raise NotFound('Unknown client name: %s' % name)
Esempio n. 13
0
    def connect(self):
        """ Returns a new :class:`~plexapi.client.PlexClient` object. Sometimes there is more than
            one address specified for a server or client. After trying to connect to all
            available addresses for this resource and assuming at least one connection was
            successful, the PlexClient object is built and returned.

            Raises:
                :class:`~plexapi.exceptions.NotFound`: When unable to connect to any addresses for this device.
        """
        # Try connecting to all known resource connections in parellel, but
        # only return the first server (in order) that provides a response.
        listargs = [[c] for c in self.connections]
        results = utils.threaded(self._connect, listargs)
        # At this point we have a list of result tuples containing (url, token, PlexServer)
        # or (url, token, None) in the case a connection could not be
        # established.
        for url, token, result in results:
            okerr = 'OK' if result else 'ERR'
            log.info('Testing device connection: %s?X-Plex-Token=%s %s', url,
                     token, okerr)
        results = [r[2] for r in results if r and r[2] is not None]
        if not results:
            raise NotFound('Unable to connect to resource: %s' % self.name)
        log.info('Connecting to server: %s?X-Plex-Token=%s',
                 results[0].baseurl, results[0].token)
        return results[0]
Esempio n. 14
0
 def account(self):
     """ Returns the :class:`~plexapi.server.SystemAccount` associated with the bandwidth data. """
     accounts = self._server.systemAccounts()
     try:
         return next(account for account in accounts if account.id == self.accountID)
     except StopIteration:
         raise NotFound('Unknown account for this bandwidth data: accountID=%s' % self.accountID)
Esempio n. 15
0
def searchType(libtype):
    libtype = str(libtype)
    if libtype in [str(v) for v in SEARCHTYPES.values()]:
        return libtype
    if SEARCHTYPES.get(libtype) is not None:
        return SEARCHTYPES[libtype]
    raise NotFound('Unknown libtype: %s' % libtype)
Esempio n. 16
0
 def connect(self):
     try:
         data = self.query('/resources')[0]
         self._loadData(data)
     except Exception as err:
         log.error('%s: %s', self.baseurl, err)
         raise NotFound('No client found at: %s' % self.baseurl)
Esempio n. 17
0
def _findItem(items, value, attrs=None):
    attrs = attrs or ['name']
    for item in items:
        for attr in attrs:
            if value.lower() == getattr(item, attr).lower():
                return item
    raise NotFound('Unable to find item %s' % value)
Esempio n. 18
0
 def getServer(self, nameOrSourceTitle):
     search = nameOrSourceTitle.lower()
     for server in self.servers():
         if server.name and search == server.name.lower(): return server
         if server.sourceTitle and search == server.sourceTitle.lower():
             return server
     raise NotFound('Unable to find server: %s' % nameOrSourceTitle)
Esempio n. 19
0
 def connect(self):
     # Create a list of addresses to try connecting to.
     # TODO: setup local addresses before external
     devices = MyPlexDevice.fetchDevices(self.accessToken)
     devices = filter(
         lambda x: x.clientIdentifier == self.machineIdentifier, devices)
     addresses = []
     if len(devices) == 1:
         addresses += devices[0].connections
     else:
         addresses.append(Connection(self.address, self.port))
         if self.owned:
             for local in self.localAddresses:
                 addresses.append(Connection(local, self.port))
     # Attempt to connect to all known addresses in parellel to save time, but
     # only return the first server (in order) that provides a response.
     threads = [None] * len(addresses)
     results = [None] * len(addresses)
     for i in range(len(addresses)):
         args = (addresses[i], results, i)
         threads[i] = Thread(target=self._connect, args=args)
         threads[i].start()
     for thread in threads:
         thread.join()
     results = filter(None, results)
     if results: return results[0]
     raise NotFound('Unable to connect to server: %s' % (self.name))
Esempio n. 20
0
 def getDevice(self, nameOrClientIdentifier):
     search = nameOrClientIdentifier.lower()
     for device in self.devices():
         device_name = device.name.lower()
         device_cid = device.clientIdentifier.lower()
         if search in (device_name, device_cid):
             return device
     raise NotFound('Unable to find device: %s' % nameOrClientIdentifier)
Esempio n. 21
0
 def server(self):
     server = list(
         filter(lambda x: x.machineIdentifier == self.machineIdentifier,
                self._servers))
     if 0 == len(server):
         raise NotFound('Unable to find server with uuid %s' %
                        self.machineIdentifier)
     return server[0]
Esempio n. 22
0
def find_key(server, key):
    path = '/library/metadata/{0}'.format(key)
    try:
        # Video seems to be the first sub element
        elem = server.query(path)[0]
        return build_item(server, elem, path)
    except:
        raise NotFound('Unable to find key: %s' % key)
Esempio n. 23
0
 def reload(self):
     """ Reload attribute values from Plex XML response.  """
     try:
         data = self.query('/')
         self._loadData(data)
     except Exception as err:
         log.error('%s: %s', self.baseurl, err)
         raise NotFound('No server found at: %s' % self.baseurl)
Esempio n. 24
0
 def getServer(self, nameOrSourceTitle):
     for server in self.servers():
         if nameOrSourceTitle.lower() in [
                 server.name.lower(),
                 server.sourceTitle.lower()
         ]:
             return server
     raise NotFound('Unable to find server: %s' % nameOrSourceTitle)
Esempio n. 25
0
    def fetchItem(self, ekey, cls=None, **kwargs):
        """ Load the specified key to find and build the first item with the
            specified tag and attrs. If no tag or attrs are specified then
            the first item in the result set is returned.

            Parameters:
                ekey (str or int): Path in Plex to fetch items from. If an int is passed
                    in, the key will be translated to /library/metadata/<key>. This allows
                    fetching an item only knowing its key-id.
                cls (:class:`~plexapi.base.PlexObject`): If you know the class of the
                    items to be fetched, passing this in will help the parser ensure
                    it only returns those items. By default we convert the xml elements
                    with the best guess PlexObjects based on tag and type attrs.
                etag (str): Only fetch items with the specified tag.
                **kwargs (dict): Optionally add attribute filters on the items to fetch. For
                    example, passing in viewCount=0 will only return matching items. Filtering
                    is done before the Python objects are built to help keep things speedy.
                    Note: Because some attribute names are already used as arguments to this
                    function, such as 'tag', you may still reference the attr tag byappending
                    an underscore. For example, passing in _tag='foobar' will return all items
                    where tag='foobar'. Also Note: Case very much matters when specifying kwargs
                    -- Optionally, operators can be specified by append it
                    to the end of the attribute name for more complex lookups. For example,
                    passing in viewCount__gte=0 will return all items where viewCount >= 0.
                    Available operations include:

                    * __contains: Value contains specified arg.
                    * __endswith: Value ends with specified arg.
                    * __exact: Value matches specified arg.
                    * __exists (bool): Value is or is not present in the attrs.
                    * __gt: Value is greater than specified arg.
                    * __gte: Value is greater than or equal to specified arg.
                    * __icontains: Case insensative value contains specified arg.
                    * __iendswith: Case insensative value ends with specified arg.
                    * __iexact: Case insensative value matches specified arg.
                    * __in: Value is in a specified list or tuple.
                    * __iregex: Case insensative value matches the specified regular expression.
                    * __istartswith: Case insensative value starts with specified arg.
                    * __lt: Value is less than specified arg.
                    * __lte: Value is less than or equal to specified arg.
                    * __regex: Value matches the specified regular expression.
                    * __startswith: Value starts with specified arg.
        """
        if ekey is None:
            raise BadRequest('ekey was not provided')
        if isinstance(ekey, int):
            ekey = '/library/metadata/%s' % ekey

        resp = self._server.query(ekey)

        for elem in resp:
            if self._checkAttrs(elem, **kwargs):
                videoItem = self._buildItem(elem, cls, ekey)
                videoItem.librarySectionID = resp.attrib['librarySectionID']
                return videoItem
        clsname = cls.__name__ if cls else 'None'
        raise NotFound('Unable to find elem: cls=%s, attrs=%s' %
                       (clsname, kwargs))
Esempio n. 26
0
 def editAdvanced(self, **kwargs):
     """ Edit a Plex object's advanced settings. """
     data = {}
     key = '%s/prefs?' % self.key
     preferences = {pref.id: pref for pref in self.preferences() if pref.enumValues}
     for settingID, value in kwargs.items():
         try:
             pref = preferences[settingID]
         except KeyError:
             raise NotFound('%s not found in %s' % (value, list(preferences.keys())))
         
         enumValues = pref.enumValues
         if enumValues.get(value, enumValues.get(str(value))):
             data[settingID] = value
         else:
             raise NotFound('%s not found in %s' % (value, list(enumValues)))
     url = key + urlencode(data)
     self._server.query(url, method=self._server._session.put)
Esempio n. 27
0
def _findItem(items, value, attrs=None):
    """ This will return the first item in the list of items where value is
        found in any of the specified attributes.
    """
    attrs = attrs or ['name']
    for item in items:
        for attr in attrs:
            if value.lower() == getattr(item, attr).lower():
                return item
    raise NotFound('Unable to find item %s' % value)
Esempio n. 28
0
    def resource(self, name):
        """ Returns the :class:`~plexapi.myplex.MyPlexResource` that matches the name specified.

            Parameters:
                name (str): Name to match against.
        """
        for resource in self.resources():
            if resource.name.lower() == name.lower():
                return resource
        raise NotFound('Unable to find resource %s' % name)
Esempio n. 29
0
def getAgentIdentifier(section, agent):
    """ Return the full agent identifier from a short identifier, name, or confirm full identifier. """
    agents = []
    for ag in section.agents():
        identifiers = [ag.identifier, ag.shortIdentifier, ag.name]
        if agent in identifiers:
            return ag.identifier
        agents += identifiers
    raise NotFound('Could not find "%s" in agents list (%s)' %
                   (agent, ', '.join(agents)))
Esempio n. 30
0
    def section(self, title=None):
        """ Returns the :class:`~plexapi.library.LibrarySection` that matches the specified title.

            Parameters:
                title (str): Title of the section to return.
        """
        for section in self.sections():
            if section.title.lower() == title.lower():
                return section
        raise NotFound('Invalid library section: %s' % title)