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 ] ]
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] ]
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
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
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
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
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
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
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
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]})
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_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)
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)
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
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
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
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
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]})
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
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_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
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
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
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
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
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
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
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_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
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
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
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_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
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)
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 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
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
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)
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}")
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
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
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
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
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()
def sleep(self, track_uri): return self.api.sleep_song(PandoraUri.factory(track_uri).token)
def add_artist_bookmark(self, track_uri): return self.api.add_artist_bookmark(PandoraUri.factory(track_uri).token)