Merge pull request #584 from jodal/feature/mpd-regexp-improvements
MPD regexp improvements
This commit is contained in:
commit
384e5b29b6
@ -44,10 +44,15 @@ def handle_request(pattern, auth_required=True):
|
||||
For example, if the command is ``do that thing`` the ``what`` argument will
|
||||
be ``this thing``::
|
||||
|
||||
@handle_request('^do (?P<what>.+)$')
|
||||
@handle_request('do\ (?P<what>.+)$')
|
||||
def do(what):
|
||||
...
|
||||
|
||||
Note that the patterns are compiled with the :attr:`re.VERBOSE` flag. Thus,
|
||||
you must escape any space characters you want to match, but you're also
|
||||
free to add non-escaped whitespace to format the pattern for easier
|
||||
reading.
|
||||
|
||||
:param pattern: regexp pattern for matching commands
|
||||
:type pattern: string
|
||||
"""
|
||||
@ -56,7 +61,7 @@ def handle_request(pattern, auth_required=True):
|
||||
if match is not None:
|
||||
mpd_commands.add(
|
||||
MpdCommand(name=match.group(), auth_required=auth_required))
|
||||
compiled_pattern = re.compile(pattern, flags=re.UNICODE)
|
||||
compiled_pattern = re.compile(pattern, flags=(re.UNICODE | re.VERBOSE))
|
||||
if compiled_pattern in request_handlers:
|
||||
raise ValueError('Tried to redefine handler for %s with %s' % (
|
||||
pattern, func))
|
||||
|
||||
@ -4,7 +4,7 @@ from mopidy.frontends.mpd.exceptions import MpdNoExistError
|
||||
from mopidy.frontends.mpd.protocol import handle_request
|
||||
|
||||
|
||||
@handle_request(r'^disableoutput "(?P<outputid>\d+)"$')
|
||||
@handle_request(r'disableoutput\ "(?P<outputid>\d+)"$')
|
||||
def disableoutput(context, outputid):
|
||||
"""
|
||||
*musicpd.org, audio output section:*
|
||||
@ -19,7 +19,7 @@ def disableoutput(context, outputid):
|
||||
raise MpdNoExistError('No such audio output', command='disableoutput')
|
||||
|
||||
|
||||
@handle_request(r'^enableoutput "(?P<outputid>\d+)"$')
|
||||
@handle_request(r'enableoutput\ "(?P<outputid>\d+)"$')
|
||||
def enableoutput(context, outputid):
|
||||
"""
|
||||
*musicpd.org, audio output section:*
|
||||
@ -34,7 +34,7 @@ def enableoutput(context, outputid):
|
||||
raise MpdNoExistError('No such audio output', command='enableoutput')
|
||||
|
||||
|
||||
@handle_request(r'^outputs$')
|
||||
@handle_request(r'outputs$')
|
||||
def outputs(context):
|
||||
"""
|
||||
*musicpd.org, audio output section:*
|
||||
|
||||
@ -4,7 +4,7 @@ from mopidy.frontends.mpd.protocol import handle_request
|
||||
from mopidy.frontends.mpd.exceptions import MpdNotImplemented
|
||||
|
||||
|
||||
@handle_request(r'^subscribe "(?P<channel>[A-Za-z0-9:._-]+)"$')
|
||||
@handle_request(r'subscribe\ "(?P<channel>[A-Za-z0-9:._-]+)"$')
|
||||
def subscribe(context, channel):
|
||||
"""
|
||||
*musicpd.org, client to client section:*
|
||||
@ -18,7 +18,7 @@ def subscribe(context, channel):
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
|
||||
@handle_request(r'^unsubscribe "(?P<channel>[A-Za-z0-9:._-]+)"$')
|
||||
@handle_request(r'unsubscribe\ "(?P<channel>[A-Za-z0-9:._-]+)"$')
|
||||
def unsubscribe(context, channel):
|
||||
"""
|
||||
*musicpd.org, client to client section:*
|
||||
@ -30,7 +30,7 @@ def unsubscribe(context, channel):
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
|
||||
@handle_request(r'^channels$')
|
||||
@handle_request(r'channels$')
|
||||
def channels(context):
|
||||
"""
|
||||
*musicpd.org, client to client section:*
|
||||
@ -43,7 +43,7 @@ def channels(context):
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
|
||||
@handle_request(r'^readmessages$')
|
||||
@handle_request(r'readmessages$')
|
||||
def readmessages(context):
|
||||
"""
|
||||
*musicpd.org, client to client section:*
|
||||
@ -57,7 +57,7 @@ def readmessages(context):
|
||||
|
||||
|
||||
@handle_request(
|
||||
r'^sendmessage "(?P<channel>[A-Za-z0-9:._-]+)" "(?P<text>[^"]*)"$')
|
||||
r'sendmessage\ "(?P<channel>[A-Za-z0-9:._-]+)"\ "(?P<text>[^"]*)"$')
|
||||
def sendmessage(context, channel, text):
|
||||
"""
|
||||
*musicpd.org, client to client section:*
|
||||
|
||||
@ -4,7 +4,7 @@ from mopidy.frontends.mpd.protocol import handle_request
|
||||
from mopidy.frontends.mpd.exceptions import MpdUnknownCommand
|
||||
|
||||
|
||||
@handle_request(r'^command_list_begin$')
|
||||
@handle_request(r'command_list_begin$')
|
||||
def command_list_begin(context):
|
||||
"""
|
||||
*musicpd.org, command list section:*
|
||||
@ -26,7 +26,7 @@ def command_list_begin(context):
|
||||
context.dispatcher.command_list = []
|
||||
|
||||
|
||||
@handle_request(r'^command_list_end$')
|
||||
@handle_request(r'command_list_end$')
|
||||
def command_list_end(context):
|
||||
"""See :meth:`command_list_begin()`."""
|
||||
if not context.dispatcher.command_list_receiving:
|
||||
@ -49,7 +49,7 @@ def command_list_end(context):
|
||||
return command_list_response
|
||||
|
||||
|
||||
@handle_request(r'^command_list_ok_begin$')
|
||||
@handle_request(r'command_list_ok_begin$')
|
||||
def command_list_ok_begin(context):
|
||||
"""See :meth:`command_list_begin()`."""
|
||||
context.dispatcher.command_list_receiving = True
|
||||
|
||||
@ -5,7 +5,7 @@ from mopidy.frontends.mpd.exceptions import (
|
||||
MpdPasswordError, MpdPermissionError)
|
||||
|
||||
|
||||
@handle_request(r'^close$', auth_required=False)
|
||||
@handle_request(r'close$', auth_required=False)
|
||||
def close(context):
|
||||
"""
|
||||
*musicpd.org, connection section:*
|
||||
@ -17,7 +17,7 @@ def close(context):
|
||||
context.session.close()
|
||||
|
||||
|
||||
@handle_request(r'^kill$')
|
||||
@handle_request(r'kill$')
|
||||
def kill(context):
|
||||
"""
|
||||
*musicpd.org, connection section:*
|
||||
@ -29,7 +29,7 @@ def kill(context):
|
||||
raise MpdPermissionError(command='kill')
|
||||
|
||||
|
||||
@handle_request(r'^password "(?P<password>[^"]+)"$', auth_required=False)
|
||||
@handle_request(r'password\ "(?P<password>[^"]+)"$', auth_required=False)
|
||||
def password_(context, password):
|
||||
"""
|
||||
*musicpd.org, connection section:*
|
||||
@ -45,7 +45,7 @@ def password_(context, password):
|
||||
raise MpdPasswordError('incorrect password', command='password')
|
||||
|
||||
|
||||
@handle_request(r'^ping$', auth_required=False)
|
||||
@handle_request(r'ping$', auth_required=False)
|
||||
def ping(context):
|
||||
"""
|
||||
*musicpd.org, connection section:*
|
||||
|
||||
@ -6,7 +6,7 @@ from mopidy.frontends.mpd.exceptions import (
|
||||
from mopidy.frontends.mpd.protocol import handle_request
|
||||
|
||||
|
||||
@handle_request(r'^add "(?P<uri>[^"]*)"$')
|
||||
@handle_request(r'add\ "(?P<uri>[^"]*)"$')
|
||||
def add(context, uri):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
@ -27,7 +27,7 @@ def add(context, uri):
|
||||
raise MpdNoExistError('directory or file not found', command='add')
|
||||
|
||||
|
||||
@handle_request(r'^addid "(?P<uri>[^"]*)"( "(?P<songpos>\d+)")*$')
|
||||
@handle_request(r'addid\ "(?P<uri>[^"]*)"(\ "(?P<songpos>\d+)")*$')
|
||||
def addid(context, uri, songpos=None):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
@ -58,7 +58,7 @@ def addid(context, uri, songpos=None):
|
||||
return ('Id', tl_tracks[0].tlid)
|
||||
|
||||
|
||||
@handle_request(r'^delete "(?P<start>\d+):(?P<end>\d+)*"$')
|
||||
@handle_request(r'delete\ "(?P<start>\d+):(?P<end>\d+)*"$')
|
||||
def delete_range(context, start, end=None):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
@ -79,7 +79,7 @@ def delete_range(context, start, end=None):
|
||||
context.core.tracklist.remove(tlid=[tlid])
|
||||
|
||||
|
||||
@handle_request(r'^delete "(?P<songpos>\d+)"$')
|
||||
@handle_request(r'delete\ "(?P<songpos>\d+)"$')
|
||||
def delete_songpos(context, songpos):
|
||||
"""See :meth:`delete_range`"""
|
||||
try:
|
||||
@ -91,7 +91,7 @@ def delete_songpos(context, songpos):
|
||||
raise MpdArgError('Bad song index', command='delete')
|
||||
|
||||
|
||||
@handle_request(r'^deleteid "(?P<tlid>\d+)"$')
|
||||
@handle_request(r'deleteid\ "(?P<tlid>\d+)"$')
|
||||
def deleteid(context, tlid):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
@ -106,7 +106,7 @@ def deleteid(context, tlid):
|
||||
raise MpdNoExistError('No such song', command='deleteid')
|
||||
|
||||
|
||||
@handle_request(r'^clear$')
|
||||
@handle_request(r'clear$')
|
||||
def clear(context):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
@ -118,7 +118,7 @@ def clear(context):
|
||||
context.core.tracklist.clear()
|
||||
|
||||
|
||||
@handle_request(r'^move "(?P<start>\d+):(?P<end>\d+)*" "(?P<to>\d+)"$')
|
||||
@handle_request(r'move\ "(?P<start>\d+):(?P<end>\d+)*"\ "(?P<to>\d+)"$')
|
||||
def move_range(context, start, to, end=None):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
@ -136,7 +136,7 @@ def move_range(context, start, to, end=None):
|
||||
context.core.tracklist.move(start, end, to)
|
||||
|
||||
|
||||
@handle_request(r'^move "(?P<songpos>\d+)" "(?P<to>\d+)"$')
|
||||
@handle_request(r'move\ "(?P<songpos>\d+)"\ "(?P<to>\d+)"$')
|
||||
def move_songpos(context, songpos, to):
|
||||
"""See :meth:`move_range`."""
|
||||
songpos = int(songpos)
|
||||
@ -144,7 +144,7 @@ def move_songpos(context, songpos, to):
|
||||
context.core.tracklist.move(songpos, songpos + 1, to)
|
||||
|
||||
|
||||
@handle_request(r'^moveid "(?P<tlid>\d+)" "(?P<to>\d+)"$')
|
||||
@handle_request(r'moveid\ "(?P<tlid>\d+)"\ "(?P<to>\d+)"$')
|
||||
def moveid(context, tlid, to):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
@ -164,7 +164,7 @@ def moveid(context, tlid, to):
|
||||
context.core.tracklist.move(position, position + 1, to)
|
||||
|
||||
|
||||
@handle_request(r'^playlist$')
|
||||
@handle_request(r'playlist$')
|
||||
def playlist(context):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
@ -180,8 +180,7 @@ def playlist(context):
|
||||
return playlistinfo(context)
|
||||
|
||||
|
||||
@handle_request(r'^playlistfind (?P<tag>[^"]+) "(?P<needle>[^"]+)"$')
|
||||
@handle_request(r'^playlistfind "(?P<tag>[^"]+)" "(?P<needle>[^"]+)"$')
|
||||
@handle_request(r'playlistfind\ ("?)(?P<tag>[^"]+)\1\ "(?P<needle>[^"]+)"$')
|
||||
def playlistfind(context, tag, needle):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
@ -203,7 +202,8 @@ def playlistfind(context, tag, needle):
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
|
||||
@handle_request(r'^playlistid( "(?P<tlid>\d+)")*$')
|
||||
@handle_request(r'playlistid$')
|
||||
@handle_request(r'playlistid\ "(?P<tlid>\d+)"$')
|
||||
def playlistid(context, tlid=None):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
@ -225,9 +225,9 @@ def playlistid(context, tlid=None):
|
||||
context.core.tracklist.tl_tracks.get())
|
||||
|
||||
|
||||
@handle_request(r'^playlistinfo$')
|
||||
@handle_request(r'^playlistinfo "(?P<songpos>-?\d+)"$')
|
||||
@handle_request(r'^playlistinfo "(?P<start>\d+):(?P<end>\d+)*"$')
|
||||
@handle_request(r'playlistinfo$')
|
||||
@handle_request(r'playlistinfo\ "(?P<songpos>-?\d+)"$')
|
||||
@handle_request(r'playlistinfo\ "(?P<start>\d+):(?P<end>\d+)*"$')
|
||||
def playlistinfo(context, songpos=None, start=None, end=None):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
@ -263,8 +263,7 @@ def playlistinfo(context, songpos=None, start=None, end=None):
|
||||
return translator.tracks_to_mpd_format(tl_tracks, start, end)
|
||||
|
||||
|
||||
@handle_request(r'^playlistsearch "(?P<tag>[^"]+)" "(?P<needle>[^"]+)"$')
|
||||
@handle_request(r'^playlistsearch (?P<tag>\w+) "(?P<needle>[^"]+)"$')
|
||||
@handle_request(r'playlistsearch\ ("?)(?P<tag>\w+)\1\ "(?P<needle>[^"]+)"$')
|
||||
def playlistsearch(context, tag, needle):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
@ -282,8 +281,7 @@ def playlistsearch(context, tag, needle):
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
|
||||
@handle_request(r'^plchanges (?P<version>-?\d+)$')
|
||||
@handle_request(r'^plchanges "(?P<version>-?\d+)"$')
|
||||
@handle_request(r'plchanges\ ("?)(?P<version>-?\d+)\1$')
|
||||
def plchanges(context, version):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
@ -305,7 +303,7 @@ def plchanges(context, version):
|
||||
context.core.tracklist.tl_tracks.get())
|
||||
|
||||
|
||||
@handle_request(r'^plchangesposid "(?P<version>\d+)"$')
|
||||
@handle_request(r'plchangesposid\ "(?P<version>\d+)"$')
|
||||
def plchangesposid(context, version):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
@ -329,8 +327,8 @@ def plchangesposid(context, version):
|
||||
return result
|
||||
|
||||
|
||||
@handle_request(r'^shuffle$')
|
||||
@handle_request(r'^shuffle "(?P<start>\d+):(?P<end>\d+)*"$')
|
||||
@handle_request(r'shuffle$')
|
||||
@handle_request(r'shuffle\ "(?P<start>\d+):(?P<end>\d+)*"$')
|
||||
def shuffle(context, start=None, end=None):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
@ -347,7 +345,7 @@ def shuffle(context, start=None, end=None):
|
||||
context.core.tracklist.shuffle(start, end)
|
||||
|
||||
|
||||
@handle_request(r'^swap "(?P<songpos1>\d+)" "(?P<songpos2>\d+)"$')
|
||||
@handle_request(r'swap\ "(?P<songpos1>\d+)"\ "(?P<songpos2>\d+)"$')
|
||||
def swap(context, songpos1, songpos2):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
@ -369,7 +367,7 @@ def swap(context, songpos1, songpos2):
|
||||
context.core.tracklist.add(tracks)
|
||||
|
||||
|
||||
@handle_request(r'^swapid "(?P<tlid1>\d+)" "(?P<tlid2>\d+)"$')
|
||||
@handle_request(r'swapid\ "(?P<tlid1>\d+)"\ "(?P<tlid2>\d+)"$')
|
||||
def swapid(context, tlid1, tlid2):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
|
||||
@ -3,7 +3,7 @@ from __future__ import unicode_literals
|
||||
from mopidy.frontends.mpd.protocol import handle_request
|
||||
|
||||
|
||||
@handle_request(r'^[ ]*$')
|
||||
@handle_request(r'[\ ]*$')
|
||||
def empty(context):
|
||||
"""The original MPD server returns ``OK`` on an empty request."""
|
||||
pass
|
||||
|
||||
@ -2,6 +2,7 @@ from __future__ import unicode_literals
|
||||
|
||||
import functools
|
||||
import itertools
|
||||
import re
|
||||
|
||||
from mopidy.models import Track
|
||||
from mopidy.frontends.mpd import translator
|
||||
@ -9,10 +10,114 @@ from mopidy.frontends.mpd.exceptions import MpdArgError, MpdNotImplemented
|
||||
from mopidy.frontends.mpd.protocol import handle_request, stored_playlists
|
||||
|
||||
|
||||
QUERY_RE = (
|
||||
r'(?P<mpd_query>("?([Aa]lbum|[Aa]rtist|[Aa]lbumartist|[Cc]omment|'
|
||||
r'[Cc]omposer|[Dd]ate|[Ff]ile|[Ff]ilename|[Gg]enre|[Pp]erformer|'
|
||||
r'[Tt]itle|[Tt]rack|[Aa]ny)"? "[^"]*"\s?)+)$')
|
||||
LIST_QUERY = r"""
|
||||
("?) # Optional quote around the field type
|
||||
(?P<field>( # 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<mpd_query>.*)
|
||||
)?
|
||||
$
|
||||
"""
|
||||
|
||||
SEARCH_FIELDS = r"""
|
||||
[Aa]lbum
|
||||
| [Aa]lbumartist
|
||||
| [Aa]ny
|
||||
| [Aa]rtist
|
||||
| [Cc]omment
|
||||
| [Cc]omposer
|
||||
| [Dd]ate
|
||||
| [Ff]ile
|
||||
| [Ff]ilename
|
||||
| [Gg]enre
|
||||
| [Pp]erformer
|
||||
| [Tt]itle
|
||||
| [Tt]rack
|
||||
"""
|
||||
|
||||
# TODO Would be nice to get ("?)...\1 working for the quotes here
|
||||
SEARCH_QUERY = r"""
|
||||
(?P<mpd_query>
|
||||
(?: # Non-capturing group for repeating query pairs
|
||||
"? # Optional quote around the field type
|
||||
(?:
|
||||
""" + SEARCH_FIELDS + r"""
|
||||
)
|
||||
"? # End of optional quote around the field type
|
||||
\ # A single space
|
||||
"[^"]*" # Matching a quoted search string
|
||||
\s?
|
||||
)+
|
||||
)
|
||||
$
|
||||
"""
|
||||
|
||||
# TODO Would be nice to get ("?)...\1 working for the quotes here
|
||||
SEARCH_PAIR_WITHOUT_GROUPS = r"""
|
||||
\b # Only begin matching at word bundaries
|
||||
"? # Optional quote around the field type
|
||||
(?: # A non-capturing group for the field type
|
||||
""" + SEARCH_FIELDS + """
|
||||
)
|
||||
"? # End of optional quote around the field type
|
||||
\ # A single space
|
||||
"[^"]+" # Matching a quoted search string
|
||||
"""
|
||||
SEARCH_PAIR_WITHOUT_GROUPS_RE = re.compile(
|
||||
SEARCH_PAIR_WITHOUT_GROUPS, flags=(re.UNICODE | re.VERBOSE))
|
||||
|
||||
# TODO Would be nice to get ("?)...\1 working for the quotes here
|
||||
SEARCH_PAIR_WITH_GROUPS = r"""
|
||||
\b # Only begin matching at word bundaries
|
||||
"? # Optional quote around the field type
|
||||
(?P<field>( # A capturing group for the field type
|
||||
""" + SEARCH_FIELDS + """
|
||||
))
|
||||
"? # End of optional quote around the field type
|
||||
\ # A single space
|
||||
"(?P<what>[^"]+)" # Capturing a quoted search string
|
||||
"""
|
||||
SEARCH_PAIR_WITH_GROUPS_RE = re.compile(
|
||||
SEARCH_PAIR_WITH_GROUPS, 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
|
||||
"""
|
||||
pairs = SEARCH_PAIR_WITHOUT_GROUPS_RE.findall(mpd_query)
|
||||
query = {}
|
||||
for pair in pairs:
|
||||
m = SEARCH_PAIR_WITH_GROUPS_RE.match(pair)
|
||||
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):
|
||||
@ -40,7 +145,7 @@ def _artist_as_track(artist):
|
||||
artists=[artist])
|
||||
|
||||
|
||||
@handle_request(r'^count ' + QUERY_RE)
|
||||
@handle_request(r'count\ ' + SEARCH_QUERY)
|
||||
def count(context, mpd_query):
|
||||
"""
|
||||
*musicpd.org, music database section:*
|
||||
@ -56,7 +161,7 @@ def count(context, mpd_query):
|
||||
- use multiple tag-needle pairs to make more specific searches.
|
||||
"""
|
||||
try:
|
||||
query = translator.query_from_mpd_search_format(mpd_query)
|
||||
query = _query_from_mpd_search_format(mpd_query)
|
||||
except ValueError:
|
||||
raise MpdArgError('incorrect arguments', command='count')
|
||||
results = context.core.library.find_exact(**query).get()
|
||||
@ -67,7 +172,7 @@ def count(context, mpd_query):
|
||||
]
|
||||
|
||||
|
||||
@handle_request(r'^find ' + QUERY_RE)
|
||||
@handle_request(r'find\ ' + SEARCH_QUERY)
|
||||
def find(context, mpd_query):
|
||||
"""
|
||||
*musicpd.org, music database section:*
|
||||
@ -96,7 +201,7 @@ def find(context, mpd_query):
|
||||
- uses "file" instead of "filename".
|
||||
"""
|
||||
try:
|
||||
query = translator.query_from_mpd_search_format(mpd_query)
|
||||
query = _query_from_mpd_search_format(mpd_query)
|
||||
except ValueError:
|
||||
return
|
||||
results = context.core.library.find_exact(**query).get()
|
||||
@ -112,7 +217,7 @@ def find(context, mpd_query):
|
||||
return translator.tracks_to_mpd_format(result_tracks)
|
||||
|
||||
|
||||
@handle_request(r'^findadd ' + QUERY_RE)
|
||||
@handle_request(r'findadd\ ' + SEARCH_QUERY)
|
||||
def findadd(context, mpd_query):
|
||||
"""
|
||||
*musicpd.org, music database section:*
|
||||
@ -123,17 +228,14 @@ def findadd(context, mpd_query):
|
||||
current playlist. Parameters have the same meaning as for ``find``.
|
||||
"""
|
||||
try:
|
||||
query = translator.query_from_mpd_search_format(mpd_query)
|
||||
query = _query_from_mpd_search_format(mpd_query)
|
||||
except ValueError:
|
||||
return
|
||||
results = context.core.library.find_exact(**query).get()
|
||||
context.core.tracklist.add(_get_tracks(results))
|
||||
|
||||
|
||||
@handle_request(
|
||||
r'^list "?(?P<field>([Aa]rtist|[Aa]lbumartist|[Aa]lbum|[Cc]omposer|'
|
||||
r'[Dd]ate|[Gg]enre|[Pp]erformer))"?'
|
||||
r'( (?P<mpd_query>.*))?$')
|
||||
@handle_request(r'list\ ' + LIST_QUERY)
|
||||
def list_(context, field, mpd_query=None):
|
||||
"""
|
||||
*musicpd.org, music database section:*
|
||||
@ -305,8 +407,8 @@ def _list_genre(context, query):
|
||||
return genres
|
||||
|
||||
|
||||
@handle_request(r'^listall$')
|
||||
@handle_request(r'^listall "(?P<uri>[^"]+)"$')
|
||||
@handle_request(r'listall$')
|
||||
@handle_request(r'listall\ "(?P<uri>[^"]+)"$')
|
||||
def listall(context, uri=None):
|
||||
"""
|
||||
*musicpd.org, music database section:*
|
||||
@ -318,8 +420,8 @@ def listall(context, uri=None):
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
|
||||
@handle_request(r'^listallinfo$')
|
||||
@handle_request(r'^listallinfo "(?P<uri>[^"]+)"$')
|
||||
@handle_request(r'listallinfo$')
|
||||
@handle_request(r'listallinfo\ "(?P<uri>[^"]+)"$')
|
||||
def listallinfo(context, uri=None):
|
||||
"""
|
||||
*musicpd.org, music database section:*
|
||||
@ -332,8 +434,8 @@ def listallinfo(context, uri=None):
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
|
||||
@handle_request(r'^lsinfo$')
|
||||
@handle_request(r'^lsinfo "(?P<uri>[^"]*)"$')
|
||||
@handle_request(r'lsinfo$')
|
||||
@handle_request(r'lsinfo\ "(?P<uri>[^"]*)"$')
|
||||
def lsinfo(context, uri=None):
|
||||
"""
|
||||
*musicpd.org, music database section:*
|
||||
@ -355,7 +457,8 @@ def lsinfo(context, uri=None):
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
|
||||
@handle_request(r'^rescan( "(?P<uri>[^"]+)")*$')
|
||||
@handle_request(r'rescan$')
|
||||
@handle_request(r'rescan\ "(?P<uri>[^"]+)"$')
|
||||
def rescan(context, uri=None):
|
||||
"""
|
||||
*musicpd.org, music database section:*
|
||||
@ -367,7 +470,7 @@ def rescan(context, uri=None):
|
||||
return update(context, uri, rescan_unmodified_files=True)
|
||||
|
||||
|
||||
@handle_request(r'^search ' + QUERY_RE)
|
||||
@handle_request(r'search\ ' + SEARCH_QUERY)
|
||||
def search(context, mpd_query):
|
||||
"""
|
||||
*musicpd.org, music database section:*
|
||||
@ -396,7 +499,7 @@ def search(context, mpd_query):
|
||||
- uses "file" instead of "filename".
|
||||
"""
|
||||
try:
|
||||
query = translator.query_from_mpd_search_format(mpd_query)
|
||||
query = _query_from_mpd_search_format(mpd_query)
|
||||
except ValueError:
|
||||
return
|
||||
results = context.core.library.search(**query).get()
|
||||
@ -406,7 +509,7 @@ def search(context, mpd_query):
|
||||
return translator.tracks_to_mpd_format(artists + albums + tracks)
|
||||
|
||||
|
||||
@handle_request(r'^searchadd ' + QUERY_RE)
|
||||
@handle_request(r'searchadd\ ' + SEARCH_QUERY)
|
||||
def searchadd(context, mpd_query):
|
||||
"""
|
||||
*musicpd.org, music database section:*
|
||||
@ -420,14 +523,14 @@ def searchadd(context, mpd_query):
|
||||
not case sensitive.
|
||||
"""
|
||||
try:
|
||||
query = translator.query_from_mpd_search_format(mpd_query)
|
||||
query = _query_from_mpd_search_format(mpd_query)
|
||||
except ValueError:
|
||||
return
|
||||
results = context.core.library.search(**query).get()
|
||||
context.core.tracklist.add(_get_tracks(results))
|
||||
|
||||
|
||||
@handle_request(r'^searchaddpl "(?P<playlist_name>[^"]+)" ' + QUERY_RE)
|
||||
@handle_request(r'searchaddpl\ "(?P<playlist_name>[^"]+)"\ ' + SEARCH_QUERY)
|
||||
def searchaddpl(context, playlist_name, mpd_query):
|
||||
"""
|
||||
*musicpd.org, music database section:*
|
||||
@ -443,7 +546,7 @@ def searchaddpl(context, playlist_name, mpd_query):
|
||||
not case sensitive.
|
||||
"""
|
||||
try:
|
||||
query = translator.query_from_mpd_search_format(mpd_query)
|
||||
query = _query_from_mpd_search_format(mpd_query)
|
||||
except ValueError:
|
||||
return
|
||||
results = context.core.library.search(**query).get()
|
||||
@ -456,7 +559,8 @@ def searchaddpl(context, playlist_name, mpd_query):
|
||||
context.core.playlists.save(playlist)
|
||||
|
||||
|
||||
@handle_request(r'^update( "(?P<uri>[^"]+)")*$')
|
||||
@handle_request(r'update$')
|
||||
@handle_request(r'update\ "(?P<uri>[^"]+)"$')
|
||||
def update(context, uri=None, rescan_unmodified_files=False):
|
||||
"""
|
||||
*musicpd.org, music database section:*
|
||||
|
||||
@ -6,8 +6,7 @@ from mopidy.frontends.mpd.exceptions import (
|
||||
MpdArgError, MpdNoExistError, MpdNotImplemented)
|
||||
|
||||
|
||||
@handle_request(r'^consume (?P<state>[01])$')
|
||||
@handle_request(r'^consume "(?P<state>[01])"$')
|
||||
@handle_request(r'consume\ ("?)(?P<state>[01])\1$')
|
||||
def consume(context, state):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
@ -24,7 +23,7 @@ def consume(context, state):
|
||||
context.core.tracklist.consume = False
|
||||
|
||||
|
||||
@handle_request(r'^crossfade "(?P<seconds>\d+)"$')
|
||||
@handle_request(r'crossfade\ "(?P<seconds>\d+)"$')
|
||||
def crossfade(context, seconds):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
@ -37,7 +36,7 @@ def crossfade(context, seconds):
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
|
||||
@handle_request(r'^next$')
|
||||
@handle_request(r'next$')
|
||||
def next_(context):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
@ -95,8 +94,8 @@ def next_(context):
|
||||
return context.core.playback.next().get()
|
||||
|
||||
|
||||
@handle_request(r'^pause$')
|
||||
@handle_request(r'^pause "(?P<state>[01])"$')
|
||||
@handle_request(r'pause$')
|
||||
@handle_request(r'pause\ "(?P<state>[01])"$')
|
||||
def pause(context, state=None):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
@ -120,7 +119,7 @@ def pause(context, state=None):
|
||||
context.core.playback.resume()
|
||||
|
||||
|
||||
@handle_request(r'^play$')
|
||||
@handle_request(r'play$')
|
||||
def play(context):
|
||||
"""
|
||||
The original MPD server resumes from the paused state on ``play``
|
||||
@ -129,8 +128,7 @@ def play(context):
|
||||
return context.core.playback.play().get()
|
||||
|
||||
|
||||
@handle_request(r'^playid (?P<tlid>-?\d+)$')
|
||||
@handle_request(r'^playid "(?P<tlid>-?\d+)"$')
|
||||
@handle_request(r'playid\ ("?)(?P<tlid>-?\d+)\1$')
|
||||
def playid(context, tlid):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
@ -157,8 +155,7 @@ def playid(context, tlid):
|
||||
return context.core.playback.play(tl_tracks[0]).get()
|
||||
|
||||
|
||||
@handle_request(r'^play (?P<songpos>-?\d+)$')
|
||||
@handle_request(r'^play "(?P<songpos>-?\d+)"$')
|
||||
@handle_request(r'play\ ("?)(?P<songpos>-?\d+)\1$')
|
||||
def playpos(context, songpos):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
@ -205,7 +202,7 @@ def _play_minus_one(context):
|
||||
return # Fail silently
|
||||
|
||||
|
||||
@handle_request(r'^previous$')
|
||||
@handle_request(r'previous$')
|
||||
def previous(context):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
@ -252,8 +249,7 @@ def previous(context):
|
||||
return context.core.playback.previous().get()
|
||||
|
||||
|
||||
@handle_request(r'^random (?P<state>[01])$')
|
||||
@handle_request(r'^random "(?P<state>[01])"$')
|
||||
@handle_request(r'random\ ("?)(?P<state>[01])\1$')
|
||||
def random(context, state):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
@ -268,8 +264,7 @@ def random(context, state):
|
||||
context.core.tracklist.random = False
|
||||
|
||||
|
||||
@handle_request(r'^repeat (?P<state>[01])$')
|
||||
@handle_request(r'^repeat "(?P<state>[01])"$')
|
||||
@handle_request(r'repeat\ ("?)(?P<state>[01])\1$')
|
||||
def repeat(context, state):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
@ -284,7 +279,7 @@ def repeat(context, state):
|
||||
context.core.tracklist.repeat = False
|
||||
|
||||
|
||||
@handle_request(r'^replay_gain_mode "(?P<mode>(off|track|album))"$')
|
||||
@handle_request(r'replay_gain_mode\ "(?P<mode>(off|track|album))"$')
|
||||
def replay_gain_mode(context, mode):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
@ -301,7 +296,7 @@ def replay_gain_mode(context, mode):
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
|
||||
@handle_request(r'^replay_gain_status$')
|
||||
@handle_request(r'replay_gain_status$')
|
||||
def replay_gain_status(context):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
@ -314,8 +309,7 @@ def replay_gain_status(context):
|
||||
return 'off' # TODO
|
||||
|
||||
|
||||
@handle_request(r'^seek (?P<songpos>\d+) (?P<seconds>\d+)$')
|
||||
@handle_request(r'^seek "(?P<songpos>\d+)" "(?P<seconds>\d+)"$')
|
||||
@handle_request(r'seek\ ("?)(?P<songpos>\d+)\1\ ("?)(?P<seconds>\d+)\3$')
|
||||
def seek(context, songpos, seconds):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
@ -335,7 +329,7 @@ def seek(context, songpos, seconds):
|
||||
context.core.playback.seek(int(seconds) * 1000).get()
|
||||
|
||||
|
||||
@handle_request(r'^seekid "(?P<tlid>\d+)" "(?P<seconds>\d+)"$')
|
||||
@handle_request(r'seekid\ "(?P<tlid>\d+)"\ "(?P<seconds>\d+)"$')
|
||||
def seekid(context, tlid, seconds):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
@ -350,8 +344,8 @@ def seekid(context, tlid, seconds):
|
||||
context.core.playback.seek(int(seconds) * 1000).get()
|
||||
|
||||
|
||||
@handle_request(r'^seekcur "(?P<position>\d+)"$')
|
||||
@handle_request(r'^seekcur "(?P<diff>[-+]\d+)"$')
|
||||
@handle_request(r'seekcur\ "(?P<position>\d+)"$')
|
||||
@handle_request(r'seekcur\ "(?P<diff>[-+]\d+)"$')
|
||||
def seekcur(context, position=None, diff=None):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
@ -370,8 +364,7 @@ def seekcur(context, position=None, diff=None):
|
||||
context.core.playback.seek(position).get()
|
||||
|
||||
|
||||
@handle_request(r'^setvol (?P<volume>[-+]*\d+)$')
|
||||
@handle_request(r'^setvol "(?P<volume>[-+]*\d+)"$')
|
||||
@handle_request(r'setvol\ ("?)(?P<volume>[-+]*\d+)\1$')
|
||||
def setvol(context, volume):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
@ -392,8 +385,7 @@ def setvol(context, volume):
|
||||
context.core.playback.volume = volume
|
||||
|
||||
|
||||
@handle_request(r'^single (?P<state>[01])$')
|
||||
@handle_request(r'^single "(?P<state>[01])"$')
|
||||
@handle_request(r'single\ ("?)(?P<state>[01])\1$')
|
||||
def single(context, state):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
@ -410,7 +402,7 @@ def single(context, state):
|
||||
context.core.tracklist.single = False
|
||||
|
||||
|
||||
@handle_request(r'^stop$')
|
||||
@handle_request(r'stop$')
|
||||
def stop(context):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
|
||||
@ -4,7 +4,7 @@ from mopidy.frontends.mpd.exceptions import MpdPermissionError
|
||||
from mopidy.frontends.mpd.protocol import handle_request, mpd_commands
|
||||
|
||||
|
||||
@handle_request(r'^config$', auth_required=False)
|
||||
@handle_request(r'config$', auth_required=False)
|
||||
def config(context):
|
||||
"""
|
||||
*musicpd.org, reflection section:*
|
||||
@ -18,7 +18,7 @@ def config(context):
|
||||
raise MpdPermissionError(command='config')
|
||||
|
||||
|
||||
@handle_request(r'^commands$', auth_required=False)
|
||||
@handle_request(r'commands$', auth_required=False)
|
||||
def commands(context):
|
||||
"""
|
||||
*musicpd.org, reflection section:*
|
||||
@ -45,7 +45,7 @@ def commands(context):
|
||||
('command', command_name) for command_name in sorted(command_names)]
|
||||
|
||||
|
||||
@handle_request(r'^decoders$')
|
||||
@handle_request(r'decoders$')
|
||||
def decoders(context):
|
||||
"""
|
||||
*musicpd.org, reflection section:*
|
||||
@ -72,7 +72,7 @@ def decoders(context):
|
||||
return # TODO
|
||||
|
||||
|
||||
@handle_request(r'^notcommands$', auth_required=False)
|
||||
@handle_request(r'notcommands$', auth_required=False)
|
||||
def notcommands(context):
|
||||
"""
|
||||
*musicpd.org, reflection section:*
|
||||
@ -95,7 +95,7 @@ def notcommands(context):
|
||||
('command', command_name) for command_name in sorted(command_names)]
|
||||
|
||||
|
||||
@handle_request(r'^tagtypes$')
|
||||
@handle_request(r'tagtypes$')
|
||||
def tagtypes(context):
|
||||
"""
|
||||
*musicpd.org, reflection section:*
|
||||
@ -107,7 +107,7 @@ def tagtypes(context):
|
||||
pass # TODO
|
||||
|
||||
|
||||
@handle_request(r'^urlhandlers$')
|
||||
@handle_request(r'urlhandlers$')
|
||||
def urlhandlers(context):
|
||||
"""
|
||||
*musicpd.org, reflection section:*
|
||||
|
||||
@ -13,7 +13,7 @@ SUBSYSTEMS = [
|
||||
'stored_playlist', 'update']
|
||||
|
||||
|
||||
@handle_request(r'^clearerror$')
|
||||
@handle_request(r'clearerror$')
|
||||
def clearerror(context):
|
||||
"""
|
||||
*musicpd.org, status section:*
|
||||
@ -26,7 +26,7 @@ def clearerror(context):
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
|
||||
@handle_request(r'^currentsong$')
|
||||
@handle_request(r'currentsong$')
|
||||
def currentsong(context):
|
||||
"""
|
||||
*musicpd.org, status section:*
|
||||
@ -42,8 +42,8 @@ def currentsong(context):
|
||||
return track_to_mpd_format(tl_track, position=position)
|
||||
|
||||
|
||||
@handle_request(r'^idle$')
|
||||
@handle_request(r'^idle (?P<subsystems>.+)$')
|
||||
@handle_request(r'idle$')
|
||||
@handle_request(r'idle\ (?P<subsystems>.+)$')
|
||||
def idle(context, subsystems=None):
|
||||
"""
|
||||
*musicpd.org, status section:*
|
||||
@ -100,7 +100,7 @@ def idle(context, subsystems=None):
|
||||
return response
|
||||
|
||||
|
||||
@handle_request(r'^noidle$')
|
||||
@handle_request(r'noidle$')
|
||||
def noidle(context):
|
||||
"""See :meth:`_status_idle`."""
|
||||
if not context.subscriptions:
|
||||
@ -110,7 +110,7 @@ def noidle(context):
|
||||
context.session.prevent_timeout = False
|
||||
|
||||
|
||||
@handle_request(r'^stats$')
|
||||
@handle_request(r'stats$')
|
||||
def stats(context):
|
||||
"""
|
||||
*musicpd.org, status section:*
|
||||
@ -137,7 +137,7 @@ def stats(context):
|
||||
}
|
||||
|
||||
|
||||
@handle_request(r'^status$')
|
||||
@handle_request(r'status$')
|
||||
def status(context):
|
||||
"""
|
||||
*musicpd.org, status section:*
|
||||
|
||||
@ -5,8 +5,8 @@ from mopidy.frontends.mpd.exceptions import MpdNotImplemented
|
||||
|
||||
|
||||
@handle_request(
|
||||
r'^sticker delete "(?P<field>[^"]+)" '
|
||||
r'"(?P<uri>[^"]+)"( "(?P<name>[^"]+)")*$')
|
||||
r'sticker\ delete\ "(?P<field>[^"]+)"\ '
|
||||
r'"(?P<uri>[^"]+)"(\ "(?P<name>[^"]+)")*$')
|
||||
def sticker_delete(context, field, uri, name=None):
|
||||
"""
|
||||
*musicpd.org, sticker section:*
|
||||
@ -20,7 +20,7 @@ def sticker_delete(context, field, uri, name=None):
|
||||
|
||||
|
||||
@handle_request(
|
||||
r'^sticker find "(?P<field>[^"]+)" "(?P<uri>[^"]+)" '
|
||||
r'sticker\ find\ "(?P<field>[^"]+)"\ "(?P<uri>[^"]+)"\ '
|
||||
r'"(?P<name>[^"]+)"$')
|
||||
def sticker_find(context, field, uri, name):
|
||||
"""
|
||||
@ -36,7 +36,7 @@ def sticker_find(context, field, uri, name):
|
||||
|
||||
|
||||
@handle_request(
|
||||
r'^sticker get "(?P<field>[^"]+)" "(?P<uri>[^"]+)" '
|
||||
r'sticker\ get\ "(?P<field>[^"]+)"\ "(?P<uri>[^"]+)"\ '
|
||||
r'"(?P<name>[^"]+)"$')
|
||||
def sticker_get(context, field, uri, name):
|
||||
"""
|
||||
@ -49,7 +49,7 @@ def sticker_get(context, field, uri, name):
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
|
||||
@handle_request(r'^sticker list "(?P<field>[^"]+)" "(?P<uri>[^"]+)"$')
|
||||
@handle_request(r'sticker\ list\ "(?P<field>[^"]+)"\ "(?P<uri>[^"]+)"$')
|
||||
def sticker_list(context, field, uri):
|
||||
"""
|
||||
*musicpd.org, sticker section:*
|
||||
@ -62,8 +62,8 @@ def sticker_list(context, field, uri):
|
||||
|
||||
|
||||
@handle_request(
|
||||
r'^sticker set "(?P<field>[^"]+)" "(?P<uri>[^"]+)" '
|
||||
r'"(?P<name>[^"]+)" "(?P<value>[^"]+)"$')
|
||||
r'sticker\ set\ "(?P<field>[^"]+)"\ "(?P<uri>[^"]+)"\ '
|
||||
r'"(?P<name>[^"]+)"\ "(?P<value>[^"]+)"$')
|
||||
def sticker_set(context, field, uri, name, value):
|
||||
"""
|
||||
*musicpd.org, sticker section:*
|
||||
|
||||
@ -7,8 +7,7 @@ from mopidy.frontends.mpd.protocol import handle_request
|
||||
from mopidy.frontends.mpd.translator import playlist_to_mpd_format
|
||||
|
||||
|
||||
@handle_request(r'^listplaylist (?P<name>\w+)$')
|
||||
@handle_request(r'^listplaylist "(?P<name>[^"]+)"$')
|
||||
@handle_request(r'listplaylist\ ("?)(?P<name>[^"]+)\1$')
|
||||
def listplaylist(context, name):
|
||||
"""
|
||||
*musicpd.org, stored playlists section:*
|
||||
@ -29,8 +28,7 @@ def listplaylist(context, name):
|
||||
return ['file: %s' % t.uri for t in playlist.tracks]
|
||||
|
||||
|
||||
@handle_request(r'^listplaylistinfo (?P<name>\w+)$')
|
||||
@handle_request(r'^listplaylistinfo "(?P<name>[^"]+)"$')
|
||||
@handle_request(r'listplaylistinfo\ ("?)(?P<name>[^"]+)\1$')
|
||||
def listplaylistinfo(context, name):
|
||||
"""
|
||||
*musicpd.org, stored playlists section:*
|
||||
@ -50,7 +48,7 @@ def listplaylistinfo(context, name):
|
||||
return playlist_to_mpd_format(playlist)
|
||||
|
||||
|
||||
@handle_request(r'^listplaylists$')
|
||||
@handle_request(r'listplaylists$')
|
||||
def listplaylists(context):
|
||||
"""
|
||||
*musicpd.org, stored playlists section:*
|
||||
@ -92,7 +90,8 @@ def listplaylists(context):
|
||||
return result
|
||||
|
||||
|
||||
@handle_request(r'^load "(?P<name>[^"]+)"( "(?P<start>\d+):(?P<end>\d+)*")*$')
|
||||
@handle_request(
|
||||
r'load\ "(?P<name>[^"]+)"(\ "(?P<start>\d+):(?P<end>\d+)*")*$')
|
||||
def load(context, name, start=None, end=None):
|
||||
"""
|
||||
*musicpd.org, stored playlists section:*
|
||||
@ -124,7 +123,7 @@ def load(context, name, start=None, end=None):
|
||||
context.core.tracklist.add(playlist.tracks[start:end])
|
||||
|
||||
|
||||
@handle_request(r'^playlistadd "(?P<name>[^"]+)" "(?P<uri>[^"]+)"$')
|
||||
@handle_request(r'playlistadd\ "(?P<name>[^"]+)"\ "(?P<uri>[^"]+)"$')
|
||||
def playlistadd(context, name, uri):
|
||||
"""
|
||||
*musicpd.org, stored playlists section:*
|
||||
@ -138,7 +137,7 @@ def playlistadd(context, name, uri):
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
|
||||
@handle_request(r'^playlistclear "(?P<name>[^"]+)"$')
|
||||
@handle_request(r'playlistclear\ "(?P<name>[^"]+)"$')
|
||||
def playlistclear(context, name):
|
||||
"""
|
||||
*musicpd.org, stored playlists section:*
|
||||
@ -150,7 +149,7 @@ def playlistclear(context, name):
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
|
||||
@handle_request(r'^playlistdelete "(?P<name>[^"]+)" "(?P<songpos>\d+)"$')
|
||||
@handle_request(r'playlistdelete\ "(?P<name>[^"]+)"\ "(?P<songpos>\d+)"$')
|
||||
def playlistdelete(context, name, songpos):
|
||||
"""
|
||||
*musicpd.org, stored playlists section:*
|
||||
@ -163,8 +162,8 @@ def playlistdelete(context, name, songpos):
|
||||
|
||||
|
||||
@handle_request(
|
||||
r'^playlistmove "(?P<name>[^"]+)" '
|
||||
r'"(?P<from_pos>\d+)" "(?P<to_pos>\d+)"$')
|
||||
r'playlistmove\ "(?P<name>[^"]+)"\ '
|
||||
r'"(?P<from_pos>\d+)"\ "(?P<to_pos>\d+)"$')
|
||||
def playlistmove(context, name, from_pos, to_pos):
|
||||
"""
|
||||
*musicpd.org, stored playlists section:*
|
||||
@ -183,7 +182,7 @@ def playlistmove(context, name, from_pos, to_pos):
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
|
||||
@handle_request(r'^rename "(?P<old_name>[^"]+)" "(?P<new_name>[^"]+)"$')
|
||||
@handle_request(r'rename\ "(?P<old_name>[^"]+)"\ "(?P<new_name>[^"]+)"$')
|
||||
def rename(context, old_name, new_name):
|
||||
"""
|
||||
*musicpd.org, stored playlists section:*
|
||||
@ -195,7 +194,7 @@ def rename(context, old_name, new_name):
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
|
||||
@handle_request(r'^rm "(?P<name>[^"]+)"$')
|
||||
@handle_request(r'rm\ "(?P<name>[^"]+)"$')
|
||||
def rm(context, name):
|
||||
"""
|
||||
*musicpd.org, stored playlists section:*
|
||||
@ -207,7 +206,7 @@ def rm(context, name):
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
|
||||
@handle_request(r'^save "(?P<name>[^"]+)"$')
|
||||
@handle_request(r'save\ "(?P<name>[^"]+)"$')
|
||||
def save(context, name):
|
||||
"""
|
||||
*musicpd.org, stored playlists section:*
|
||||
|
||||
@ -199,85 +199,6 @@ def query_from_mpd_list_format(field, mpd_query):
|
||||
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
|
||||
\s # A single space
|
||||
"[^"]+" # Matching a quoted search string
|
||||
""", 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
|
||||
\s # A single space
|
||||
"(?P<what>[^"]+)" # Capturing a quoted search string
|
||||
""", 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.
|
||||
def tracks_to_tag_cache_format(tracks, media_dir):
|
||||
"""
|
||||
|
||||
@ -1,10 +1,27 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import unittest
|
||||
|
||||
from mopidy.frontends.mpd.protocol import music_db
|
||||
from mopidy.models import Album, Artist, SearchResult, Track
|
||||
|
||||
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):
|
||||
def test_count(self):
|
||||
self.sendRequest('count "artist" "needle"')
|
||||
|
||||
@ -128,20 +128,6 @@ class PlaylistMpdFormatTest(unittest.TestCase):
|
||||
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):
|
||||
def setUp(self):
|
||||
self.media_dir = '/dir/subdir'
|
||||
|
||||
Loading…
Reference in New Issue
Block a user