From f428546b72ebdfa0861ab9de945b218ff7609c72 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 28 Aug 2010 14:02:20 +0200 Subject: [PATCH 1/6] Update 'list' docs with a bunch of valid examples --- mopidy/frontends/mpd/protocol/music_db.py | 74 +++++++++++++++++++---- 1 file changed, 61 insertions(+), 13 deletions(-) diff --git a/mopidy/frontends/mpd/protocol/music_db.py b/mopidy/frontends/mpd/protocol/music_db.py index d4dcf50d..dfdec727 100644 --- a/mopidy/frontends/mpd/protocol/music_db.py +++ b/mopidy/frontends/mpd/protocol/music_db.py @@ -101,22 +101,70 @@ def list_(frontend, field, artist=None): This filters the result list by an artist. + *Clarifications:* + + The musicpd.org documentation for ``list`` is far from complete. The + command also supports the following variant: + + ``list {TYPE} {QUERY}`` + + Where ``QUERY`` applies to all ``TYPE``. ``QUERY`` is one or more pairs + of a field name and a value. If the ``QUERY`` consists of more than one + pair, the pairs are AND-ed together to find the result. Examples of + valid queries and what they should return: + + ``list "artist" "artist" "ABBA"`` + List artists where the artist name is "ABBA". Response:: + + Artist: ABBA + OK + + ``list "album" "artist" "ABBA"`` + Lists albums where the artist name is "ABBA". Response:: + + Album: More ABBA Gold: More ABBA Hits + Album: Absolute More Christmas + Album: Gold: Greatest Hits + OK + + ``list "artist" "album" "Gold: Greatest Hits"`` + Lists artists where the album name is "Gold: Greatest Hits". + Response:: + + Artist: ABBA + OK + + ``list "artist" "artist" "ABBA" "artist" "TLC"`` + Lists artists where the artist name is "ABBA" *and* "TLC". Should + never match anything. Response:: + + OK + + ``list "date" "artist" "ABBA"`` + Lists dates where artist name is "ABBA". Response:: + + Date: + Date: 1992 + Date: 1993 + OK + + ``list "date" "artist" "ABBA" "album" "Gold: Greatest Hits"`` + Lists dates where artist name is "ABBA" and album name is "Gold: + Greatest Hits". Response:: + + Date: 1992 + OK + + ``list "genre" "artist" "The Rolling Stones"`` + Lists genres where artist name is "The Rolling Stones". Response:: + + Genre: + Genre: Rock + OK + *GMPC:* - does not add quotes around the field argument. - - asks for "list artist" to get available artists and will not query - for artist/album information if this is not retrived - - asks for multiple fields, i.e.:: - - list album artist "an artist name" - - returns the albums available for the asked artist:: - - list album artist "Tiesto" - Album: Radio Trance Vol 4-Promo-CD - Album: Ur A Tear in the Open CDR - Album: Simple Trance 2004 Step One - Album: In Concert 05-10-2003 *ncmpc:* From 802811e43524a93d3adb138a8159fc47775dc2bb Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 28 Aug 2010 15:56:34 +0200 Subject: [PATCH 2/6] libspotify: Return all tracks in stored playlists upon empty search query --- mopidy/backends/libspotify/library.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mopidy/backends/libspotify/library.py b/mopidy/backends/libspotify/library.py index ffb9ee57..68512ffa 100644 --- a/mopidy/backends/libspotify/library.py +++ b/mopidy/backends/libspotify/library.py @@ -6,6 +6,7 @@ from spotify import Link from mopidy.backends.base import BaseLibraryController from mopidy.backends.libspotify import ENCODING from mopidy.backends.libspotify.translator import LibspotifyTranslator +from mopidy.models import Playlist logger = logging.getLogger('mopidy.backends.libspotify.library') @@ -23,6 +24,13 @@ class LibspotifyLibraryController(BaseLibraryController): pass # TODO def search(self, **query): + if not query: + # Since we can't search for the entire Spotify library, we return + # all tracks in the stored playlists when the query is empty. + tracks = [] + for playlist in self.backend.stored_playlists.playlists: + tracks += playlist.tracks + return Playlist(tracks=tracks) spotify_query = [] for (field, values) in query.iteritems(): if not hasattr(values, '__iter__'): From 4b4c4b709e937d1ffda0c05d559343206f1b06c9 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 28 Aug 2010 16:07:44 +0200 Subject: [PATCH 3/6] Rewrite list command to support more advanced queries --- mopidy/frontends/mpd/protocol/music_db.py | 75 ++++++--- tests/frontends/mpd/music_db_test.py | 177 ++++++++++++++++------ 2 files changed, 182 insertions(+), 70 deletions(-) diff --git a/mopidy/frontends/mpd/protocol/music_db.py b/mopidy/frontends/mpd/protocol/music_db.py index dfdec727..2bccab3d 100644 --- a/mopidy/frontends/mpd/protocol/music_db.py +++ b/mopidy/frontends/mpd/protocol/music_db.py @@ -1,7 +1,8 @@ import re +import shlex from mopidy.frontends.mpd.protocol import handle_pattern, stored_playlists -from mopidy.frontends.mpd.exceptions import MpdNotImplemented +from mopidy.frontends.mpd.exceptions import MpdArgError, MpdNotImplemented def _build_query(mpd_query): """ @@ -81,13 +82,9 @@ def findadd(frontend, query): # TODO Add result to current playlist #result = frontend.find(query) -@handle_pattern(r'^list (?P[Aa]rtist)$') -@handle_pattern(r'^list "(?P[Aa]rtist)"$') -@handle_pattern(r'^list (?Palbum( artist)?)' - '( "(?P[^"]+)")*$') -@handle_pattern(r'^list "(?Palbum(" "artist)?)"' - '( "(?P[^"]+)")*$') -def list_(frontend, field, artist=None): +@handle_pattern(r'^list "?(?P([Aa]rtist|[Aa]lbum|[Dd]ate|[Gg]enre))"?' + '( (?P.*))?$') +def list_(frontend, field, mpd_query=None): """ *musicpd.org, music database section:* @@ -172,29 +169,59 @@ def list_(frontend, field, artist=None): - capitalizes the field argument. """ field = field.lower() + query = _list_build_query(field, mpd_query) if field == u'artist': - return _list_artist(frontend) - elif field == u'album artist': - return _list_album_artist(frontend, artist) - # TODO More to implement + return _list_artist(frontend, query) + elif field == u'album': + return _list_album(frontend, query) + elif field == u'date': + pass # TODO + elif field == u'genre': + pass # TODO -def _list_artist(frontend): - """ - Since we don't know exactly all available artists, we respond with - the artists we know for sure, which is all artists in our stored playlists. - """ +def _list_build_query(field, mpd_query): + """Converts a ``list`` query to a Mopidy query.""" + if mpd_query is None: + return {} + # shlex does not seem to be friends with unicode objects + tokens = shlex.split(mpd_query.encode('utf-8')) + tokens = [t.decode('utf-8') for t in tokens] + if len(tokens) == 1: + if field == u'album': + return {'artist': [tokens[0]]} + else: + raise MpdArgError( + u'should be "Album" for 3 arguments', command=u'list') + elif len(tokens) % 2 == 0: + query = {} + while tokens: + key = tokens[0].lower() + value = tokens[1] + tokens = tokens[2:] + if key not in (u'artist', u'album', u'date', u'genre'): + raise MpdArgError(u'not able to parse args', command=u'list') + if key in query: + query[key].append(value) + else: + query[key] = [value] + return query + else: + raise MpdArgError(u'not able to parse args', command=u'list') + +def _list_artist(frontend, query): artists = set() - for playlist in frontend.backend.stored_playlists.playlists: - for track in playlist.tracks: - for artist in track.artists: - artists.add((u'Artist', artist.name)) + playlist = frontend.backend.library.find_exact(**query) + for track in playlist.tracks: + for artist in track.artists: + artists.add((u'Artist', artist.name)) return artists -def _list_album_artist(frontend, artist): - playlist = frontend.backend.library.find_exact(artist=[artist]) +def _list_album(frontend, query): albums = set() + playlist = frontend.backend.library.find_exact(**query) for track in playlist.tracks: - albums.add((u'Album', track.album.name)) + if track.album is not None: + albums.add((u'Album', track.album.name)) return albums @handle_pattern(r'^listall "(?P[^"]+)"') diff --git a/tests/frontends/mpd/music_db_test.py b/tests/frontends/mpd/music_db_test.py index 5fcc393c..408961b2 100644 --- a/tests/frontends/mpd/music_db_test.py +++ b/tests/frontends/mpd/music_db_test.py @@ -15,6 +15,59 @@ class MusicDatabaseHandlerTest(unittest.TestCase): self.assert_(u'playtime: 0' in result) self.assert_(u'OK' in result) + def test_findadd(self): + result = self.h.handle_request(u'findadd "album" "what"') + self.assert_(u'OK' in result) + + def test_listall(self): + result = self.h.handle_request(u'listall "file:///dev/urandom"') + self.assert_(u'ACK [0@0] {} Not implemented' in result) + + def test_listallinfo(self): + result = self.h.handle_request(u'listallinfo "file:///dev/urandom"') + self.assert_(u'ACK [0@0] {} Not implemented' in result) + + def test_lsinfo_without_path_returns_same_as_listplaylists(self): + lsinfo_result = self.h.handle_request(u'lsinfo') + listplaylists_result = self.h.handle_request(u'listplaylists') + self.assertEqual(lsinfo_result, listplaylists_result) + + def test_lsinfo_with_empty_path_returns_same_as_listplaylists(self): + lsinfo_result = self.h.handle_request(u'lsinfo ""') + listplaylists_result = self.h.handle_request(u'listplaylists') + self.assertEqual(lsinfo_result, listplaylists_result) + + def test_lsinfo_for_root_returns_same_as_listplaylists(self): + lsinfo_result = self.h.handle_request(u'lsinfo "/"') + listplaylists_result = self.h.handle_request(u'listplaylists') + self.assertEqual(lsinfo_result, listplaylists_result) + + def test_update_without_uri(self): + result = self.h.handle_request(u'update') + self.assert_(u'OK' in result) + self.assert_(u'updating_db: 0' in result) + + def test_update_with_uri(self): + result = self.h.handle_request(u'update "file:///dev/urandom"') + self.assert_(u'OK' in result) + self.assert_(u'updating_db: 0' in result) + + def test_rescan_without_uri(self): + result = self.h.handle_request(u'rescan') + self.assert_(u'OK' in result) + self.assert_(u'updating_db: 0' in result) + + def test_rescan_with_uri(self): + result = self.h.handle_request(u'rescan "file:///dev/urandom"') + self.assert_(u'OK' in result) + self.assert_(u'updating_db: 0' in result) + + +class MusicDatabaseFindTest(unittest.TestCase): + def setUp(self): + self.b = DummyBackend(mixer_class=DummyMixer) + self.h = dispatcher.MpdDispatcher(backend=self.b) + def test_find_album(self): result = self.h.handle_request(u'find "album" "what"') self.assert_(u'OK' in result) @@ -48,11 +101,20 @@ class MusicDatabaseHandlerTest(unittest.TestCase): u'find album "album_what" artist "artist_what"') self.assert_(u'OK' in result) - def test_findadd(self): - result = self.h.handle_request(u'findadd "album" "what"') - self.assert_(u'OK' in result) - def test_list_artist(self): +class MusicDatabaseListTest(unittest.TestCase): + def setUp(self): + self.b = DummyBackend(mixer_class=DummyMixer) + self.h = dispatcher.MpdDispatcher(backend=self.b) + + def test_list_foo_returns_ack(self): + result = self.h.handle_request(u'list "foo"') + self.assertEqual(result[0], + u'ACK [2@0] {list} incorrect arguments') + + ### Artist + + def test_list_artist_with_quotes(self): result = self.h.handle_request(u'list "artist"') self.assert_(u'OK' in result) @@ -64,44 +126,85 @@ class MusicDatabaseHandlerTest(unittest.TestCase): result = self.h.handle_request(u'list Artist') self.assert_(u'OK' in result) - def test_list_artist_with_artist_should_fail(self): + def test_list_artist_with_query_of_one_token(self): result = self.h.handle_request(u'list "artist" "anartist"') - self.assertEqual(result[0], u'ACK [2@0] {list} incorrect arguments') + self.assertEqual(result[0], + u'ACK [2@0] {list} should be "Album" for 3 arguments') - def test_list_album_without_artist(self): + def test_list_artist_with_unknown_field_in_query_returns_ack(self): + result = self.h.handle_request(u'list "artist" "foo" "bar"') + self.assertEqual(result[0], + u'ACK [2@0] {list} not able to parse args') + + ### Album + + def test_list_album_with_quotes(self): result = self.h.handle_request(u'list "album"') self.assert_(u'OK' in result) - def test_list_album_with_artist(self): + def test_list_album_without_quotes(self): + result = self.h.handle_request(u'list album') + self.assert_(u'OK' in result) + + def test_list_album_without_quotes_and_capitalized(self): + result = self.h.handle_request(u'list Album') + self.assert_(u'OK' in result) + + def test_list_album_with_artist_name(self): result = self.h.handle_request(u'list "album" "anartist"') self.assert_(u'OK' in result) - def test_list_album_artist_with_artist_without_quotes(self): - result = self.h.handle_request(u'list album artist "anartist"') + def test_list_album_with_artist_query(self): + result = self.h.handle_request(u'list "album" "artist" "anartist"') self.assert_(u'OK' in result) - def test_listall(self): - result = self.h.handle_request(u'listall "file:///dev/urandom"') - self.assert_(u'ACK [0@0] {} Not implemented' in result) + ### Date - def test_listallinfo(self): - result = self.h.handle_request(u'listallinfo "file:///dev/urandom"') - self.assert_(u'ACK [0@0] {} Not implemented' in result) + def test_list_date_with_quotes(self): + result = self.h.handle_request(u'list "date"') + self.assert_(u'OK' in result) - def test_lsinfo_without_path_returns_same_as_listplaylists(self): - lsinfo_result = self.h.handle_request(u'lsinfo') - listplaylists_result = self.h.handle_request(u'listplaylists') - self.assertEqual(lsinfo_result, listplaylists_result) + def test_list_date_without_quotes(self): + result = self.h.handle_request(u'list date') + self.assert_(u'OK' in result) - def test_lsinfo_with_empty_path_returns_same_as_listplaylists(self): - lsinfo_result = self.h.handle_request(u'lsinfo ""') - listplaylists_result = self.h.handle_request(u'listplaylists') - self.assertEqual(lsinfo_result, listplaylists_result) + def test_list_date_without_quotes_and_capitalized(self): + result = self.h.handle_request(u'list Date') + self.assert_(u'OK' in result) - def test_lsinfo_for_root_returns_same_as_listplaylists(self): - lsinfo_result = self.h.handle_request(u'lsinfo "/"') - listplaylists_result = self.h.handle_request(u'listplaylists') - self.assertEqual(lsinfo_result, listplaylists_result) + def test_list_date_with_query_of_one_token(self): + result = self.h.handle_request(u'list "date" "anartist"') + self.assertEqual(result[0], + u'ACK [2@0] {list} should be "Album" for 3 arguments') + + # TODO Tests for the rest of "list date ..." + + ### Genre + + def test_list_genre_with_quotes(self): + result = self.h.handle_request(u'list "genre"') + self.assert_(u'OK' in result) + + def test_list_genre_without_quotes(self): + result = self.h.handle_request(u'list genre') + self.assert_(u'OK' in result) + + def test_list_genre_without_quotes_and_capitalized(self): + result = self.h.handle_request(u'list Genre') + self.assert_(u'OK' in result) + + def test_list_genre_with_query_of_one_token(self): + result = self.h.handle_request(u'list "genre" "anartist"') + self.assertEqual(result[0], + u'ACK [2@0] {list} should be "Album" for 3 arguments') + + # TODO Tests for the rest of "list genre ..." + + +class MusicDatabaseSearchTest(unittest.TestCase): + def setUp(self): + self.b = DummyBackend(mixer_class=DummyMixer) + self.h = dispatcher.MpdDispatcher(backend=self.b) def test_search_album(self): result = self.h.handle_request(u'search "album" "analbum"') @@ -147,22 +250,4 @@ class MusicDatabaseHandlerTest(unittest.TestCase): result = self.h.handle_request(u'search "sometype" "something"') self.assertEqual(result[0], u'ACK [2@0] {search} incorrect arguments') - def test_update_without_uri(self): - result = self.h.handle_request(u'update') - self.assert_(u'OK' in result) - self.assert_(u'updating_db: 0' in result) - def test_update_with_uri(self): - result = self.h.handle_request(u'update "file:///dev/urandom"') - self.assert_(u'OK' in result) - self.assert_(u'updating_db: 0' in result) - - def test_rescan_without_uri(self): - result = self.h.handle_request(u'rescan') - self.assert_(u'OK' in result) - self.assert_(u'updating_db: 0' in result) - - def test_rescan_with_uri(self): - result = self.h.handle_request(u'rescan "file:///dev/urandom"') - self.assert_(u'OK' in result) - self.assert_(u'updating_db: 0' in result) From 9b73cbb18deba6a5a70c12ab37d342fd03cc8044 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 28 Aug 2010 16:20:25 +0200 Subject: [PATCH 4/6] Add more tests to prove the new 'list' query hendling --- tests/frontends/mpd/music_db_test.py | 98 +++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 3 deletions(-) diff --git a/tests/frontends/mpd/music_db_test.py b/tests/frontends/mpd/music_db_test.py index 408961b2..05b8ebd0 100644 --- a/tests/frontends/mpd/music_db_test.py +++ b/tests/frontends/mpd/music_db_test.py @@ -136,6 +136,31 @@ class MusicDatabaseListTest(unittest.TestCase): self.assertEqual(result[0], u'ACK [2@0] {list} not able to parse args') + def test_list_artist_by_artist(self): + result = self.h.handle_request(u'list "artist" "artist" "anartist"') + self.assert_(u'OK' in result) + + def test_list_artist_by_album(self): + result = self.h.handle_request(u'list "artist" "album" "analbum"') + self.assert_(u'OK' in result) + + def test_list_artist_by_full_date(self): + result = self.h.handle_request(u'list "artist" "date" "2001-01-01"') + self.assert_(u'OK' in result) + + def test_list_artist_by_year(self): + result = self.h.handle_request(u'list "artist" "date" "2001"') + self.assert_(u'OK' in result) + + def test_list_artist_by_genre(self): + result = self.h.handle_request(u'list "artist" "genre" "agenre"') + self.assert_(u'OK' in result) + + def test_list_artist_by_artist_and_album(self): + result = self.h.handle_request( + u'list "artist" "artist" "anartist" "album" "analbum"') + self.assert_(u'OK' in result) + ### Album def test_list_album_with_quotes(self): @@ -154,10 +179,31 @@ class MusicDatabaseListTest(unittest.TestCase): result = self.h.handle_request(u'list "album" "anartist"') self.assert_(u'OK' in result) - def test_list_album_with_artist_query(self): + def test_list_album_by_artist(self): result = self.h.handle_request(u'list "album" "artist" "anartist"') self.assert_(u'OK' in result) + def test_list_album_by_album(self): + result = self.h.handle_request(u'list "album" "album" "analbum"') + self.assert_(u'OK' in result) + + def test_list_album_by_full_date(self): + result = self.h.handle_request(u'list "album" "date" "2001-01-01"') + self.assert_(u'OK' in result) + + def test_list_album_by_year(self): + result = self.h.handle_request(u'list "album" "date" "2001"') + self.assert_(u'OK' in result) + + def test_list_album_by_genre(self): + result = self.h.handle_request(u'list "album" "genre" "agenre"') + self.assert_(u'OK' in result) + + def test_list_album_by_artist_and_album(self): + result = self.h.handle_request( + u'list "album" "artist" "anartist" "album" "analbum"') + self.assert_(u'OK' in result) + ### Date def test_list_date_with_quotes(self): @@ -177,7 +223,30 @@ class MusicDatabaseListTest(unittest.TestCase): self.assertEqual(result[0], u'ACK [2@0] {list} should be "Album" for 3 arguments') - # TODO Tests for the rest of "list date ..." + def test_list_date_by_artist(self): + result = self.h.handle_request(u'list "date" "artist" "anartist"') + self.assert_(u'OK' in result) + + def test_list_date_by_album(self): + result = self.h.handle_request(u'list "date" "album" "analbum"') + self.assert_(u'OK' in result) + + def test_list_date_by_full_date(self): + result = self.h.handle_request(u'list "date" "date" "2001-01-01"') + self.assert_(u'OK' in result) + + def test_list_date_by_year(self): + result = self.h.handle_request(u'list "date" "date" "2001"') + self.assert_(u'OK' in result) + + def test_list_date_by_genre(self): + result = self.h.handle_request(u'list "date" "genre" "agenre"') + self.assert_(u'OK' in result) + + def test_list_date_by_artist_and_album(self): + result = self.h.handle_request( + u'list "date" "artist" "anartist" "album" "analbum"') + self.assert_(u'OK' in result) ### Genre @@ -198,7 +267,30 @@ class MusicDatabaseListTest(unittest.TestCase): self.assertEqual(result[0], u'ACK [2@0] {list} should be "Album" for 3 arguments') - # TODO Tests for the rest of "list genre ..." + def test_list_genre_by_artist(self): + result = self.h.handle_request(u'list "genre" "artist" "anartist"') + self.assert_(u'OK' in result) + + def test_list_genre_by_album(self): + result = self.h.handle_request(u'list "genre" "album" "analbum"') + self.assert_(u'OK' in result) + + def test_list_genre_by_full_date(self): + result = self.h.handle_request(u'list "genre" "date" "2001-01-01"') + self.assert_(u'OK' in result) + + def test_list_genre_by_year(self): + result = self.h.handle_request(u'list "genre" "date" "2001"') + self.assert_(u'OK' in result) + + def test_list_genre_by_genre(self): + result = self.h.handle_request(u'list "genre" "genre" "agenre"') + self.assert_(u'OK' in result) + + def test_list_genre_by_artist_and_album(self): + result = self.h.handle_request( + u'list "genre" "artist" "anartist" "album" "analbum"') + self.assert_(u'OK' in result) class MusicDatabaseSearchTest(unittest.TestCase): From dafd5ac9ecc6e9f863e712d46b9b46a8d308f324 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 28 Aug 2010 16:59:14 +0200 Subject: [PATCH 5/6] Add 'list date' support --- mopidy/frontends/mpd/protocol/music_db.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/mopidy/frontends/mpd/protocol/music_db.py b/mopidy/frontends/mpd/protocol/music_db.py index 2bccab3d..4c2031aa 100644 --- a/mopidy/frontends/mpd/protocol/music_db.py +++ b/mopidy/frontends/mpd/protocol/music_db.py @@ -175,9 +175,9 @@ def list_(frontend, field, mpd_query=None): elif field == u'album': return _list_album(frontend, query) elif field == u'date': - pass # TODO + return _list_date(frontend, query) elif field == u'genre': - pass # TODO + pass # TODO We don't have genre in our internal data structures yet def _list_build_query(field, mpd_query): """Converts a ``list`` query to a Mopidy query.""" @@ -224,6 +224,14 @@ def _list_album(frontend, query): albums.add((u'Album', track.album.name)) return albums +def _list_date(frontend, query): + dates = set() + playlist = frontend.backend.library.find_exact(**query) + for track in playlist.tracks: + if track.date is not None: + dates.add((u'Date', track.date.strftime('%Y-%m-%d'))) + return dates + @handle_pattern(r'^listall "(?P[^"]+)"') def listall(frontend, uri): """ From 5f95ebf9dcd2ca051611c3b338755c6c2efb3a7a Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 28 Aug 2010 17:00:09 +0200 Subject: [PATCH 6/6] Add 'year:1997' search filter support to libspotify backend --- mopidy/backends/libspotify/library.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/mopidy/backends/libspotify/library.py b/mopidy/backends/libspotify/library.py index 68512ffa..7e545af8 100644 --- a/mopidy/backends/libspotify/library.py +++ b/mopidy/backends/libspotify/library.py @@ -33,13 +33,18 @@ class LibspotifyLibraryController(BaseLibraryController): return Playlist(tracks=tracks) spotify_query = [] for (field, values) in query.iteritems(): + if field == u'track': + field = u'title' + if field == u'date': + field = u'year' if not hasattr(values, '__iter__'): values = [values] for value in values: - if field == u'track': - field = u'title' if field == u'any': spotify_query.append(value) + elif field == u'year': + value = int(value.split('-')[0]) # Extract year + spotify_query.append(u'%s:%d' % (field, value)) else: spotify_query.append(u'%s:"%s"' % (field, value)) spotify_query = u' '.join(spotify_query)