diff --git a/mopidy_spotify/browse.py b/mopidy_spotify/browse.py index 6df456b4..5c9a1176 100644 --- a/mopidy_spotify/browse.py +++ b/mopidy_spotify/browse.py @@ -3,7 +3,7 @@ from mopidy import models import spotify -from mopidy_spotify import countries, translator +from mopidy_spotify import countries, playlists, translator logger = logging.getLogger(__name__) @@ -11,10 +11,12 @@ _TOP_LIST_DIR = models.Ref.directory(uri="spotify:top", name="Top lists") _YOUR_MUSIC_DIR = models.Ref.directory(uri="spotify:your", name="Your music") +_PLAYLISTS_DIR = models.Ref.directory(uri="spotify:playlists", name="Playlists") _ROOT_DIR_CONTENTS = [ _TOP_LIST_DIR, _YOUR_MUSIC_DIR, + _PLAYLISTS_DIR, ] _TOP_LIST_DIR_CONTENTS = [ @@ -28,6 +30,10 @@ models.Ref.directory(uri="spotify:your:albums", name="Your albums"), ] +_PLAYLISTS_DIR_CONTENTS = [ + models.Ref.directory(uri="spotify:playlists:featured", name="Featured"), +] + _TOPLIST_TYPES = { "albums": spotify.ToplistType.ALBUMS, "artists": spotify.ToplistType.ARTISTS, @@ -47,8 +53,10 @@ def browse(*, config, session, web_client, uri): return _TOP_LIST_DIR_CONTENTS elif uri == _YOUR_MUSIC_DIR.uri: return _YOUR_MUSIC_DIR_CONTENTS - elif uri.startswith("spotify:user:"): - return _browse_playlist(session, uri, config) + elif uri == _PLAYLISTS_DIR.uri: + return _PLAYLISTS_DIR_CONTENTS + elif uri.startswith("spotify:user:") or uri.startswith("spotify:playlist:"): + return _browse_playlist(session, web_client, uri, config) elif uri.startswith("spotify:album:"): return _browse_album(session, uri, config) elif uri.startswith("spotify:artist:"): @@ -70,16 +78,18 @@ def browse(*, config, session, web_client, uri): parts = uri.replace("spotify:your:", "").split(":") if len(parts) == 1: return _browse_your_music(web_client, variant=parts[0]) + elif uri.startswith("spotify:playlists:"): + parts = uri.replace("spotify:playlists:", "").split(":") + if len(parts) == 1: + return _browse_playlists(web_client, variant=parts[0]) logger.info(f"Failed to browse {uri!r}: Unknown URI type") return [] -def _browse_playlist(session, uri, config): - sp_playlist = session.get_playlist(uri) - sp_playlist.load(config["timeout"]) - return list( - translator.to_track_refs(sp_playlist.tracks, timeout=config["timeout"]) +def _browse_playlist(session, web_client, uri, config): + return playlists.playlist_lookup( + session, web_client, uri, config["bitrate"], as_items=True ) @@ -212,3 +222,18 @@ def _browse_your_music(web_client, variant): return list(translator.web_to_album_refs(items)) else: return [] + + +def _browse_playlists(web_client, variant): + if not web_client.logged_in: + return [] + + if variant == "featured": + items = ( + web_client.get_one(f"browse/{variant}") + .get("playlists", {}) + .get("items", []) + ) + return list(translator.to_playlist_refs(items)) + else: + return [] diff --git a/tests/conftest.py b/tests/conftest.py index 0ab29a02..86824951 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,7 +5,7 @@ from mopidy import models import spotify -from mopidy_spotify import backend, library, utils, web +from mopidy_spotify import backend, library, utils, playlists, web @pytest.yield_fixture() @@ -428,4 +428,5 @@ def backend_listener_mock(): @pytest.fixture def provider(backend_mock): + playlists._sp_links.clear() return library.SpotifyLibraryProvider(backend_mock) diff --git a/tests/test_browse.py b/tests/test_browse.py index a72b940b..251e3a55 100644 --- a/tests/test_browse.py +++ b/tests/test_browse.py @@ -14,11 +14,15 @@ def test_has_a_root_directory(provider): def test_browse_root_directory(provider): results = provider.browse("spotify:directory") - assert len(results) == 2 + assert len(results) == 3 assert models.Ref.directory(uri="spotify:top", name="Top lists") in results assert ( models.Ref.directory(uri="spotify:your", name="Your music") in results ) + assert ( + models.Ref.directory(uri="spotify:playlists", name="Playlists") + in results + ) def test_browse_top_lists_directory(provider): @@ -53,18 +57,25 @@ def test_browse_your_music_directory(provider): ) -def test_browse_playlist( - session_mock, sp_playlist_mock, sp_track_mock, provider -): - session_mock.get_playlist.return_value = sp_playlist_mock - sp_playlist_mock.tracks = [sp_track_mock, sp_track_mock] +def test_browse_playlists_directory(provider): + results = provider.browse("spotify:playlists") + + assert len(results) == 1 + assert ( + models.Ref.directory(uri="spotify:playlists:featured", name="Featured") + in results + ) + + +def test_browse_playlist(web_client_mock, web_playlist_mock, provider): + web_client_mock.get_playlist.return_value = web_playlist_mock results = provider.browse("spotify:user:alice:playlist:foo") - session_mock.get_playlist.assert_called_once_with( + web_client_mock.get_playlist.assert_called_once_with( "spotify:user:alice:playlist:foo" ) - assert len(results) == 2 + assert len(results) == 1 assert results[0] == models.Ref.track( uri="spotify:track:abc", name="ABC 123" ) @@ -457,3 +468,17 @@ def test_browse_your_music_albums(web_client_mock, web_album_mock, provider): assert results[0] == models.Ref.album( uri="spotify:album:def", name="DEF 456" ) + + +def test_browse_playlists_featured( + web_client_mock, web_playlist_mock, provider +): + web_client_mock.get_one.return_value = { + "playlists": {"items": [web_playlist_mock]} + } + + results = provider.browse("spotify:playlists:featured") + + assert len(results) == 1 + assert results[0].name == "Foo" + assert results[0].uri == "spotify:user:alice:playlist:foo"