def test_track_playback_ended_scrobbles_played_track(pylast_mock, frontend): frontend.last_start_time = 123 frontend.lastfm = mock.Mock(spec=pylast.LastFMNetwork) artists = [models.Artist(name="ABC"), models.Artist(name="XYZ")] album = models.Album(name="The Collection") track = models.Track( name="One Two Three", artists=artists, album=album, track_no=3, length=180432, musicbrainz_id="123-456", ) tl_track = models.TlTrack(track=track, tlid=17) frontend.track_playback_ended(tl_track, 150000) frontend.lastfm.scrobble.assert_called_with( "ABC, XYZ", "One Two Three", "123", duration="180", album="The Collection", track_number="3", mbid="123-456", )
def tracks(album): return [ models.Track( uri='podcast+http://www.example.com/everything.xml#episode3', name='Shake Shake Shake Your Spices', artists=[models.Artist(name='John Doe')], album=album, genre='Technology', date='2014-06-15', length=424000, track_no=3 ), models.Track( uri='podcast+http://www.example.com/everything.xml#episode2', name='Socket Wrench Shootout', artists=[models.Artist(name='Jane Doe')], album=album, genre='Technology', date='2014-06-08', length=274000, track_no=2 ), models.Track( uri=('podcast+http://www.example.com/everything.xml' '#http://example.com/everything/Episode1.mp3'), name='Red, Whine, & Blue', artists=[models.Artist(name='Various')], album=album, genre='Technology', date='2014-06-01', length=239000, track_no=1 ), ]
def create_artists(self, item={}, name=None): """Create artist object from jellyfin item. :param track: item :type track: dict :param name: Name :type name: str :returns: List of artists :rtype: list of mopidy.models.Artist """ item_type = item.get('Type', '') if item_type == 'MusicArtist': # Artists have a slightly different structure return [ models.Artist(name=item.get('Name'), uri=f'jellyfin:artist:{item.get("Id")}') ] elif item_type: # For tracks and albums return [ models.Artist(name=artist, uri=f'jellyfin:artist:{item.get("Id")}') for artist in item.get('Artists', []) ] else: # In case we only get a name return [models.Artist(name=name)]
def search(self, query): """Search Emby for a term. :param query: Search query :type query: dict :returns: Search results :rtype: mopidy.models.SearchResult """ logger.debug('Searching in Emby for {}'.format(query)) # something to store the results in data = [] tracks = [] albums = [] artists = [] for itemtype, term in query.items(): for item in term: data.extend(self._get_search(itemtype, item)) # walk through all items and create stuff for item in data: if item['Type'] == 'Audio': track_artists = [ models.Artist(name=artist) for artist in item['Artists'] ] tracks.append( models.Track(uri='emby:track:{}'.format(item['ItemId']), track_no=item.get('IndexNumber'), name=item.get('Name'), artists=track_artists, album=models.Album(name=item.get('Album'), artists=track_artists))) elif item['Type'] == 'MusicAlbum': album_artists = [ models.Artist(name=artist) for artist in item['Artists'] ] albums.append( models.Album(uri='emby:album:{}'.format(item['ItemId']), name=item.get('Name'), artists=album_artists)) elif item['Type'] == 'MusicArtist': artists.append( models.Artist(uri='emby:artist:{}'.format(item['ItemId']), name=item.get('Name'))) return models.SearchResult(uri='emby:search', tracks=tracks, artists=artists, albums=albums)
def album(): return models.Album( uri='podcast+http://www.example.com/everything.xml', name='All About Everything', artists=[models.Artist(name='John Doe')], num_tracks=3 )
def lookup(self, uri): pandora_uri = PandoraUri.factory(uri) logger.info("Looking up Pandora {} {}...".format( pandora_uri.uri_type, pandora_uri.uri)) if isinstance(pandora_uri, SearchUri): # Create the station first so that it can be browsed. station_uri = self._create_station_for_token(pandora_uri.token) track = self._browse_tracks(station_uri.uri)[0] # Recursive call to look up first track in station that was searched for. return self.lookup(track.uri) track_kwargs = {"uri": uri} (album_kwargs, artist_kwargs) = {}, {} if isinstance(pandora_uri, TrackUri): try: track = self.lookup_pandora_track(uri) except KeyError: logger.exception(f"Failed to lookup Pandora URI '{uri}'.") return [] else: if isinstance(pandora_uri, AdItemUri): track_kwargs["name"] = "Advertisement" if not track.title: track.title = "(Title not specified)" artist_kwargs["name"] = track.title if not track.company_name: track.company_name = "(Company name not specified)" album_kwargs["name"] = track.company_name else: track_kwargs["name"] = track.song_name track_kwargs["length"] = track.track_length * 1000 try: track_kwargs["bitrate"] = int(track.bitrate) except TypeError: # Bitrate not specified for this stream, ignore. pass artist_kwargs["name"] = track.artist_name album_kwargs["name"] = track.album_name elif isinstance(pandora_uri, StationUri): station = self.backend.api.get_station(pandora_uri.station_id) track_kwargs["name"] = station.name artist_kwargs["name"] = "Pandora Station" album_kwargs["name"] = ", ".join(station.genre) else: raise ValueError( "Unexpected type to perform Pandora track lookup: {}.".format( pandora_uri.uri_type)) artist_kwargs[ "uri"] = uri # Artist lookups should just point back to the track itself. track_kwargs["artists"] = [models.Artist(**artist_kwargs)] album_kwargs[ "uri"] = uri # Album lookups should just point back to the track itself. track_kwargs["album"] = models.Album(**album_kwargs) return [models.Track(**track_kwargs)]
def test_successful_translation(self, web_album_mock): album = translator.web_to_album(web_album_mock) artists = [models.Artist(uri='spotify:artist:abba', name='ABBA')] assert album.uri == 'spotify:album:def' assert album.name == 'DEF 456' assert list(album.artists) == artists
def create_artists(self, track={}, name=None): """Create artist object from track. :param track: Track :type track: dict :param name: Name :type name: str :returns: List of artists :rtype: list of mopidy.models.Artist """ if track: return [ models.Artist(name=name if name else artist.get('Name')) for artist in track.get('ArtistItems') ] else: return [models.Artist(name=name)]
def test_filters_out_none_artists(self, web_track_mock): web_track_mock["artists"].insert(0, {}) web_track_mock["artists"].insert(0, {"foo": "bar"}) track = translator.web_to_track(web_track_mock) artists = [models.Artist(uri="spotify:artist:abba", name="ABBA")] assert list(track.artists) == artists
def test_successful_translation(self, web_album_mock): album = translator.web_to_album(web_album_mock) artists = [models.Artist(uri="spotify:artist:abba", name="ABBA")] assert album.uri == "spotify:album:def" assert album.name == "DEF 456" assert list(album.artists) == artists
def lookup(self, uri): pandora_uri = PandoraUri.factory(uri) if isinstance(pandora_uri, SearchUri): # Create the station first so that it can be browsed. station_uri = self._create_station_for_token(pandora_uri.token) track = self._browse_tracks(station_uri.uri)[0] # Recursive call to look up first track in station that was searched for. return self.lookup(track.uri) if isinstance(pandora_uri, TrackUri): try: track = self.lookup_pandora_track(uri) except KeyError: logger.exception( "Failed to lookup Pandora URI '{}'.".format(uri)) return [] else: track_kwargs = {'uri': uri} (album_kwargs, artist_kwargs) = {}, {} # TODO: Album.images has been deprecated in Mopidy 1.2. Remove this code when all frontends have been # updated to make use of the newer LibraryController.get_images() images = self.get_images([uri])[uri] if len(images) > 0: album_kwargs = {'images': [image.uri for image in images]} if isinstance(pandora_uri, AdItemUri): track_kwargs['name'] = 'Advertisement' if not track.title: track.title = '(Title not specified)' artist_kwargs['name'] = track.title if not track.company_name: track.company_name = '(Company name not specified)' album_kwargs['name'] = track.company_name else: track_kwargs['name'] = track.song_name track_kwargs['length'] = track.track_length * 1000 try: track_kwargs['bitrate'] = int(track.bitrate) except TypeError: # Bitrate not specified for this stream, ignore. pass artist_kwargs['name'] = track.artist_name album_kwargs['name'] = track.album_name else: raise ValueError( 'Unexpected type to perform Pandora track lookup: {}.'.format( pandora_uri.uri_type)) artist_kwargs[ 'uri'] = uri # Artist lookups should just point back to the track itself. track_kwargs['artists'] = [models.Artist(**artist_kwargs)] album_kwargs[ 'uri'] = uri # Album lookups should just point back to the track itself. track_kwargs['album'] = models.Album(**album_kwargs) return [models.Track(**track_kwargs)]
def test_successful_translation(self, sp_track_mock): track = translator.to_track(sp_track_mock, bitrate=320) assert track.uri == 'spotify:track:abc' assert track.name == 'ABC 123' assert list(track.artists) == [ models.Artist(uri='spotify:artist:abba', name='ABBA') ] assert track.album == models.Album( uri='spotify:album:def', name='DEF 456', artists=[models.Artist(uri='spotify:artist:abba', name='ABBA')], date='2001') assert track.track_no == 7 assert track.disc_no == 1 assert track.date == '2001' assert track.length == 174300 assert track.bitrate == 320
def test_handle_json_encodes_mopidy_models(self): self.jrw.handle_data = mock.Mock() self.jrw.handle_data.return_value = {'foo': models.Artist(name='bar')} request = '[]' response = self.jrw.handle_json(request) self.assertEqual(response, '{"foo": {"__model__": "Artist", "name": "bar"}}')
def test_handle_json_decodes_mopidy_models(self): self.jrw.handle_data = mock.Mock() self.jrw.handle_data.return_value = [] request = '{"foo": {"__model__": "Artist", "name": "bar"}}' self.jrw.handle_json(request) self.jrw.handle_data.assert_called_once_with( {'foo': models.Artist(name='bar')})
def search(self, query=None, uris=None, exact=False, **kwargs): search_text = self._formatted_search_query(query) if not search_text: # No value provided for search query, abort. logger.info(f"Unsupported Pandora search query: {query}") return [] search_result = self.backend.api.search(search_text, include_near_matches=False, include_genre_stations=True) tracks = [] for genre in search_result.genre_stations: tracks.append( models.Track( uri=SearchUri(genre.token).uri, name=f"{genre.station_name} (Pandora genre)", artists=[models.Artist(name=genre.station_name)], )) for song in search_result.songs: tracks.append( models.Track( uri=SearchUri(song.token).uri, name=f"{song.song_name} (Pandora station)", artists=[models.Artist(name=song.artist)], )) artists = [] for artist in search_result.artists: search_uri = SearchUri(artist.token) if search_uri.is_artist_search: station_name = f"{artist.artist} (Pandora artist)" else: station_name = f"{artist.artist} (Pandora composer)" artists.append(models.Artist(uri=search_uri.uri, name=station_name)) return models.SearchResult( uri=f"pandora:search:{search_text}", tracks=tracks, artists=artists, )
def test_successful_translation(self, sp_track_mock): track = translator.to_track(sp_track_mock, bitrate=320) assert track.uri == "spotify:track:abc" assert track.name == "ABC 123" assert list(track.artists) == [ models.Artist(uri="spotify:artist:abba", name="ABBA") ] assert track.album == models.Album( uri="spotify:album:def", name="DEF 456", artists=[models.Artist(uri="spotify:artist:abba", name="ABBA")], date="2001", ) assert track.track_no == 7 assert track.disc_no == 1 assert track.date == "2001" assert track.length == 174300 assert track.bitrate == 320
def get_albums(self, query): raw_artist = [""] raw_albums = [] # Check query for artist name if 'artist' in query: raw_artist = query.get('artist') elif 'albumartist' in query: raw_artist = query.get('albumartist') else: return [] # URL encode artist string artist = quote(raw_artist[0].encode('utf8')).replace('/', '-') artist_ref = [models.Artist(name=raw_artist[0])] url = self.api_url( '/Artists/{}?UserId={}'.format( artist, self.user_id) ) # Pull out artist_id artist_data = self.http.get(url) artist_id = artist_data.get('Id') # Get album list if self.albumartistsort: url = self.api_url( '/Items?AlbumArtistIds={}&UserId={}&' 'IncludeItemTypes=MusicAlbum&Recursive=true'.format( artist_id, self.user_id ) ) else: url = self.api_url( '/Items?ArtistIds={}&UserId={}&' 'IncludeItemTypes=MusicAlbum&Recursive=true'.format( artist_id, self.user_id ) ) result = self.http.get(url) if result: raw_albums = result.get('Items') albums = [ models.Album( uri='jellyfin:album:{}'.format(item.get('Id')), name=item.get('Name'), artists=artist_ref ) for item in raw_albums ] return albums
def create_artists(self, track): """Create artist object from track. :param track: Track :type track: dict :returns: List of artists :rtype: list of mopidy.models.Artist """ return [ models.Artist(name=artist['Name']) for artist in track['ArtistItems'] ]
def test_handle_json_encodes_mopidy_models(self): self.jrw.handle_data = mock.Mock() self.jrw.handle_data.return_value = {'foo': models.Artist(name='bar')} request = '[]' response = json.loads(self.jrw.handle_json(request)) self.assertIn('foo', response) self.assertIn('__model__', response['foo']) self.assertEqual(response['foo']['__model__'], 'Artist') self.assertIn('name', response['foo']) self.assertEqual(response['foo']['name'], 'bar')
def test_handle_json_encodes_mopidy_models(self): self.jrw.handle_data = mock.Mock() self.jrw.handle_data.return_value = {"foo": models.Artist(name="bar")} request = "[]" response = json.loads(self.jrw.handle_json(request)) assert "foo" in response assert "__model__" in response["foo"] assert response["foo"]["__model__"] == "Artist" assert "name" in response["foo"] assert response["foo"]["name"] == "bar"
def compose_bt_track(self, uri=BTPlayerUri.BT_SONG): # Old behaviour -> Use real track data on lookup # Since track data are immutable, it is hacky to change the data on track changes # New behaviour -> Use track structure with the device name and info if not self.bt_player.is_connected(): return None dev_name = unicode(self.bt_player.get_device_name()) bt_info = {'name': dev_name, 'artists': [models.Artist(name='Bluetooth Player')], 'album': models.Album(name=dev_name, uri=BTPlayerUri.BT_DEVICE), 'uri': uri} return models.Track(**bt_info)
def translate_track_data(bt_track_data, uri): if bt_track_data: mp_track_data = { 'uri': uri, 'name': unicode(bt_track_data.get('Title', '')), 'artists': [models.Artist(name=unicode(bt_track_data.get('Artist')))], 'album': models.Album(name=unicode(bt_track_data.get('Album'))), 'length': int(bt_track_data.get('Duration')) if bt_track_data.get('Duration') != 0xFFFFFFFF else None, 'genre': unicode(bt_track_data.get('Genre'))} return models.Track(**mp_track_data) else: return {}
def setUp(self): self.config = { 'scrobbler': { 'lastfm_username': '******', 'lastfm_password': '******', 'librefm_username': '', 'librefm_password': '', } } self.frontend = frontend_lib.ScrobblerFrontend(self.config, mock.sentinel.core) self.frontend.lastfm = mock.Mock(spec=pylast.LastFMNetwork) self.frontend.networks['Last.fm'] = self.frontend.lastfm self.artists = [models.Artist(name='ABC'), models.Artist(name='XYZ')] self.track = models.Track(name='One Two Three', artists=self.artists, album=models.Album(name='The Collection'), track_no=3, length=180432, musicbrainz_id='123-456')
def pylast_to_mopidy_tracks(self, pylast_tracks): for tracks in util.segment(pylast_tracks, 20): for playlink, track in zip( self.network.get_track_play_links(tracks), tracks): if playlink is not None: yield models.Track(uri=playlink, name=track.title, artists=[ models.Artist( name=track.artist.name, uri='lastfm:arteest') ])
def lookup(self, uri): if not uri.startswith("transistor:"): return [] if uri == "transistor:noise": return [models.Track(name="Random Noise", uri=uri)] split_uri = uri.split(":") if len(split_uri) == 4: if split_uri[1] == "radios": bank_radios = self.lib.data["radio_banks"][split_uri[2]] for rad in bank_radios: if rad["position"] == int(split_uri[3]): return [ models.Track( name=rad["name"], uri=uri, artists=[models.Artist(name=rad["name"])], album=models.Album(name=split_uri[2]), ) ] if split_uri[1] == "podcasts": for podcast in self.lib.data["podcasts"]: if podcast["position"] == int(split_uri[2]): for ep in podcast["episodes"]: if split_uri[3] == ep["title"]: return [ models.Track( name=ep["title"], uri=uri, artists=[ models.Artist(name=podcast["name"]) ], album=models.Album(name=""), ) ] return []
def test_track_playback_started_updates_now_playing(self, pylast_mock): self.frontend.lastfm = mock.Mock(spec=pylast.LastFMNetwork) artists = [models.Artist(name='ABC'), models.Artist(name='XYZ')] album = models.Album(name='The Collection') track = models.Track( name='One Two Three', artists=artists, album=album, track_no=3, length=180432, musicbrainz_id='123-456') tl_track = models.TlTrack(track=track, tlid=17) self.frontend.track_playback_started(tl_track) self.frontend.lastfm.update_now_playing.assert_called_with( 'ABC, XYZ', 'One Two Three', duration='180', album='The Collection', track_number='3', mbid='123-456')
def test_successful_translation(self, web_track_mock): track = translator.web_to_track(web_track_mock) assert track.uri == 'spotify:track:abc' assert track.name == 'ABC 123' assert list(track.artists) == [ models.Artist(uri='spotify:artist:abba', name='ABBA') ] assert track.album == models.Album(uri='spotify:album:def', name='DEF 456') assert track.track_no == 7 assert track.disc_no == 1 assert track.length == 174300
def artistRelationshipsToRefs(self, relationships): artists = [] if not relationships['artists']: return [] for artistJSON in relationships['artists']['data']: uri = ARTIST_PREFIX + artistJSON['id'] name = artistJSON['attributes']['name'] artist = models.Artist(uri=uri, name=name) artists.append(artist) return artists
def test_successful_translation(self, web_track_mock): track = translator.web_to_track(web_track_mock) artists = [models.Artist(uri="spotify:artist:abba", name="ABBA")] assert track.uri == "spotify:track:abc" assert track.name == "ABC 123" assert list(track.artists) == artists assert track.album == models.Album(uri="spotify:album:def", name="DEF 456", artists=artists) assert track.track_no == 7 assert track.disc_no == 1 assert track.length == 174300
def test_ignores_invalid_artists(self, web_album_mock, web_artist_mock): invalid_artist1 = {"name": "FOO", "uri": None, "type": "artist"} invalid_artist2 = {"name": "BAR", "type": "football"} web_album_mock["artists"] = [ invalid_artist1, web_artist_mock, invalid_artist2, ] album = translator.web_to_album(web_album_mock) artists = [models.Artist(uri="spotify:artist:abba", name="ABBA")] assert album.uri == "spotify:album:def" assert album.name == "DEF 456" assert list(album.artists) == artists