def get_items(self, uri): '''Get the items in a playlist specified by uri. Returns a list of Ref objects referring to the playlist’s items. If a playlist with the given uri doesn’t exist, it returns None. Return type: list of mopidy.models.Ref, or None ''' logger.debug('Playlist: get_items %r', uri) _rx = re.compile(r'plex:playlist:(?P<plid>\d+)').match(uri) if _rx is None: return None def wrap_ref(item): return Ref.track(uri='plex:track:{}'.format(item.ratingKey), name=item.title) return [ wrap_ref(item) for item in self.plex.fetchItems('/playlists/{}/items'.format( _rx.group('plid'))) ]
def translate_uri(self, uri): '''Convert custom URI scheme to real playable URI. MAY be reimplemented by subclass. This is very likely the only thing you need to override as a backend author. Typically this is where you convert any Mopidy specific URI to a real URI and then return it. If you can’t convert the URI just return None. Parameters: uri (string) – the URI to translate Return type: string or None if the URI could not be translated''' logger.debug("Playback.translate_uri Plex with uri '%s'", uri) _rx = re.compile(r'plex:track:(?P<track_id>\d+)').match(uri) if _rx is None: # uri unknown logger.info('Unkown uri: %s', uri) return None elem = plexutils.findKey(self.backend.plex, _rx.group('track_id')) logger.info('getting file parts for eleme %r', elem) try: p = list(elem.iterParts())[0].key # hackisly get direct url of first part return '%s%s?X-Plex-Token=%s' % (elem.server.baseurl, p, self.backend.plex.token) except Exception as e: logger.exception(e) logger.info('fallback to returning stream for elem %r', elem) return elem.getStreamUrl()
def lookup(self, uri): '''Lookup playlist with given URI in both the set of playlists and in any other playlist source. Returns the playlists or None if not found. Parameters: uri (string) – playlist URI Return type: mopidy.models.Playlist or None ''' logger.debug('Playlist: lookup %r', uri) _rx = re.compile(r'plex:playlist:(?P<plid>\d+)').match(uri) if _rx is None: return None plexlist = self.plex.fetchItems('/playlists/{:s}'.format( _rx.group('plid')))[0] PL = Playlist( uri=uri, name=plexlist.title, tracks=[ wrap_track(_t, self.backend.plex_uri) for _t in plexlist.items() ], last_modified=None, # TODO: find this value ) return PL
def as_list(self): '''Get a list of the currently available playlists. Returns a list of `mopidy.models.Ref` objects referring to the playlists. In other words, no information about the playlists’ content is given.''' logger.debug('Playlist: as_list') audiolists = [l for l in self.plex.playlists() if l.playlistType == 'audio'] return [Ref(uri='plex:playlist:{}'.format(playlist.ratingKey), name=playlist.title) for playlist in audiolists]
def save(self, playlist): '''Save the given playlist. The playlist must have an uri attribute set. To create a new playlist with an URI, use create(). Returns the saved playlist or None on failure. Parameters: playlist (mopidy.models.Playlist) – the playlist to save Return type: mopidy.models.Playlist or None ''' logger.debug('Playlist: save %r', playlist)
def __init__(self, config, audio): super(PlexBackend, self).__init__(audio=audio) self.config = config self.session = get_requests_session(proxy_config=config['proxy'], user_agent='%s/%s' % (mopidy_plex.Extension.dist_name, mopidy_plex.__version__) ) self.account = MyPlexAccount.signin(config['plex']['username'], config['plex']['password']) self.plex = self.account.resource(config['plex']['server']).connect() self.music = [s for s in self.plex.library.sections() if s.TYPE == MusicSection.TYPE][0] logger.debug('Found music section on plex server %s: %s', self.plex, self.music) self.uri_schemes = ['plex', ] self.library = PlexLibraryProvider(backend=self) self.playback = PlexPlaybackProvider(audio=audio, backend=self) self.playlists = PlexPlaylistsProvider(backend=self)
def search(self, query=None, uris=None, exact=False): '''Search the library for tracks where field contains values. Parameters: query (dict) – one or more queries to search for - the dict's keys being: { 'any': *, # this is what we get without explicit modifiers 'albumartist': *, 'date': *, 'track_name': *, 'track_number': *, } uris (list of string or None) – zero or more URI roots to limit the search to exact (bool) – if the search should use exact matching Returns mopidy.models.SearchResult, which has these properties uri (string) – search result URI tracks (list of Track elements) – matching tracks artists (list of Artist elements) – matching artists albums (list of Album elements) – matching albums ''' logger.debug("Searching Plex for track '%s'", query) if query is None: logger.debug('Ignored search without query') return SearchResult(uri='plex:search') if 'uri' in query and False: # TODO add uri limiting pass else: search_query = ' '.join(query.values()[0]) search_uri = 'plex:search:%s' % urllib.quote( search_query.encode('utf-8')) logger.debug("Searching Plex with query '%s'", search_query) artists = [] tracks = [] albums = [] for hit in self.plex.search(search_query): if isinstance(hit, plexaudio.Artist): artists.append(wrap_artist(hit, self.backend.plex_uri)) elif isinstance(hit, plexaudio.Track): tracks.append(wrap_track(hit, self.backend.plex_uri)) elif isinstance(hit, plexaudio.Album): albums.append( wrap_album(hit, self.backend.plex_uri, self.backend.resolve_uri)) logger.debug("Got results: %s, %s, %s", artists, tracks, albums) return SearchResult(uri=search_uri, tracks=tracks, artists=artists, albums=albums)
def get_items(self, uri): '''Get the items in a playlist specified by uri. Returns a list of Ref objects referring to the playlist’s items. If a playlist with the given uri doesn’t exist, it returns None. Return type: list of mopidy.models.Ref, or None ''' logger.debug('Playlist: get_items %r', uri) _rx = re.compile(r'plex:playlist:(?P<plid>\d+)').match(uri) if _rx is None: return None def wrap_ref(item): return Ref.track(uri='plex:track:{}'.format(item.ratingKey), name=item.title) return [wrap_ref(item) for item in listItems(self.plex, '/playlists/{}/items'.format(_rx.group('plid')))]
def lookup(self, uri): '''Lookup playlist with given URI in both the set of playlists and in any other playlist source. Returns the playlists or None if not found. Parameters: uri (string) – playlist URI Return type: mopidy.models.Playlist or None ''' logger.debug('Playlist: lookup %r', uri) _rx = re.compile(r'plex:playlist:(?P<plid>\d+)').match(uri) if _rx is None: return None plexlist = listItems(self.plex, '/playlists/{:s}'.format(_rx.group('plid')))[0] PL = Playlist(uri=uri, name=plexlist.title, tracks=[wrap_track(_t, self.backend.plex_uri) for _t in plexlist.items()], last_modified=None, # TODO: find this value ) return PL
def search(self, query=None, uris=None, exact=False): '''Search the library for tracks where field contains values. Parameters: query (dict) – one or more queries to search for - the dict's keys being: { 'any': *, # this is what we get without explicit modifiers 'albumartist': *, 'date': *, 'track_name': *, 'track_number': *, } uris (list of string or None) – zero or more URI roots to limit the search to exact (bool) – if the search should use exact matching Returns mopidy.models.SearchResult, which has these properties uri (string) – search result URI tracks (list of Track elements) – matching tracks artists (list of Artist elements) – matching artists albums (list of Album elements) – matching albums ''' logger.info("Searching Plex for track '%s'", query) if query is None: logger.debug('Ignored search without query') return SearchResult(uri='plex:search') if 'uri' in query and False: # TODO add uri limiting pass else: search_query = ' '.join(query.values()[0]) search_uri = 'plex:search:%s' % urllib.quote(search_query.encode('utf-8')) logger.info("Searching Plex with query '%s'", search_query) artists = [] tracks = [] albums = [] for hit in self.plex.searchAudio(search_query): logger.debug('Got plex hit from query "%s": %s', search_query, hit) if isinstance(hit, plexaudio.Artist): artists.append(wrap_artist(hit, self.backend.plex_uri)) elif isinstance(hit, plexaudio.Track): tracks.append(wrap_track(hit, self.backend.plex_uri)) elif isinstance(hit, plexaudio.Album): albums.append(wrap_album(hit, self.backend.plex_uri, self.backend.resolve_uri)) logger.debug("Got results: %s, %s, %s", artists, tracks, albums) return SearchResult( uri=search_uri, tracks=tracks, artists=artists, albums=albums )
def __init__(self, config, audio): super(PlexBackend, self).__init__(audio=audio) self.config = config self.session = get_requests_session( proxy_config=config['proxy'], user_agent='%s/%s' % (mopidy_plex.Extension.dist_name, mopidy_plex.__version__)) self.account = MyPlexAccount.signin(config['plex']['username'], config['plex']['password']) self.plex = self.account.resource(config['plex']['server']).connect() self.music = [ s for s in self.plex.library.sections() if s.TYPE == MusicSection.TYPE ][0] logger.debug('Found music section on plex server %s: %s', self.plex, self.music) self.uri_schemes = [ 'plex', ] self.library = PlexLibraryProvider(backend=self) self.playback = PlexPlaybackProvider(audio=audio, backend=self) self.playlists = PlexPlaylistsProvider(backend=self)
def __init__(self, config, audio): super(PlexBackend, self).__init__(audio=audio) self.config = config self.session = get_requests_session( proxy_config=config['proxy'], user_agent='%s/%s' % (mopidy_plex.Extension.dist_name, mopidy_plex.__version__)) baseurl = (config['plex']['server']) token = (config['plex']['token']) self.plex = PlexServer(baseurl, token) self.music = [ s for s in self.plex.library.sections() if s.TYPE == MusicSection.TYPE ][0] logger.debug('Found music section on plex server %s: %s', self.plex, self.music) self.library_id = config['plex']['library_id'] self.uri_schemes = [ 'plex', ] self.library = PlexLibraryProvider(backend=self) self.playback = PlexPlaybackProvider(audio=audio, backend=self)
def __init__(self, config, audio): super(PlexBackend, self).__init__(audio=audio) self.config = config self.session = get_requests_session( proxy_config=config['proxy'], user_agent='%s/%s' % (mopidy_plex.Extension.dist_name, mopidy_plex.__version__)) type = config['plex']['type'] library = (config['plex']['library']) self.plex = None self.music = None if type == 'myplex': server = (config['plex']['server']) user = (config['plex']['username']) password = (config['plex']['password']) account = self.myplex_login(user, password) logger.info('Connecting to plex server: %s', server) self.plex = account.resource(server).connect(ssl=False) self.music = self.plex.library.section(library) elif type == 'direct': baseurl = (config['plex']['server']) token = (config['plex']['token']) self.plex = PlexServer(baseurl, token) self.music = self.plex.library.section(library) else: logger.error('Invalid value for plex backend type: %s', type) logger.info('Connected to plex server') logger.debug('Found music section on plex server %s: %s', self.plex, self.music) self.library_id = self.music.key self.uri_schemes = [ 'plex', ] self.library = PlexLibraryProvider(backend=self) self.playback = PlexPlaybackProvider(audio=audio, backend=self) self.playlists = PlexPlaylistsProvider(backend=self)
def create(self, name): '''Create a new empty playlist with the given name. Returns a new playlist with the given name and an URI.''' logger.debug('Playlist: create %r', name)
def browse(self, uri): logger.debug('browse: %s', str(uri)) if not uri: return [] if uri == self.root_directory.uri: return self._root parts = uri.split(':') sections = self.plex.library.sections() artists = [sec for sec in sections if sec.type == 'artist'] # albums if uri == 'plex:album': logger.debug('self._browse_albums()') albums = list() for a in artists: try: albums += a.albums() except Exception as e: logger.warning( 'Failed to process albums for {}: {}'.format(a, e)) # logger.info('Albums: {}'.format([a.title for a in albums])) logger.debug('{} albums found'.format(len(albums))) return [self._item_ref(item, 'album') for item in albums] # a single album # uri == 'plex:album:album_id' if len(parts) == 3 and parts[1] == 'album': logger.debug('self._browse_album(uri)') album_id = parts[2] key = '/library/metadata/{}/children'.format(album_id) return [ self._item_ref(item, 'track') for item in self.plex.fetchItems(key) ] # artists if uri == 'plex:artist': logger.debug('self._browse_artists()') return [self._item_ref(item, 'artist') for item in artists] # a single artist # uri == 'plex:artist:artist_id' if len(parts) == 3 and parts[1] == 'artist': logger.debug('self._browse_artist(uri)') artist_id = parts[2] # get albums and tracks ret = [] for item in plexutils.listItems( self.plex, '/library/metadata/{}/children'.format(artist_id)): ret.append(self._item_ref(item, 'album')) for item in plexutils.listItems( self.plex, '/library/metadata/{}/allLeaves'.format(artist_id)): ret.append(self._item_ref(item, 'track')) return ret # all tracks of a single artist # uri == 'plex:artist:artist_id:all' if len(parts) == 4 and parts[1] == 'artist' and parts[3] == 'all': logger.debug('self._browse_artist_all_tracks(uri)') artist_id = parts[2] return [ self._item_ref(item, 'track') for item in plexutils.listItems( self.plex, '/library/metadata/{}/allLeaves'.format( artist_id)) ] logger.debug('Unknown uri for browse request: %s', uri) return []
def delete(self, uri): '''Delete playlist identified by the URI.''' logger.debug('Playlist: delete %r', uri)
def browse(self, uri): logger.debug('browse: %s', str(uri)) if not uri: return [] if uri == self.root_directory.uri: return self._root parts = uri.split(':') # albums if uri == 'plex:album': logger.debug('self._browse_albums()') return [self._item_ref(item, 'album') for item in plexutils.listItems(self.plex, '/library/sections/4/albums')] # a single album # uri == 'plex:album:album_id' if len(parts) == 3 and parts[1] == 'album': logger.debug('self._browse_album(uri)') album_id = parts[2] return [self._item_ref(item, 'track') for item in plexutils.listItems(self.plex, '/library/metadata/{}/children'.format(album_id))] # artists if uri == 'plex:artist': logger.debug('self._browse_artists()') return [self._item_ref(item, 'artist') for item in plexutils.listItems(self.plex, '/library/sections/4/all')] # a single artist # uri == 'plex:artist:artist_id' if len(parts) == 3 and parts[1] == 'artist': logger.debug('self._browse_artist(uri)') artist_id = parts[2] # get albums and tracks ret = [] for item in plexutils.listItems(self.plex, '/library/metadata/{}/children'.format(artist_id)): ret.append(self._item_ref(item, 'album')) for item in plexutils.listItems(self.plex, '/library/metadata/{}/allLeaves'.format(artist_id)): ret.append(self._item_ref(item, 'track')) return ret # all tracks of a single artist # uri == 'plex:artist:artist_id:all' if len(parts) == 4 and parts[1] == 'artist' and parts[3] == 'all': logger.debug('self._browse_artist_all_tracks(uri)') artist_id = parts[2] return [self._item_ref(item, 'track') for item in plexutils.listItems(self.plex, '/library/metadata/{}/allLeaves'.format(artist_id))] logger.debug('Unknown uri for browse request: %s', uri) return []
def refresh(self): '''Refresh the playlists in playlists.''' logger.debug('Refresh')