Skip to content

Commit

Permalink
Merge pull request #247 from kingosticks/feature/featured-playlists
Browse files Browse the repository at this point in the history
Feature/featured playlists
  • Loading branch information
jodal authored Dec 22, 2019
2 parents e1a03b6 + d47f53b commit 36511d6
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 25 deletions.
41 changes: 33 additions & 8 deletions mopidy_spotify/browse.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,20 @@
from mopidy import models

import spotify
from mopidy_spotify import countries, translator
from mopidy_spotify import countries, playlists, translator

logger = logging.getLogger(__name__)

ROOT_DIR = models.Ref.directory(uri="spotify:directory", name="Spotify")

_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 = [
Expand All @@ -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,
Expand All @@ -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:"):
Expand All @@ -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
)


Expand Down Expand Up @@ -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 []
13 changes: 5 additions & 8 deletions mopidy_spotify/playlists.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,12 @@ def as_list(self):

def _get_flattened_playlist_refs(self):
if not self._backend._web_client.logged_in:
return
return []

web_client = self._backend._web_client
for web_playlist in web_client.get_user_playlists():
playlist_ref = translator.to_playlist_ref(
web_playlist, web_client.user_id
)
if playlist_ref is not None:
yield playlist_ref
user_playlists = self._backend._web_client.get_user_playlists()
return translator.to_playlist_refs(
user_playlists, self._backend._web_client.user_id
)

def get_items(self, uri):
with utils.time_logger(f"playlist.get_items({uri!r})", logging.DEBUG):
Expand Down
7 changes: 7 additions & 0 deletions mopidy_spotify/translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,13 @@ def to_playlist_ref(web_playlist, username=None):
return models.Ref.playlist(uri=web_playlist["uri"], name=name)


def to_playlist_refs(web_playlists, username=None):
for web_playlist in web_playlists:
ref = to_playlist_ref(web_playlist, username)
if ref is not None:
yield ref


# Maps from Mopidy search query field to Spotify search query field.
# `None` if there is no matching concept.
SEARCH_FIELD_MAP = {
Expand Down
3 changes: 2 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -428,4 +428,5 @@ def backend_listener_mock():

@pytest.fixture
def provider(backend_mock):
playlists._sp_links.clear()
return library.SpotifyLibraryProvider(backend_mock)
41 changes: 33 additions & 8 deletions tests/test_browse.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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"
)
Expand Down Expand Up @@ -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"
27 changes: 27 additions & 0 deletions tests/test_translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,33 @@ def test_includes_by_owner_in_name_if_owned_by_another_user(
assert ref.name == "Foo (by bob)"


class TestToPlaylistRefs:
def test_returns_playlist_refs(self, web_playlist_mock):
web_playlists = [web_playlist_mock] * 3
refs = list(translator.to_playlist_refs(web_playlists))

assert refs == [refs[0], refs[0], refs[0]]

assert refs[0].type == models.Ref.PLAYLIST
assert refs[0].uri == "spotify:user:alice:playlist:foo"
assert refs[0].name == "Foo"

def test_bad_playlist_filtered(self, web_playlist_mock):
refs = list(
translator.to_playlist_refs([{}, web_playlist_mock, {"foo": 1}])
)

assert len(refs) == 1

assert refs[0].type == models.Ref.PLAYLIST
assert refs[0].uri == "spotify:user:alice:playlist:foo"

def test_passes_username(self, web_playlist_mock):
refs = list(translator.to_playlist_refs([web_playlist_mock], "bob"))

assert refs[0].name == "Foo (by alice)"


class TestSpotifySearchQuery:
def test_any_maps_to_no_field(self):
query = translator.sp_search_query({"any": ["ABC", "DEF"]})
Expand Down

0 comments on commit 36511d6

Please sign in to comment.