Beispiel #1
0
 def _browse_genre_stations(self, uri):
     return [
         models.Ref.directory(name=station.name, uri=PandoraUri.factory(station).uri)
         for station in self.backend.api.get_genre_stations()[
             PandoraUri.factory(uri).category_name
         ]
     ]
Beispiel #2
0
 def _browse_genre_stations(self, uri):
     return [
         models.Ref.directory(name=station.name,
                              uri=PandoraUri.factory(station).uri)
         for station in self.backend.api.get_genre_stations()[
             PandoraUri.factory(uri).category_name]
     ]
Beispiel #3
0
def test_factory_returns_correct_station_uri_types():
        station_mock = mock.PropertyMock(spec=GenreStation)
        station_mock.id = 'G100'
        station_mock.token = 'G100'
        assert type(PandoraUri.factory(station_mock)) is GenreStationUri

        station_mock = mock.PropertyMock(spec=Station)
        station_mock.id = 'id_mock'
        station_mock.token = 'token_mock'
        assert type(PandoraUri.factory(station_mock)) is StationUri
Beispiel #4
0
def test_factory_returns_correct_station_uri_types():
    station_mock = mock.PropertyMock(spec=GenreStation)
    station_mock.id = 'G100'
    station_mock.token = 'G100'
    assert type(PandoraUri.factory(station_mock)) is GenreStationUri

    station_mock = mock.PropertyMock(spec=Station)
    station_mock.id = 'id_mock'
    station_mock.token = 'token_mock'
    assert type(PandoraUri.factory(station_mock)) is StationUri
Beispiel #5
0
 def is_station_changed(self, track):
     try:
         previous_track_uri = PandoraUri.factory(
             self.core.history.get_history().get()[1][1].uri)
         if previous_track_uri.station_id != PandoraUri.factory(
                 track.uri).station_id:
             return True
     except (IndexError, NotImplementedError):
         # No tracks in history, or last played track was not a Pandora track. Ignore
         pass
     return False
Beispiel #6
0
 def is_station_changed(self, track):
     try:
         previous_track_uri = PandoraUri.factory(
             self.core.history.get_history().get()[1][1].uri
         )
         if (
             previous_track_uri.station_id
             != PandoraUri.factory(track.uri).station_id
         ):
             return True
     except (IndexError, NotImplementedError):
         # No tracks in history, or last played track was not a Pandora track. Ignore
         pass
     return False
Beispiel #7
0
def test_factory_track():
    track = models.Track(name='name_mock', uri='pandora:track:station_id_mock:track_token_mock')

    obj = PandoraUri.factory(track)

    assert type(obj) is PlaylistItemUri
    assert obj.uri == track.uri
def test_change_track_skips_if_track_not_available_in_buffer(provider, playlist_item_mock, caplog):
    track = PandoraUri.factory(playlist_item_mock)

    provider.backend.prepare_next_track = mock.PropertyMock()

    assert provider.change_track(track) is False
    assert "Error changing Pandora track: failed to lookup '{}'.".format(track.uri) in caplog.text
Beispiel #9
0
 def get_images(self, uris):
     result = {}
     for uri in uris:
         image_uris = set()
         try:
             track = self.lookup_pandora_track(uri)
             if track.is_ad is True:
                 image_uri = track.image_url
             else:
                 image_uri = track.album_art_url
             if image_uri:
                 image_uris.update([image_uri])
         except (TypeError, KeyError):
             pandora_uri = PandoraUri.factory(uri)
             if isinstance(pandora_uri, TrackUri):
                 # Could not find the track as expected - exception.
                 logger.exception(
                     "Failed to lookup image for Pandora URI '{}'.".format(
                         uri))
             else:
                 # Lookup
                 logger.warning(
                     "No images available for Pandora URIs of type '{}'.".
                     format(pandora_uri.uri_type))
             pass
         result[uri] = [models.Image(uri=u) for u in image_uris]
     return result
def test_get_images_for_unsupported_uri_type_issues_warning(config, caplog):
    backend = conftest.get_backend(config)

    search_uri = PandoraUri.factory('pandora:search:R12345')
    results = backend.library.get_images([search_uri.uri])
    assert len(results[search_uri.uri]) == 0
    assert "No images available for Pandora URIs of type 'search'.".format(search_uri.uri) in caplog.text
