diff --git a/mopidy/backends/__init__.py b/mopidy/backends/__init__.py index dd82109e..67c1708d 100644 --- a/mopidy/backends/__init__.py +++ b/mopidy/backends/__init__.py @@ -312,14 +312,18 @@ class BaseLibraryController(object): """ raise NotImplementedError - def search(self, query): + def search(self, **query): """ - Search the library for tracks where ``field`` contains ``query``. + Search the library for tracks where ``field`` contains ``values``. - :param field: 'track', 'artist', 'album', 'uri', and 'any' - :type field: string - :param query: the search query - :type query: string + Examples:: + + search(any=['a']) # Returns results matching 'a' + search(artist=['xyz']) # Returns results matching artist 'xyz' + search(any=['a', 'b'], artist=['xyz']) # Returns results matching 'a' and 'b' and artist 'xyz' + + :param query: one or more queries to search for + :type query: dict :rtype: :class:`mopidy.models.Playlist` """ raise NotImplementedError diff --git a/mopidy/backends/despotify.py b/mopidy/backends/despotify.py index a27d7a0c..05a95fb5 100644 --- a/mopidy/backends/despotify.py +++ b/mopidy/backends/despotify.py @@ -58,15 +58,18 @@ class DespotifyLibraryController(BaseLibraryController): track = self.backend.spotify.lookup(uri.encode(ENCODING)) return DespotifyTranslator.to_mopidy_track(track) - def search(self, query): + def search(self, **query): spotify_query = [] - for (field, what) in query: - if field == u'track': - field = u'title' - if field is u'any': - spotify_query.append(what) - else: - spotify_query.append(u'%s:"%s"' % (field, what)) + for (field, values) in query.iteritems(): + if not hasattr(values, '__iter__'): + values = [values] + for value in values: + if field == u'track': + field = u'title' + if field is u'any': + spotify_query.append(value) + else: + spotify_query.append(u'%s:"%s"' % (field, value)) spotify_query = u' '.join(query) result = self.backend.spotify.search(spotify_query.encode(ENCODING)) if (result is None or result.playlist.tracks[0].get_uri() == diff --git a/mopidy/backends/dummy.py b/mopidy/backends/dummy.py index 8d8a595e..6d8b74f6 100644 --- a/mopidy/backends/dummy.py +++ b/mopidy/backends/dummy.py @@ -30,7 +30,7 @@ class DummyLibraryController(BaseLibraryController): if matches: return matches[0] - def search(self, query): + def search(self, **query): return Playlist() find_exact = search diff --git a/mopidy/backends/gstreamer.py b/mopidy/backends/gstreamer.py index 9a8fef5a..41d81150 100644 --- a/mopidy/backends/gstreamer.py +++ b/mopidy/backends/gstreamer.py @@ -236,35 +236,34 @@ class GStreamerLibraryController(BaseLibraryController): result_tracks = filter(filter_func, result_tracks) return Playlist(tracks=result_tracks) - def search(self, query): - for (field, what) in query: - if not what: - raise LookupError('Missing query') - + def search(self, **query): result_tracks = self._uri_mapping.values() - for (field, what) in query: - q = what.strip().lower() + for (field, values) in query: + if not hasattr(values, '__iter__'): + values = [values] + for value in values: + q = value.strip().lower() - # FIXME this is bound to be slow for large libraries - track_filter = lambda t: q in t.name.lower() - album_filter = lambda t: q in getattr(t, 'album', Album()).name.lower() - artist_filter = lambda t: filter(lambda a: q in a.name.lower(), - t.artists) - uri_filter = lambda t: q in t.uri.lower() - any_filter = lambda t: track_filter(t) or album_filter(t) or \ - artist_filter(t) or uri_filter(t) + # FIXME this is bound to be slow for large libraries + track_filter = lambda t: q in t.name.lower() + album_filter = lambda t: q in getattr(t, 'album', Album()).name.lower() + artist_filter = lambda t: filter(lambda a: q in a.name.lower(), + t.artists) + uri_filter = lambda t: q in t.uri.lower() + any_filter = lambda t: track_filter(t) or album_filter(t) or \ + artist_filter(t) or uri_filter(t) - if field == 'track': - result_tracks = filter(track_filter, result_tracks) - elif field == 'album': - result_tracks = filter(album_filter, result_tracks) - elif field == 'artist': - result_tracks = filter(artist_filter, result_tracks) - elif field == 'uri': - result_tracks = filter(uri_filter, result_tracks) - elif field == 'any': - result_tracks = filter(any_filter, result_tracks) - else: - raise LookupError('Invalid lookup field: %s' % field) + if field == 'track': + result_tracks = filter(track_filter, result_tracks) + elif field == 'album': + result_tracks = filter(album_filter, result_tracks) + elif field == 'artist': + result_tracks = filter(artist_filter, result_tracks) + elif field == 'uri': + result_tracks = filter(uri_filter, result_tracks) + elif field == 'any': + result_tracks = filter(any_filter, result_tracks) + else: + raise LookupError('Invalid lookup field: %s' % field) return Playlist(tracks=result_tracks) diff --git a/mopidy/backends/libspotify.py b/mopidy/backends/libspotify.py index a535048e..08459243 100644 --- a/mopidy/backends/libspotify.py +++ b/mopidy/backends/libspotify.py @@ -69,13 +69,18 @@ class LibspotifyLibraryController(BaseLibraryController): spotify_track = Link.from_string(uri).as_track() return LibspotifyTranslator.to_mopidy_track(spotify_track) - def search(self, query): + def search(self, **query): spotify_query = [] - for (field, what) in query: - if field is u'any': - spotify_query.append(what) - else: - spotify_query.append(u'%s:"%s"' % (field, what)) + for (field, values) in query.iteritems(): + if not hasattr(values, '__iter__'): + values = [values] + for value in values: + if field == u'track': + field = u'title' + if field is u'any': + spotify_query.append(value) + else: + spotify_query.append(u'%s:"%s"' % (field, value)) spotify_query = u' '.join(spotify_query) logger.debug(u'In search method, search for: %s' % spotify_query) my_end, other_end = multiprocessing.Pipe() diff --git a/mopidy/mpd/frontend.py b/mopidy/mpd/frontend.py index 563a1567..1a37fa1b 100644 --- a/mopidy/mpd/frontend.py +++ b/mopidy/mpd/frontend.py @@ -118,14 +118,17 @@ class MpdFrontend(object): query_part_pattern = ( r'"?(?P([Aa]lbum|[Aa]rtist|[Ff]ilename|[Tt]itle|[Aa]ny))"?\s' r'"(?P[^"]+)"') - query = [] + query = {} for query_part in query_parts: m = re.match(query_part_pattern, query_part) field = m.groupdict()['field'].lower() if field == u'title': field = u'track' what = m.groupdict()['what'].lower() - query.append((field, what)) + if field in query: + query[field].append(what) + else: + query[field] = [what] return query @handle_pattern(r'^disableoutput "(?P\d+)"$') @@ -646,7 +649,7 @@ class MpdFrontend(object): - capitalizes the type argument. """ query = self._build_query(mpd_query) - return self.backend.library.find_exact(query).mpd_format() + return self.backend.library.find_exact(**query).mpd_format() @handle_pattern(r'^findadd ' r'(?P("?([Aa]lbum|[Aa]rtist|[Ff]ilename|[Tt]itle|[Aa]ny)"? "[^"]+"\s?)+)$') @@ -722,7 +725,7 @@ class MpdFrontend(object): return artists def __music_db_list_album_artist(self, artist): - playlist = self.backend.library.find_exact([(u'artist', artist)]) + playlist = self.backend.library.find_exact(artist=[artist]) albums = set() for track in playlist.tracks: albums.add((u'Album', track.album.name)) @@ -810,7 +813,7 @@ class MpdFrontend(object): - capitalizes the field argument. """ query = self._build_query(mpd_query) - return self.backend.library.search(query).mpd_format() + return self.backend.library.search(**query).mpd_format() @handle_pattern(r'^update( "(?P[^"]+)")*$') def _music_db_update(self, uri=None, rescan_unmodified_files=False):