From ca358e05db85fce299a48a1102ed6c58f24f4cda Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Wed, 27 Nov 2013 23:27:31 +0100 Subject: [PATCH] local: Move find_exact and search out of tag cache. --- mopidy/backends/local/search.py | 179 ++++++++++++++++++++++ mopidy/backends/local/tagcache/library.py | 173 +-------------------- 2 files changed, 184 insertions(+), 168 deletions(-) create mode 100644 mopidy/backends/local/search.py diff --git a/mopidy/backends/local/search.py b/mopidy/backends/local/search.py new file mode 100644 index 00000000..870afcfd --- /dev/null +++ b/mopidy/backends/local/search.py @@ -0,0 +1,179 @@ +from __future__ import unicode_literals + +from mopidy.models import Album, SearchResult + + +def find_exact(tracks, query=None, uris=None): + # TODO Only return results within URI roots given by ``uris`` + + if query is None: + query = {} + + _validate_query(query) + + for (field, values) in query.iteritems(): + if not hasattr(values, '__iter__'): + values = [values] + # FIXME this is bound to be slow for large libraries + for value in values: + if field == 'track_no': + q = _convert_to_int(value) + else: + q = value.strip() + + uri_filter = lambda t: q == t.uri + track_name_filter = lambda t: q == t.name + album_filter = lambda t: q == getattr(t, 'album', Album()).name + artist_filter = lambda t: filter( + lambda a: q == a.name, t.artists) + albumartist_filter = lambda t: any([ + q == a.name + for a in getattr(t.album, 'artists', [])]) + composer_filter = lambda t: any([ + q == a.name + for a in getattr(t, 'composers', [])]) + performer_filter = lambda t: any([ + q == a.name + for a in getattr(t, 'performers', [])]) + track_no_filter = lambda t: q == t.track_no + genre_filter = lambda t: t.genre and q == t.genre + date_filter = lambda t: q == t.date + comment_filter = lambda t: q == t.comment + any_filter = lambda t: ( + uri_filter(t) or + track_name_filter(t) or + album_filter(t) or + artist_filter(t) or + albumartist_filter(t) or + composer_filter(t) or + performer_filter(t) or + track_no_filter(t) or + genre_filter(t) or + date_filter(t) or + comment_filter(t)) + + if field == 'uri': + tracks = filter(uri_filter, tracks) + elif field == 'track_name': + tracks = filter(track_name_filter, tracks) + elif field == 'album': + tracks = filter(album_filter, tracks) + elif field == 'artist': + tracks = filter(artist_filter, tracks) + elif field == 'albumartist': + tracks = filter(albumartist_filter, tracks) + elif field == 'composer': + tracks = filter(composer_filter, tracks) + elif field == 'performer': + tracks = filter(performer_filter, tracks) + elif field == 'track_no': + tracks = filter(track_no_filter, tracks) + elif field == 'genre': + tracks = filter(genre_filter, tracks) + elif field == 'date': + tracks = filter(date_filter, tracks) + elif field == 'comment': + tracks = filter(comment_filter, tracks) + elif field == 'any': + tracks = filter(any_filter, tracks) + else: + raise LookupError('Invalid lookup field: %s' % field) + + # TODO: add local:search: + return SearchResult(uri='local:search', tracks=tracks) + + +def search(tracks, query=None, uris=None): + # TODO Only return results within URI roots given by ``uris`` + + if query is None: + query = {} + + _validate_query(query) + + for (field, values) in query.iteritems(): + if not hasattr(values, '__iter__'): + values = [values] + # FIXME this is bound to be slow for large libraries + for value in values: + if field == 'track_no': + q = _convert_to_int(value) + else: + q = value.strip().lower() + + uri_filter = lambda t: q in t.uri.lower() + track_name_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) + albumartist_filter = lambda t: any([ + q in a.name.lower() + for a in getattr(t.album, 'artists', [])]) + composer_filter = lambda t: any([ + q in a.name.lower() + for a in getattr(t, 'composers', [])]) + performer_filter = lambda t: any([ + q in a.name.lower() + for a in getattr(t, 'performers', [])]) + track_no_filter = lambda t: q == t.track_no + genre_filter = lambda t: t.genre and q in t.genre.lower() + date_filter = lambda t: t.date and t.date.startswith(q) + comment_filter = lambda t: t.comment and q in t.comment.lower() + any_filter = lambda t: ( + uri_filter(t) or + track_name_filter(t) or + album_filter(t) or + artist_filter(t) or + albumartist_filter(t) or + composer_filter(t) or + performer_filter(t) or + track_no_filter(t) or + genre_filter(t) or + date_filter(t) or + comment_filter(t)) + + if field == 'uri': + tracks = filter(uri_filter, tracks) + elif field == 'track_name': + tracks = filter(track_name_filter, tracks) + elif field == 'album': + tracks = filter(album_filter, tracks) + elif field == 'artist': + tracks = filter(artist_filter, tracks) + elif field == 'albumartist': + tracks = filter(albumartist_filter, tracks) + elif field == 'composer': + tracks = filter(composer_filter, tracks) + elif field == 'performer': + tracks = filter(performer_filter, tracks) + elif field == 'track_no': + tracks = filter(track_no_filter, tracks) + elif field == 'genre': + tracks = filter(genre_filter, tracks) + elif field == 'date': + tracks = filter(date_filter, tracks) + elif field == 'comment': + tracks = filter(comment_filter, tracks) + elif field == 'any': + tracks = filter(any_filter, tracks) + else: + raise LookupError('Invalid lookup field: %s' % field) + # TODO: add local:search: + return SearchResult(uri='local:search', tracks=tracks) + + +def _validate_query(query): + for (_, values) in query.iteritems(): + if not values: + raise LookupError('Missing query') + for value in values: + if not value: + raise LookupError('Missing query') + + +def _convert_to_int(string): + try: + return int(string) + except ValueError: + return object() diff --git a/mopidy/backends/local/tagcache/library.py b/mopidy/backends/local/tagcache/library.py index c795cdc1..fdc0be35 100644 --- a/mopidy/backends/local/tagcache/library.py +++ b/mopidy/backends/local/tagcache/library.py @@ -6,7 +6,7 @@ import tempfile from mopidy.backends import base from mopidy.backends.local.translator import local_to_file_uri -from mopidy.models import Album, SearchResult +from mopidy.backends.local import search from .translator import parse_mpd_tag_cache, tracks_to_tag_cache_format @@ -21,12 +21,6 @@ class LocalTagcacheLibraryProvider(base.BaseLibraryProvider): self._tag_cache_file = self.backend.config['local']['tag_cache_file'] self.refresh() - def _convert_to_int(self, string): - try: - return int(string) - except ValueError: - return object() - def refresh(self, uri=None): logger.debug( 'Loading local tracks from %s using %s', @@ -54,169 +48,12 @@ class LocalTagcacheLibraryProvider(base.BaseLibraryProvider): return [] def find_exact(self, query=None, uris=None): - # TODO Only return results within URI roots given by ``uris`` - - if query is None: - query = {} - self._validate_query(query) - result_tracks = self._uri_mapping.values() - - for (field, values) in query.iteritems(): - if not hasattr(values, '__iter__'): - values = [values] - # FIXME this is bound to be slow for large libraries - for value in values: - if field == 'track_no': - q = self._convert_to_int(value) - else: - q = value.strip() - - uri_filter = lambda t: q == t.uri - track_name_filter = lambda t: q == t.name - album_filter = lambda t: q == getattr(t, 'album', Album()).name - artist_filter = lambda t: filter( - lambda a: q == a.name, t.artists) - albumartist_filter = lambda t: any([ - q == a.name - for a in getattr(t.album, 'artists', [])]) - composer_filter = lambda t: any([ - q == a.name - for a in getattr(t, 'composers', [])]) - performer_filter = lambda t: any([ - q == a.name - for a in getattr(t, 'performers', [])]) - track_no_filter = lambda t: q == t.track_no - genre_filter = lambda t: t.genre and q == t.genre - date_filter = lambda t: q == t.date - comment_filter = lambda t: q == t.comment - any_filter = lambda t: ( - uri_filter(t) or - track_name_filter(t) or - album_filter(t) or - artist_filter(t) or - albumartist_filter(t) or - composer_filter(t) or - performer_filter(t) or - track_no_filter(t) or - genre_filter(t) or - date_filter(t) or - comment_filter(t)) - - if field == 'uri': - result_tracks = filter(uri_filter, result_tracks) - elif field == 'track_name': - result_tracks = filter(track_name_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 == 'albumartist': - result_tracks = filter(albumartist_filter, result_tracks) - elif field == 'composer': - result_tracks = filter(composer_filter, result_tracks) - elif field == 'performer': - result_tracks = filter(performer_filter, result_tracks) - elif field == 'track_no': - result_tracks = filter(track_no_filter, result_tracks) - elif field == 'genre': - result_tracks = filter(genre_filter, result_tracks) - elif field == 'date': - result_tracks = filter(date_filter, result_tracks) - elif field == 'comment': - result_tracks = filter(comment_filter, result_tracks) - elif field == 'any': - result_tracks = filter(any_filter, result_tracks) - else: - raise LookupError('Invalid lookup field: %s' % field) - # TODO: add local:search: - return SearchResult(uri='local:search', tracks=result_tracks) + tracks = self._uri_mapping.values() + return search.find_exact(tracks, query=query, uris=uris) def search(self, query=None, uris=None): - # TODO Only return results within URI roots given by ``uris`` - - if query is None: - query = {} - self._validate_query(query) - result_tracks = self._uri_mapping.values() - - for (field, values) in query.iteritems(): - if not hasattr(values, '__iter__'): - values = [values] - # FIXME this is bound to be slow for large libraries - for value in values: - if field == 'track_no': - q = self._convert_to_int(value) - else: - q = value.strip().lower() - - uri_filter = lambda t: q in t.uri.lower() - track_name_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) - albumartist_filter = lambda t: any([ - q in a.name.lower() - for a in getattr(t.album, 'artists', [])]) - composer_filter = lambda t: any([ - q in a.name.lower() - for a in getattr(t, 'composers', [])]) - performer_filter = lambda t: any([ - q in a.name.lower() - for a in getattr(t, 'performers', [])]) - track_no_filter = lambda t: q == t.track_no - genre_filter = lambda t: t.genre and q in t.genre.lower() - date_filter = lambda t: t.date and t.date.startswith(q) - comment_filter = lambda t: t.comment and q in t.comment.lower() - any_filter = lambda t: ( - uri_filter(t) or - track_name_filter(t) or - album_filter(t) or - artist_filter(t) or - albumartist_filter(t) or - composer_filter(t) or - performer_filter(t) or - track_no_filter(t) or - genre_filter(t) or - date_filter(t) or - comment_filter(t)) - - if field == 'uri': - result_tracks = filter(uri_filter, result_tracks) - elif field == 'track_name': - result_tracks = filter(track_name_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 == 'albumartist': - result_tracks = filter(albumartist_filter, result_tracks) - elif field == 'composer': - result_tracks = filter(composer_filter, result_tracks) - elif field == 'performer': - result_tracks = filter(performer_filter, result_tracks) - elif field == 'track_no': - result_tracks = filter(track_no_filter, result_tracks) - elif field == 'genre': - result_tracks = filter(genre_filter, result_tracks) - elif field == 'date': - result_tracks = filter(date_filter, result_tracks) - elif field == 'comment': - result_tracks = filter(comment_filter, result_tracks) - elif field == 'any': - result_tracks = filter(any_filter, result_tracks) - else: - raise LookupError('Invalid lookup field: %s' % field) - # TODO: add local:search: - return SearchResult(uri='local:search', tracks=result_tracks) - - def _validate_query(self, query): - for (_, values) in query.iteritems(): - if not values: - raise LookupError('Missing query') - for value in values: - if not value: - raise LookupError('Missing query') + tracks = self._uri_mapping.values() + return search.search(tracks, query=query, uris=uris) class LocalTagcacheLibraryUpdateProvider(base.BaseLibraryProvider):