Beispiel #11
0
    def change_track(self, track):
        if track.uri is None:
            logger.warning(
                "No URI for Pandora track '{}'. Track cannot be played.".
                format(track))
            return False

        pandora_uri = PandoraUri.factory(track.uri)
        if isinstance(pandora_uri, StationUri):
            # Change to first track in station playlist.
            logger.warning(
                "Cannot play Pandora stations directly. "
                f"Retrieving tracks for station with ID: {pandora_uri.station_id}"
            )
            self.backend.end_of_tracklist_reached(
                station_id=pandora_uri.station_id, auto_play=True)
            return False
        try:
            self._trigger_track_changing(track)
            self.check_skip_limit()
            self.change_pandora_track(track)
            return super().change_track(track)

        except KeyError:
            logger.exception(
                f"Error changing Pandora track: failed to lookup {track.uri!r}"
            )
            return False
        except (MaxSkipLimitExceeded, Unplayable) as e:
            logger.warning(e)
            return False
Beispiel #12
0
    def track_unplayable(self, track):
        if self.is_end_of_tracklist_reached(track):
            self.core.playback.stop()
            self._trigger_end_of_tracklist_reached(
                PandoraUri.factory(track).station_id, auto_play=True)

        self.core.tracklist.remove({'uri': [track.uri]})
Beispiel #13
0
 def update_tracklist(self, track):
     if self.is_station_changed(track):
         # Station has changed, remove tracks from previous station from tracklist.
         self._trim_tracklist(keep_only=track)
     if self.is_end_of_tracklist_reached(track):
         self._trigger_end_of_tracklist_reached(
             PandoraUri.factory(track).station_id, auto_play=False)
Beispiel #14
0
def test_change_track_enforces_skip_limit_if_no_audio_url(
        provider, playlist_item_mock, caplog):
    with mock.patch.object(
            PandoraLibraryProvider,
            "lookup_pandora_track",
            return_value=playlist_item_mock,
    ):
        track = PandoraUri.factory(playlist_item_mock)

        provider._trigger_track_unplayable = mock.PropertyMock()
        provider._trigger_skip_limit_exceeded = mock.PropertyMock(0)

        playlist_item_mock.audio_url = None

        for i in range(PandoraPlaybackProvider.SKIP_LIMIT + 1):
            assert provider.change_track(track) is False
            if i < PandoraPlaybackProvider.SKIP_LIMIT - 1:
                assert provider._trigger_track_unplayable.called
                provider._trigger_track_unplayable.reset_mock()
                assert not provider._trigger_skip_limit_exceeded.called
            else:
                assert not provider._trigger_track_unplayable.called
                assert provider._trigger_skip_limit_exceeded.called

        assert ("Maximum track skip limit ({:d}) exceeded.".format(
            PandoraPlaybackProvider.SKIP_LIMIT) in caplog.text)
Beispiel #15
0
def test_change_track_enforces_skip_limit_on_request_exceptions(
        provider, playlist_item_mock, caplog):
    with mock.patch.object(
            PandoraLibraryProvider,
            "lookup_pandora_track",
            return_value=playlist_item_mock,
    ):
        with mock.patch.object(
                APITransport,
                "__call__",
                side_effect=conftest.request_exception_mock,
        ):
            track = PandoraUri.factory(playlist_item_mock)

            provider._trigger_track_unplayable = mock.PropertyMock()
            provider._trigger_skip_limit_exceeded = mock.PropertyMock(0)
            playlist_item_mock.audio_url = "pandora:track:mock_id:mock_token"

            for i in range(PandoraPlaybackProvider.SKIP_LIMIT + 1):
                assert provider.change_track(track) is False
                if i < PandoraPlaybackProvider.SKIP_LIMIT - 1:
                    assert provider._trigger_track_unplayable.called
                    provider._trigger_track_unplayable.reset_mock()
                    assert not provider._trigger_skip_limit_exceeded.called
                else:
                    assert not provider._trigger_track_unplayable.called
                    assert provider._trigger_skip_limit_exceeded.called

            assert ("Maximum track skip limit ({:d}) exceeded.".format(
                PandoraPlaybackProvider.SKIP_LIMIT) in caplog.text)
