mpd: Move query_from_mpd_search_format into only module using it
This commit is contained in:
parent
25380fbbed
commit
b0cffb8681
@ -2,6 +2,7 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import functools
|
import functools
|
||||||
import itertools
|
import itertools
|
||||||
|
import re
|
||||||
|
|
||||||
from mopidy.models import Track
|
from mopidy.models import Track
|
||||||
from mopidy.frontends.mpd import translator
|
from mopidy.frontends.mpd import translator
|
||||||
@ -37,6 +38,81 @@ QUERY_RE = r"""
|
|||||||
$
|
$
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
MPD_SEARCH_QUERY_RE = re.compile(r"""
|
||||||
|
\b # Only begin matching at word bundaries
|
||||||
|
"? # Optional quote around the field type
|
||||||
|
(?: # A non-capturing group for the field type
|
||||||
|
[Aa]lbum
|
||||||
|
| [Aa]rtist
|
||||||
|
| [Aa]lbumartist
|
||||||
|
| [Cc]omment
|
||||||
|
| [Cc]omposer
|
||||||
|
| [Dd]ate
|
||||||
|
| [Ff]ile
|
||||||
|
| [Ff]ilename
|
||||||
|
| [Gg]enre
|
||||||
|
| [Pp]erformer
|
||||||
|
| [Tt]itle
|
||||||
|
| [Tt]rack
|
||||||
|
| [Aa]ny
|
||||||
|
)
|
||||||
|
"? # End of optional quote around the field type
|
||||||
|
\ # A single space
|
||||||
|
"[^"]+" # Matching a quoted search string
|
||||||
|
""", flags=(re.UNICODE | re.VERBOSE))
|
||||||
|
|
||||||
|
MPD_SEARCH_QUERY_PART_RE = re.compile(r"""
|
||||||
|
\b # Only begin matching at word bundaries
|
||||||
|
"? # Optional quote around the field type
|
||||||
|
(?P<field>( # A capturing group for the field type
|
||||||
|
[Aa]lbum
|
||||||
|
| [Aa]rtist
|
||||||
|
| [Aa]lbumartist
|
||||||
|
| [Cc]omment
|
||||||
|
| [Cc]omposer
|
||||||
|
| [Dd]ate
|
||||||
|
| [Ff]ile
|
||||||
|
| [Ff]ilename
|
||||||
|
| [Gg]enre
|
||||||
|
| [Pp]erformer
|
||||||
|
| [Tt]itle
|
||||||
|
| [Tt]rack
|
||||||
|
| [Aa]ny
|
||||||
|
))
|
||||||
|
"? # End of optional quote around the field type
|
||||||
|
\ # A single space
|
||||||
|
"(?P<what>[^"]+)" # Capturing a quoted search string
|
||||||
|
""", flags=(re.UNICODE | re.VERBOSE))
|
||||||
|
|
||||||
|
|
||||||
|
def _query_from_mpd_search_format(mpd_query):
|
||||||
|
"""
|
||||||
|
Parses an MPD ``search`` or ``find`` query and converts it to the Mopidy
|
||||||
|
query format.
|
||||||
|
|
||||||
|
:param mpd_query: the MPD search query
|
||||||
|
:type mpd_query: string
|
||||||
|
"""
|
||||||
|
query_parts = MPD_SEARCH_QUERY_RE.findall(mpd_query)
|
||||||
|
query = {}
|
||||||
|
for query_part in query_parts:
|
||||||
|
m = MPD_SEARCH_QUERY_PART_RE.match(query_part)
|
||||||
|
field = m.groupdict()['field'].lower()
|
||||||
|
if field == 'title':
|
||||||
|
field = 'track_name'
|
||||||
|
elif field == 'track':
|
||||||
|
field = 'track_no'
|
||||||
|
elif field in ('file', 'filename'):
|
||||||
|
field = 'uri'
|
||||||
|
what = m.groupdict()['what']
|
||||||
|
if not what:
|
||||||
|
raise ValueError
|
||||||
|
if field in query:
|
||||||
|
query[field].append(what)
|
||||||
|
else:
|
||||||
|
query[field] = [what]
|
||||||
|
return query
|
||||||
|
|
||||||
|
|
||||||
def _get_field(field, search_results):
|
def _get_field(field, search_results):
|
||||||
return list(itertools.chain(*[getattr(r, field) for r in search_results]))
|
return list(itertools.chain(*[getattr(r, field) for r in search_results]))
|
||||||
@ -79,7 +155,7 @@ def count(context, mpd_query):
|
|||||||
- use multiple tag-needle pairs to make more specific searches.
|
- use multiple tag-needle pairs to make more specific searches.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
query = translator.query_from_mpd_search_format(mpd_query)
|
query = _query_from_mpd_search_format(mpd_query)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise MpdArgError('incorrect arguments', command='count')
|
raise MpdArgError('incorrect arguments', command='count')
|
||||||
results = context.core.library.find_exact(**query).get()
|
results = context.core.library.find_exact(**query).get()
|
||||||
@ -119,7 +195,7 @@ def find(context, mpd_query):
|
|||||||
- uses "file" instead of "filename".
|
- uses "file" instead of "filename".
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
query = translator.query_from_mpd_search_format(mpd_query)
|
query = _query_from_mpd_search_format(mpd_query)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return
|
return
|
||||||
results = context.core.library.find_exact(**query).get()
|
results = context.core.library.find_exact(**query).get()
|
||||||
@ -146,7 +222,7 @@ def findadd(context, mpd_query):
|
|||||||
current playlist. Parameters have the same meaning as for ``find``.
|
current playlist. Parameters have the same meaning as for ``find``.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
query = translator.query_from_mpd_search_format(mpd_query)
|
query = _query_from_mpd_search_format(mpd_query)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return
|
return
|
||||||
results = context.core.library.find_exact(**query).get()
|
results = context.core.library.find_exact(**query).get()
|
||||||
@ -419,7 +495,7 @@ def search(context, mpd_query):
|
|||||||
- uses "file" instead of "filename".
|
- uses "file" instead of "filename".
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
query = translator.query_from_mpd_search_format(mpd_query)
|
query = _query_from_mpd_search_format(mpd_query)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return
|
return
|
||||||
results = context.core.library.search(**query).get()
|
results = context.core.library.search(**query).get()
|
||||||
@ -443,7 +519,7 @@ def searchadd(context, mpd_query):
|
|||||||
not case sensitive.
|
not case sensitive.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
query = translator.query_from_mpd_search_format(mpd_query)
|
query = _query_from_mpd_search_format(mpd_query)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return
|
return
|
||||||
results = context.core.library.search(**query).get()
|
results = context.core.library.search(**query).get()
|
||||||
@ -466,7 +542,7 @@ def searchaddpl(context, playlist_name, mpd_query):
|
|||||||
not case sensitive.
|
not case sensitive.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
query = translator.query_from_mpd_search_format(mpd_query)
|
query = _query_from_mpd_search_format(mpd_query)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return
|
return
|
||||||
results = context.core.library.search(**query).get()
|
results = context.core.library.search(**query).get()
|
||||||
|
|||||||
@ -199,85 +199,6 @@ def query_from_mpd_list_format(field, mpd_query):
|
|||||||
raise MpdArgError('not able to parse args', command='list')
|
raise MpdArgError('not able to parse args', command='list')
|
||||||
|
|
||||||
|
|
||||||
# XXX The regexps below should be refactored to reuse common patterns here
|
|
||||||
# and in mopidy.frontends.mpd.protocol.music_db.QUERY_RE.
|
|
||||||
|
|
||||||
MPD_SEARCH_QUERY_RE = re.compile(r"""
|
|
||||||
\b # Only begin matching at word bundaries
|
|
||||||
"? # Optional quote around the field type
|
|
||||||
(?: # A non-capturing group for the field type
|
|
||||||
[Aa]lbum
|
|
||||||
| [Aa]rtist
|
|
||||||
| [Aa]lbumartist
|
|
||||||
| [Cc]omment
|
|
||||||
| [Cc]omposer
|
|
||||||
| [Dd]ate
|
|
||||||
| [Ff]ile
|
|
||||||
| [Ff]ilename
|
|
||||||
| [Gg]enre
|
|
||||||
| [Pp]erformer
|
|
||||||
| [Tt]itle
|
|
||||||
| [Tt]rack
|
|
||||||
| [Aa]ny
|
|
||||||
)
|
|
||||||
"? # End of optional quote around the field type
|
|
||||||
\ # A single space
|
|
||||||
"[^"]+" # Matching a quoted search string
|
|
||||||
""", flags=(re.UNICODE | re.VERBOSE))
|
|
||||||
|
|
||||||
MPD_SEARCH_QUERY_PART_RE = re.compile(r"""
|
|
||||||
\b # Only begin matching at word bundaries
|
|
||||||
"? # Optional quote around the field type
|
|
||||||
(?P<field>( # A capturing group for the field type
|
|
||||||
[Aa]lbum
|
|
||||||
| [Aa]rtist
|
|
||||||
| [Aa]lbumartist
|
|
||||||
| [Cc]omment
|
|
||||||
| [Cc]omposer
|
|
||||||
| [Dd]ate
|
|
||||||
| [Ff]ile
|
|
||||||
| [Ff]ilename
|
|
||||||
| [Gg]enre
|
|
||||||
| [Pp]erformer
|
|
||||||
| [Tt]itle
|
|
||||||
| [Tt]rack
|
|
||||||
| [Aa]ny
|
|
||||||
))
|
|
||||||
"? # End of optional quote around the field type
|
|
||||||
\ # A single space
|
|
||||||
"(?P<what>[^"]+)" # Capturing a quoted search string
|
|
||||||
""", flags=(re.UNICODE | re.VERBOSE))
|
|
||||||
|
|
||||||
|
|
||||||
def query_from_mpd_search_format(mpd_query):
|
|
||||||
"""
|
|
||||||
Parses an MPD ``search`` or ``find`` query and converts it to the Mopidy
|
|
||||||
query format.
|
|
||||||
|
|
||||||
:param mpd_query: the MPD search query
|
|
||||||
:type mpd_query: string
|
|
||||||
"""
|
|
||||||
query_parts = MPD_SEARCH_QUERY_RE.findall(mpd_query)
|
|
||||||
query = {}
|
|
||||||
for query_part in query_parts:
|
|
||||||
m = MPD_SEARCH_QUERY_PART_RE.match(query_part)
|
|
||||||
field = m.groupdict()['field'].lower()
|
|
||||||
if field == 'title':
|
|
||||||
field = 'track_name'
|
|
||||||
elif field == 'track':
|
|
||||||
field = 'track_no'
|
|
||||||
elif field in ('file', 'filename'):
|
|
||||||
field = 'uri'
|
|
||||||
what = m.groupdict()['what']
|
|
||||||
if not what:
|
|
||||||
raise ValueError
|
|
||||||
if field in query:
|
|
||||||
query[field].append(what)
|
|
||||||
else:
|
|
||||||
query[field] = [what]
|
|
||||||
return query
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: move to tagcache backend.
|
# TODO: move to tagcache backend.
|
||||||
def tracks_to_tag_cache_format(tracks, media_dir):
|
def tracks_to_tag_cache_format(tracks, media_dir):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -1,10 +1,27 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from mopidy.frontends.mpd.protocol import music_db
|
||||||
from mopidy.models import Album, Artist, SearchResult, Track
|
from mopidy.models import Album, Artist, SearchResult, Track
|
||||||
|
|
||||||
from tests.frontends.mpd import protocol
|
from tests.frontends.mpd import protocol
|
||||||
|
|
||||||
|
|
||||||
|
class QueryFromMpdSearchFormatTest(unittest.TestCase):
|
||||||
|
def test_dates_are_extracted(self):
|
||||||
|
result = music_db._query_from_mpd_search_format(
|
||||||
|
'Date "1974-01-02" Date "1975"')
|
||||||
|
self.assertEqual(result['date'][0], '1974-01-02')
|
||||||
|
self.assertEqual(result['date'][1], '1975')
|
||||||
|
|
||||||
|
# TODO Test more mappings
|
||||||
|
|
||||||
|
|
||||||
|
class QueryFromMpdListFormatTest(unittest.TestCase):
|
||||||
|
pass # TODO
|
||||||
|
|
||||||
|
|
||||||
class MusicDatabaseHandlerTest(protocol.BaseTestCase):
|
class MusicDatabaseHandlerTest(protocol.BaseTestCase):
|
||||||
def test_count(self):
|
def test_count(self):
|
||||||
self.sendRequest('count "artist" "needle"')
|
self.sendRequest('count "artist" "needle"')
|
||||||
|
|||||||
@ -128,20 +128,6 @@ class PlaylistMpdFormatTest(unittest.TestCase):
|
|||||||
self.assertEqual(dict(result[0])['Track'], 2)
|
self.assertEqual(dict(result[0])['Track'], 2)
|
||||||
|
|
||||||
|
|
||||||
class QueryFromMpdSearchFormatTest(unittest.TestCase):
|
|
||||||
def test_dates_are_extracted(self):
|
|
||||||
result = translator.query_from_mpd_search_format(
|
|
||||||
'Date "1974-01-02" Date "1975"')
|
|
||||||
self.assertEqual(result['date'][0], '1974-01-02')
|
|
||||||
self.assertEqual(result['date'][1], '1975')
|
|
||||||
|
|
||||||
# TODO Test more mappings
|
|
||||||
|
|
||||||
|
|
||||||
class QueryFromMpdListFormatTest(unittest.TestCase):
|
|
||||||
pass # TODO
|
|
||||||
|
|
||||||
|
|
||||||
class TracksToTagCacheFormatTest(unittest.TestCase):
|
class TracksToTagCacheFormatTest(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.media_dir = '/dir/subdir'
|
self.media_dir = '/dir/subdir'
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user