def extend(self, start=None, size=None): path = '/playlists/all?playlistType={0}'.format(self.type) args = {} if size is not None: args['X-Plex-Container-Start'] = start args['X-Plex-Container-Size'] = size else: start = 0 if args: path += '&' + util.joinArgs(args).lstrip('?') items = plexobjects.listItems(self.server, path) if not items: return self.set('offset', start) self.set('size', len(items)) self.set('more', (items[0].container.offset.asInt() + items[0].container.size.asInt() < items[0].container.totalSize.asInt()) and '1' or '') return items
def jumpList(self, filter_=None, sort=None, unwatched=False, type_=None): if self.key.startswith('/'): path = '{0}/firstCharacter'.format(self.key) else: path = '/library/sections/{0}/firstCharacter'.format(self.key) args = {} if filter_: args[filter_[0]] = filter_[1] if sort: args['sort'] = '{0}:{1}'.format(*sort) if type_: args['type'] = str(type_) if unwatched: args[self.TYPE == 'movie' and 'unwatched' or 'unwatchedLeaves'] = 1 if args: path += util.joinArgs(args) try: return plexobjects.listItems(self.server, path, bytag=True) except exceptions.BadRequest: util.ERROR('jumpList() request error for path: {0}'.format( repr(path))) return None
def all(self, start=None, size=None, filter_=None, sort=None, unwatched=False, type_=None): if self.key.startswith('/'): path = '{0}/all'.format(self.key) else: path = '/library/sections/{0}/all'.format(self.key) args = {} if size is not None: args['X-Plex-Container-Start'] = start args['X-Plex-Container-Size'] = size if filter_: args[filter_[0]] = filter_[1] if sort: args['sort'] = '{0}:{1}'.format(*sort) if type_: args['type'] = str(type_) if unwatched: args[self.TYPE == 'movie' and 'unwatched' or 'unwatchedLeaves'] = 1 if args: path += util.joinArgs(args) return plexobjects.listItems(self.server, path)
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. title: General string query to search for. sort: column:dir; column can be any of {addedAt, originallyAvailableAt, lastViewedAt, titleSort, rating, mediaHeight, duration}. dir can be asc or desc. maxresults: Only return the specified number of results libtype: Filter results to a spcifiec libtype {movie, show, episode, artist, album, track} kwargs: 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'] = plexobjects.searchType(libtype) # Iterate over the results results, subresults = [], '_init' args['X-Plex-Container-Start'] = 0 args['X-Plex-Container-Size'] = min(util.X_PLEX_CONTAINER_SIZE, maxresults) while subresults and maxresults > len(results): query = '/library/sections/%s/all%s' % (self.key, util.joinArgs(args)) subresults = plexobjects.listItems(self.server, query) results += subresults[:maxresults - len(results)] args['X-Plex-Container-Start'] += args['X-Plex-Container-Size'] return results
def extend(self, start=None, size=None): path = self.key args = {} if size is not None: args['X-Plex-Container-Start'] = start args['X-Plex-Container-Size'] = size if args: path += util.joinArgs( args ) if '?' not in path else '&' + util.joinArgs(args).lstrip('?') items = plexobjects.listItems(self.server, path) self.offset = plexobjects.PlexValue(start) self.size = plexobjects.PlexValue(len(items)) self.more = plexobjects.PlexValue( (items[0].container.offset.asInt() + items[0].container.size.asInt() < items[0].container.totalSize.asInt()) and '1' or '') return items
def extend(self, start=0, size=0): if not self._items: self._items = [None] * self.leafCount.asInt() args = {} if size is not None: args['X-Plex-Container-Start'] = start args['X-Plex-Container-Size'] = size path = '/playlists/{0}/items'.format(self.ratingKey) if args: path += util.joinArgs( args ) if '?' not in path else '&' + util.joinArgs(args).lstrip('?') items = plexobjects.listItems(self.server, path) self._items[start:start + len(items)] = items self.trigger('items.added') return items
def search(self, title, 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'] = plexobjects.searchType(libtype) for attr, value in kwargs.items(): args[attr] = value query = '/library/all%s' % util.joinArgs(args) return plexobjects.listItems(self.server, query)
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 exceptions.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'] = plexobjects.searchType(libtype) query = '/library/sections/%s/%s%s' % (self.key, category, util.joinArgs(args)) return plexobjects.listItems(self.server, query, bytag=True)
def jumpList(self, filter_=None, sort=None, unwatched=False): if self.key.startswith('/'): path = '{0}/firstCharacter'.format(self.key) else: path = '/library/sections/{0}/firstCharacter'.format(self.key) args = {} if filter_: args[filter_[0]] = filter_[1] if sort: args['sort'] = '{0}:{1}'.format(*sort) if unwatched: args[self.TYPE == 'movie' and 'unwatched' or 'unwatchedLeaves'] = 1 if args: path += util.joinArgs(args) return plexobjects.listItems(self.server, path, bytag=True)
def createRemotePlayQueue(item, contentType, options, args): util.DEBUG_LOG('Creating remote playQueue request...') obj = PlayQueue(item.getServer(), contentType, options) # The item's URI is made up of the library section UUID, a descriptor of # the item type (item or directory), and the item's path, URL-encoded. uri = "library://" + item.getLibrarySectionUuid() + "/" itemType = item.isDirectory() and "directory" or "item" path = None if not options.key: # if item.onDeck and len(item.onDeck) > 0: # options.key = item.onDeck[0].getAbsolutePath("key") # el if not item.isDirectory(): options.key = item.get("key") # If we're asked to play unwatched, ignore the option unless we are unwatched. options.unwatched = options.unwatched and item.isUnwatched() # TODO(schuyler): Until we build postplay, we're not allowed to queue containers for episodes. if item.type == "episode": options.context = options.CONTEXT_SELF elif item.type == "movie": if not options.extrasPrefixCount and not options.resume: options.extrasPrefixCount = plexapp.INTERFACE.getPreference( "cinema_trailers", 0) # How exactly to construct the item URI depends on the metadata type, though # whenever possible we simply use /library/metadata/:id. if item.isLibraryItem() and not item.isLibraryPQ: path = "/library/metadata/" + item.ratingKey else: path = item.getAbsolutePath("key") if options.context == options.CONTEXT_SELF: # If the context is specifically for just this item,: just use the # item's key and get out. pass elif item.type == "playlist": path = None uri = item.get("ratingKey") options.isPlaylist = True elif item.type == "track": # TODO(rob): Is there ever a time the container address is wrong? If we # expect to play a single track,: use options.CONTEXT_SELF. path = item.container.address or "/library/metadata/" + item.get( "parentRatingKey", "") itemType = "directory" elif item.isPhotoOrDirectoryItem(): if item.type == "photoalbum" or item.parentKey: path = item.getParentPath(item.type == "photoalbum" and "key" or "parentKey") itemType = "item" elif item.isDirectory(): path = item.getAbsolutePath("key") else: path = item.container.address itemType = "directory" options.key = item.getAbsolutePath("key") elif item.type == "episode": path = "/library/metadata/" + item.get("grandparentRatingKey", "") itemType = "directory" options.key = item.getAbsolutePath("key") # elif item.type == "show": # path = "/library/metadata/" + item.get("ratingKey", "") if path: if args: path += util.joinArgs(args) util.DEBUG_LOG("playQueue path: " + str(path)) if "/search" not in path: # Convert a few params to the PQ spec convert = {'type': "sourceType", 'unwatchedLeaves': "unwatched"} for key in convert: regex = re.compile("(?i)([?&])" + key + "=") path = regex.sub("\1" + convert[key] + "=", path) util.DEBUG_LOG("playQueue path: " + str(path)) uri = uri + itemType + "/" + urllib.quote_plus(path) util.DEBUG_LOG("playQueue uri: " + str(uri)) # Create the PQ request request = plexrequest.PlexRequest(obj.server, "/playQueues") request.addParam(not options.isPlaylist and "uri" or "playlistID", uri) request.addParam("type", contentType) # request.addParam('X-Plex-Client-Identifier', plexapp.INTERFACE.getGlobal('clientIdentifier')) # Add options we pass once during PQ creation if options.shuffle: request.addParam("shuffle", "1") options.key = None else: request.addParam("shuffle", "0") if options.key: request.addParam("key", options.key) # Add options we pass every time querying PQs obj.addRequestOptions(request) util.DEBUG_LOG('Initial playQueue request started...') context = request.createRequestContext("create", callback.Callable(obj.onResponse)) plexapp.APP.startRequest(request, context, body='') return obj