Beispiel #16
0
 def get_images(self, uris):
     result = {}
     for uri in uris:
         image_uris = set()
         try:
             track = self.lookup_pandora_track(uri)
             if track.is_ad is True:
                 image_uri = track.image_url
             else:
                 image_uri = track.album_art_url
             if image_uri:
                 image_uris.update([image_uri])
         except (TypeError, KeyError):
             pandora_uri = PandoraUri.factory(uri)
             if isinstance(pandora_uri, TrackUri):
                 # Could not find the track as expected - exception.
                 logger.exception(
                     "Failed to lookup image for Pandora URI '{}'.".format(uri)
                 )
             else:
                 # Lookup
                 logger.warning(
                     "No images available for Pandora URIs of type '{}'.".format(
                         pandora_uri.uri_type
                     )
                 )
             pass
         result[uri] = [models.Image(uri=u) for u in image_uris]
     return result
def test_get_images_for_unknown_uri_returns_empty_list(config, caplog):
    backend = conftest.get_backend(config)

    track_uri = PandoraUri.factory('pandora:track:mock_id:mock_token')
    results = backend.library.get_images([track_uri.uri])
    assert len(results[track_uri.uri]) == 0
    assert "Failed to lookup image for Pandora URI '{}'.".format(track_uri.uri) in caplog.text
Beispiel #18
0
    def change_track(self, track):
        if track.uri is None:
            logger.warning(
                "No URI for Pandora track '{}'. Track cannot be played.".format(track)
            )
            return False

        pandora_uri = PandoraUri.factory(track.uri)
        if isinstance(pandora_uri, StationUri):
            # Change to first track in station playlist.
            logger.warning(
                "Cannot play Pandora stations directly. Retrieving tracks for station with ID: {}...".format(
                    pandora_uri.station_id
                )
            )
            self.backend.end_of_tracklist_reached(
                station_id=pandora_uri.station_id, auto_play=True
            )
            return False
        try:
            self._trigger_track_changing(track)
            self.check_skip_limit()
            self.change_pandora_track(track)
            return super(PandoraPlaybackProvider, self).change_track(track)

        except KeyError:
            logger.exception(
                "Error changing Pandora track: failed to lookup '{}'.".format(track.uri)
            )
            return False
        except (MaxSkipLimitExceeded, Unplayable) as e:
            logger.warning(e)
            return False
Beispiel #19
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_get_images_for_ad_with_images(config, ad_item_mock):
    backend = conftest.get_backend(config)

    ad_uri = PandoraUri.factory('pandora:ad:{}:{}'.format(conftest.MOCK_STATION_ID, conftest.MOCK_TRACK_AD_TOKEN))
    backend.library.pandora_track_cache[ad_uri.uri] = TrackCacheItem(mock.Mock(spec=models.Ref.track), ad_item_mock)
    results = backend.library.get_images([ad_uri.uri])
    assert len(results[ad_uri.uri]) == 1
    assert results[ad_uri.uri][0].uri == ad_item_mock.image_url
Beispiel #21
0
 def update_tracklist(self, track):
     if self.is_station_changed(track):
         # Station has changed, remove tracks from previous station from tracklist.
         self._trim_tracklist(keep_only=track)
     if self.is_end_of_tracklist_reached(track):
         self._trigger_end_of_tracklist_reached(
             PandoraUri.factory(track).station_id, auto_play=False
         )
def test_lookup_of_missing_track(config, playlist_item_mock, caplog):
    backend = conftest.get_backend(config)

    track_uri = PandoraUri.factory(playlist_item_mock)
    results = backend.library.lookup(track_uri.uri)

    assert len(results) == 0
    assert "Failed to lookup Pandora URI '{}'.".format(track_uri.uri) in caplog.text
Beispiel #23
0
    def track_unplayable(self, track):
        if self.is_end_of_tracklist_reached(track):
            self.core.playback.stop()
            self._trigger_end_of_tracklist_reached(
                PandoraUri.factory(track).station_id, auto_play=True
            )

        self.core.tracklist.remove({"uri": [track.uri]})
Beispiel #24
0
def test_factory_track_ref():
    track_ref = models.Ref(
        name="name_mock", uri="pandora:track:station_id_mock:track_token_mock")

    obj = PandoraUri.factory(track_ref)

    assert type(obj) is PlaylistItemUri
    assert obj.uri == track_ref.uri
