def browse(self, uri): logger.debug('browse: %r', uri) if not uri: return [] if uri in self._refs: return self._refs[uri] # show root if uri == self._archive_uri_base: # add current stream refs = [self._track_to_ref(self._stream_track, self._stream_uri)] # browse archive fd = urllib.urlopen(self._archive_url) soup = BeautifulSoup(fd) container = soup.find(id='listingContainer') if container: for link in soup.find_all('a'): url = link.get('href', None) name = link.string if url and name and name[0] in '1234567': # show active shows only # former shows are down # file names for special events/shows are hard to parse refs.append( Ref.album(uri=self._archive_uri % url, name=name)) if len(refs) > 0: self._refs[uri] = refs return refs parts = uri.split(':') # browse archive # uri == 'bassdrive:archive:/url if len(parts) == 3 and parts[1] == 'archive': base_url = parts[2] fd = urllib.urlopen('http://archives.bassdrivearchive.com%s' % base_url) soup = BeautifulSoup(fd) refs = [] for link in soup.find_all('a'): url = link.get('href', None) name = str(link.string).strip('/') if url and name and str(url[0]) != '/': name = str(name).strip() url = base_url + url ref_uri = self._archive_uri % url logger.debug('ref_uri: %r', ref_uri) if url[-1] == '/': refs.append(Ref.album(uri=ref_uri, name=name)) else: track = self._link_to_track(ref_uri) if track: refs.append(self._track_to_ref(track, ref_uri)) if len(refs) > 0: self._refs[uri] = refs return refs logger.warning('Unknown uri: %r', uri) return []
def test_browse_charts(config, library, genres, charts, lookup): responses.add(responses.GET, re.compile(r'.*/genres\b.*'), json=genres) responses.add(responses.GET, re.compile(r'.*/charts\b.*'), json=charts) responses.add(responses.GET, re.compile(r'.*/lookup\b.*'), json=lookup) assert library.browse('podcast+itunes:charts:1000') == [ Ref.album(name='foo', uri='podcast+http://example.com/1234'), Ref.album(name='bar', uri='podcast+http://example.com/5678') ]
def createRef(refType, name, uri): if refType == Ref.TRACK: return Ref.track(name=name, uri=uri) elif refType == Ref.ALBUM: return Ref.album(name=name, uri=uri) elif refType == Ref.ARTIST: return Ref.artist(name=name, uri=uri) elif refType == Ref.DIRECTORY: return Ref.directory(name=name, uri=uri) elif refType == Ref.PLAYLIST: return Ref.album(name=name, uri=uri) else: return None
def test_album_ref(): assert translator.ref({ 'DisplayName': 'Foo', 'URI': BASEURI + '/foo', 'Type': 'container', 'TypeEx': ALBUM_TYPE }) == Ref.album(uri=BASEURI+'/foo', name='Foo')
def raw_album_to_ref(self, album): if album is None: return None return Ref.album( name=album.get("title") or album.get("name") or UNKNOWN_ALBUM, uri=uri.get_album_uri(album.get("id")), )
def ref(obj, uri=uri): identifier = obj["identifier"] mediatype = obj["mediatype"] if mediatype == "collection": return Ref.directory(name=name(obj), uri=uri(identifier)) else: return Ref.album(name=name(obj), uri=uri(identifier))
def ref(obj, uri=uri): identifier = obj['identifier'] mediatype = obj['mediatype'] if mediatype == 'collection': return Ref.directory(name=name(obj), uri=uri(identifier)) else: return Ref.album(name=name(obj), uri=uri(identifier))
def test_listallinfo_without_uri(self): tracks = [ Track(uri="dummy:/a", name="a"), Track(uri="dummy:/foo/b", name="b"), ] self.backend.library.dummy_library = tracks self.backend.library.dummy_browse_result = { "dummy:/": [ Ref.track(uri="dummy:/a", name="a"), Ref.directory(uri="dummy:/foo", name="foo"), Ref.album(uri="dummy:/album", name="album"), Ref.artist(uri="dummy:/artist", name="artist"), Ref.playlist(uri="dummy:/pl", name="pl"), ], "dummy:/foo": [Ref.track(uri="dummy:/foo/b", name="b")], } self.send_request("listallinfo") self.assertInResponse("file: dummy:/a") self.assertInResponse("Title: a") self.assertInResponse("directory: dummy/foo") self.assertInResponse("directory: dummy/album") self.assertInResponse("directory: dummy/artist") self.assertInResponse("directory: dummy/pl") self.assertInResponse("file: dummy:/foo/b") self.assertInResponse("Title: b") self.assertInResponse("OK")
def test_listallinfo_without_uri(self): tracks = [ Track(uri='dummy:/a', name='a'), Track(uri='dummy:/foo/b', name='b') ] self.backend.library.dummy_library = tracks self.backend.library.dummy_browse_result = { 'dummy:/': [ Ref.track(uri='dummy:/a', name='a'), Ref.directory(uri='dummy:/foo', name='foo'), Ref.album(uri='dummy:/album', name='album'), Ref.artist(uri='dummy:/artist', name='artist'), Ref.playlist(uri='dummy:/pl', name='pl') ], 'dummy:/foo': [Ref.track(uri='dummy:/foo/b', name='b')] } self.send_request('listallinfo') self.assertInResponse('file: dummy:/a') self.assertInResponse('Title: a') self.assertInResponse('directory: /dummy/foo') self.assertInResponse('directory: /dummy/album') self.assertInResponse('directory: /dummy/artist') self.assertInResponse('directory: /dummy/pl') self.assertInResponse('file: dummy:/foo/b') self.assertInResponse('Title: b') self.assertInResponse('OK')
def spotify_browse_process_results(results): logger.debug('Processing spotify browse result') if 'categories' in results: result_list = results['categories'] browse_uri = 'spotifyweb:browse:categories:' arr = [ Ref.directory(uri=browse_uri + cat['id'], name=cat['name']) for cat in result_list['items'] ] elif 'playlists' in results: result_list = results['playlists'] arr = [ Ref.playlist(uri=playlist['uri'], name=playlist['name']) for playlist in result_list['items'] ] elif 'albums' in results: result_list = results['albums'] arr = [ Ref.album(uri=album['uri'], name=album['name']) for album in result_list['items'] ] else: result_list = None arr = [] cont = result_list is not None and result_list['next'] is not None logger.debug('Spotify browse result cont: %s' % cont) return arr, cont
def select_favourites(self): self.add_level(_favourites_ref) favs=self.favourites.favourites refs=[] for f in favs: name=f[0] uri=f[1] ref=_favourites_cache.get(uri) if ref is None: ref=self.core.library.browse(uri).get() if ref is not None: if len(ref) == 0: ref=self.core.library.lookup(uri).get() if len(ref)>0:ref=Ref.track(name=name,uri=uri) else: ref = None elif len(ref) == 1: ref=ref[0] else: r = next((rf for rf in ref if rf.uri==uri),None) if r is not None: ref=r elif all(r.type in self.playable_types for r in ref): ref=Ref.album(name=f[0],uri=f[1]) else: ref=Ref.directory(name=f[0],uri=f[1]) if ref is not None: _favourites_cache.add(uri,ref) if ref is not None: refs.append(ref) self.current_list = refs
def select_subscriptions(self): if not self.include_subscriptions: return self.add_level(_subscriptions_ref) refs=[Ref.album(name=c.name, uri='{}{}'.format(_channels_scheme,c.uri)) for c in \ self.subscriptions.channels] self.current_list = refs
def spotify_browse_process_results(results): logger.debug('Processing spotify browse result') if 'categories' in results: result_list = results['categories'] browse_uri = 'spotifyweb:browse:categories:' arr = [Ref.directory(uri=browse_uri + cat['id'], name=cat['name']) for cat in result_list['items']] elif 'playlists' in results: result_list = results['playlists'] arr = [Ref.playlist(uri=playlist['uri'], name=playlist['name']) for playlist in result_list['items']] elif 'albums' in results: result_list = results['albums'] arr = [Ref.album(uri=album['uri'], name=album['name']) for album in result_list['items']] else: result_list = None arr = [] cont = result_list is not None and result_list['next'] is not None logger.debug('Spotify browse result cont: %s' % cont) return arr, cont
def _ref(metadata): identifier = metadata['identifier'] uri = uritools.uricompose(SCHEME, path=identifier) name = metadata.get('title', identifier) if metadata.get('mediatype', 'collection') == 'collection': return Ref.directory(uri=uri, name=name) else: return Ref.album(uri=uri, name=name)
def browse(self, uri): logger.debug("Request to browse %s in SpotifyWebLibraryProvider", uri) if uri == self.root_directory.uri: return self._root elif uri == 'spotifyweb:artists': return self._cache.sortedArtists # return Ref directory with all artists elif uri.startswith('spotifyweb:artist:'): # get artist uri return self._cache.artists2albums.get(uri) # return Ref directory with all albums for artist elif uri.startswith('spotifyweb:album:'): # get album uri return self._cache.albums2tracks.get(uri) # return Ref directory with all tracks for album elif uri == 'spotifyweb:albums': return self._cache.sortedAlbums # return Ref directory for all albums elif uri.startswith('spotifyweb:featured-playlists') or \ uri.startswith('spotifyweb:new-releases') or \ uri.startswith('spotifyweb:categories') : ids = uri.split(':') webapi_url = 'browse/' + '/'.join(ids[1:]) # we browse the /playlists endpoint for categories if len(ids) == 3 and ids[1] == 'categories': webapi_url += '/playlists' try: offset = 0 arr = [] while True: results = self.sp_webapi()._get(webapi_url, limit=50, offset=offset) if results.has_key('categories'): result_list = results['categories'] arr += [ Ref.directory(uri='spotifyweb:categories:'+cat['id'], name=cat['name']) for cat in result_list['items']] elif results.has_key('playlists'): result_list = results['playlists'] arr += [ Ref.playlist(uri=playlist['uri'], name=playlist['name']) for playlist in result_list['items']] elif results.has_key('albums'): result_list = results['albums'] arr += [ Ref.album(uri=album['uri'], name=album['name']) for album in result_list['items']] if result_list['next'] is None: break offset = len(arr) return arr except spotipy.SpotifyException as e: logger.info('spotipy called failed') return [] else: return []
def test_album_ref(): assert translator.ref( { "DisplayName": "Foo", "URI": BASEURI + "/foo", "Type": "container", "TypeEx": ALBUM_TYPE, } ) == Ref.album(uri=BASEURI + "/foo", name="Foo")
def spotify_albums_results(results): logger.debug('Processing spotify albums result') albums = [Ref.album(uri=album['uri'], name=album['name']) for album in results['items']] logger.debug('Processing spotify albums result; next %s' % results['next']) cont = results['next'] is not None logger.debug('Spotify get albums result cont: %s' % cont) return albums, cont
def spotify_albums_results(results): logger.debug('Processing spotify albums result') albums = [ Ref.album(uri=album['uri'], name=album['name']) for album in results['items'] ] logger.debug('Processing spotify albums result; next %s' % results['next']) cont = results['next'] is not None logger.debug('Spotify get albums result cont: %s' % cont) return albums, cont
def loadRootAlbumRefs(self): refs = [] # get the user details payload = {'client_id': self.clientId} r = requests.get(sc_api + '/users/' + self.userId, params=payload, headers=defaultHeaders, timeout=10) jsono = json.loads(r.text) # get stream node streamUri = scs_uri_stream ref = Ref.album(name=myStreamLabel, uri=streamUri) imguri = jsono['avatar_url'] imguri = imguri.replace("large.jpg", imageSelector) self.imageCache[streamUri] = Image(uri=imguri) refs.append(ref) # get followings logger.info("Loading followings for user " + self.userId) payload = {'client_id': self.clientId, 'limit': limit} r = requests.get(sc_api + '/users/' + self.userId + '/followings', params=payload, headers=defaultHeaders, timeout=10) if r.status_code != 200: logger.warn("Got HTTP " + str(r.status_code)) jsono = json.loads(r.text) for follow in jsono['collection']: followingUri = scs_uri_user + str(follow['id']) ref = Ref.album(name=follow['username'], uri=followingUri) imguri = follow['avatar_url'] imguri = imguri.replace("large.jpg", imageSelector) self.imageCache[followingUri] = Image(uri=imguri) refs.append(ref) self.refCache[scs_uri_root] = refs return refs
def make_ref(data): if isinstance(data, Track): ref = Ref.track(uri=data.uri, name=data.name) elif isinstance(data, Album): ref = Ref.album(uri=data.uri, name=data.name) elif isinstance(data, Artist): ref = Ref.artist(uri=data.uri, name=data.name) elif isinstance(data, Playlist): ref = Ref.playlist(uri=data.uri, name=data.name) elif isinstance(data, Directory): ref = Ref.directory(uri=data.uri, name=data.name) return RefWithData(ref, data)
def test_browse_albums(tmp_path, caplog): make_album(tmp_path / "media" / "a1", {"name": "John Doe - One Day"}) provider = KitchenLibraryProvider(backend={}, config=make_config(tmp_path)) result = provider.browse("kitchen:albums") assert caplog.text == "" assert len(result) > 0 assert result[0].type == "album" assert result == [ Ref.album(uri="kitchen:album:95506c273e4ecb0333d19824d66ab586", name="John Doe - One Day") ]
def loadRootAlbumRefs(self): refs=[] # latest shows r =requests.get(mx_api + "/" + self.mxaccount,timeout=10) logger.info("Loading root") jsono = json.loads(r.text) ref = Ref.album(name=latestShowsLabel, uri=mc_uri_stream) imguri = jsono['pictures']['320wx320h'] self.imageCache[mc_uri_stream] = Image(uri=imguri) refs.append(ref) # following's tracks r =requests.get(mx_api + "/" + self.mxaccount + '/following/',timeout=10) logger.info("Loading followings") jsono = json.loads(r.text) for p in jsono['data']: accounturi = mc_uri + p['key'] ref = Ref.album(name=p['name'], uri=accounturi) self.imageCache[accounturi] = Image(uri=p['pictures']['320wx320h']) refs.append(ref) self.refCache[mc_uri_root] = refs return refs
def browse(self, uri): result = [] if not uri.startswith('yle:'): return result if uri == 'yle:root': categories = self.__yleapi.get_yle_category('root') for item in categories: result.append(Ref.directory(name=item['name'], uri=item['uri'])) result.append( Ref.directory(name='Live radio', uri='yle:category:live')) return result elif uri.startswith('yle:category:'): item_url = uri.split(':') id = item_url[2] result = [] if id == 'live': for station in LIVERADIO: result.append( Ref.track(name=LIVERADIO[station], uri='yle:liveradio:{0}'.format(station))) else: categories = self.__yleapi.get_yle_category(id) if categories: for item in categories: result.append( Ref.directory(name=item['name'], uri=item['uri'])) else: albums, tracks = self.__yleapi.get_yle_item(offset=0, category=id, limit=100) for i in albums: result.append( Ref.album(name=albums[i]['name'], uri=albums[i]['uri'])) return result elif uri.startswith('yle:series:'): item_url = uri.split(':') id = item_url[2] tracks = self.__yleapi.get_yle_series_info(id) result = [] for track in tracks: result.append(Ref.track(name=track['name'], uri=track['uri'])) return result return result
def test_browse_stations(tmp_path, caplog): make_station(tmp_path / "media" / "r1", { "name": "Radio 1", "stream": "http://radio1.com/stream" }) provider = KitchenLibraryProvider(backend={}, config=make_config(tmp_path)) result = provider.browse("kitchen:stations") assert caplog.text == "" assert len(result) > 0 assert result[0].type == "album" assert result == [ Ref.album(uri="kitchen:station:770e06d40b8b4d64e89c24098d25fdc2", name="Radio 1") ]
def browse(self, uri): logger.debug(u"Browse being called for %s" % uri) level = urisplit(uri).path query = self._sanitize_query(dict(urisplit(uri).getquerylist())) logger.debug("Got parsed to level: %s - query: %s" % (level, query)) result = [] if not level: logger.error("No level for uri %s" % uri) # import pdb; pdb.set_trace() if level == 'root': for row in self._browse_genre(): result.append(Ref.directory( uri=uricompose('beetslocal', None, 'genre', dict(genre=row[0])), name=row[0] if bool(row[0]) else u'No Genre')) elif level == "genre": # artist refs not browsable via mpd for row in self._browse_artist(query): result.append(Ref.directory( uri=uricompose('beetslocal', None, 'artist', dict(genre=query['genre'][0], artist=row[1])), name=row[0] if bool(row[0]) else u'No Artist')) elif level == "artist": for album in self._browse_album(query): result.append(Ref.album( uri=uricompose('beetslocal', None, 'album', dict(album=album.id)), name=album.album)) elif level == "album": for track in self._browse_track(query): result.append(Ref.track( uri="beetslocal:track:%s:%s" % ( track.id, uriencode(track.path, '/')), name=track.title)) else: logger.debug('Unknown URI: %s', uri) # logger.debug(result) return result
def test_listall_without_uri(self): tracks = [Track(uri='dummy:/a', name='a'), Track(uri='dummy:/foo/b', name='b')] self.backend.library.dummy_library = tracks self.backend.library.dummy_browse_result = { 'dummy:/': [Ref.track(uri='dummy:/a', name='a'), Ref.directory(uri='dummy:/foo', name='foo'), Ref.album(uri='dummy:/album', name='album'), Ref.playlist(uri='dummy:/pl', name='pl')], 'dummy:/foo': [Ref.track(uri='dummy:/foo/b', name='b')]} self.sendRequest('listall') self.assertInResponse('file: dummy:/a') self.assertInResponse('directory: /dummy/foo') self.assertInResponse('directory: /dummy/album') self.assertInResponse('directory: /dummy/pl') self.assertInResponse('file: dummy:/foo/b') self.assertInResponse('OK')
def browse(self, uri): logger.debug(u"Browse being called for %s" % uri) level = urisplit(uri).path query = self._sanitize_query(dict(urisplit(uri).getquerylist())) logger.debug("Got parsed to level: %s - query: %s" % (level, query)) result = [] if not level: logger.error("No level for uri %s" % uri) # import pdb; pdb.set_trace() if level == 'root': for row in self._browse_genre(): result.append( Ref.directory( uri=uricompose('beetslocal', None, 'genre', dict(genre=row[0])), name=row[0] if bool(row[0]) else u'No Genre')) elif level == "genre": # artist refs not browsable via mpd for row in self._browse_artist(query): result.append( Ref.directory( uri=uricompose( 'beetslocal', None, 'artist', dict(genre=query['genre'][0], artist=row[1])), name=row[0] if bool(row[0]) else u'No Artist')) elif level == "artist": for album in self._browse_album(query): result.append( Ref.album(uri=uricompose('beetslocal', None, 'album', dict(album=album.id)), name=album.album)) elif level == "album": for track in self._browse_track(query): result.append( Ref.track(uri="beetslocal:track:%s:%s" % (track.id, uriencode(track.path, '/')), name=track.title)) else: logger.debug('Unknown URI: %s', uri) # logger.debug(result) return result
def test_listall_without_uri(self): tracks = [Track(uri="dummy:/a", name="a"), Track(uri="dummy:/foo/b", name="b")] self.backend.library.dummy_library = tracks self.backend.library.dummy_browse_result = { "dummy:/": [ Ref.track(uri="dummy:/a", name="a"), Ref.directory(uri="dummy:/foo", name="foo"), Ref.album(uri="dummy:/album", name="album"), Ref.playlist(uri="dummy:/pl", name="pl"), ], "dummy:/foo": [Ref.track(uri="dummy:/foo/b", name="b")], } self.sendRequest("listall") self.assertInResponse("file: dummy:/a") self.assertInResponse("directory: /dummy/foo") self.assertInResponse("directory: /dummy/album") self.assertInResponse("directory: /dummy/pl") self.assertInResponse("file: dummy:/foo/b") self.assertInResponse("OK")
def list_user(user_name): try: name = enc(user_name[1:-1]) decoded = dec(urllib.unquote(name)) pre0 = u'{} '.format(decoded) pre = u"{}'s ".format(decoded) except: pre0 = '' pre = u'' cloudcasts = Ref.album(name=pre + u'cloudcasts', uri=make_special_uri(user_name, uri_cloudcasts)) favorites = Ref.directory(name=pre + u'favorites', uri=make_special_uri(user_name, uri_favorites)) playlists = Ref.directory(name=pre + u'playlists', uri=make_special_uri(user_name, uri_playlists)) following = Ref.directory(name=pre0 + u'follows', uri=make_special_uri(user_name, uri_following)) followers = Ref.directory(name=pre + u'followers', uri=make_special_uri(user_name, uri_followers)) listens = Ref.directory(name=pre0 + u'listened to', uri=make_special_uri(user_name, uri_listens)) return [cloudcasts, favorites, playlists, following, followers, listens]
def releases_to_mopidy_albums(tunigo_releases): releases = [] for release in tunigo_releases: name = '{} - {}'.format(release.artist_name, release.album_name) releases.append(Ref.album(uri=release.uri, name=name)) return releases
def create_album(tidal_album): return Ref.album(uri="tidal:album:" + str(tidal_album.id), name=tidal_album.name)
def browse(self, uri): logger.info("YTMusic browsing uri \"%s\"", uri) if uri == "ytm:root": return [ Ref.directory(uri="ytm:artist", name="Artists"), Ref.directory(uri="ytm:album", name="Albums"), Ref.directory(uri="ytm:liked", name="Liked Songs"), ] elif uri == "ytm:artist": try: library_artists = [ Ref.artist( uri=f"ytm:artist?id={a['browseId']}&upload=false", name=a["artist"]) for a in API.get_library_artists(limit=100) ] logger.info("YTMusic found %d artists in library", len(library_artists)) except Exception: logger.exception("YTMusic failed getting artists from library") library_artists = [] # try: # upload_artists = [ # Ref.artist(uri=f"ytm:artist?id={a['browseId']}&upload=true", name=a["artist"]) # for a in API.get_library_upload_artists(limit=100) # ] # logger.info("YTMusic found %d uploaded artists", len(upload_artists)) # except Exception: # logger.exception("YTMusic failed getting uploaded artists") # upload_artists = [] return library_artists # + upload_artists elif uri == "ytm:album": try: library_albums = [ Ref.album(uri=f"ytm:album?id={a['browseId']}&upload=false", name=a["title"]) for a in API.get_library_albums(limit=100) ] logger.info("YTMusic found %d albums in library", len(library_albums)) except Exception: logger.exception("YTMusic failed getting albums from library") library_albums = [] # try: # upload_albums = [ # Ref.album(uri=f"ytm:album?id={a['browseId']}&upload=true", name=a["title"]) # for a in API.get_library_upload_albums(limit=100) # ] # logger.info("YTMusic found %d uploaded albums", len(upload_albums)) # except Exception: # logger.exception("YTMusic failed getting uploaded albums") # upload_albums = [] return library_albums # + upload_albums elif uri == "ytm:liked": try: res = API.get_liked_songs(limit=100) logger.info("YTMusic found %d liked songs", len(res["tracks"])) playlistToTracks(res) return [ Ref.track(uri=f"ytm:video?id={t['videoId']}", name=t["title"]) for t in ("tracks" in res and res["tracks"]) or [] ] except Exception: logger.exception("YTMusic failed getting liked songs") elif uri.startswith("ytm:artist?"): id_, upload = parse_uri(uri) # if upload: # try: # res = API.get_library_upload_artist(id_) # uploadArtistToTracks(res) # return [ # Ref.track(uri=f"ytm:album?id={t['videoId']}", name=t["title"]) # for t in res # ] # logger.info("YTMusic found %d songs for uploaded artist \"%s\"", len(res), res[0]["artist"]["name"]) # except Exception: # logger.exception("YTMusic failed getting tracks for uploaded artist \"%s\"", id_) # else: try: res = API.get_artist(id_) logger.info( "YTMusic found %d songs for artist \"%s\" in library", len(res["songs"]), res["name"]) artistToTracks(res) return [ Ref.track(uri=f"ytm:video?id={t['videoId']}", name=t["title"]) for t in ("songs" in res and "results" in res["songs"] and res["songs"]["results"]) or [] ] except Exception: logger.exception( "YTMusic failed getting tracks for artist \"%s\"", id_) elif uri.startswith("ytm:album?"): id_, upload = parse_uri(uri) # if upload: # try: # res = API.get_library_upload_album(id_) # uploadAlbumToTracks(res, id_) # return [ # Ref.track(uri=f"ytm:video?id={t['videoId']}", name=t["title"]) # for t in ("tracks" in res and res["tracks"]) or [] # ] # logger.info("YTMusic found %d songs for uploaded album \"%s\"", len(res["tracks"]), res["title"]) # except Exception: # logger.exception("YTMusic failed getting tracks for uploaded album \"%s\"", id_) # else: try: res = API.get_album(id_) logger.info( "YTMusic found %d songs for album \"%s\" in library", len(res["tracks"]), res["title"]) albumToTracks(res, id_) return [ Ref.track(uri=f"ytm:video?id={t['videoId']}", name=t["title"]) for t in ("tracks" in res and res["tracks"]) or [] ] except Exception: logger.exception( "YTMusic failed getting tracks for album \"%s\"", id_) return []
def raw_album_to_ref(self, album): if album is None: return None return Ref.album(name=album.get('title') or album.get('name') or UNKNOWN_ALBUM, uri=uri.get_album_uri(album.get('id')))
def ref(album): return Ref.album(name=album.name, uri=album.uri)
def _make_station_ref(station_id: str, station: StationIndex): return Ref.album(uri=str(StationUri(station_id)), name=station.name)
def browse(self, uri): if not uri: return [] logger.debug("YoutubeMusic browsing uri \"%s\"", uri) if uri == "youtubemusic:root": dirs = [] if self.backend.auth: dirs += [ Ref.directory(uri="youtubemusic:artist", name="Artists"), Ref.directory(uri="youtubemusic:album", name="Albums"), ] if self.backend.liked_songs: dirs.append( Ref.directory(uri="youtubemusic:liked", name="Liked Songs")) if self.backend.history: dirs.append( Ref.directory(uri="youtubemusic:history", name="Recently Played")) if self.backend.subscribed_artist_limit: dirs.append( Ref.directory(uri="youtubemusic:subscriptions", name="Subscriptions")) dirs.append( Ref.directory(uri="youtubemusic:watch", name="Similar to last played")) if self.backend.mood_genre: dirs.append( Ref.directory(uri="youtubemusic:mood", name="Mood and Genre Playlists")) if self.backend._auto_playlist_refresh_rate: dirs.append( Ref.directory(uri="youtubemusic:auto", name="Auto Playlists")) return (dirs) elif uri == "youtubemusic:subscriptions" and self.backend.subscribed_artist_limit: try: subs = self.backend.api.get_library_subscriptions( limit=self.backend.subscribed_artist_limit) logger.debug("YoutubeMusic found %d artists in subscriptions", len(subs)) return [ Ref.artist(uri=f"youtubemusic:artist:{a['browseId']}", name=a["artist"]) for a in subs ] except Exception: logger.exception( "YoutubeMusic failed getting artists from subscriptions") elif uri == "youtubemusic:artist": try: library_artists = [ Ref.artist(uri=f"youtubemusic:artist:{a['browseId']}", name=a["artist"]) for a in self.backend.api.get_library_artists(limit=100) ] logger.debug("YoutubeMusic found %d artists in library", len(library_artists)) except Exception: logger.exception( "YoutubeMusic failed getting artists from library") library_artists = [] if self.backend.auth: try: upload_artists = [ Ref.artist( uri=f"youtubemusic:artist:{a['browseId']}:upload", name=a["artist"]) for a in self.backend.api.get_library_upload_artists( limit=100) ] logger.debug("YoutubeMusic found %d uploaded artists", len(upload_artists)) except Exception: logger.exception( "YoutubeMusic failed getting uploaded artists") upload_artists = [] else: upload_artists = [] return library_artists + upload_artists elif uri == "youtubemusic:album": try: library_albums = [ Ref.album(uri=f"youtubemusic:album:{a['browseId']}", name=a["title"]) for a in self.backend.api.get_library_albums(limit=100) ] logger.debug("YoutubeMusic found %d albums in library", len(library_albums)) except Exception: logger.exception( "YoutubeMusic failed getting albums from library") library_albums = [] if self.backend.auth: try: upload_albums = [ Ref.album( uri=f"youtubemusic:album:{a['browseId']}:upload", name=a["title"]) for a in self.backend.api.get_library_upload_albums( limit=100) ] logger.debug("YoutubeMusic found %d uploaded albums", len(upload_albums)) except Exception: logger.exception( "YoutubeMusic failed getting uploaded albums") upload_albums = [] else: upload_albums = [] return library_albums + upload_albums elif uri == "youtubemusic:liked": try: res = self.backend.api.get_liked_songs( limit=self.backend.playlist_item_limit) tracks = self.playlistToTracks(res) logger.debug("YoutubeMusic found %d liked songs", len(res["tracks"])) return [Ref.track(uri=t.uri, name=t.name) for t in tracks] except Exception: logger.exception("YoutubeMusic failed getting liked songs") elif uri == "youtubemusic:history": try: res = self.backend.api.get_history() tracks = self.playlistToTracks({'tracks': res}) logger.debug("YoutubeMusic found %d songs from recent history", len(res)) return [Ref.track(uri=t.uri, name=t.name) for t in tracks] except Exception: logger.exception( "YoutubeMusic failed getting listening history") elif uri == "youtubemusic:watch": try: playback = self.backend.playback if playback.last_id is not None: track_id = playback.last_id elif self.backend.auth: hist = self.backend.api.get_history() track_id = hist[0]['videoId'] if track_id: res = self.backend.api.get_watch_playlist( track_id, limit=self.backend.playlist_item_limit) if 'tracks' in res: logger.debug( "YoutubeMusic found %d watch songs for \"%s\"", len(res["tracks"]), track_id) res['tracks'].pop(0) tracks = self.playlistToTracks(res) return [ Ref.track(uri=t.uri, name=t.name) for t in tracks ] except Exception: logger.exception("YoutubeMusic failed getting watch songs") elif uri == "youtubemusic:mood": try: logger.debug('YoutubeMusic loading mood/genre playlists') moods = {} response = self.backend.api._send_request( 'browse', {"browseId": "FEmusic_moods_and_genres"}) for sect in nav(response, SINGLE_COLUMN_TAB + SECTION_LIST): for cat in nav(sect, ['gridRenderer', 'items']): title = nav(cat, [ 'musicNavigationButtonRenderer', 'buttonText', 'runs', 0, 'text' ]).strip() endpnt = nav(cat, [ 'musicNavigationButtonRenderer', 'clickCommand', 'browseEndpoint', 'browseId' ]) params = nav(cat, [ 'musicNavigationButtonRenderer', 'clickCommand', 'browseEndpoint', 'params' ]) moods[title] = { 'name': title, 'uri': 'youtubemusic:mood:' + params + ':' + endpnt } return [ Ref.directory(uri=moods[a]['uri'], name=moods[a]['name']) for a in sorted(moods.keys()) ] except Exception: logger.exception( 'YoutubeMusic failed to load mood/genre playlists') elif uri.startswith("youtubemusic:mood:"): try: ret = [] _, _, params, endpnt = uri.split(':') response = self.backend.api._send_request( 'browse', { "browseId": endpnt, "params": params }) for sect in nav(response, SINGLE_COLUMN_TAB + SECTION_LIST): key = [] if 'gridRenderer' in sect: key = ['gridRenderer', 'items'] elif 'musicCarouselShelfRenderer' in sect: key = ['musicCarouselShelfRenderer', 'contents'] elif 'musicImmersiveCarouselShelfRenderer' in sect: key = [ 'musicImmersiveCarouselShelfRenderer', 'contents' ] if len(key): for item in nav(sect, key): title = nav(item, ['musicTwoRowItemRenderer'] + TITLE_TEXT).strip() # if 'subtitle' in item['musicTwoRowItemRenderer']: # title += ' (' # for st in item['musicTwoRowItemRenderer']['subtitle']['runs']: # title += st['text'] # title += ')' brId = nav(item, ['musicTwoRowItemRenderer'] + NAVIGATION_BROWSE_ID) ret.append( Ref.playlist( uri=f"youtubemusic:playlist:{brId}", name=title)) return (ret) except Exception: logger.exception( 'YoutubeMusic failed getting mood/genre playlist "%s"', uri) elif uri == "youtubemusic:auto" and self.backend._auto_playlist_refresh_rate: try: return [ Ref.directory(uri=a['uri'], name=a['name']) for a in self.ytbrowse ] except Exception: logger.exception('YoutubeMusic failed getting auto playlists') elif uri.startswith("youtubemusic:auto:" ) and self.backend._auto_playlist_refresh_rate: try: for a in self.ytbrowse: if a['uri'] == uri: ret = [] for i in a['items']: if i['type'] == 'playlist': ret.append( Ref.playlist(uri=i['uri'], name=i['name'])) logger.debug("playlist: %s - %s", i['name'], i['uri']) elif i['type'] == 'artist': ret.append( Ref.artist(uri=i['uri'], name=i['name'])) logger.debug("artist: %s - %s", i['name'], i['uri']) elif i['type'] == 'album': ret.append( Ref.album(uri=i['uri'], name=i['name'])) logger.debug("album: %s - %s", i['name'], i['uri']) return (ret) except Exception: logger.exception( 'YoutubeMusic failed getting auto playlist "%s"', uri) elif uri.startswith("youtubemusic:artist:"): bId, upload = parse_uri(uri) if upload: try: res = self.backend.api.get_library_upload_artist(bId) tracks = self.uploadArtistToTracks(res) logger.debug( "YoutubeMusic found %d songs for uploaded artist \"%s\"", len(res), res[0]["artist"]["name"]) return [Ref.track(uri=t.uri, name=t.name) for t in tracks] except Exception: logger.exception( "YoutubeMusic failed getting tracks for uploaded artist \"%s\"", bId) else: try: res = self.backend.api.get_artist(bId) tracks = self.artistToTracks(res) logger.debug( "YoutubeMusic found %d songs for artist \"%s\" in library", len(res["songs"]), res["name"]) return [Ref.track(uri=t.uri, name=t.name) for t in tracks] except Exception: logger.exception( "YoutubeMusic failed getting tracks for artist \"%s\"", bId) elif uri.startswith("youtubemusic:album:"): bId, upload = parse_uri(uri) if upload: try: res = self.backend.api.get_library_upload_album(bId) tracks = self.uploadAlbumToTracks(res, bId) logger.debug( "YoutubeMusic found %d songs for uploaded album \"%s\"", len(res["tracks"]), res["title"]) return [Ref.track(uri=t.uri, name=t.name) for t in tracks] except Exception: logger.exception( "YoutubeMusic failed getting tracks for uploaded album \"%s\"", bId) else: try: res = self.backend.api.get_album(bId) tracks = self.albumToTracks(res, bId) logger.debug( "YoutubeMusic found %d songs for album \"%s\" in library", len(res["tracks"]), res["title"]) return [Ref.track(uri=t.uri, name=t.name) for t in tracks] except Exception: logger.exception( "YoutubeMusic failed getting tracks for album \"%s\"", bId) elif uri.startswith("youtubemusic:playlist:"): bId, upload = parse_uri(uri) try: res = self.backend.api.get_playlist( bId, limit=self.backend.playlist_item_limit) tracks = self.playlistToTracks(res) return [Ref.track(uri=t.uri, name=t.name) for t in tracks] except Exception: logger.exception( "YoutubeMusic failed to get tracks from playlist '%s'", bId) return []
def test_album_constructor(self): ref = Ref.album(uri='foo', name='bar') self.assertEqual(ref.uri, 'foo') self.assertEqual(ref.name, 'bar') self.assertEqual(ref.type, Ref.ALBUM)
def test_album_constructor(self): ref = Ref.album(uri="foo", name="bar") assert ref.uri == "foo" assert ref.name == "bar" assert ref.type == Ref.ALBUM
def browse(self, uri): if not uri: return [] logger.debug('Bandcamp browse : "%s"', uri) if uri == "bandcamp:browse": dirs = [] if self.pages: dirs += [ Ref.directory(uri="bandcamp:genres", name="Discover by Genre"), Ref.directory(uri="bandcamp:tags", name="Discover by Tag"), ] if self.backend.config["bandcamp"]["identity"]: dirs.append( Ref.directory(uri="bandcamp:collection", name="Collection")) dirs.append( Ref.directory(uri="bandcamp:wishlist", name="Wishlist")) return dirs for colltype in ["collection", "wishlist"]: if uri.startswith("bandcamp:" + colltype): token = None if uri != "bandcamp:" + colltype: token = uri.split(":", 2)[2] out = [] try: data = self.backend.bandcamp.get_collection(token=token, ctype=colltype) for i in data["items"]: if "item_art" in i and "art_id" in i["item_art"]: art = (f"a{i['item_art']['art_id']:010d}" if i["item_art"]["art_id"] else None) if i["tralbum_type"] == "a": aId = f"{i['band_id']}-{i['album_id']}" name = f"{i['band_name']} - {i['album_title']} (Album)" if art: self.images[aId] = art if colltype == "collection": out.append( Ref.album(uri=f"bandcamp:myalbum:{aId}", name=name)) self.scrape_urls[ f"bandcamp:myalbum:{aId}"] = i["item_url"] else: out.append( Ref.album(uri=f"bandcamp:album:{aId}", name=name)) elif i["tralbum_type"] == "t": aId = 0 if i["album_id"] is not None: aId = i["album_id"] tId = f"{i['band_id']}-{aId}-{i['item_id']}" name = f"{i['item_title']} (Track)" if art: self.images[tId] = art if colltype == "collection": out.append( Ref.album(uri=f"bandcamp:mytrack:{tId}", name=name)) self.scrape_urls[ f"bandcamp:mytrack:{tId}"] = i["item_url"] else: out.append( Ref.album(uri=f"bandcamp:track:{tId}", name=name)) if data["more_available"]: out.append( Ref.directory( uri="bandcamp:" + colltype + ":" + data["last_token"], name="More...", )) except Exception: logger.exception("Failed to get collection") return out if uri == "bandcamp:genres" or uri == "bandcamp:tags": stype = uri.split(":")[1] return [ Ref.directory( uri="bandcamp:" + ("tag:" if stype == "tags" else "genre:") + re.sub(r",", "%2C", re.sub(r"[^a-z0-9,]", "-", d.lower())), name=d, ) for d in (self.tags if stype == "tags" else self.genres) ] if re.match(r"^bandcamp:(genre|tag):", uri): component = uri.split(":") stype, sid = component[1:3] total = 0 pagenum = int(component[3]) if (len(component) > 3) else 0 out = [] for page in range(self.pages): try: if stype == "genre": resp = self.backend.bandcamp.discover(genre=sid, page=page + pagenum) else: resp = self.backend.bandcamp.discover(tag=sid, page=page + pagenum) except Exception: logger.exception('Bandcamp failed to discover genre "%s"', uri) total = resp["total_count"] if ("total_count" in resp) else 0 for i in resp["items"] if ("items" in resp) else []: art = f"a{i['art_id']:010d}" if ("art_id" in i) else None if i["type"] == "a": aId = f"{i['band_id']}-{i['id']}" name = f"{i['secondary_text']} - {i['primary_text']} (Album)" if art: self.images[aId] = art out.append( Ref.album(uri="bandcamp:album:" + aId, name=name)) else: # Only seen discover return album types. logger.info("Found unknown type: '%s'", i["type"]) logger.info(i) if (pagenum + self.pages) * self.backend.bandcamp.PAGE_ITEMS < total: pagenum += self.pages out.append( Ref.directory(uri=f"bandcamp:{stype}:{sid}:{pagenum}", name="More...")) return out elif re.match(r"^bandcamp:(my)?(track|album):", uri): tracks = self.lookup(uri) return [Ref.track(uri=t.uri, name=t.name) for t in tracks]