Example #1
0
 def sendCommand(self, command, proxy=None, **params):
     command = command.strip('/')
     controller = command.split('/')[0]
     if controller not in self.protocolCapabilities:
         raise Unsupported('Client %s does not support the %s controller.' % (self.title, controller))
     path = '/player/%s%s' % (command, utils.joinArgs(params))
     headers = {'X-Plex-Target-Client-Identifier':self.machineIdentifier}
     self._commandId += 1; params['commandID'] = self._commandId
     proxy = self._proxyThroughServer if proxy is None else proxy
     if proxy:
         return self.server.query(path, headers=headers)
     path = '/player/%s%s' % (command, utils.joinArgs(params))
     return self.query(path, headers=headers)
Example #2
0
 def sendCommand(self, command, proxy=None, **params):
     proxy = self._proxyThroughServer if proxy is None else proxy
     if proxy:
         # send command via server proxy
         self._commandId += 1; params['commandID'] = self._commandId
         path = '/system/players/%s/%s%s' % (self.address, command, utils.joinArgs(params))
         return self.server.query(path)
     else:
         # send command directly to client
         command = command.strip('/')
         controller = command.split('/')[0]
         if controller not in self.protocolCapabilities:
             raise Unsupported('Client %s does not support the %s controller.' % (self.title, controller))
         self._commandId += 1; params['commandID'] = self._commandId
         return self.query('/player/%s%s' % (command, utils.joinArgs(params)))
Example #3
0
    def sendCommand(self, command, proxy=None, **params):
        """ Convenience wrapper around :func:`~plexapi.client.PlexClient.query()` to more easily
            send simple commands to the client. Returns an ElementTree object containing
            the response.

            Parameters:
                command (str): Command to be sent in for format '<controller>/<command>'.
                proxy (bool): Set True to proxy this command through the PlexServer.
                **params (dict): Additional GET parameters to include with the command.

            Raises:
                :class:`~plexapi.exceptions.Unsupported`: When we detect the client
                    doesn't support this capability.
        """
        command = command.strip('/')
        controller = command.split('/')[0]
        if controller not in self.protocolCapabilities:
            log.debug('Client %s doesnt support %s controller.'
                      'What your trying might not work' % (self.title, controller))

        params['commandID'] = self._nextCommandId()
        key = '/player/%s%s' % (command, utils.joinArgs(params))
        headers = {'X-Plex-Target-Client-Identifier': self.machineIdentifier}
        proxy = self._proxyThroughServer if proxy is None else proxy
        if proxy:
            return self._server.query(key, headers=headers)
        return self.query(key, headers=headers)
Example #4
0
    def create(cls, server, item, shuffle=0, repeat=0, includeChapters=1, includeRelated=1):
        """ Create and returns a new :class:`~plexapi.playqueue.PlayQueue`.

            Paramaters:
                server (:class:`~plexapi.server.PlexServer`): Server you are connected to.
                item (:class:`~plexapi.media.Media` or class:`~plexapi.playlist.Playlist`): A media or Playlist.
                shuffle (int, optional): Start the playqueue shuffled.
                repeat (int, optional): Start the playqueue shuffled.
                includeChapters (int, optional): include Chapters.
                includeRelated (int, optional): include Related.
        """
        args = {}
        args['includeChapters'] = includeChapters
        args['includeRelated'] = includeRelated
        args['repeat'] = repeat
        args['shuffle'] = shuffle
        if item.type == 'playlist':
            args['playlistID'] = item.ratingKey
            args['type'] = item.playlistType
        else:
            uuid = item.section().uuid
            args['key'] = item.key
            args['type'] = item.listType
            args['uri'] = 'library://%s/item/%s' % (uuid, item.key)
        path = '/playQueues%s' % utils.joinArgs(args)
        data = server.query(path, method=server._session.post)
        c = cls(server, data, initpath=path)
        # we manually add a key so we can pass this to playMedia
        # since the data, does not contain a key.
        c.key = item.key
        return c