Beispiel #25
0
def test_factory_track():
    track = models.Track(name='name_mock',
                         uri='pandora:track:station_id_mock:track_token_mock')

    obj = PandoraUri.factory(track)

    assert type(obj) is PlaylistItemUri
    assert obj.uri == track.uri
Beispiel #26
0
def test_lookup_of_missing_track(config, playlist_item_mock, caplog):
    backend = conftest.get_backend(config)

    track_uri = PandoraUri.factory(playlist_item_mock)
    results = backend.library.lookup(track_uri.uri)

    assert len(results) == 0
    assert f"Failed to lookup Pandora URI '{track_uri.uri}'." in caplog.text
Beispiel #27
0
def test_get_images_for_unknown_uri_returns_empty_list(config, caplog):
    backend = conftest.get_backend(config)

    track_uri = PandoraUri.factory('pandora:track:mock_id:mock_token')
    results = backend.library.get_images([track_uri.uri])
    assert len(results[track_uri.uri]) == 0
    assert "Failed to lookup image for Pandora URI '{}'.".format(
        track_uri.uri) in caplog.text
Beispiel #28
0
def test_get_images_for_unsupported_uri_type_issues_warning(config, caplog):
    backend = conftest.get_backend(config)

    search_uri = PandoraUri.factory('pandora:search:R12345')
    results = backend.library.get_images([search_uri.uri])
    assert len(results[search_uri.uri]) == 0
    assert "No images available for Pandora URIs of type 'search'.".format(
        search_uri.uri) in caplog.text
Beispiel #29
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)]
def test_change_track_fetches_next_track_if_station_uri(provider, get_station_mock_return_value, caplog):
    station = PandoraUri.factory(get_station_mock_return_value)

    provider.backend._trigger_next_track_available = mock.PropertyMock()

    assert provider.change_track(station) is False
    assert 'Cannot play Pandora stations directly. Retrieving tracks for station with ID: {}...'.format(
        station.station_id) in caplog.text
    assert provider.backend._trigger_next_track_available.called
def test_change_track_resets_skips_on_success(provider, playlist_item_mock):
    with mock.patch.object(PandoraLibraryProvider, 'lookup_pandora_track', return_value=playlist_item_mock):
        with mock.patch.object(PlaylistItem, 'get_is_playable', return_value=True):
            track = PandoraUri.factory(playlist_item_mock)

            provider._consecutive_track_skips = 1

            assert provider.change_track(track) is True
            assert provider._consecutive_track_skips == 0
def test_change_track_triggers_event_on_success(provider, playlist_item_mock):
    with mock.patch.object(PandoraLibraryProvider, 'lookup_pandora_track', return_value=playlist_item_mock):
        with mock.patch.object(PlaylistItem, 'get_is_playable', return_value=True):
            track = PandoraUri.factory(playlist_item_mock)

            provider._trigger_track_changing = mock.PropertyMock()

            assert provider.change_track(track) is True
            assert provider._trigger_track_changing.called
Beispiel #33
0
def test_change_track_skips_if_track_not_available_in_buffer(
        provider, playlist_item_mock, caplog):
    track = PandoraUri.factory(playlist_item_mock)

    provider.backend.prepare_next_track = mock.PropertyMock()

    assert provider.change_track(track) is False
    assert (f"Error changing Pandora track: failed to lookup '{track.uri}'"
            in caplog.text)
def test_get_images_for_track_with_images(config, playlist_item_mock):
    backend = conftest.get_backend(config)

    track_uri = PandoraUri.factory('pandora:track:mock_id:mock_token')
    backend.library.pandora_track_cache[track_uri.uri] = TrackCacheItem(mock.Mock(spec=models.Ref.track),
                                                                        playlist_item_mock)
    results = backend.library.get_images([track_uri.uri])
    assert len(results[track_uri.uri]) == 1
    assert results[track_uri.uri][0].uri == playlist_item_mock.album_art_url
Beispiel #35
0
def test_get_images_for_track_with_images(config, playlist_item_mock):
    backend = conftest.get_backend(config)

    track_uri = PandoraUri.factory('pandora:track:mock_id:mock_token')
    backend.library.pandora_track_cache[track_uri.uri] = TrackCacheItem(
        mock.Mock(spec=models.Ref.track), playlist_item_mock)
    results = backend.library.get_images([track_uri.uri])
    assert len(results[track_uri.uri]) == 1
    assert results[track_uri.uri][0].uri == playlist_item_mock.album_art_url
