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)
def search(self, query, mediatype=None, limit=None): """ Returns a list of media items or filter categories from the resulting `Hub Search <https://www.plex.tv/blog/seek-plex-shall-find-leveling-web-app/>`_ against all items in your Plex library. This searches genres, actors, directors, playlists, as well as all the obvious media titles. It performs spell-checking against your search terms (because KUROSAWA is hard to spell). It also provides contextual search results. So for example, if you search for 'Pernice', it’ll return 'Pernice Brothers' as the artist result, but we’ll also go ahead and return your most-listened to albums and tracks from the artist. If you type 'Arnold' you’ll get a result for the actor, but also the most recently added movies he’s in. Parameters: query (str): Query to use when searching your library. mediatype (str): Optionally limit your search to the specified media type. limit (int): Optionally limit to the specified number of results per Hub. """ results = [] params = {'query': query} if mediatype: params['section'] = utils.SEARCHTYPES[mediatype] if limit: params['limit'] = limit key = '/hubs/search?%s' % urlencode(params) for hub in self.fetchItems(key, Hub): results += hub.items return results
def getStreamURL(self, **params): """ Returns a stream url that may be used by external applications such as VLC. Parameters: **params (dict): optional parameters to manipulate the playback when accessing the stream. A few known parameters include: maxVideoBitrate, videoResolution offset, copyts, protocol, mediaIndex, platform. Raises: Unsupported: When the item doesn't support fetching a stream URL. """ if self.TYPE not in ('movie', 'episode', 'track'): raise Unsupported('Fetching stream URL for %s is unsupported.' % self.TYPE) mvb = params.get('maxVideoBitrate') vr = params.get('videoResolution', '') params = { 'path': self.key, 'offset': params.get('offset', 0), 'copyts': params.get('copyts', 1), 'protocol': params.get('protocol'), 'mediaIndex': params.get('mediaIndex', 0), 'X-Plex-Platform': params.get('platform', 'Chrome'), 'maxVideoBitrate': max(mvb, 64) if mvb else None, 'videoResolution': vr if re.match('^\d+x\d+$', vr) else None } # remove None values params = {k: v for k, v in params.items() if v is not None} streamtype = 'audio' if self.TYPE in ('track', 'album') else 'video' # sort the keys since the randomness f***s with my tests.. sorted_params = sorted(params.items(), key=lambda val: val[0]) return self._server.url('/%s/:/transcode/universal/start.m3u8?%s' % (streamtype, urlencode(sorted_params)), includeToken=True)
def getStreamURL(self, **params): """ Returns a stream url that may be used by external applications such as VLC. Parameters: **params (dict): optional parameters to manipulate the playback when accessing the stream. A few known parameters include: maxVideoBitrate, videoResolution offset, copyts, protocol, mediaIndex, platform. Raises: Unsupported: When the item doesn't support fetching a stream URL. """ if self.TYPE not in ('movie', 'episode', 'track'): raise Unsupported('Fetching stream URL for %s is unsupported.' % self.TYPE) mvb = params.get('maxVideoBitrate') vr = params.get('videoResolution', '') params = { 'path': self.key, 'offset': params.get('offset', 0), 'copyts': params.get('copyts', 1), 'protocol': params.get('protocol'), 'mediaIndex': params.get('mediaIndex', 0), 'X-Plex-Platform': params.get('platform', 'Chrome'), 'maxVideoBitrate': max(mvb, 64) if mvb else None, 'videoResolution': vr if re.match('^\d+x\d+$', vr) else None } # remove None values params = {k: v for k, v in params.items() if v is not None} streamtype = 'audio' if self.TYPE in ('track', 'album') else 'video' # sort the keys since the randomness f***s with my tests.. sorted_params = sorted(params.items(), key=lambda val: val[0]) return self._server.url('/%s/:/transcode/universal/start.m3u8?%s' % (streamtype, urlencode(sorted_params)))
def matches(self, agent=None, title=None, year=None, language=None): """ Return list of (:class:`~plexapi.media.SearchResult`) metadata matches. Parameters: agent (str): Agent name to be used (imdb, thetvdb, themoviedb, etc.) title (str): Title of item to search for year (str): Year of item to search in language (str) : Language of item to search in Examples: 1. video.matches() 2. video.matches(title="something", year=2020) 3. video.matches(title="something") 4. video.matches(year=2020) 5. video.matches(title="something", year="") 6. video.matches(title="", year=2020) 7. video.matches(title="", year="") 1. The default behaviour in Plex Web = no params in plexapi 2. Both title and year specified by user 3. Year automatically filled in 4. Title automatically filled in 5. Explicitly searches for title with blank year 6. Explicitly searches for blank title with year 7. I don't know what the user is thinking... return the same result as 1 For 2 to 7, the agent and language is automatically filled in """ key = '/library/metadata/%s/matches' % self.ratingKey params = {'manual': 1} if agent and not any([title, year, language]): params['language'] = self.section().language params['agent'] = utils.getAgentIdentifier(self.section(), agent) else: if any(x is not None for x in [agent, title, year, language]): if title is None: params['title'] = self.title else: params['title'] = title if year is None: params['year'] = self.year else: params['year'] = year params['language'] = language or self.section().language if agent is None: params['agent'] = self.section().agent else: params['agent'] = utils.getAgentIdentifier( self.section(), agent) key = key + '?' + urlencode(params) data = self._server.query(key, method=self._server._session.get) return self.findItems(data, initpath=key)
def edit(self, **kwargs): """ Edit a library (Note: agent is required). See :class:`~plexapi.library.Library` for example usage. Parameters: kwargs (dict): Dict of settings to edit. """ part = '/library/sections/%s?%s' % (self.key, urlencode(kwargs)) self._server.query(part, method=self._server._session.put) # Reload this way since the self.key dont have a full path, but is simply a id. for s in self._server.library.sections(): if s.key == self.key: return s
def _buildDetailsKey(self, **kwargs): """ Builds the details key with the XML include parameters. All parameters are included by default with the option to override each parameter or disable each parameter individually by setting it to False or 0. """ details_key = self.key if hasattr(self, '_INCLUDES'): includes = {} for k, v in self._INCLUDES.items(): value = kwargs.get(k, v) if value not in [False, 0, '0']: includes[k] = 1 if value is True else value if includes: details_key += '?' + urlencode(sorted(includes.items())) return details_key
def edit(self, **kwargs): """ Edit an object. Parameters: kwargs (dict): Dict of settings to edit. Example: {'type': 1, 'id': movie.ratingKey, 'collection[0].tag.tag': 'Super', 'collection.locked': 0} """ if 'id' not in kwargs: kwargs['id'] = self.ratingKey if 'type' not in kwargs: kwargs['type'] = utils.searchType(self.type) part = '/library/sections/%s/all?%s' % (self.librarySectionID, urlencode(kwargs)) self._server.query(part, method=self._server._session.put)
def fixMatch(self, searchResult=None, auto=False): """ 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() """ key = '/library/metadata/%s/match' % self.ratingKey if auto: searchResult = self.matches()[0] 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)
def getStreamURL(self, **params): if self.TYPE not in ('movie', 'episode', 'track'): raise Unsupported('Fetching stream URL for %s is unsupported.' % self.TYPE) mvb = params.get('maxVideoBitrate') vr = params.get('videoResolution', '') params = { 'path': self.key, 'offset': params.get('offset', 0), 'copyts': params.get('copyts', 1), 'protocol': params.get('protocol'), 'mediaIndex': params.get('mediaIndex', 0), 'X-Plex-Platform': params.get('platform', 'Chrome'), 'maxVideoBitrate': max(mvb, 64) if mvb else None, 'videoResolution': vr if re.match('^\d+x\d+$', vr) else None } params = {k: v for k, v in params.items() if v is not None} # remove None values streamtype = 'audio' if self.TYPE in ('track', 'album') else 'video' return self.server.url('/%s/:/transcode/universal/start.m3u8?%s' % (streamtype, urlencode(params)))
def getStreamURL(self, **params): if self.TYPE not in ('movie', 'episode', 'track'): raise Unsupported('Fetching stream URL for %s is unsupported.' % self.TYPE) mvb = params.get('maxVideoBitrate') vr = params.get('videoResolution', '') params = { 'path': self.key, 'offset': params.get('offset', 0), 'copyts': params.get('copyts', 1), 'protocol': params.get('protocol'), 'mediaIndex': params.get('mediaIndex', 0), 'X-Plex-Platform': params.get('platform', 'Chrome'), 'maxVideoBitrate': max(mvb,64) if mvb else None, 'videoResolution': vr if re.match('^\d+x\d+$', vr) else None } params = {k:v for k,v in params.items() if v is not None} # remove None values streamtype = 'audio' if self.TYPE in ('track', 'album') else 'video' return self.server.url('/%s/:/transcode/universal/start.m3u8?%s' % (streamtype, urlencode(params)))
def add(self, name='', type='', agent='', scanner='', location='', language='en', *args, **kwargs): """ Simplified add for the most common options. Parameters: name (str): Name of the library agent (str): Example com.plexapp.agents.imdb type (str): movie, show, # check me location (str): /path/to/files language (str): Two letter language fx en kwargs (dict): Advanced options should be passed as a dict. where the id is the key. **Photo Preferences** * **agent** (str): com.plexapp.agents.none * **enableAutoPhotoTags** (bool): Tag photos. Default value false. * **enableBIFGeneration** (bool): Enable video preview thumbnails. Default value true. * **includeInGlobal** (bool): Include in dashboard. Default value true. * **scanner** (str): Plex Photo Scanner **Movie Preferences** * **agent** (str): com.plexapp.agents.none, com.plexapp.agents.imdb, com.plexapp.agents.themoviedb * **enableBIFGeneration** (bool): Enable video preview thumbnails. Default value true. * **enableCinemaTrailers** (bool): Enable Cinema Trailers. Default value true. * **includeInGlobal** (bool): Include in dashboard. Default value true. * **scanner** (str): Plex Movie Scanner, Plex Video Files Scanner **IMDB Movie Options** (com.plexapp.agents.imdb) * **title** (bool): Localized titles. Default value false. * **extras** (bool): Find trailers and extras automatically (Plex Pass required). Default value true. * **only_trailers** (bool): Skip extras which aren't trailers. Default value false. * **redband** (bool): Use red band (restricted audiences) trailers when available. Default value false. * **native_subs** (bool): Include extras with subtitles in Library language. Default value false. * **cast_list** (int): Cast List Source: Default value 1 Possible options: 0:IMDb,1:The Movie Database. * **ratings** (int): Ratings Source, Default value 0 Possible options: 0:Rotten Tomatoes, 1:IMDb, 2:The Movie Database. * **summary** (int): Plot Summary Source: Default value 1 Possible options: 0:IMDb,1:The Movie Database. * **country** (int): Default value 46 Possible options 0:Argentina, 1:Australia, 2:Austria, 3:Belgium, 4:Belize, 5:Bolivia, 6:Brazil, 7:Canada, 8:Chile, 9:Colombia, 10:Costa Rica, 11:Czech Republic, 12:Denmark, 13:Dominican Republic, 14:Ecuador, 15:El Salvador, 16:France, 17:Germany, 18:Guatemala, 19:Honduras, 20:Hong Kong SAR, 21:Ireland, 22:Italy, 23:Jamaica, 24:Korea, 25:Liechtenstein, 26:Luxembourg, 27:Mexico, 28:Netherlands, 29:New Zealand, 30:Nicaragua, 31:Panama, 32:Paraguay, 33:Peru, 34:Portugal, 35:Peoples Republic of China, 36:Puerto Rico, 37:Russia, 38:Singapore, 39:South Africa, 40:Spain, 41:Sweden, 42:Switzerland, 43:Taiwan, 44:Trinidad, 45:United Kingdom, 46:United States, 47:Uruguay, 48:Venezuela. * **collections** (bool): Use collection info from The Movie Database. Default value false. * **localart** (bool): Prefer artwork based on library language. Default value true. * **adult** (bool): Include adult content. Default value false. * **usage** (bool): Send anonymous usage data to Plex. Default value true. **TheMovieDB Movie Options** (com.plexapp.agents.themoviedb) * **collections** (bool): Use collection info from The Movie Database. Default value false. * **localart** (bool): Prefer artwork based on library language. Default value true. * **adult** (bool): Include adult content. Default value false. * **country** (int): Country (used for release date and content rating). Default value 47 Possible options 0:, 1:Argentina, 2:Australia, 3:Austria, 4:Belgium, 5:Belize, 6:Bolivia, 7:Brazil, 8:Canada, 9:Chile, 10:Colombia, 11:Costa Rica, 12:Czech Republic, 13:Denmark, 14:Dominican Republic, 15:Ecuador, 16:El Salvador, 17:France, 18:Germany, 19:Guatemala, 20:Honduras, 21:Hong Kong SAR, 22:Ireland, 23:Italy, 24:Jamaica, 25:Korea, 26:Liechtenstein, 27:Luxembourg, 28:Mexico, 29:Netherlands, 30:New Zealand, 31:Nicaragua, 32:Panama, 33:Paraguay, 34:Peru, 35:Portugal, 36:Peoples Republic of China, 37:Puerto Rico, 38:Russia, 39:Singapore, 40:South Africa, 41:Spain, 42:Sweden, 43:Switzerland, 44:Taiwan, 45:Trinidad, 46:United Kingdom, 47:United States, 48:Uruguay, 49:Venezuela. **Show Preferences** * **agent** (str): com.plexapp.agents.none, com.plexapp.agents.thetvdb, com.plexapp.agents.themoviedb * **enableBIFGeneration** (bool): Enable video preview thumbnails. Default value true. * **episodeSort** (int): Episode order. Default -1 Possible options: 0:Oldest first, 1:Newest first. * **flattenSeasons** (int): Seasons. Default value 0 Possible options: 0:Show,1:Hide. * **includeInGlobal** (bool): Include in dashboard. Default value true. * **scanner** (str): Plex Series Scanner **TheTVDB Show Options** (com.plexapp.agents.thetvdb) * **extras** (bool): Find trailers and extras automatically (Plex Pass required). Default value true. * **native_subs** (bool): Include extras with subtitles in Library language. Default value false. **TheMovieDB Show Options** (com.plexapp.agents.themoviedb) * **collections** (bool): Use collection info from The Movie Database. Default value false. * **localart** (bool): Prefer artwork based on library language. Default value true. * **adult** (bool): Include adult content. Default value false. * **country** (int): Country (used for release date and content rating). Default value 47 options 0:, 1:Argentina, 2:Australia, 3:Austria, 4:Belgium, 5:Belize, 6:Bolivia, 7:Brazil, 8:Canada, 9:Chile, 10:Colombia, 11:Costa Rica, 12:Czech Republic, 13:Denmark, 14:Dominican Republic, 15:Ecuador, 16:El Salvador, 17:France, 18:Germany, 19:Guatemala, 20:Honduras, 21:Hong Kong SAR, 22:Ireland, 23:Italy, 24:Jamaica, 25:Korea, 26:Liechtenstein, 27:Luxembourg, 28:Mexico, 29:Netherlands, 30:New Zealand, 31:Nicaragua, 32:Panama, 33:Paraguay, 34:Peru, 35:Portugal, 36:Peoples Republic of China, 37:Puerto Rico, 38:Russia, 39:Singapore, 40:South Africa, 41:Spain, 42:Sweden, 43:Switzerland, 44:Taiwan, 45:Trinidad, 46:United Kingdom, 47:United States, 48:Uruguay, 49:Venezuela. **Other Video Preferences** * **agent** (str): com.plexapp.agents.none, com.plexapp.agents.imdb, com.plexapp.agents.themoviedb * **enableBIFGeneration** (bool): Enable video preview thumbnails. Default value true. * **enableCinemaTrailers** (bool): Enable Cinema Trailers. Default value true. * **includeInGlobal** (bool): Include in dashboard. Default value true. * **scanner** (str): Plex Movie Scanner, Plex Video Files Scanner **IMDB Other Video Options** (com.plexapp.agents.imdb) * **title** (bool): Localized titles. Default value false. * **extras** (bool): Find trailers and extras automatically (Plex Pass required). Default value true. * **only_trailers** (bool): Skip extras which aren't trailers. Default value false. * **redband** (bool): Use red band (restricted audiences) trailers when available. Default value false. * **native_subs** (bool): Include extras with subtitles in Library language. Default value false. * **cast_list** (int): Cast List Source: Default value 1 Possible options: 0:IMDb,1:The Movie Database. * **ratings** (int): Ratings Source Default value 0 Possible options: 0:Rotten Tomatoes,1:IMDb,2:The Movie Database. * **summary** (int): Plot Summary Source: Default value 1 Possible options: 0:IMDb,1:The Movie Database. * **country** (int): Country: Default value 46 Possible options: 0:Argentina, 1:Australia, 2:Austria, 3:Belgium, 4:Belize, 5:Bolivia, 6:Brazil, 7:Canada, 8:Chile, 9:Colombia, 10:Costa Rica, 11:Czech Republic, 12:Denmark, 13:Dominican Republic, 14:Ecuador, 15:El Salvador, 16:France, 17:Germany, 18:Guatemala, 19:Honduras, 20:Hong Kong SAR, 21:Ireland, 22:Italy, 23:Jamaica, 24:Korea, 25:Liechtenstein, 26:Luxembourg, 27:Mexico, 28:Netherlands, 29:New Zealand, 30:Nicaragua, 31:Panama, 32:Paraguay, 33:Peru, 34:Portugal, 35:Peoples Republic of China, 36:Puerto Rico, 37:Russia, 38:Singapore, 39:South Africa, 40:Spain, 41:Sweden, 42:Switzerland, 43:Taiwan, 44:Trinidad, 45:United Kingdom, 46:United States, 47:Uruguay, 48:Venezuela. * **collections** (bool): Use collection info from The Movie Database. Default value false. * **localart** (bool): Prefer artwork based on library language. Default value true. * **adult** (bool): Include adult content. Default value false. * **usage** (bool): Send anonymous usage data to Plex. Default value true. **TheMovieDB Other Video Options** (com.plexapp.agents.themoviedb) * **collections** (bool): Use collection info from The Movie Database. Default value false. * **localart** (bool): Prefer artwork based on library language. Default value true. * **adult** (bool): Include adult content. Default value false. * **country** (int): Country (used for release date and content rating). Default value 47 Possible options 0:, 1:Argentina, 2:Australia, 3:Austria, 4:Belgium, 5:Belize, 6:Bolivia, 7:Brazil, 8:Canada, 9:Chile, 10:Colombia, 11:Costa Rica, 12:Czech Republic, 13:Denmark, 14:Dominican Republic, 15:Ecuador, 16:El Salvador, 17:France, 18:Germany, 19:Guatemala, 20:Honduras, 21:Hong Kong SAR, 22:Ireland, 23:Italy, 24:Jamaica, 25:Korea, 26:Liechtenstein, 27:Luxembourg, 28:Mexico, 29:Netherlands, 30:New Zealand, 31:Nicaragua, 32:Panama, 33:Paraguay, 34:Peru, 35:Portugal, 36:Peoples Republic of China, 37:Puerto Rico, 38:Russia, 39:Singapore, 40:South Africa, 41:Spain, 42:Sweden, 43:Switzerland, 44:Taiwan, 45:Trinidad, 46:United Kingdom, 47:United States, 48:Uruguay, 49:Venezuela. """ part = '/library/sections?name=%s&type=%s&agent=%s&scanner=%s&language=%s&location=%s' % ( quote_plus(name), type, agent, quote_plus(scanner), language, quote_plus(location)) # noqa E126 if kwargs: part += urlencode(kwargs) return self._server.query(part, method=self._server._session.post)
def optimize(self, title=None, target="", targetTagID=None, locationID=-1, policyScope='all', policyValue="", policyUnwatched=0, videoQuality=None, deviceProfile=None): """ Optimize item locationID (int): -1 in folder with orginal items 2 library path target (str): custom quality name. if none provided use "Custom: {deviceProfile}" targetTagID (int): Default quality settings 1 Mobile 2 TV 3 Original Quality deviceProfile (str): Android, IOS, Universal TV, Universal Mobile, Windows Phone, Windows, Xbox One Example: Optimize for Mobile item.optimize(targetTagID="Mobile") or item.optimize(targetTagID=1") Optimize for Android 10 MBPS 1080p item.optimize(deviceProfile="Android", videoQuality=10) Optimize for IOS Original Quality item.optimize(deviceProfile="IOS", videoQuality=-1) * see sync.py VIDEO_QUALITIES for additional information for using videoQuality """ tagValues = [1, 2, 3] tagKeys = ["Mobile", "TV", "Original Quality"] tagIDs = tagKeys + tagValues if targetTagID not in tagIDs and (deviceProfile is None or videoQuality is None): raise BadRequest('Unexpected or missing quality profile.') if isinstance(targetTagID, str): tagIndex = tagKeys.index(targetTagID) targetTagID = tagValues[tagIndex] if title is None: title = self.title backgroundProcessing = self.fetchItem('/playlists?type=42') key = '%s/items?' % backgroundProcessing.key params = { 'Item[type]': 42, 'Item[target]': target, 'Item[targetTagID]': targetTagID if targetTagID else '', 'Item[locationID]': locationID, 'Item[Policy][scope]': policyScope, 'Item[Policy][value]': policyValue, 'Item[Policy][unwatched]': policyUnwatched } if deviceProfile: params['Item[Device][profile]'] = deviceProfile if videoQuality: from plexapi.sync import MediaSettings mediaSettings = MediaSettings.createVideo(videoQuality) params[ 'Item[MediaSettings][videoQuality]'] = mediaSettings.videoQuality params[ 'Item[MediaSettings][videoResolution]'] = mediaSettings.videoResolution params[ 'Item[MediaSettings][maxVideoBitrate]'] = mediaSettings.maxVideoBitrate params['Item[MediaSettings][audioBoost]'] = '' params['Item[MediaSettings][subtitleSize]'] = '' params['Item[MediaSettings][musicBitrate]'] = '' params['Item[MediaSettings][photoQuality]'] = '' titleParam = {'Item[title]': title} section = self._server.library.sectionByID(self.librarySectionID) params['Item[Location][uri]'] = 'library://' + section.uuid + '/item/' + \ quote_plus(self.key + '?includeExternalMedia=1') data = key + urlencode(params) + '&' + urlencode(titleParam) return self._server.query(data, method=self._server._session.put)