Example #5
0
 def sendClientCommand(self, command, args=None):
     url = '%s%s' % (self.url(command), utils.joinArgs(args))
     log.info('GET %s', url)
     response = requests.get(url, 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')
     return ElementTree.fromstring(data) if data else None
Example #6
0
 def listChoices(self, category, libtype=None, **kwargs):
     """ List choices for the specified filter category. kwargs can be any of the same
         kwargs in self.search() to help narrow down the choices to only those that
         matter in your current context.
     """
     if category in kwargs:
         raise BadRequest('Cannot include kwarg equal to specified category: %s' % category)
     args = {}
     for subcategory, value in kwargs.items():
         args[category] = self._cleanSearchFilter(subcategory, value)
     if libtype is not None: args['type'] = utils.searchType(libtype)
     query = '/library/sections/%s/%s%s' % (self.key, category, utils.joinArgs(args))
     return utils.listItems(self.server, query, bytag=True)
Example #7
0
 def create(cls, server, video, shuffle=0, continuous=0):
     # NOTE: I have not yet figured out what __GID__ is below or where the proper value
     # can be obtained. However, the good news is passing anything in seems to work.
     path = 'playQueues%s' % utils.joinArgs({
         'uri': 'library://__GID__/item/%s' % video.key,
         'key': video.key,
         'type': 'video',
         'shuffle': shuffle,
         'continuous': continuous,
         'X-Plex-Client-Identifier': plexapi.X_PLEX_IDENTIFIER,
     })
     data = server.query(path, method=requests.post)
     return cls(server, data, initpath=path)
Example #8
0
    def addItem(self, video, next=0):
        path = 'playQueues/%s/%s' % (self.playQueueID, utils.joinArgs({
            'uri': 'library://__GID__/item/%s' % video.key,
            'key': video.key,
            'type': 'video',
            'shuffle': 0,
            'continuous': 0,
            'repeat': 0,
            'next': next,
            'X-Plex-Client-Identifier': plexapi.X_PLEX_IDENTIFIER,
        }))

        return self.server.query(path, method=requests.put)
Example #9
0
    def search(self, title=None, sort=None, maxresults=999999, libtype=None, **kwargs):
        """ Search the library. If there are many results, they will be fetched from the server
            in batches of X_PLEX_CONTAINER_SIZE amounts. If you're only looking for the first <num>
            results, it would be wise to set the maxresults option to that amount so this functions
            doesn't iterate over all results on the server.

            Parameters:
                title (str): General string query to search for (optional).
                sort (str): column:dir; column can be any of {addedAt, originallyAvailableAt, lastViewedAt,
                      titleSort, rating, mediaHeight, duration}. dir can be asc or desc (optional).
                maxresults (int): Only return the specified number of results (optional).
                libtype (str): Filter results to a spcifiec libtype (movie, show, episode, artist,
                    album, track; optional).
                **kwargs (dict): Any of the available filters for the current library section. Partial string
                        matches allowed. Multiple matches OR together. All inputs will be compared with the
                        available options and a warning logged if the option does not appear valid.

                        * unwatched: Display or hide unwatched content (True, False). [all]
                        * duplicate: Display or hide duplicate items (True, False). [movie]
                        * actor: List of actors to search ([actor_or_id, ...]). [movie]
                        * collection: List of collections to search within ([collection_or_id, ...]). [all]
                        * contentRating: List of content ratings to search within ([rating_or_key, ...]). [movie,tv]
                        * country: List of countries to search within ([country_or_key, ...]). [movie,music]
                        * decade: List of decades to search within ([yyy0, ...]). [movie]
                        * director: List of directors to search ([director_or_id, ...]). [movie]
                        * genre: List Genres to search within ([genere_or_id, ...]). [all]
                        * network: List of TV networks to search within ([resolution_or_key, ...]). [tv]
                        * resolution: List of video resolutions to search within ([resolution_or_key, ...]). [movie]
                        * studio: List of studios to search within ([studio_or_key, ...]). [music]
                        * year: List of years to search within ([yyyy, ...]). [all]
        """
        # cleanup the core arguments
        args = {}
        for category, value in kwargs.items():
            args[category] = self._cleanSearchFilter(category, value, libtype)
        if title is not None:
            args['title'] = title
        if sort is not None:
            args['sort'] = self._cleanSearchSort(sort)
        if libtype is not None:
            args['type'] = utils.searchType(libtype)
        # iterate over the results
        results, subresults = [], '_init'
        args['X-Plex-Container-Start'] = 0
        args['X-Plex-Container-Size'] = min(X_PLEX_CONTAINER_SIZE, maxresults)
        while subresults and maxresults > len(results):
            key = '/library/sections/%s/all%s' % (self.key, utils.joinArgs(args))
            subresults = self.fetchItems(key)
            results += subresults[:maxresults - len(results)]
            args['X-Plex-Container-Start'] += args['X-Plex-Container-Size']
        return results
 def search(self, title, filter='all', vtype=None, **tags):
     """ Search section content.
         title: Title to search (pass None to search all titles).
         filter: One of {'all', 'newest', 'onDeck', 'recentlyAdded', 'recentlyViewed', 'unwatched'}.
         videotype: One of {'movie', 'show', 'season', 'episode'}.
         tags: One of {country, director, genre, producer, actor, writer, decade, year, contentRating, <etc>}.
     """
     args = {}
     if title: args['title'] = title
     if vtype: args['type'] = video.search_type(vtype)
     for tag, obj in tags.items():
         args[tag] = obj.id
     query = '/library/sections/%s/%s%s' % (self.key, filter, utils.joinArgs(args))
     return video.list_items(self.server, query)
Example #11
0
 def addItems(self, items):
     if not isinstance(items, (list, tuple)):
         items = [items]
     ratingKeys = []
     for item in items:
         if item.listType != self.playlistType:
             raise BadRequest('Can not mix media types when building a playlist: %s and %s' % (self.playlistType, item.listType))
         ratingKeys.append(item.ratingKey)
     uuid = items[0].section().uuid
     ratingKeys = ','.join(ratingKeys)
     path = '%s/items%s' % (self.key, utils.joinArgs({
         'uri': 'library://%s/directory//library/metadata/%s' % (uuid, ratingKeys),
     }))
     return self.server.query(path, method=self.server.session.put)
Example #12
0
    def sendCommand(self, command, proxy=None, **params):
        """ Convenience wrapper around :func:`~plexapi.client.PlexClient.query()` to more easily
            send simple commands to the client. Returns an ElementTree object containing
            the response.

            Parameters:
                command (str): Command to be sent in for format '<controller>/<command>'.
                proxy (bool): Set True to proxy this command through the PlexServer.
                **params (dict): Additional GET parameters to include with the command.

            Raises:
                :class:`plexapi.exceptions.Unsupported`: When we detect the client doesn't support this capability.
        """
        command = command.strip('/')
        controller = command.split('/')[0]
        headers = {'X-Plex-Target-Client-Identifier': self.machineIdentifier}
        if controller not in self.protocolCapabilities:
            log.debug('Client %s doesnt support %s controller.'
                      'What your trying might not work' %
                      (self.title, controller))

        proxy = self._proxyThroughServer if proxy is None else proxy
        query = self._server.query if proxy else self.query

        # Workaround for ptp. See https://github.com/pkkid/python-plexapi/issues/244
        t = time.time()
        if command == 'timeline/poll':
            self._last_call = t
        elif t - self._last_call >= 80 and self.product in (
                'ptp', 'Plex Media Player'):
            self._last_call = t
            self.timeline(wait=0)

        params['commandID'] = self._nextCommandId()
        key = '/player/%s%s' % (command, utils.joinArgs(params))

        try:
            return query(key, headers=headers)
        except ElementTree.ParseError:
            # Workaround for players which don't return valid XML on successful commands
            #   - Plexamp, Plex for Android: `b'OK'`
            #   - Plex for Samsung: `b'<?xml version="1.0"?><Response code="200" status="OK">'`
            if self.product in (
                    'Plexamp',
                    'Plex for Android (TV)',
                    'Plex for Android (Mobile)',
                    'Plex for Samsung',
            ):
                return
            raise
Example #13
0
 def search(self, title, filter='all', vtype=None, **tags):
     """ Search all available content.
         title: Title to search (pass None to search all titles).
         filter: One of {'all', 'onDeck', 'recentlyAdded'}.
         videotype: One of {'movie', 'show', 'season', 'episode'}.
         tags: One of {country, director, genre, producer, actor, writer}.
     """
     args = {}
     if title: args['title'] = title
     if vtype: args['type'] = video.search_type(vtype)
     for tag, obj in tags.iteritems():
         args[tag] = obj.id
     query = '/library/%s%s' % (filter, utils.joinArgs(args))
     return video.list_items(self.server, query)
Example #14
0
 def search(self, title=None, libtype=None, **kwargs):
     """ Searching within a library section is much more powerful. It seems certain attributes on the media
         objects can be targeted to filter this search down a bit, but I havent found the documentation for
         it. For example: "studio=Comedy%20Central" or "year=1999" "title=Kung Fu" all work. Other items
         such as actor=<id> seem to work, but require you already know the id of the actor.
         TLDR: This is untested but seems to work. Use library section search when you can.
     """
     args = {}
     if title: args['title'] = title
     if libtype: args['type'] = utils.searchType(libtype)
     for attr, value in kwargs.items():
         args[attr] = value
     query = '/library/all%s' % utils.joinArgs(args)
     return utils.listItems(self.server, query)
Example #15
0
    def edit(self, title=None, summary=None):
        """ Edit the playlist.
        
            Parameters:
                title (str, optional): The title of the playlist.
                summary (str, optional): The summary of the playlist.
        """
        args = {}
        if title:
            args['title'] = title
        if summary:
            args['summary'] = summary

        key = '%s%s' % (self.key, utils.joinArgs(args))
        self._server.query(key, method=self._server._session.put)
Example #16
0
    def _edit(self, **kwargs):
        """ Actually edit an object. """
        if isinstance(self._edits, dict):
            self._edits.update(kwargs)
            return self

        if 'id' not in kwargs:
            kwargs['id'] = self.ratingKey
        if 'type' not in kwargs:
            kwargs['type'] = utils.searchType(self._searchType)

        part = '/library/sections/%s/all%s' % (self.librarySectionID,
                                               utils.joinArgs(kwargs))
        self._server.query(part, method=self._server._session.put)
        return self
Example #17
0
 def search(self, title, filter='all', vtype=None, **tags):
     """ Search section content.
         title: Title to search (pass None to search all titles).
         filter: One of {'all', 'newest', 'onDeck', 'recentlyAdded', 'recentlyViewed', 'unwatched'}.
         videotype: One of {'movie', 'show', 'season', 'episode'}.
         tags: One of {country, director, genre, producer, actor, writer}.
     """
     args = {}
     if title: args['title'] = title
     if vtype: args['type'] = video.search_type(vtype)
     for tag, obj in tags.iteritems():
         args[tag] = obj.id
     query = '/library/sections/%s/%s%s' % (self.key, filter,
                                            utils.joinArgs(args))
     return video.list_items(self.server, query)
Example #18
0
 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
Example #19
0
 def addItems(self, items):
     """ Add items to a playlist. """
     if not isinstance(items, (list, tuple)):
         items = [items]
     ratingKeys = []
     for item in items:
         if item.listType != self.playlistType:
             raise BadRequest('Can not mix media types when building a playlist: %s and %s' %
                 (self.playlistType, item.listType))
         ratingKeys.append(str(item.ratingKey))
     uuid = items[0].section().uuid
     ratingKeys = ','.join(ratingKeys)
     key = '%s/items%s' % (self.key, utils.joinArgs({
         'uri': 'library://%s/directory//library/metadata/%s' % (uuid, ratingKeys)
     }))
     return self._server.query(key, method=self._server._session.put)
Example #20
0
 def listChoices(self, category, libtype=None, **kwargs):
     """ List choices for the specified filter category. kwargs can be any of the same
         kwargs in self.search() to help narrow down the choices to only those that
         matter in your current context.
     """
     if category in kwargs:
         raise BadRequest(
             'Cannot include kwarg equal to specified category: %s' %
             category)
     args = {}
     for subcategory, value in kwargs.items():
         args[category] = self._cleanSearchFilter(subcategory, value)
     if libtype is not None: args['type'] = utils.searchType(libtype)
     query = '/library/sections/%s/%s%s' % (self.key, category,
                                            utils.joinArgs(args))
     return utils.listItems(self.server, query, bytag=True)
Example #21
0
 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
Example #22
0
    def _createSmart(cls, server, title, section, limit=None, sort=None, filters=None, **kwargs):
        """ Create a smart playlist. """
        if not isinstance(section, LibrarySection):
            section = server.library.section(section)

        searchKey = section._buildSearchKey(
            sort=sort, libtype=section.METADATA_TYPE, limit=limit, filters=filters, **kwargs)
        uri = '%s%s' % (server._uriRoot(), searchKey)

        key = '/playlists%s' % utils.joinArgs({
            'uri': uri,
            'type': section.CONTENT_TYPE,
            'title': title,
            'smart': 1,
        })
        data = server.query(key, method=server._session.post)[0]
        return cls(server, data, initpath=key)
Example #23
0
 def create(cls, server, item, shuffle=0, repeat=0, includeChapters=1, includeRelated=1):
     args = {}
     args['includeChapters'] = includeChapters
     args['includeRelated'] = includeRelated
     args['repeat'] = repeat
     args['shuffle'] = shuffle
     if item.type == 'playlist':
         args['playlistID'] = item.ratingKey
         args['type'] = item.playlistType
     else:
         uuid = item.section().uuid
         args['key'] = item.key
         args['type'] = item.listType
         args['uri'] = 'library://%s/item/%s' % (uuid, item.key)
     path = '/playQueues%s' % utils.joinArgs(args)
     data = server.query(path, method=requests.post)
     return cls(server, data, initpath=path)
Example #24
0
    def search(self, title=None, libtype=None, **kwargs):
        """ Searching within a library section is much more powerful. It seems certain
            attributes on the media objects can be targeted to filter this search down
            a bit, but I havent found the documentation for it.

            Example: "studio=Comedy%20Central" or "year=1999" "title=Kung Fu" all work. Other items
            such as actor=<id> seem to work, but require you already know the id of the actor.
            TLDR: This is untested but seems to work. Use library section search when you can.
        """
        args = {}
        if title:
            args['title'] = title
        if libtype:
            args['type'] = utils.searchType(libtype)
        for attr, value in kwargs.items():
            args[attr] = value
        key = '/library/all%s' % utils.joinArgs(args)
        return self.fetchItems(key)
Example #25
0
 def create(cls, server, title, items):
     if not isinstance(items, (list, tuple)):
         items = [items]
     ratingKeys = []
     for item in items:
         if item.listType != items[0].listType:
             raise BadRequest('Can not mix media types when building a playlist')
         ratingKeys.append(item.ratingKey)
     ratingKeys = ','.join(ratingKeys)
     uuid = items[0].section().uuid
     path = '/playlists%s' % utils.joinArgs({
         'uri': 'library://%s/directory//library/metadata/%s' % (uuid, ratingKeys),
         'type': items[0].listType,
         'title': title,
         'smart': 0
     })
     data = server.query(path, method=server.session.post)[0]
     return cls(server, data, initpath=path)
Example #26
0
 def addItems(self, items):
     """ Add items to a playlist. """
     if not isinstance(items, (list, tuple)):
         items = [items]
     ratingKeys = []
     for item in items:
         if item.listType != self.playlistType:  # pragma: no cover
             raise BadRequest('Can not mix media types when building a playlist: %s and %s' %
                 (self.playlistType, item.listType))
         ratingKeys.append(str(item.ratingKey))
     uuid = items[0].section().uuid
     ratingKeys = ','.join(ratingKeys)
     key = '%s/items%s' % (self.key, utils.joinArgs({
         'uri': 'library://%s/directory//library/metadata/%s' % (uuid, ratingKeys)
     }))
     result = self._server.query(key, method=self._server._session.put)
     self.reload()
     return result
Example #27
0
    def _createSmart(cls, server, title, section, limit=None, libtype=None, sort=None, filters=None, **kwargs):
        """ Create a smart collection. """
        if not isinstance(section, LibrarySection):
            section = server.library.section(section)

        libtype = libtype or section.TYPE

        searchKey = section._buildSearchKey(
            sort=sort, libtype=libtype, limit=limit, filters=filters, **kwargs)
        uri = '%s%s' % (server._uriRoot(), searchKey)

        key = '/library/collections%s' % utils.joinArgs({
            'uri': uri,
            'type': utils.searchType(libtype),
            'title': title,
            'smart': 1,
            'sectionId': section.key
        })
        data = server.query(key, method=server._session.post)[0]
        return cls(server, data, initpath=key)
Example #28
0
    def sendCommand(self, command, proxy=None, **params):
        """ Convenience wrapper around :func:`~plexapi.client.PlexClient.query()` to more easily
            send simple commands to the client. Returns an ElementTree object containing
            the response.

            Parameters:
                command (str): Command to be sent in for format '<controller>/<command>'.
                proxy (bool): Set True to proxy this command through the PlexServer.
                **params (dict): Additional GET parameters to include with the command.

            Raises:
                :class:`plexapi.exceptions.Unsupported`: When we detect the client doesn't support this capability.
        """
        command = command.strip('/')
        controller = command.split('/')[0]
        headers = {'X-Plex-Target-Client-Identifier': self.machineIdentifier}
        if controller not in self.protocolCapabilities:
            log.debug('Client %s doesnt support %s controller.'
                      'What your trying might not work' %
                      (self.title, controller))

        # Workaround for ptp. See https://github.com/pkkid/python-plexapi/issues/244
        t = time.time()
        if t - self._last_call >= 80 and self.product in ('ptp',
                                                          'Plex Media Player'):
            url = '/player/timeline/poll?wait=0&commandID=%s' % self._nextCommandId(
            )
            if proxy:
                self._server.query(url, headers=headers)
            else:
                self.query(url, headers=headers)
            self._last_call = t

        params['commandID'] = self._nextCommandId()
        key = '/player/%s%s' % (command, utils.joinArgs(params))

        proxy = self._proxyThroughServer if proxy is None else proxy

        if proxy:
            return self._server.query(key, headers=headers)
        return self.query(key, headers=headers)
Example #29
0
    def create(cls,
               server,
               item,
               shuffle=0,
               repeat=0,
               includeChapters=1,
               includeRelated=1,
               continuous=0):
        """ Create and returns a new :class:`~plexapi.playqueue.PlayQueue`.

            Paramaters:
                server (:class:`~plexapi.server.PlexServer`): Server you are connected to.
                item (:class:`~plexapi.media.Media` or class:`~plexapi.playlist.Playlist`): A media or Playlist.
                shuffle (int, optional): Start the playqueue shuffled.
                repeat (int, optional): Start the playqueue shuffled.
                includeChapters (int, optional): include Chapters.
                includeRelated (int, optional): include Related.
                continuous (int, optional): include additional items after the initial item. For a show this would be the next episodes, for a movie it does nothing.
        """
        args = {}
        args['includeChapters'] = includeChapters
        args['includeRelated'] = includeRelated
        args['repeat'] = repeat
        args['shuffle'] = shuffle
        args['continuous'] = continuous
        if item.type == 'playlist':
            args['playlistID'] = item.ratingKey
            args['type'] = item.playlistType
        else:
            uuid = item.section().uuid
            args['key'] = item.key
            args['type'] = item.listType
            args['uri'] = 'library://%s/item/%s' % (uuid, item.key)
        path = '/playQueues%s' % utils.joinArgs(args)
        data = server.query(path, method=server._session.post)
        c = cls(server, data, initpath=path)
        # we manually add a key so we can pass this to playMedia
        # since the data, does not contain a key.
        c.key = item.key
        return c
Example #30
0
    def get(
        cls,
        server,
        playQueueID,
        own=False,
        center=None,
        window=50,
        includeBefore=True,
        includeAfter=True,
    ):
        """Retrieve an existing :class:`~plexapi.playqueue.PlayQueue` by identifier.

        Parameters:
            server (:class:`~plexapi.server.PlexServer`): Server you are connected to.
            playQueueID (int): Identifier of an existing PlayQueue.
            own (bool, optional): If server should transfer ownership.
            center (int, optional): The playQueueItemID of the center of the window. Does not change selectedItem.
            window (int, optional): Number of items to return from each side of the center item.
            includeBefore (bool, optional):
                Include items before the center, defaults True. Does not include center if False.
            includeAfter (bool, optional):
                Include items after the center, defaults True. Does not include center if False.
        """
        args = {
            "own": utils.cast(int, own),
            "window": window,
            "includeBefore": utils.cast(int, includeBefore),
            "includeAfter": utils.cast(int, includeAfter),
        }
        if center:
            args["center"] = center

        path = "/playQueues/{playQueueID}{args}".format(
            playQueueID=playQueueID, args=utils.joinArgs(args))
        data = server.query(path, method=server._session.get)
        c = cls(server, data, initpath=path)
        c._server = server
        return c
Example #31
0
 def create(cls,
            server,
            item,
            shuffle=0,
            repeat=0,
            includeChapters=1,
            includeRelated=1):
     args = {}
     args['includeChapters'] = includeChapters
     args['includeRelated'] = includeRelated
     args['repeat'] = repeat
     args['shuffle'] = shuffle
     if item.type == 'playlist':
         args['playlistID'] = item.ratingKey
         args['type'] = item.playlistType
     else:
         uuid = item.section().uuid
         args['key'] = item.key
         args['type'] = item.listType
         args['uri'] = 'library://%s/item/%s' % (uuid, item.key)
     path = '/playQueues%s' % utils.joinArgs(args)
     data = server.query(path, method=requests.post)
     return cls(server, data, initpath=path)
Example #32
0
    def history(self, maxresults=9999999, mindate=None):
        """ Returns a list of media items from watched history. If there are many results, they will
            be fetched from the server in batches of X_PLEX_CONTAINER_SIZE amounts. If you're only
            looking for the first <num> results, it would be wise to set the maxresults option to that
            amount so this functions doesn't iterate over all results on the server.

            Parameters:
                maxresults (int): Only return the specified number of results (optional).
                mindate (datetime): Min datetime to return results from. This really helps speed 
                    up the result listing. For example: datetime.now() - timedelta(days=7)
        """
        results, subresults = [], '_init'
        args = {'sort': 'viewedAt:desc'}
        if mindate:
            args['viewedAt>'] = int(mindate.timestamp())
        args['X-Plex-Container-Start'] = 0
        args['X-Plex-Container-Size'] = min(X_PLEX_CONTAINER_SIZE, maxresults)
        while subresults and maxresults > len(results):
            key = '/status/sessions/history/all%s' % utils.joinArgs(args)
            subresults = self.fetchItems(key)
            results += subresults[:maxresults - len(results)]
            args['X-Plex-Container-Start'] += args['X-Plex-Container-Size']
        return results
Example #33
0
    def addItem(self, item, playNext=False, refresh=True):
        """
        Append the provided item to the "Up Next" section of the PlayQueue.
        Items can only be added to the section immediately following the current playing item.

        Parameters:
            item (:class:`~plexapi.media.Media` or :class:`~plexapi.playlist.Playlist`): Single media item or Playlist.
            playNext (bool, optional): If True, add this item to the front of the "Up Next" section.
                If False, the item will be appended to the end of the "Up Next" section.
                Only has an effect if an item has already been added to the "Up Next" section.
                See https://support.plex.tv/articles/202188298-play-queues/ for more details.
            refresh (bool, optional): Refresh the PlayQueue from the server before updating.
        """
        if refresh:
            self.refresh()

        args = {}
        if item.type == "playlist":
            args["playlistID"] = item.ratingKey
            itemType = item.playlistType
        else:
            uuid = item.section().uuid
            itemType = item.listType
            args["uri"] = "library://{uuid}/item{key}".format(uuid=uuid,
                                                              key=item.key)

        if itemType != self.playQueueType:
            raise Unsupported("Item type does not match PlayQueue type")

        if playNext:
            args["next"] = 1

        path = "/playQueues/{playQueueID}{args}".format(
            playQueueID=self.playQueueID, args=utils.joinArgs(args))
        data = self._server.query(path, method=self._server._session.put)
        self._loadData(data)
Example #34
0
    def listChoices(self, category, libtype=None, **kwargs):
        """ Returns a list of :class:`~plexapi.library.FilterChoice` objects for the
            specified category and libtype. kwargs can be any of the same kwargs in
            :func:`plexapi.library.LibraySection.search()` to help narrow down the choices
            to only those that matter in your current context.

            Parameters:
                category (str): Category to list choices for (genre, contentRating, etc).
                libtype (int): Library type of item filter.
                **kwargs (dict): Additional kwargs to narrow down the choices.

            Raises:
                :class:`~plexapi.exceptions.BadRequest`: Cannot include kwarg equal to specified category.
        """
        # TODO: Should this be moved to base?
        if category in kwargs:
            raise BadRequest('Cannot include kwarg equal to specified category: %s' % category)
        args = {}
        for subcategory, value in kwargs.items():
            args[category] = self._cleanSearchFilter(subcategory, value)
        if libtype is not None:
            args['type'] = utils.searchType(libtype)
        key = '/library/sections/%s/%s%s' % (self.key, category, utils.joinArgs(args))
        return self.fetchItems(key, cls=FilterChoice)
Example #35
0
    def listChoices(self, category, libtype=None, **kwargs):
        """ Returns a list of :class:`~plexapi.library.FilterChoice` objects for the
            specified category and libtype. kwargs can be any of the same kwargs in
            :func:`plexapi.library.LibraySection.search()` to help narrow down the choices
            to only those that matter in your current context.

            Parameters:
                category (str): Category to list choices for (genre, contentRating, etc).
                libtype (int): Library type of item filter.
                **kwargs (dict): Additional kwargs to narrow down the choices.

            Raises:
                :class:`~plexapi.exceptions.BadRequest`: Cannot include kwarg equal to specified category.
        """
        # TODO: Should this be moved to base?
        if category in kwargs:
            raise BadRequest('Cannot include kwarg equal to specified category: %s' % category)
        args = {}
        for subcategory, value in kwargs.items():
            args[category] = self._cleanSearchFilter(subcategory, value)
        if libtype is not None:
            args['type'] = utils.searchType(libtype)
        key = '/library/sections/%s/%s%s' % (self.key, category, utils.joinArgs(args))
        return self.fetchItems(key, cls=FilterChoice)
Example #36
0
    def updateFilters(self,
                      libtype=None,
                      limit=None,
                      sort=None,
                      filters=None,
                      **kwargs):
        """ Update the filters for a smart collection.

            Parameters:
                libtype (str): The specific type of content to filter
                    (movie, show, season, episode, artist, album, track, photoalbum, photo, collection).
                limit (int): Limit the number of items in the collection.
                sort (str or list, optional): A string of comma separated sort fields
                    or a list of sort fields in the format ``column:dir``.
                    See :func:`~plexapi.library.LibrarySection.search` for more info.
                filters (dict): A dictionary of advanced filters.
                    See :func:`~plexapi.library.LibrarySection.search` for more info.
                **kwargs (dict): Additional custom filters to apply to the search results.
                    See :func:`~plexapi.library.LibrarySection.search` for more info.

            Raises:
                :class:`plexapi.exceptions.BadRequest`: When trying update filters for a regular collection.
        """
        if not self.smart:
            raise BadRequest('Cannot update filters for a regular collection.')

        section = self.section()
        searchKey = section._buildSearchKey(sort=sort,
                                            libtype=libtype,
                                            limit=limit,
                                            filters=filters,
                                            **kwargs)
        uri = '%s%s' % (self._server._uriRoot(), searchKey)

        key = '%s/items%s' % (self.key, utils.joinArgs({'uri': uri}))
        self._server.query(key, method=self._server._session.put)
Example #37
0
    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
Example #38
0
def test_utils_joinArgs():
    test_dict = {"genre": "action", "type": 1337}
    assert utils.joinArgs(test_dict) == "?genre=action&type=1337"
Example #39
0
    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
Example #40
0
 def sendServerCommand(self, command, args=None):
     path = '/system/players/%s/%s%s' % (self.address, command, utils.joinArgs(args))
     self.server.query(path)
Example #41
0
    def create(
        cls,
        server,
        items,
        startItem=None,
        shuffle=0,
        repeat=0,
        includeChapters=1,
        includeRelated=1,
        continuous=0,
    ):
        """Create and return a new :class:`~plexapi.playqueue.PlayQueue`.

        Parameters:
            server (:class:`~plexapi.server.PlexServer`): Server you are connected to.
            items (:class:`~plexapi.media.Media` or :class:`~plexapi.playlist.Playlist`):
                A media item, list of media items, or Playlist.
            startItem (:class:`~plexapi.media.Media`, optional):
                Media item in the PlayQueue where playback should begin.
            shuffle (int, optional): Start the playqueue shuffled.
            repeat (int, optional): Start the playqueue shuffled.
            includeChapters (int, optional): include Chapters.
            includeRelated (int, optional): include Related.
            continuous (int, optional): include additional items after the initial item.
                For a show this would be the next episodes, for a movie it does nothing.
        """
        args = {
            "includeChapters": includeChapters,
            "includeRelated": includeRelated,
            "repeat": repeat,
            "shuffle": shuffle,
            "continuous": continuous,
        }

        if isinstance(items, list):
            item_keys = ",".join([str(x.ratingKey) for x in items])
            uri_args = quote_plus(
                "/library/metadata/{item_keys}".format(item_keys=item_keys))
            args["uri"] = "library:///directory/{uri_args}".format(
                uri_args=uri_args)
            args["type"] = items[0].listType
        elif items.type == "playlist":
            args["type"] = items.playlistType
            if items.radio:
                args[
                    "uri"] = f"server://{server.machineIdentifier}/{server.library.identifier}{items.key}"
            else:
                args["playlistID"] = items.ratingKey
        else:
            uuid = items.section().uuid
            args["type"] = items.listType
            args["uri"] = "library://{uuid}/item/{key}".format(uuid=uuid,
                                                               key=items.key)

        if startItem:
            args["key"] = startItem.key

        path = "/playQueues{args}".format(args=utils.joinArgs(args))
        data = server.query(path, method=server._session.post)
        c = cls(server, data, initpath=path)
        c.playQueueType = args["type"]
        c._server = server
        return c
Example #42
0
 def edit(self, title=None, summary=None):
     path = '/library/metadata/%s%s' % (self.ratingKey, utils.joinArgs({'title':title, 'summary':summary}))
     return self.server.query(path, method=self.server.session.put)
Example #43
0
 def _edit(self, **kwargs):
     """ Actually edit the playlist. """
     key = '%s%s' % (self.key, utils.joinArgs(kwargs))
     self._server.query(key, method=self._server._session.put)
Example #44
0
 def edit(self, title=None, summary=None):
     """ Edit playlist. """
     key = '/library/metadata/%s%s' % (self.ratingKey, utils.joinArgs({'title': title, 'summary': summary}))
     result = self._server.query(key, method=self._server._session.put)
     self.reload()
     return result
Example #45
0
 def edit(self, title=None, summary=None):
     """ Edit playlist. """
     key = '/library/metadata/%s%s' % (self.ratingKey, utils.joinArgs({'title': title, 'summary': summary}))
     result = self._server.query(key, method=self._server._session.put)
     self.reload()
     return result
Example #46
0
def test_utils_joinArgs():
    test_dict = {'genre': 'action', 'type': 1337}
    assert utils.joinArgs(test_dict) == '?genre=action&type=1337'
Example #47
0
    def create(cls,
               server,
               item,
               shuffle=0,
               repeat=0,
               includeChapters=1,
               includeRelated=1,
               continuous=0,
               parent=None,
               sort=None):
        """ Create and returns a new :class:`~plexapi.playqueue.PlayQueue`.

            Paramaters:
                server (:class:`~plexapi.server.PlexServer`): Server you are connected to.
                item (:class:`~plexapi.media.Media` or class:`~plexapi.playlist.Playlist`): A media or Playlist.
                shuffle (int, optional): Start the playqueue shuffled.
                repeat (int, optional): Start the playqueue shuffled.
                includeChapters (int, optional): include Chapters.
                includeRelated (int, optional): include Related.
                continuous (int, optional): include rest of item collection.
                parent (str, optional): use a custom uri.
                sort (str, optional): if playing a section this param will be used.
        """
        args = {}
        args['includeChapters'] = includeChapters
        args['includeRelated'] = includeRelated
        args['repeat'] = repeat
        args['shuffle'] = shuffle
        args['continuous'] = continuous
        if item.type == 'playlist':
            args['playlistID'] = item.ratingKey
            args['type'] = item.playlistType
        elif hasattr(item, 'uuid'):
            args['type'] = item.type
            sortStr = ""
            if sort is not None:
                sortStr = "sort=" + sort
            args[
                'uri'] = 'server://%s/com.plexapp.plugins.library/library/sections/%s/all?%s' % (
                    server.machineIdentifier, item.key, sortStr)
        elif item.TYPE == 'clip':
            args['type'] = 'video'
            args['uri'] = 'server://%s/com.plexapp.plugins.library%s' % (
                server.machineIdentifier, item.key)
        else:
            uuid = item.section().uuid
            args['key'] = item.key
            args['type'] = item.listType
            if parent is not None:
                args['uri'] = 'library://%s/item/%s' % (uuid, parent.key)
            else:
                args['uri'] = 'library://%s/item/%s' % (uuid, item.key)
        path = '/playQueues%s' % utils.joinArgs(args)
        data = server.query(path, method=server._session.post)
        c = cls(server, data, initpath=path)
        # we manually add a key so we can pass this to playMedia
        # since the data, does not contain a key.
        c.key = item.key
        c.repeat = repeat
        c.includeChapters = includeChapters
        c.includeRelated = includeRelated
        c.server = server
        return c