Beispiel #36
0
    def get_images(self, uris):
        result = {}
        for uri in uris:
            image_uris = set()
            try:
                image_uri = None
                pandora_uri = PandoraUri.factory(uri)

                logger.info("Retrieving images for Pandora {} {}...".format(
                    pandora_uri.uri_type, pandora_uri.uri))

                if isinstance(pandora_uri, AdItemUri) or isinstance(
                        pandora_uri, TrackUri):
                    track = self.lookup_pandora_track(uri)
                    if track.is_ad is True:
                        image_uri = track.image_url
                    else:
                        image_uri = track.album_art_url
                elif isinstance(pandora_uri, StationUri):
                    # GenreStations don't appear to have artwork available via the
                    # json API
                    if not isinstance(pandora_uri, GenreStationUri):
                        station = self.backend.api.get_station(
                            pandora_uri.station_id)
                        image_uri = station.art_url
                else:
                    # Lookup
                    logger.warning(
                        "No images available for Pandora URIs of type '{}'.".
                        format(pandora_uri.uri_type))

                if image_uri:
                    image_uris.update(
                        [image_uri.replace("http://", "https://", 1)])
            except (TypeError, KeyError):
                pandora_uri = PandoraUri.factory(uri)
                if isinstance(pandora_uri, TrackUri):
                    # Could not find the track as expected - exception.
                    logger.exception(
                        "Failed to lookup image for Pandora URI '{}'.".format(
                            uri))

            result[uri] = [models.Image(uri=u) for u in image_uris]
        return result
def test_change_track_fetches_next_track_if_unplayable(provider, playlist_item_mock, caplog):
    with mock.patch.object(PandoraLibraryProvider, 'lookup_pandora_track', return_value=None):
        track = PandoraUri.factory(playlist_item_mock)

        provider._trigger_track_unplayable = mock.PropertyMock()

        assert provider.change_track(track) is False
        assert provider._trigger_track_unplayable.called

        assert 'Error changing Pandora track' in caplog.text
Beispiel #38
0
def test_change_track_fetches_next_track_if_station_uri(
        provider, get_station_mock_return_value, caplog):
    station = PandoraUri.factory(get_station_mock_return_value)

    provider.backend._trigger_next_track_available = mock.PropertyMock()

    assert provider.change_track(station) is False
    assert 'Cannot play Pandora stations directly. Retrieving tracks for station with ID: {}...'.format(
        station.station_id) in caplog.text
    assert provider.backend._trigger_next_track_available.called
Beispiel #39
0
def test_get_images_for_ad_with_images(config, ad_item_mock):
    backend = conftest.get_backend(config)

    ad_uri = PandoraUri.factory('pandora:ad:{}:{}'.format(
        conftest.MOCK_STATION_ID, conftest.MOCK_TRACK_AD_TOKEN))
    backend.library.pandora_track_cache[ad_uri.uri] = TrackCacheItem(
        mock.Mock(spec=models.Ref.track), ad_item_mock)
    results = backend.library.get_images([ad_uri.uri])
    assert len(results[ad_uri.uri]) == 1
    assert results[ad_uri.uri][0].uri == ad_item_mock.image_url
def test_get_next_pandora_track_fetches_track(config, playlist_item_mock):
    backend = conftest.get_backend(config)

    station_mock = mock.Mock(spec=Station)
    station_mock.id = 'id_token_mock'
    backend.library.pandora_station_cache[station_mock.id] = StationCacheItem(station_mock, iter([playlist_item_mock]))

    ref = backend.library.get_next_pandora_track('id_token_mock')
    assert ref.uri == PandoraUri.factory(playlist_item_mock).uri
    assert backend.library.pandora_track_cache[ref.uri].ref == ref
    assert backend.library.pandora_track_cache[ref.uri].track == playlist_item_mock
Beispiel #41
0
def test_change_track_triggers_event_on_success(provider, playlist_item_mock):
    with mock.patch.object(PandoraLibraryProvider,
                           'lookup_pandora_track',
                           return_value=playlist_item_mock):
        with mock.patch.object(PlaylistItem,
                               'get_is_playable',
                               return_value=True):
            track = PandoraUri.factory(playlist_item_mock)

            provider._trigger_track_changing = mock.PropertyMock()

            assert provider.change_track(track) is True
            assert provider._trigger_track_changing.called
