Example #1
0
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",
    )
Example #2
0
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
        ),
    ]
Example #3
0
    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)]
Example #4
0
    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)
Example #5
0
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
    )
Example #6
0
    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
Example #8
0
    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)]
Example #9
0
    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
Example #10
0
    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
Example #11
0
    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)]
Example #12
0
    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
Example #13
0
    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"}}')
Example #14
0
    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')})
Example #15
0
    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,
        )
Example #16
0
    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
Example #17
0
    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
Example #18
0
    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']
        ]
Example #19
0
    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')
Example #20
0
    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"
Example #21
0
 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)
Example #22
0
 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 {}
Example #23
0
    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')
Example #24
0
 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')
                                    ])
Example #25
0
    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 []
Example #26
0
    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')
Example #27
0
    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
Example #28
0
    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
Example #29
0
    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
Example #30
0
    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