mpd: Include artists and albums in search results
This commit is contained in:
parent
a8c0f6baa8
commit
455f0145e7
@ -58,6 +58,11 @@ v0.11.0 (in development)
|
||||
|
||||
- Add support for search by date.
|
||||
|
||||
- Include fake tracks representing albums and artists in the search results.
|
||||
When these are added to the tracklist, they expand to either all tracks in
|
||||
the album or all tracks by the artist. This makes it easy to play full albums
|
||||
in proper order, which is a feature that have been frequently requested.
|
||||
|
||||
**Internal changes**
|
||||
|
||||
*Models:*
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import functools
|
||||
import itertools
|
||||
|
||||
from mopidy.models import Track
|
||||
from mopidy.frontends.mpd import translator
|
||||
from mopidy.frontends.mpd.exceptions import MpdNotImplemented
|
||||
from mopidy.frontends.mpd.protocol import handle_request, stored_playlists
|
||||
@ -12,8 +14,28 @@ QUERY_RE = (
|
||||
r'[Tt]itle|[Aa]ny)"? "[^"]*"\s?)+)$')
|
||||
|
||||
|
||||
def _get_tracks(search_results):
|
||||
return list(itertools.chain(*[r.tracks for r in search_results]))
|
||||
def _get_field(field, search_results):
|
||||
return list(itertools.chain(*[getattr(r, field) for r in search_results]))
|
||||
|
||||
|
||||
_get_albums = functools.partial(_get_field, 'albums')
|
||||
_get_artists = functools.partial(_get_field, 'artists')
|
||||
_get_tracks = functools.partial(_get_field, 'tracks')
|
||||
|
||||
|
||||
def _album_as_track(album):
|
||||
return Track(
|
||||
uri=album.uri,
|
||||
name='Album: ' + album.name,
|
||||
album=album,
|
||||
artists=album.artists)
|
||||
|
||||
|
||||
def _artist_as_track(artist):
|
||||
return Track(
|
||||
uri=artist.uri,
|
||||
name='Artist: ' + artist.name,
|
||||
artists=[artist])
|
||||
|
||||
|
||||
@handle_request(r'^count "(?P<tag>[^"]+)" "(?P<needle>[^"]*)"$')
|
||||
@ -62,7 +84,10 @@ def find(context, mpd_query):
|
||||
except ValueError:
|
||||
return
|
||||
results = context.core.library.find_exact(**query).get()
|
||||
return translator.tracks_to_mpd_format(_get_tracks(results))
|
||||
albums = [_album_as_track(a) for a in _get_albums(results)]
|
||||
artists = [_artist_as_track(a) for a in _get_artists(results)]
|
||||
tracks = _get_tracks(results)
|
||||
return translator.tracks_to_mpd_format(artists + albums + tracks)
|
||||
|
||||
|
||||
@handle_request(r'^findadd ' + QUERY_RE)
|
||||
@ -304,7 +329,10 @@ def search(context, mpd_query):
|
||||
except ValueError:
|
||||
return
|
||||
results = context.core.library.search(**query).get()
|
||||
return translator.tracks_to_mpd_format(_get_tracks(results))
|
||||
albums = [_album_as_track(a) for a in _get_albums(results)]
|
||||
artists = [_artist_as_track(a) for a in _get_artists(results)]
|
||||
tracks = _get_tracks(results)
|
||||
return translator.tracks_to_mpd_format(artists + albums + tracks)
|
||||
|
||||
|
||||
@handle_request(r'^searchadd ' + QUERY_RE)
|
||||
|
||||
@ -115,6 +115,23 @@ class MusicDatabaseHandlerTest(protocol.BaseTestCase):
|
||||
|
||||
|
||||
class MusicDatabaseFindTest(protocol.BaseTestCase):
|
||||
def test_find(self):
|
||||
self.backend.library.dummy_find_exact_result = SearchResult(
|
||||
albums=[Album(uri='dummy:album:a', name='A')],
|
||||
artists=[Artist(uri='dummy:artist:b', name='B')],
|
||||
tracks=[Track(uri='dummy:track:c', name='C')])
|
||||
|
||||
self.sendRequest('find "any" "foo"')
|
||||
|
||||
self.assertInResponse('file: dummy:album:a')
|
||||
self.assertInResponse('Title: Album: A')
|
||||
self.assertInResponse('file: dummy:artist:b')
|
||||
self.assertInResponse('Title: Artist: B')
|
||||
self.assertInResponse('file: dummy:track:c')
|
||||
self.assertInResponse('Title: C')
|
||||
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_find_album(self):
|
||||
self.sendRequest('find "album" "what"')
|
||||
self.assertInResponse('OK')
|
||||
@ -409,6 +426,23 @@ class MusicDatabaseListTest(protocol.BaseTestCase):
|
||||
|
||||
|
||||
class MusicDatabaseSearchTest(protocol.BaseTestCase):
|
||||
def test_search(self):
|
||||
self.backend.library.dummy_search_result = SearchResult(
|
||||
albums=[Album(uri='dummy:album:a', name='A')],
|
||||
artists=[Artist(uri='dummy:artist:b', name='B')],
|
||||
tracks=[Track(uri='dummy:track:c', name='C')])
|
||||
|
||||
self.sendRequest('search "any" "foo"')
|
||||
|
||||
self.assertInResponse('file: dummy:album:a')
|
||||
self.assertInResponse('Title: Album: A')
|
||||
self.assertInResponse('file: dummy:artist:b')
|
||||
self.assertInResponse('Title: Artist: B')
|
||||
self.assertInResponse('file: dummy:track:c')
|
||||
self.assertInResponse('Title: C')
|
||||
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_search_album(self):
|
||||
self.sendRequest('search "album" "analbum"')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
Loading…
Reference in New Issue
Block a user