Beispiel #42
0
def test_get_next_pandora_track_fetches_track(config, playlist_item_mock):
    backend = conftest.get_backend(config)

    station_mock = mock.Mock(spec=Station)
    station_mock.id = 'id_token_mock'
    backend.library.pandora_station_cache[station_mock.id] = StationCacheItem(
        station_mock, iter([playlist_item_mock]))

    ref = backend.library.get_next_pandora_track('id_token_mock')
    assert ref.uri == PandoraUri.factory(playlist_item_mock).uri
    assert backend.library.pandora_track_cache[ref.uri].ref == ref
    assert backend.library.pandora_track_cache[
        ref.uri].track == playlist_item_mock
Beispiel #43
0
def test_change_track_resets_skips_on_success(provider, playlist_item_mock):
    with mock.patch.object(PandoraLibraryProvider,
                           'lookup_pandora_track',
                           return_value=playlist_item_mock):
        with mock.patch.object(PlaylistItem,
                               'get_is_playable',
                               return_value=True):
            track = PandoraUri.factory(playlist_item_mock)

            provider._consecutive_track_skips = 1

            assert provider.change_track(track) is True
            assert provider._consecutive_track_skips == 0
Beispiel #44
0
def test_change_track_fetches_next_track_if_unplayable(provider,
                                                       playlist_item_mock,
                                                       caplog):
    with mock.patch.object(PandoraLibraryProvider,
                           "lookup_pandora_track",
                           return_value=None):
        track = PandoraUri.factory(playlist_item_mock)

        provider._trigger_track_unplayable = mock.PropertyMock()

        assert provider.change_track(track) is False
        assert provider._trigger_track_unplayable.called

        assert "Error changing Pandora track" in caplog.text
Beispiel #45
0
def test_get_images_for_track_with_images(config, playlist_item_mock):
    backend = conftest.get_backend(config)

    track_uri = PandoraUri.factory("pandora:track:mock_id:mock_token")
    backend.library.pandora_track_cache[track_uri.uri] = TrackCacheItem(
        mock.Mock(spec=models.Ref.track), playlist_item_mock)
    results = backend.library.get_images([track_uri.uri])
    assert len(results[track_uri.uri]) == 1
    # chrome blocks getting non https images from Pandora, therefore the
    # library provider substitutes https for http.  We need to perform
    # the same substitution here to verify it worked correctly
    assert results[
        track_uri.uri][0].uri == playlist_item_mock.album_art_url.replace(
            "http://", "https://", 1)
Beispiel #46
0
    def browse(self, uri):
        self.backend.playback.reset_skip_limits()
        if uri == self.root_directory.uri:
            return self._browse_stations()

        if uri == self.genre_directory.uri:
            return self._browse_genre_categories()

        pandora_uri = PandoraUri.factory(uri)

        if isinstance(pandora_uri, GenreUri):
            return self._browse_genre_stations(uri)

        if isinstance(pandora_uri, StationUri):
            return self._browse_tracks(uri)
Beispiel #47
0
 def process_event(self, track_uri, pandora_event):
     func = getattr(self, pandora_event)
     try:
         if pandora_event == 'delete_station':
             logger.info("Triggering event '{}' for Pandora station with ID: '{}'."
                         .format(pandora_event, PandoraUri.factory(track_uri).station_id))
         else:
             logger.info("Triggering event '{}' for Pandora song: '{}'."
                         .format(pandora_event, self.library.lookup_pandora_track(track_uri).song_name))
         func(track_uri)
         self._trigger_event_processed(track_uri, pandora_event)
         return True
     except PandoraException:
         logger.exception('Error calling Pandora event: {}.'.format(pandora_event))
         return False
Beispiel #48
0
    def browse(self, uri):
        self.backend.playback.reset_skip_limits()
        if uri == self.root_directory.uri:
            return self._browse_stations()

        if uri == self.genre_directory.uri:
            return self._browse_genre_categories()

        pandora_uri = PandoraUri.factory(uri)

        if isinstance(pandora_uri, GenreUri):
            return self._browse_genre_stations(uri)

        if isinstance(pandora_uri, StationUri):
            return self._browse_tracks(uri)
