diff --git a/mopidy/mpd/protocol/music_db.py b/mopidy/mpd/protocol/music_db.py index 6f4c0fc6..2472e1d9 100644 --- a/mopidy/mpd/protocol/music_db.py +++ b/mopidy/mpd/protocol/music_db.py @@ -6,27 +6,7 @@ import itertools from mopidy.models import Ref, Track from mopidy.mpd import exceptions, protocol, translator - -LIST_QUERY = r""" - ("?) # Optional quote around the field type - (?P( # Field to list in the response - [Aa]lbum - | [Aa]lbumartist - | [Aa]rtist - | [Cc]omposer - | [Dd]ate - | [Gg]enre - | [Pp]erformer - )) - \1 # End of optional quote around the field type - (?: # Non-capturing group for optional search query - \ # A single space - (?P.*) - )? - $ -""" - -_SEARCH_FIELD_MAPPING = { +_SEARCH_MAPPING = { 'album': 'album', 'albumartist': 'albumartist', 'any': 'any', @@ -41,13 +21,22 @@ _SEARCH_FIELD_MAPPING = { 'title': 'track_name', 'track': 'track_no'} +_LIST_MAPPING = { + 'album': 'album', + 'albumartist': 'albumartist', + 'artist': 'artist', + 'composer': 'composer', + 'date': 'date', + 'genre': 'genre', + 'performer': 'performer'} -def _query_from_mpd_search_parameters(parameters): + +def _query_from_mpd_search_parameters(parameters, mapping): query = {} parameters = list(parameters) while parameters: # TODO: does it matter that this is now case insensitive - field = _SEARCH_FIELD_MAPPING.get(parameters.pop(0).lower()) + field = mapping.get(parameters.pop(0).lower()) if not field: raise exceptions.MpdArgError('incorrect arguments') if not parameters: @@ -97,7 +86,7 @@ def count(context, *args): - use multiple tag-needle pairs to make more specific searches. """ try: - query = _query_from_mpd_search_parameters(args) + query = _query_from_mpd_search_parameters(args, _SEARCH_MAPPING) except ValueError: raise exceptions.MpdArgError('incorrect arguments') results = context.core.library.find_exact(**query).get() @@ -137,7 +126,7 @@ def find(context, *args): - uses "file" instead of "filename". """ try: - query = _query_from_mpd_search_parameters(args) + query = _query_from_mpd_search_parameters(args, _SEARCH_MAPPING) except ValueError: return @@ -165,15 +154,15 @@ def findadd(context, *args): current playlist. Parameters have the same meaning as for ``find``. """ try: - query = _query_from_mpd_search_parameters(args) + query = _query_from_mpd_search_parameters(args, _SEARCH_MAPPING) except ValueError: return results = context.core.library.find_exact(**query).get() context.core.tracklist.add(_get_tracks(results)) -@protocol.handle_request(r'list\ ' + LIST_QUERY) -def list_(context, field, mpd_query=None): +@protocol.commands.add('list') +def list_(context, *args): """ *musicpd.org, music database section:* @@ -255,11 +244,27 @@ def list_(context, field, mpd_query=None): - does not add quotes around the field argument. - capitalizes the field argument. """ - field = field.lower() + parameters = list(args) + if not parameters: + raise exceptions.MpdArgError('incorrect arguments') + field = parameters.pop(0).lower() + + if field not in _LIST_MAPPING: + raise exceptions.MpdArgError('incorrect arguments') + + if len(parameters) == 1: + if field != 'album': + raise exceptions.MpdArgError('should be "Album" for 3 arguments') + return _list_artist(context, {'artist': parameters}) + try: - query = translator.query_from_mpd_list_format(field, mpd_query) + query = _query_from_mpd_search_parameters(parameters, _LIST_MAPPING) + except exceptions.MpdArgError as e: + e.message = 'not able to parse args' + raise except ValueError: return + if field == 'artist': return _list_artist(context, query) if field == 'albumartist': @@ -509,7 +514,7 @@ def search(context, *args): - uses "file" instead of "filename". """ try: - query = _query_from_mpd_search_parameters(args) + query = _query_from_mpd_search_parameters(args, _SEARCH_MAPPING) except ValueError: return results = context.core.library.search(**query).get() @@ -533,7 +538,7 @@ def searchadd(context, *args): not case sensitive. """ try: - query = _query_from_mpd_search_parameters(args) + query = _query_from_mpd_search_parameters(args, _SEARCH_MAPPING) except ValueError: return results = context.core.library.search(**query).get() @@ -560,7 +565,7 @@ def searchaddpl(context, *args): raise exceptions.MpdArgError('incorrect arguments') playlist_name = parameters.pop(0) try: - query = _query_from_mpd_search_parameters(parameters) + query = _query_from_mpd_search_parameters(parameters, _SEARCH_MAPPING) except ValueError: return results = context.core.library.search(**query).get() diff --git a/mopidy/mpd/translator.py b/mopidy/mpd/translator.py index 520e9ac8..252725ee 100644 --- a/mopidy/mpd/translator.py +++ b/mopidy/mpd/translator.py @@ -1,9 +1,7 @@ from __future__ import unicode_literals import re -import shlex -from mopidy.mpd.exceptions import MpdArgError from mopidy.models import TlTrack # TODO: special handling of local:// uri scheme @@ -137,46 +135,3 @@ def playlist_to_mpd_format(playlist, *args, **kwargs): Arguments as for :func:`tracks_to_mpd_format`, except the first one. """ return tracks_to_mpd_format(playlist.tracks, *args, **kwargs) - - -def query_from_mpd_list_format(field, mpd_query): - """ - Converts an MPD ``list`` query to a Mopidy query. - """ - if mpd_query is None: - return {} - try: - # shlex does not seem to be friends with unicode objects - tokens = shlex.split(mpd_query.encode('utf-8')) - except ValueError as error: - if str(error) == 'No closing quotation': - raise MpdArgError('Invalid unquoted character', command='list') - else: - raise - tokens = [t.decode('utf-8') for t in tokens] - if len(tokens) == 1: - if field == 'album': - if not tokens[0]: - raise ValueError - return {'artist': [tokens[0]]} - else: - raise MpdArgError( - 'should be "Album" for 3 arguments', command='list') - elif len(tokens) % 2 == 0: - query = {} - while tokens: - key = tokens[0].lower() - value = tokens[1] - tokens = tokens[2:] - if key not in ('artist', 'album', 'albumartist', 'composer', - 'date', 'genre', 'performer'): - raise MpdArgError('not able to parse args', command='list') - if not value: - raise ValueError - if key in query: - query[key].append(value) - else: - query[key] = [value] - return query - else: - raise MpdArgError('not able to parse args', command='list') diff --git a/tests/mpd/protocol/test_music_db.py b/tests/mpd/protocol/test_music_db.py index 6e00b384..d41c05b2 100644 --- a/tests/mpd/protocol/test_music_db.py +++ b/tests/mpd/protocol/test_music_db.py @@ -12,7 +12,7 @@ from tests.mpd import protocol class QueryFromMpdSearchFormatTest(unittest.TestCase): def test_dates_are_extracted(self): result = music_db._query_from_mpd_search_parameters( - ['Date', '1974-01-02', 'Date', '1975']) + ['Date', '1974-01-02', 'Date', '1975'], music_db._SEARCH_MAPPING) self.assertEqual(result['date'][0], '1974-01-02') self.assertEqual(result['date'][1], '1975')