def test_lookup_of_station_uri(config, get_station_list_return_value_mock, get_station_mock_return_value):
    with mock.patch.object(MopidyAPIClient, 'get_station', return_value=get_station_mock_return_value):
        with mock.patch.object(APIClient, 'get_station_list', return_value=get_station_list_return_value_mock):
            backend = conftest.get_backend(config)

            station_uri = PandoraUri.factory(get_station_mock_return_value)
            results = backend.library.lookup(station_uri.uri)
            assert len(results) == 1

            track = results[0]
            assert track.uri == station_uri.uri
            assert next(iter(track.album.images)) == conftest.MOCK_STATION_ART_URL
            assert track.name == conftest.MOCK_STATION_NAME
            assert next(iter(track.artists)).name == 'Pandora Station'
            assert track.album.name == conftest.MOCK_STATION_GENRE
Beispiel #50
0
def test_get_images_for_stations(config, station_result_mock):
    backend = conftest.get_backend(config)

    station_mock = Station.from_json(backend.api,
                                     station_result_mock["result"])
    station_uri = PandoraUri.factory(
        f"pandora:station:{station_mock.id}:mock_token")
    backend.api.get_station = mock.MagicMock(return_value=station_mock)

    results = backend.library.get_images([station_uri.uri])
    assert len(results[station_uri.uri]) == 1
    # chrome blocks getting non https images from Pandora, therefore the
    # library provider substitutes https for http.  We need to perform
    # the same substitution here to verify it worked correctly
    assert results[station_uri.uri][0].uri == station_mock.art_url.replace(
        "http://", "https://", 1)
Beispiel #51
0
 def refresh(self, uri=None):
     if not uri or uri == self.root_directory.uri:
         self.backend.api.get_station_list(force_refresh=True)
     elif uri == self.genre_directory.uri:
         self.backend.api.get_genre_stations(force_refresh=True)
     else:
         pandora_uri = PandoraUri.factory(uri)
         if isinstance(pandora_uri, StationUri):
             try:
                 self.pandora_station_cache.pop(pandora_uri.station_id)
             except KeyError:
                 # Item not in cache, ignore
                 pass
         else:
             raise ValueError("Unexpected URI type to perform refresh of "
                              f"Pandora directory: {pandora_uri.uri_type}")
Beispiel #52
0
    def get_next_pandora_track(self, station_id):
        try:
            station_iter = self.pandora_station_cache[station_id].iter
            track = next(station_iter)
        except Exception:
            logger.exception("Error retrieving next Pandora track.")
            return None

        track_uri = PandoraUri.factory(track)
        if isinstance(track_uri, AdItemUri):
            track_name = "Advertisement"
        else:
            track_name = track.song_name

        ref = models.Ref.track(name=track_name, uri=track_uri.uri)
        self.pandora_track_cache[track_uri.uri] = TrackCacheItem(ref, track)
        return ref
Beispiel #53
0
 def refresh(self, uri=None):
     if not uri or uri == self.root_directory.uri:
         self.backend.api.get_station_list(force_refresh=True)
     elif uri == self.genre_directory.uri:
         self.backend.api.get_genre_stations(force_refresh=True)
     else:
         pandora_uri = PandoraUri.factory(uri)
         if isinstance(pandora_uri, StationUri):
             try:
                 self.pandora_station_cache.pop(pandora_uri.station_id)
             except KeyError:
                 # Item not in cache, ignore
                 pass
         else:
             raise ValueError(
                 "Unexpected URI type to perform refresh of Pandora directory: {}.".format(
                     pandora_uri.uri_type
                 )
             )
def test_change_track_enforces_skip_limit_if_no_track_available(provider, playlist_item_mock, caplog):
    with mock.patch.object(PandoraLibraryProvider, 'lookup_pandora_track', return_value=None):
        track = PandoraUri.factory(playlist_item_mock)

        provider._trigger_track_unplayable = mock.PropertyMock()
        provider._trigger_skip_limit_exceeded = mock.PropertyMock(0)

        for i in range(PandoraPlaybackProvider.SKIP_LIMIT+1):
            assert provider.change_track(track) is False
            if i < PandoraPlaybackProvider.SKIP_LIMIT-1:
                assert provider._trigger_track_unplayable.called
                provider._trigger_track_unplayable.reset_mock()
                assert not provider._trigger_skip_limit_exceeded.called
            else:
                assert not provider._trigger_track_unplayable.called
                assert provider._trigger_skip_limit_exceeded.called

        assert 'Maximum track skip limit ({:d}) exceeded.'.format(
            PandoraPlaybackProvider.SKIP_LIMIT) in caplog.text
def test_change_track_enforces_skip_limit_on_request_exceptions(provider, playlist_item_mock, caplog):
    with mock.patch.object(PandoraLibraryProvider, 'lookup_pandora_track', return_value=playlist_item_mock):
        with mock.patch.object(APITransport, '__call__', side_effect=conftest.request_exception_mock):
            track = PandoraUri.factory(playlist_item_mock)

            provider._trigger_track_unplayable = mock.PropertyMock()
            provider._trigger_skip_limit_exceeded = mock.PropertyMock(0)
            playlist_item_mock.audio_url = 'pandora:track:mock_id:mock_token'

            for i in range(PandoraPlaybackProvider.SKIP_LIMIT+1):
                assert provider.change_track(track) is False
                if i < PandoraPlaybackProvider.SKIP_LIMIT-1:
                    assert provider._trigger_track_unplayable.called
                    provider._trigger_track_unplayable.reset_mock()
                    assert not provider._trigger_skip_limit_exceeded.called
                else:
                    assert not provider._trigger_track_unplayable.called
                    assert provider._trigger_skip_limit_exceeded.called

            assert 'Maximum track skip limit ({:d}) exceeded.'.format(
                PandoraPlaybackProvider.SKIP_LIMIT) in caplog.text
Beispiel #56
0
    def _browse_stations(self):
        station_directories = []

        stations = self.backend.api.get_station_list()
        if stations:
            if self.sort_order == "a-z":
                stations.sort(key=lambda x: x.name, reverse=False)

            for station in self._formatted_station_list(stations):
                # As of version 5 of the Pandora API, station IDs and tokens are always equivalent.
                # We're using this assumption as we don't have the station token available for deleting the station.
                # Detect if any Pandora API changes ever breaks this assumption in the future.
                assert station.token == station.id
                station_directories.append(
                    models.Ref.directory(
                        name=station.name, uri=PandoraUri.factory(station).uri
                    )
                )

        station_directories.insert(0, self.genre_directory)

        return station_directories
Beispiel #57
0
 def process_event(self, track_uri, pandora_event):
     func = getattr(self, pandora_event)
     try:
         if pandora_event == "delete_station":
             logger.info(
                 "Triggering event '{}' for Pandora station with ID: '{}'.".format(
                     pandora_event, PandoraUri.factory(track_uri).station_id
                 )
             )
         else:
             logger.info(
                 "Triggering event '{}' for Pandora song: '{}'.".format(
                     pandora_event,
                     self.library.lookup_pandora_track(track_uri).song_name,
                 )
             )
         func(track_uri)
         self._trigger_event_processed(track_uri, pandora_event)
         return True
     except PandoraException:
         logger.exception("Error calling Pandora event: {}.".format(pandora_event))
         return False
Beispiel #58
0
    def monitor_sequences(self):
        for es in self.event_sequences:
            # Wait until all sequences have been processed
            es.wait()

        # Get the last item in the queue (will have highest ratio)
        match = None
        while not self.sequence_match_results.empty():
            match = self.sequence_match_results.get()
            self.sequence_match_results.task_done()

        if match and match.ratio == 1.0:
            if match.marker.uri and isinstance(
                PandoraUri.factory(match.marker.uri), AdItemUri
            ):
                logger.info("Ignoring doubleclick event for Pandora advertisement...")
            else:
                self._trigger_event_triggered(match.marker.event, match.marker.uri)
            # Resume playback...
            if self.core.playback.get_state().get() != PlaybackState.PLAYING:
                self.core.playback.resume()

        self._monitor_lock.release()
Beispiel #59
0
 def sleep(self, track_uri):
     return self.api.sleep_song(PandoraUri.factory(track_uri).token)
Beispiel #60
0
 def add_artist_bookmark(self, track_uri):
     return self.api.add_artist_bookmark(PandoraUri.factory(track_uri).token)