Merge branch 'develop' into feature/extract-socket-code
This commit is contained in:
commit
6575dafcb9
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,6 +1,7 @@
|
||||
*.pyc
|
||||
*.swp
|
||||
.coverage
|
||||
.idea
|
||||
.noseids
|
||||
.tox
|
||||
MANIFEST
|
||||
|
||||
@ -1,10 +0,0 @@
|
||||
def setup(app):
|
||||
app.connect('autodoc-skip-member', autodoc_private_members_with_doc)
|
||||
|
||||
def autodoc_private_members_with_doc(app, what, name, obj, skip, options):
|
||||
if not skip:
|
||||
return skip
|
||||
if (name.startswith('_') and obj.__doc__ is not None
|
||||
and not (name.startswith('__') and name.endswith('__'))):
|
||||
return False
|
||||
return skip
|
||||
@ -26,6 +26,24 @@ No description yet.
|
||||
|
||||
- Improve :option:`--list-settings` output. (Fixes: :issue:`91`)
|
||||
|
||||
- Replace not decodable characters returned from Spotify instead of throwing an
|
||||
exception, as we won't try to figure out the encoding of non-UTF-8-data.
|
||||
|
||||
- MPD frontend:
|
||||
|
||||
- Refactoring and cleanup. Most notably, all request handlers now get an
|
||||
instance of :class:`mopidy.frontends.mpd.dispatcher.MpdContext` as the
|
||||
first argument. The new class contains reference to any object in Mopidy
|
||||
the MPD protocol implementation should need access to.
|
||||
|
||||
- Close the client connection when the command ``close`` is received.
|
||||
|
||||
- Do not allow access to the command ``kill``.
|
||||
|
||||
- ``commands`` and ``notcommands`` now have correct output if password
|
||||
authentication is turned on, but the connected user has not been
|
||||
authenticated yet.
|
||||
|
||||
|
||||
v0.4.1 (2011-05-06)
|
||||
===================
|
||||
|
||||
@ -25,7 +25,7 @@ import mopidy
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = ['sphinx.ext.autodoc', 'autodoc_private_members',
|
||||
extensions = ['sphinx.ext.autodoc',
|
||||
'sphinx.ext.graphviz', 'sphinx.ext.inheritance_diagram',
|
||||
'sphinx.ext.extlinks', 'sphinx.ext.viewcode']
|
||||
|
||||
|
||||
@ -33,13 +33,13 @@ User documentation
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
|
||||
changes
|
||||
installation/index
|
||||
settings
|
||||
running
|
||||
clients/index
|
||||
authors
|
||||
licenses
|
||||
changes
|
||||
|
||||
Reference documentation
|
||||
=======================
|
||||
|
||||
@ -98,7 +98,7 @@ install Mopidy from PyPI using Pip.
|
||||
#. Then, you need to install Pip::
|
||||
|
||||
sudo aptitude install python-setuptools python-pip # On Ubuntu/Debian
|
||||
sudo brew install pip # On OS X
|
||||
sudo easy_install pip # On OS X
|
||||
|
||||
#. To install the currently latest stable release of Mopidy::
|
||||
|
||||
@ -132,7 +132,7 @@ Mopidy's ``develop`` branch.
|
||||
#. Then, you need to install Pip::
|
||||
|
||||
sudo aptitude install python-setuptools python-pip # On Ubuntu/Debian
|
||||
sudo brew install pip # On OS X
|
||||
sudo easy_install pip # On OS X
|
||||
|
||||
#. To install the latest snapshot of Mopidy, run::
|
||||
|
||||
@ -155,7 +155,7 @@ If you want to contribute to Mopidy, you should install Mopidy using Git.
|
||||
#. Then install Git, if haven't already::
|
||||
|
||||
sudo aptitude install git-core # On Ubuntu/Debian
|
||||
sudo brew install git # On OS X
|
||||
sudo brew install git # On OS X using Homebrew
|
||||
|
||||
#. Clone the official Mopidy repository, or your own fork of it::
|
||||
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
:mod:`mopidy.frontends.mpd` -- MPD server
|
||||
*****************************************
|
||||
|
||||
.. inheritance-diagram:: mopidy.frontends.mpd
|
||||
|
||||
.. automodule:: mopidy.frontends.mpd
|
||||
:synopsis: MPD frontend
|
||||
:members:
|
||||
@ -11,28 +13,30 @@
|
||||
MPD server
|
||||
==========
|
||||
|
||||
.. inheritance-diagram:: mopidy.frontends.mpd.server
|
||||
|
||||
.. automodule:: mopidy.frontends.mpd.server
|
||||
:synopsis: MPD server
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
.. inheritance-diagram:: mopidy.frontends.mpd.server
|
||||
|
||||
|
||||
MPD session
|
||||
===========
|
||||
|
||||
.. inheritance-diagram:: mopidy.frontends.mpd.session
|
||||
|
||||
.. automodule:: mopidy.frontends.mpd.session
|
||||
:synopsis: MPD client session
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
.. inheritance-diagram:: mopidy.frontends.mpd.session
|
||||
|
||||
|
||||
MPD dispatcher
|
||||
==============
|
||||
|
||||
.. inheritance-diagram:: mopidy.frontends.mpd.dispatcher
|
||||
|
||||
.. automodule:: mopidy.frontends.mpd.dispatcher
|
||||
:synopsis: MPD request dispatcher
|
||||
:members:
|
||||
|
||||
@ -2,6 +2,8 @@ from copy import copy
|
||||
import logging
|
||||
import random
|
||||
|
||||
from mopidy.models import CpTrack
|
||||
|
||||
logger = logging.getLogger('mopidy.backends.base')
|
||||
|
||||
class CurrentPlaylistController(object):
|
||||
@ -66,7 +68,7 @@ class CurrentPlaylistController(object):
|
||||
"""
|
||||
assert at_position <= len(self._cp_tracks), \
|
||||
u'at_position can not be greater than playlist length'
|
||||
cp_track = (self.version, track)
|
||||
cp_track = CpTrack(self.version, track)
|
||||
if at_position is not None:
|
||||
self._cp_tracks.insert(at_position, cp_track)
|
||||
else:
|
||||
|
||||
@ -80,12 +80,12 @@ class PlaybackController(object):
|
||||
def _get_cpid(self, cp_track):
|
||||
if cp_track is None:
|
||||
return None
|
||||
return cp_track[0]
|
||||
return cp_track.cpid
|
||||
|
||||
def _get_track(self, cp_track):
|
||||
if cp_track is None:
|
||||
return None
|
||||
return cp_track[1]
|
||||
return cp_track.track
|
||||
|
||||
@property
|
||||
def current_cpid(self):
|
||||
@ -331,7 +331,7 @@ class PlaybackController(object):
|
||||
self.stop(clear_current_track=True)
|
||||
|
||||
if self.consume:
|
||||
self.backend.current_playlist.remove(cpid=original_cp_track[0])
|
||||
self.backend.current_playlist.remove(cpid=original_cp_track.cpid)
|
||||
|
||||
def on_current_playlist_change(self):
|
||||
"""
|
||||
@ -389,7 +389,7 @@ class PlaybackController(object):
|
||||
self.state = self.STOPPED
|
||||
self.current_cp_track = cp_track
|
||||
self.state = self.PLAYING
|
||||
if not self.provider.play(cp_track[1]):
|
||||
if not self.provider.play(cp_track.track):
|
||||
# Track is not playable
|
||||
if self.random and self._shuffled:
|
||||
self._shuffled.remove(cp_track)
|
||||
|
||||
@ -16,28 +16,29 @@ class SpotifyTranslator(object):
|
||||
return Artist(name=u'[loading...]')
|
||||
return Artist(
|
||||
uri=str(Link.from_artist(spotify_artist)),
|
||||
name=spotify_artist.name().decode(ENCODING),
|
||||
name=spotify_artist.name().decode(ENCODING, 'replace'),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def to_mopidy_album(cls, spotify_album):
|
||||
if not spotify_album.is_loaded():
|
||||
if spotify_album is None or not spotify_album.is_loaded():
|
||||
return Album(name=u'[loading...]')
|
||||
# TODO pyspotify got much more data on albums than this
|
||||
return Album(name=spotify_album.name().decode(ENCODING))
|
||||
return Album(name=spotify_album.name().decode(ENCODING, 'replace'))
|
||||
|
||||
@classmethod
|
||||
def to_mopidy_track(cls, spotify_track):
|
||||
uri = str(Link.from_track(spotify_track, 0))
|
||||
if not spotify_track.is_loaded():
|
||||
return Track(uri=uri, name=u'[loading...]')
|
||||
if dt.MINYEAR <= int(spotify_track.album().year()) <= dt.MAXYEAR:
|
||||
if (spotify_track.album() is not None and
|
||||
dt.MINYEAR <= int(spotify_track.album().year()) <= dt.MAXYEAR):
|
||||
date = dt.date(spotify_track.album().year(), 1, 1)
|
||||
else:
|
||||
date = None
|
||||
return Track(
|
||||
uri=uri,
|
||||
name=spotify_track.name().decode(ENCODING),
|
||||
name=spotify_track.name().decode(ENCODING, 'replace'),
|
||||
artists=[cls.to_mopidy_artist(a) for a in spotify_track.artists()],
|
||||
album=cls.to_mopidy_album(spotify_track.album()),
|
||||
track_no=spotify_track.index(),
|
||||
@ -56,7 +57,7 @@ class SpotifyTranslator(object):
|
||||
try:
|
||||
return Playlist(
|
||||
uri=str(Link.from_playlist(spotify_playlist)),
|
||||
name=spotify_playlist.name().decode(ENCODING),
|
||||
name=spotify_playlist.name().decode(ENCODING, 'replace'),
|
||||
# FIXME if check on link is a hackish workaround for is_local
|
||||
tracks=[cls.to_mopidy_track(t) for t in spotify_playlist
|
||||
if str(Link.from_track(t, 0))],
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
import logging
|
||||
import re
|
||||
|
||||
from pykka import ActorDeadError
|
||||
from pykka.registry import ActorRegistry
|
||||
|
||||
from mopidy import settings
|
||||
from mopidy.backends.base import Backend
|
||||
from mopidy.frontends.mpd.exceptions import (MpdAckError, MpdArgError,
|
||||
MpdUnknownCommand)
|
||||
from mopidy.frontends.mpd import exceptions
|
||||
from mopidy.frontends.mpd.protocol import mpd_commands, request_handlers
|
||||
# Do not remove the following import. The protocol modules must be imported to
|
||||
# get them registered as request handlers.
|
||||
@ -16,6 +18,8 @@ from mopidy.frontends.mpd.protocol import (audio_output, command_list,
|
||||
from mopidy.mixers.base import BaseMixer
|
||||
from mopidy.utils import flatten
|
||||
|
||||
logger = logging.getLogger('mopidy.frontends.mpd.dispatcher')
|
||||
|
||||
class MpdDispatcher(object):
|
||||
"""
|
||||
The MPD session feeds the MPD dispatcher with requests. The dispatcher
|
||||
@ -23,67 +27,186 @@ class MpdDispatcher(object):
|
||||
back to the MPD session.
|
||||
"""
|
||||
|
||||
# XXX Consider merging MpdDispatcher into MpdSession
|
||||
|
||||
def __init__(self):
|
||||
backend_refs = ActorRegistry.get_by_class(Backend)
|
||||
assert len(backend_refs) == 1, 'Expected exactly one running backend.'
|
||||
self.backend = backend_refs[0].proxy()
|
||||
|
||||
mixer_refs = ActorRegistry.get_by_class(BaseMixer)
|
||||
assert len(mixer_refs) == 1, 'Expected exactly one running mixer.'
|
||||
self.mixer = mixer_refs[0].proxy()
|
||||
|
||||
def __init__(self, session=None):
|
||||
self.authenticated = False
|
||||
self.command_list = False
|
||||
self.command_list_ok = False
|
||||
self.command_list_index = None
|
||||
self.context = MpdContext(self, session=session)
|
||||
|
||||
def handle_request(self, request, command_list_index=None):
|
||||
def handle_request(self, request, current_command_list_index=None):
|
||||
"""Dispatch incoming requests to the correct handler."""
|
||||
if self.command_list is not False and request != u'command_list_end':
|
||||
self.command_list.append(request)
|
||||
return None
|
||||
try:
|
||||
(handler, kwargs) = self.find_handler(request)
|
||||
result = handler(self, **kwargs)
|
||||
except MpdAckError as e:
|
||||
if command_list_index is not None:
|
||||
e.index = command_list_index
|
||||
return self.handle_response(e.get_mpd_ack(), add_ok=False)
|
||||
if request in (u'command_list_begin', u'command_list_ok_begin'):
|
||||
return None
|
||||
if command_list_index is not None:
|
||||
return self.handle_response(result, add_ok=False)
|
||||
return self.handle_response(result)
|
||||
self.command_list_index = current_command_list_index
|
||||
response = []
|
||||
filter_chain = [
|
||||
self._catch_mpd_ack_errors_filter,
|
||||
self._authenticate_filter,
|
||||
self._command_list_filter,
|
||||
self._add_ok_filter,
|
||||
self._call_handler_filter,
|
||||
]
|
||||
return self._call_next_filter(request, response, filter_chain)
|
||||
|
||||
def find_handler(self, request):
|
||||
"""Find the correct handler for a request."""
|
||||
def _call_next_filter(self, request, response, filter_chain):
|
||||
if filter_chain:
|
||||
next_filter = filter_chain.pop(0)
|
||||
return next_filter(request, response, filter_chain)
|
||||
else:
|
||||
return response
|
||||
|
||||
|
||||
### Filter: catch MPD ACK errors
|
||||
|
||||
def _catch_mpd_ack_errors_filter(self, request, response, filter_chain):
|
||||
try:
|
||||
return self._call_next_filter(request, response, filter_chain)
|
||||
except exceptions.MpdAckError as mpd_ack_error:
|
||||
if self.command_list_index is not None:
|
||||
mpd_ack_error.index = self.command_list_index
|
||||
return [mpd_ack_error.get_mpd_ack()]
|
||||
|
||||
|
||||
### Filter: authenticate
|
||||
|
||||
def _authenticate_filter(self, request, response, filter_chain):
|
||||
if self.authenticated:
|
||||
return self._call_next_filter(request, response, filter_chain)
|
||||
elif settings.MPD_SERVER_PASSWORD is None:
|
||||
self.authenticated = True
|
||||
return self._call_next_filter(request, response, filter_chain)
|
||||
else:
|
||||
command_name = request.split(' ')[0]
|
||||
command_names_not_requiring_auth = [
|
||||
command.name for command in mpd_commands
|
||||
if not command.auth_required]
|
||||
if command_name in command_names_not_requiring_auth:
|
||||
return self._call_next_filter(request, response, filter_chain)
|
||||
else:
|
||||
raise exceptions.MpdPermissionError(command=command_name)
|
||||
|
||||
|
||||
### Filter: command list
|
||||
|
||||
def _command_list_filter(self, request, response, filter_chain):
|
||||
if self._is_receiving_command_list(request):
|
||||
self.command_list.append(request)
|
||||
return []
|
||||
else:
|
||||
response = self._call_next_filter(request, response, filter_chain)
|
||||
if (self._is_receiving_command_list(request) or
|
||||
self._is_processing_command_list(request)):
|
||||
if response and response[-1] == u'OK':
|
||||
response = response[:-1]
|
||||
return response
|
||||
|
||||
def _is_receiving_command_list(self, request):
|
||||
return (self.command_list is not False
|
||||
and request != u'command_list_end')
|
||||
|
||||
def _is_processing_command_list(self, request):
|
||||
return (self.command_list_index is not None
|
||||
and request != u'command_list_end')
|
||||
|
||||
|
||||
### Filter: add OK
|
||||
|
||||
def _add_ok_filter(self, request, response, filter_chain):
|
||||
response = self._call_next_filter(request, response, filter_chain)
|
||||
if not self._has_error(response):
|
||||
response.append(u'OK')
|
||||
return response
|
||||
|
||||
def _has_error(self, response):
|
||||
return response and response[-1].startswith(u'ACK')
|
||||
|
||||
|
||||
### Filter: call handler
|
||||
|
||||
def _call_handler_filter(self, request, response, filter_chain):
|
||||
try:
|
||||
response = self._format_response(self._call_handler(request))
|
||||
return self._call_next_filter(request, response, filter_chain)
|
||||
except ActorDeadError as e:
|
||||
logger.warning(u'Tried to communicate with dead actor.')
|
||||
raise exceptions.MpdSystemError(e.message)
|
||||
|
||||
def _call_handler(self, request):
|
||||
(handler, kwargs) = self._find_handler(request)
|
||||
return handler(self.context, **kwargs)
|
||||
|
||||
def _find_handler(self, request):
|
||||
for pattern in request_handlers:
|
||||
matches = re.match(pattern, request)
|
||||
if matches is not None:
|
||||
return (request_handlers[pattern], matches.groupdict())
|
||||
command = request.split(' ')[0]
|
||||
if command in mpd_commands:
|
||||
raise MpdArgError(u'incorrect arguments', command=command)
|
||||
raise MpdUnknownCommand(command=command)
|
||||
command_name = request.split(' ')[0]
|
||||
if command_name in [command.name for command in mpd_commands]:
|
||||
raise exceptions.MpdArgError(u'incorrect arguments',
|
||||
command=command_name)
|
||||
raise exceptions.MpdUnknownCommand(command=command_name)
|
||||
|
||||
def handle_response(self, result, add_ok=True):
|
||||
"""Format the response from a request handler."""
|
||||
response = []
|
||||
def _format_response(self, response):
|
||||
formatted_response = []
|
||||
for element in self._listify_result(response):
|
||||
formatted_response.extend(self._format_lines(element))
|
||||
return formatted_response
|
||||
|
||||
def _listify_result(self, result):
|
||||
if result is None:
|
||||
result = []
|
||||
elif isinstance(result, set):
|
||||
result = list(result)
|
||||
elif not isinstance(result, list):
|
||||
result = [result]
|
||||
for line in flatten(result):
|
||||
if isinstance(line, dict):
|
||||
for (key, value) in line.items():
|
||||
response.append(u'%s: %s' % (key, value))
|
||||
elif isinstance(line, tuple):
|
||||
(key, value) = line
|
||||
response.append(u'%s: %s' % (key, value))
|
||||
else:
|
||||
response.append(line)
|
||||
if add_ok and (not response or not response[-1].startswith(u'ACK')):
|
||||
response.append(u'OK')
|
||||
return response
|
||||
return []
|
||||
if isinstance(result, set):
|
||||
return flatten(list(result))
|
||||
if not isinstance(result, list):
|
||||
return [result]
|
||||
return flatten(result)
|
||||
|
||||
def _format_lines(self, line):
|
||||
if isinstance(line, dict):
|
||||
return [u'%s: %s' % (key, value) for (key, value) in line.items()]
|
||||
if isinstance(line, tuple):
|
||||
(key, value) = line
|
||||
return [u'%s: %s' % (key, value)]
|
||||
return [line]
|
||||
|
||||
|
||||
class MpdContext(object):
|
||||
"""
|
||||
This object is passed as the first argument to all MPD command handlers to
|
||||
give the command handlers access to important parts of Mopidy.
|
||||
"""
|
||||
|
||||
#: The current :class:`MpdDispatcher`.
|
||||
dispatcher = None
|
||||
|
||||
#: The current :class:`mopidy.frontends.mpd.session.MpdSession`.
|
||||
session = None
|
||||
|
||||
def __init__(self, dispatcher, session=None):
|
||||
self.dispatcher = dispatcher
|
||||
self.session = session
|
||||
self._backend = None
|
||||
self._mixer = None
|
||||
|
||||
@property
|
||||
def backend(self):
|
||||
"""
|
||||
The backend. An instance of :class:`mopidy.backends.base.Backend`.
|
||||
"""
|
||||
if self._backend is not None:
|
||||
return self._backend
|
||||
backend_refs = ActorRegistry.get_by_class(Backend)
|
||||
assert len(backend_refs) == 1, 'Expected exactly one running backend.'
|
||||
self._backend = backend_refs[0].proxy()
|
||||
return self._backend
|
||||
|
||||
@property
|
||||
def mixer(self):
|
||||
"""
|
||||
The mixer. An instance of :class:`mopidy.mixers.base.BaseMixer`.
|
||||
"""
|
||||
if self._mixer is not None:
|
||||
return self._mixer
|
||||
mixer_refs = ActorRegistry.get_by_class(BaseMixer)
|
||||
assert len(mixer_refs) == 1, 'Expected exactly one running mixer.'
|
||||
self._mixer = mixer_refs[0].proxy()
|
||||
return self._mixer
|
||||
|
||||
@ -16,10 +16,11 @@ class MpdAckError(MopidyException):
|
||||
ACK_ERROR_PLAYER_SYNC = 55
|
||||
ACK_ERROR_EXIST = 56
|
||||
|
||||
def __init__(self, message=u'', error_code=0, index=0, command=u''):
|
||||
super(MpdAckError, self).__init__(message, error_code, index, command)
|
||||
error_code = 0
|
||||
|
||||
def __init__(self, message=u'', index=0, command=u''):
|
||||
super(MpdAckError, self).__init__(message, index, command)
|
||||
self.message = message
|
||||
self.error_code = error_code
|
||||
self.index = index
|
||||
self.command = command
|
||||
|
||||
@ -30,31 +31,38 @@ class MpdAckError(MopidyException):
|
||||
ACK [%(error_code)i@%(index)i] {%(command)s} description
|
||||
"""
|
||||
return u'ACK [%i@%i] {%s} %s' % (
|
||||
self.error_code, self.index, self.command, self.message)
|
||||
self.__class__.error_code, self.index, self.command, self.message)
|
||||
|
||||
class MpdArgError(MpdAckError):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(MpdArgError, self).__init__(*args, **kwargs)
|
||||
self.error_code = MpdAckError.ACK_ERROR_ARG
|
||||
error_code = MpdAckError.ACK_ERROR_ARG
|
||||
|
||||
class MpdPasswordError(MpdAckError):
|
||||
error_code = MpdAckError.ACK_ERROR_PASSWORD
|
||||
|
||||
class MpdPermissionError(MpdAckError):
|
||||
error_code = MpdAckError.ACK_ERROR_PERMISSION
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(MpdPasswordError, self).__init__(*args, **kwargs)
|
||||
self.error_code = MpdAckError.ACK_ERROR_PASSWORD
|
||||
super(MpdPermissionError, self).__init__(*args, **kwargs)
|
||||
self.message = u'you don\'t have permission for "%s"' % self.command
|
||||
|
||||
class MpdUnknownCommand(MpdAckError):
|
||||
error_code = MpdAckError.ACK_ERROR_UNKNOWN
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(MpdUnknownCommand, self).__init__(*args, **kwargs)
|
||||
self.message = u'unknown command "%s"' % self.command
|
||||
self.command = u''
|
||||
self.error_code = MpdAckError.ACK_ERROR_UNKNOWN
|
||||
|
||||
class MpdNoExistError(MpdAckError):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(MpdNoExistError, self).__init__(*args, **kwargs)
|
||||
self.error_code = MpdAckError.ACK_ERROR_NO_EXIST
|
||||
error_code = MpdAckError.ACK_ERROR_NO_EXIST
|
||||
|
||||
class MpdSystemError(MpdAckError):
|
||||
error_code = MpdAckError.ACK_ERROR_SYSTEM
|
||||
|
||||
class MpdNotImplemented(MpdAckError):
|
||||
error_code = 0
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(MpdNotImplemented, self).__init__(*args, **kwargs)
|
||||
self.message = u'Not implemented'
|
||||
|
||||
@ -10,6 +10,7 @@ implement our own MPD server which is compatible with the numerous existing
|
||||
`MPD clients <http://mpd.wikia.com/wiki/Clients>`_.
|
||||
"""
|
||||
|
||||
from collections import namedtuple
|
||||
import re
|
||||
|
||||
#: The MPD protocol uses UTF-8 for encoding all data.
|
||||
@ -21,12 +22,16 @@ LINE_TERMINATOR = u'\n'
|
||||
#: The MPD protocol version is 0.16.0.
|
||||
VERSION = u'0.16.0'
|
||||
|
||||
MpdCommand = namedtuple('MpdCommand', ['name', 'auth_required'])
|
||||
|
||||
#: List of all available commands, represented as :class:`MpdCommand` objects.
|
||||
mpd_commands = set()
|
||||
|
||||
request_handlers = {}
|
||||
|
||||
def handle_pattern(pattern):
|
||||
def handle_request(pattern, auth_required=True):
|
||||
"""
|
||||
Decorator for connecting command handlers to command patterns.
|
||||
Decorator for connecting command handlers to command requests.
|
||||
|
||||
If you use named groups in the pattern, the decorated method will get the
|
||||
groups as keyword arguments. If the group is optional, remember to give the
|
||||
@ -35,7 +40,7 @@ def handle_pattern(pattern):
|
||||
For example, if the command is ``do that thing`` the ``what`` argument will
|
||||
be ``this thing``::
|
||||
|
||||
@handle_pattern('^do (?P<what>.+)$')
|
||||
@handle_request('^do (?P<what>.+)$')
|
||||
def do(what):
|
||||
...
|
||||
|
||||
@ -45,7 +50,8 @@ def handle_pattern(pattern):
|
||||
def decorator(func):
|
||||
match = re.search('([a-z_]+)', pattern)
|
||||
if match is not None:
|
||||
mpd_commands.add(match.group())
|
||||
mpd_commands.add(
|
||||
MpdCommand(name=match.group(), auth_required=auth_required))
|
||||
if pattern in request_handlers:
|
||||
raise ValueError(u'Tried to redefine handler for %s with %s' % (
|
||||
pattern, func))
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
from mopidy.frontends.mpd.protocol import handle_pattern
|
||||
from mopidy.frontends.mpd.protocol import handle_request
|
||||
from mopidy.frontends.mpd.exceptions import MpdNotImplemented
|
||||
|
||||
@handle_pattern(r'^disableoutput "(?P<outputid>\d+)"$')
|
||||
def disableoutput(frontend, outputid):
|
||||
@handle_request(r'^disableoutput "(?P<outputid>\d+)"$')
|
||||
def disableoutput(context, outputid):
|
||||
"""
|
||||
*musicpd.org, audio output section:*
|
||||
|
||||
@ -12,8 +12,8 @@ def disableoutput(frontend, outputid):
|
||||
"""
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
@handle_pattern(r'^enableoutput "(?P<outputid>\d+)"$')
|
||||
def enableoutput(frontend, outputid):
|
||||
@handle_request(r'^enableoutput "(?P<outputid>\d+)"$')
|
||||
def enableoutput(context, outputid):
|
||||
"""
|
||||
*musicpd.org, audio output section:*
|
||||
|
||||
@ -23,8 +23,8 @@ def enableoutput(frontend, outputid):
|
||||
"""
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
@handle_pattern(r'^outputs$')
|
||||
def outputs(frontend):
|
||||
@handle_request(r'^outputs$')
|
||||
def outputs(context):
|
||||
"""
|
||||
*musicpd.org, audio output section:*
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
from mopidy.frontends.mpd.protocol import handle_pattern
|
||||
from mopidy.frontends.mpd.protocol import handle_request
|
||||
from mopidy.frontends.mpd.exceptions import MpdUnknownCommand
|
||||
|
||||
@handle_pattern(r'^command_list_begin$')
|
||||
def command_list_begin(frontend):
|
||||
@handle_request(r'^command_list_begin$')
|
||||
def command_list_begin(context):
|
||||
"""
|
||||
*musicpd.org, command list section:*
|
||||
|
||||
@ -18,31 +18,33 @@ def command_list_begin(frontend):
|
||||
returned. If ``command_list_ok_begin`` is used, ``list_OK`` is
|
||||
returned for each successful command executed in the command list.
|
||||
"""
|
||||
frontend.command_list = []
|
||||
frontend.command_list_ok = False
|
||||
context.dispatcher.command_list = []
|
||||
context.dispatcher.command_list_ok = False
|
||||
|
||||
@handle_pattern(r'^command_list_end$')
|
||||
def command_list_end(frontend):
|
||||
@handle_request(r'^command_list_end$')
|
||||
def command_list_end(context):
|
||||
"""See :meth:`command_list_begin()`."""
|
||||
if frontend.command_list is False:
|
||||
if context.dispatcher.command_list is False:
|
||||
# Test for False exactly, and not e.g. empty list
|
||||
raise MpdUnknownCommand(command='command_list_end')
|
||||
(command_list, frontend.command_list) = (frontend.command_list, False)
|
||||
(command_list_ok, frontend.command_list_ok) = (
|
||||
frontend.command_list_ok, False)
|
||||
result = []
|
||||
for i, command in enumerate(command_list):
|
||||
response = frontend.handle_request(command, command_list_index=i)
|
||||
if response is not None:
|
||||
result.append(response)
|
||||
if response and response[-1].startswith(u'ACK'):
|
||||
return result
|
||||
(command_list, context.dispatcher.command_list) = (
|
||||
context.dispatcher.command_list, False)
|
||||
(command_list_ok, context.dispatcher.command_list_ok) = (
|
||||
context.dispatcher.command_list_ok, False)
|
||||
command_list_response = []
|
||||
for index, command in enumerate(command_list):
|
||||
response = context.dispatcher.handle_request(
|
||||
command, current_command_list_index=index)
|
||||
command_list_response.extend(response)
|
||||
if (command_list_response and
|
||||
command_list_response[-1].startswith(u'ACK')):
|
||||
return command_list_response
|
||||
if command_list_ok:
|
||||
response.append(u'list_OK')
|
||||
return result
|
||||
command_list_response.append(u'list_OK')
|
||||
return command_list_response
|
||||
|
||||
@handle_pattern(r'^command_list_ok_begin$')
|
||||
def command_list_ok_begin(frontend):
|
||||
@handle_request(r'^command_list_ok_begin$')
|
||||
def command_list_ok_begin(context):
|
||||
"""See :meth:`command_list_begin()`."""
|
||||
frontend.command_list = []
|
||||
frontend.command_list_ok = True
|
||||
context.dispatcher.command_list = []
|
||||
context.dispatcher.command_list_ok = True
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
from mopidy import settings
|
||||
from mopidy.frontends.mpd.protocol import handle_pattern
|
||||
from mopidy.frontends.mpd.exceptions import MpdPasswordError
|
||||
from mopidy.frontends.mpd.protocol import handle_request
|
||||
from mopidy.frontends.mpd.exceptions import (MpdPasswordError,
|
||||
MpdPermissionError)
|
||||
|
||||
@handle_pattern(r'^close$')
|
||||
def close(frontend):
|
||||
@handle_request(r'^close$', auth_required=False)
|
||||
def close(context):
|
||||
"""
|
||||
*musicpd.org, connection section:*
|
||||
|
||||
@ -11,10 +12,10 @@ def close(frontend):
|
||||
|
||||
Closes the connection to MPD.
|
||||
"""
|
||||
pass # TODO
|
||||
context.session.close()
|
||||
|
||||
@handle_pattern(r'^kill$')
|
||||
def kill(frontend):
|
||||
@handle_request(r'^kill$')
|
||||
def kill(context):
|
||||
"""
|
||||
*musicpd.org, connection section:*
|
||||
|
||||
@ -22,10 +23,10 @@ def kill(frontend):
|
||||
|
||||
Kills MPD.
|
||||
"""
|
||||
pass # TODO
|
||||
raise MpdPermissionError(command=u'kill')
|
||||
|
||||
@handle_pattern(r'^password "(?P<password>[^"]+)"$')
|
||||
def password_(frontend, password):
|
||||
@handle_request(r'^password "(?P<password>[^"]+)"$', auth_required=False)
|
||||
def password_(context, password):
|
||||
"""
|
||||
*musicpd.org, connection section:*
|
||||
|
||||
@ -34,14 +35,13 @@ def password_(frontend, password):
|
||||
This is used for authentication with the server. ``PASSWORD`` is
|
||||
simply the plaintext password.
|
||||
"""
|
||||
# You will not get to this code without being authenticated. This is for
|
||||
# when you are already authenticated, and are sending additional 'password'
|
||||
# requests.
|
||||
if settings.MPD_SERVER_PASSWORD != password:
|
||||
if password == settings.MPD_SERVER_PASSWORD:
|
||||
context.dispatcher.authenticated = True
|
||||
else:
|
||||
raise MpdPasswordError(u'incorrect password', command=u'password')
|
||||
|
||||
@handle_pattern(r'^ping$')
|
||||
def ping(frontend):
|
||||
@handle_request(r'^ping$', auth_required=False)
|
||||
def ping(context):
|
||||
"""
|
||||
*musicpd.org, connection section:*
|
||||
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
from mopidy.frontends.mpd.exceptions import (MpdArgError, MpdNoExistError,
|
||||
MpdNotImplemented)
|
||||
from mopidy.frontends.mpd.protocol import handle_pattern
|
||||
from mopidy.frontends.mpd.protocol import handle_request
|
||||
from mopidy.frontends.mpd.translator import tracks_to_mpd_format
|
||||
|
||||
@handle_pattern(r'^add "(?P<uri>[^"]*)"$')
|
||||
def add(frontend, uri):
|
||||
@handle_request(r'^add "(?P<uri>[^"]*)"$')
|
||||
def add(context, uri):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
|
||||
@ -19,17 +19,17 @@ def add(frontend, uri):
|
||||
"""
|
||||
if not uri:
|
||||
return
|
||||
for handler_prefix in frontend.backend.uri_handlers.get():
|
||||
for handler_prefix in context.backend.uri_handlers.get():
|
||||
if uri.startswith(handler_prefix):
|
||||
track = frontend.backend.library.lookup(uri).get()
|
||||
track = context.backend.library.lookup(uri).get()
|
||||
if track is not None:
|
||||
frontend.backend.current_playlist.add(track)
|
||||
context.backend.current_playlist.add(track)
|
||||
return
|
||||
raise MpdNoExistError(
|
||||
u'directory or file not found', command=u'add')
|
||||
|
||||
@handle_pattern(r'^addid "(?P<uri>[^"]*)"( "(?P<songpos>\d+)")*$')
|
||||
def addid(frontend, uri, songpos=None):
|
||||
@handle_request(r'^addid "(?P<uri>[^"]*)"( "(?P<songpos>\d+)")*$')
|
||||
def addid(context, uri, songpos=None):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
|
||||
@ -51,18 +51,18 @@ def addid(frontend, uri, songpos=None):
|
||||
raise MpdNoExistError(u'No such song', command=u'addid')
|
||||
if songpos is not None:
|
||||
songpos = int(songpos)
|
||||
track = frontend.backend.library.lookup(uri).get()
|
||||
track = context.backend.library.lookup(uri).get()
|
||||
if track is None:
|
||||
raise MpdNoExistError(u'No such song', command=u'addid')
|
||||
if songpos and songpos > len(
|
||||
frontend.backend.current_playlist.tracks.get()):
|
||||
context.backend.current_playlist.tracks.get()):
|
||||
raise MpdArgError(u'Bad song index', command=u'addid')
|
||||
cp_track = frontend.backend.current_playlist.add(track,
|
||||
cp_track = context.backend.current_playlist.add(track,
|
||||
at_position=songpos).get()
|
||||
return ('Id', cp_track[0])
|
||||
return ('Id', cp_track.cpid)
|
||||
|
||||
@handle_pattern(r'^delete "(?P<start>\d+):(?P<end>\d+)*"$')
|
||||
def delete_range(frontend, start, end=None):
|
||||
@handle_request(r'^delete "(?P<start>\d+):(?P<end>\d+)*"$')
|
||||
def delete_range(context, start, end=None):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
|
||||
@ -74,25 +74,25 @@ def delete_range(frontend, start, end=None):
|
||||
if end is not None:
|
||||
end = int(end)
|
||||
else:
|
||||
end = len(frontend.backend.current_playlist.tracks.get())
|
||||
cp_tracks = frontend.backend.current_playlist.cp_tracks.get()[start:end]
|
||||
end = len(context.backend.current_playlist.tracks.get())
|
||||
cp_tracks = context.backend.current_playlist.cp_tracks.get()[start:end]
|
||||
if not cp_tracks:
|
||||
raise MpdArgError(u'Bad song index', command=u'delete')
|
||||
for (cpid, _) in cp_tracks:
|
||||
frontend.backend.current_playlist.remove(cpid=cpid)
|
||||
context.backend.current_playlist.remove(cpid=cpid)
|
||||
|
||||
@handle_pattern(r'^delete "(?P<songpos>\d+)"$')
|
||||
def delete_songpos(frontend, songpos):
|
||||
@handle_request(r'^delete "(?P<songpos>\d+)"$')
|
||||
def delete_songpos(context, songpos):
|
||||
"""See :meth:`delete_range`"""
|
||||
try:
|
||||
songpos = int(songpos)
|
||||
(cpid, _) = frontend.backend.current_playlist.cp_tracks.get()[songpos]
|
||||
frontend.backend.current_playlist.remove(cpid=cpid)
|
||||
(cpid, _) = context.backend.current_playlist.cp_tracks.get()[songpos]
|
||||
context.backend.current_playlist.remove(cpid=cpid)
|
||||
except IndexError:
|
||||
raise MpdArgError(u'Bad song index', command=u'delete')
|
||||
|
||||
@handle_pattern(r'^deleteid "(?P<cpid>\d+)"$')
|
||||
def deleteid(frontend, cpid):
|
||||
@handle_request(r'^deleteid "(?P<cpid>\d+)"$')
|
||||
def deleteid(context, cpid):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
|
||||
@ -102,14 +102,14 @@ def deleteid(frontend, cpid):
|
||||
"""
|
||||
try:
|
||||
cpid = int(cpid)
|
||||
if frontend.backend.playback.current_cpid.get() == cpid:
|
||||
frontend.backend.playback.next()
|
||||
return frontend.backend.current_playlist.remove(cpid=cpid).get()
|
||||
if context.backend.playback.current_cpid.get() == cpid:
|
||||
context.backend.playback.next()
|
||||
return context.backend.current_playlist.remove(cpid=cpid).get()
|
||||
except LookupError:
|
||||
raise MpdNoExistError(u'No such song', command=u'deleteid')
|
||||
|
||||
@handle_pattern(r'^clear$')
|
||||
def clear(frontend):
|
||||
@handle_request(r'^clear$')
|
||||
def clear(context):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
|
||||
@ -117,10 +117,10 @@ def clear(frontend):
|
||||
|
||||
Clears the current playlist.
|
||||
"""
|
||||
frontend.backend.current_playlist.clear()
|
||||
context.backend.current_playlist.clear()
|
||||
|
||||
@handle_pattern(r'^move "(?P<start>\d+):(?P<end>\d+)*" "(?P<to>\d+)"$')
|
||||
def move_range(frontend, start, to, end=None):
|
||||
@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:*
|
||||
|
||||
@ -130,21 +130,21 @@ def move_range(frontend, start, to, end=None):
|
||||
``TO`` in the playlist.
|
||||
"""
|
||||
if end is None:
|
||||
end = len(frontend.backend.current_playlist.tracks.get())
|
||||
end = len(context.backend.current_playlist.tracks.get())
|
||||
start = int(start)
|
||||
end = int(end)
|
||||
to = int(to)
|
||||
frontend.backend.current_playlist.move(start, end, to)
|
||||
context.backend.current_playlist.move(start, end, to)
|
||||
|
||||
@handle_pattern(r'^move "(?P<songpos>\d+)" "(?P<to>\d+)"$')
|
||||
def move_songpos(frontend, songpos, to):
|
||||
@handle_request(r'^move "(?P<songpos>\d+)" "(?P<to>\d+)"$')
|
||||
def move_songpos(context, songpos, to):
|
||||
"""See :meth:`move_range`."""
|
||||
songpos = int(songpos)
|
||||
to = int(to)
|
||||
frontend.backend.current_playlist.move(songpos, songpos + 1, to)
|
||||
context.backend.current_playlist.move(songpos, songpos + 1, to)
|
||||
|
||||
@handle_pattern(r'^moveid "(?P<cpid>\d+)" "(?P<to>\d+)"$')
|
||||
def moveid(frontend, cpid, to):
|
||||
@handle_request(r'^moveid "(?P<cpid>\d+)" "(?P<to>\d+)"$')
|
||||
def moveid(context, cpid, to):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
|
||||
@ -156,13 +156,13 @@ def moveid(frontend, cpid, to):
|
||||
"""
|
||||
cpid = int(cpid)
|
||||
to = int(to)
|
||||
cp_track = frontend.backend.current_playlist.get(cpid=cpid).get()
|
||||
position = frontend.backend.current_playlist.cp_tracks.get().index(
|
||||
cp_track = context.backend.current_playlist.get(cpid=cpid).get()
|
||||
position = context.backend.current_playlist.cp_tracks.get().index(
|
||||
cp_track)
|
||||
frontend.backend.current_playlist.move(position, position + 1, to)
|
||||
context.backend.current_playlist.move(position, position + 1, to)
|
||||
|
||||
@handle_pattern(r'^playlist$')
|
||||
def playlist(frontend):
|
||||
@handle_request(r'^playlist$')
|
||||
def playlist(context):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
|
||||
@ -174,11 +174,11 @@ def playlist(frontend):
|
||||
|
||||
Do not use this, instead use ``playlistinfo``.
|
||||
"""
|
||||
return playlistinfo(frontend)
|
||||
return playlistinfo(context)
|
||||
|
||||
@handle_pattern(r'^playlistfind (?P<tag>[^"]+) "(?P<needle>[^"]+)"$')
|
||||
@handle_pattern(r'^playlistfind "(?P<tag>[^"]+)" "(?P<needle>[^"]+)"$')
|
||||
def playlistfind(frontend, tag, needle):
|
||||
@handle_request(r'^playlistfind (?P<tag>[^"]+) "(?P<needle>[^"]+)"$')
|
||||
@handle_request(r'^playlistfind "(?P<tag>[^"]+)" "(?P<needle>[^"]+)"$')
|
||||
def playlistfind(context, tag, needle):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
|
||||
@ -192,17 +192,17 @@ def playlistfind(frontend, tag, needle):
|
||||
"""
|
||||
if tag == 'filename':
|
||||
try:
|
||||
cp_track = frontend.backend.current_playlist.get(uri=needle).get()
|
||||
cp_track = context.backend.current_playlist.get(uri=needle).get()
|
||||
(cpid, track) = cp_track
|
||||
position = frontend.backend.current_playlist.cp_tracks.get().index(
|
||||
position = context.backend.current_playlist.cp_tracks.get().index(
|
||||
cp_track)
|
||||
return track.mpd_format(cpid=cpid, position=position)
|
||||
except LookupError:
|
||||
return None
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
@handle_pattern(r'^playlistid( "(?P<cpid>\d+)")*$')
|
||||
def playlistid(frontend, cpid=None):
|
||||
@handle_request(r'^playlistid( "(?P<cpid>\d+)")*$')
|
||||
def playlistid(context, cpid=None):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
|
||||
@ -214,22 +214,22 @@ def playlistid(frontend, cpid=None):
|
||||
if cpid is not None:
|
||||
try:
|
||||
cpid = int(cpid)
|
||||
cp_track = frontend.backend.current_playlist.get(cpid=cpid).get()
|
||||
position = frontend.backend.current_playlist.cp_tracks.get().index(
|
||||
cp_track = context.backend.current_playlist.get(cpid=cpid).get()
|
||||
position = context.backend.current_playlist.cp_tracks.get().index(
|
||||
cp_track)
|
||||
return cp_track[1].mpd_format(position=position, cpid=cpid)
|
||||
return cp_track.track.mpd_format(position=position, cpid=cpid)
|
||||
except LookupError:
|
||||
raise MpdNoExistError(u'No such song', command=u'playlistid')
|
||||
else:
|
||||
cpids = [ct[0] for ct in
|
||||
frontend.backend.current_playlist.cp_tracks.get()]
|
||||
context.backend.current_playlist.cp_tracks.get()]
|
||||
return tracks_to_mpd_format(
|
||||
frontend.backend.current_playlist.tracks.get(), cpids=cpids)
|
||||
context.backend.current_playlist.tracks.get(), cpids=cpids)
|
||||
|
||||
@handle_pattern(r'^playlistinfo$')
|
||||
@handle_pattern(r'^playlistinfo "(?P<songpos>-?\d+)"$')
|
||||
@handle_pattern(r'^playlistinfo "(?P<start>\d+):(?P<end>\d+)*"$')
|
||||
def playlistinfo(frontend, songpos=None,
|
||||
@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:*
|
||||
@ -255,30 +255,30 @@ def playlistinfo(frontend, songpos=None,
|
||||
if start == -1:
|
||||
end = None
|
||||
cpids = [ct[0] for ct in
|
||||
frontend.backend.current_playlist.cp_tracks.get()]
|
||||
context.backend.current_playlist.cp_tracks.get()]
|
||||
return tracks_to_mpd_format(
|
||||
frontend.backend.current_playlist.tracks.get(),
|
||||
context.backend.current_playlist.tracks.get(),
|
||||
start, end, cpids=cpids)
|
||||
else:
|
||||
if start is None:
|
||||
start = 0
|
||||
start = int(start)
|
||||
if not (0 <= start <= len(
|
||||
frontend.backend.current_playlist.tracks.get())):
|
||||
context.backend.current_playlist.tracks.get())):
|
||||
raise MpdArgError(u'Bad song index', command=u'playlistinfo')
|
||||
if end is not None:
|
||||
end = int(end)
|
||||
if end > len(frontend.backend.current_playlist.tracks.get()):
|
||||
if end > len(context.backend.current_playlist.tracks.get()):
|
||||
end = None
|
||||
cpids = [ct[0] for ct in
|
||||
frontend.backend.current_playlist.cp_tracks.get()]
|
||||
context.backend.current_playlist.cp_tracks.get()]
|
||||
return tracks_to_mpd_format(
|
||||
frontend.backend.current_playlist.tracks.get(),
|
||||
context.backend.current_playlist.tracks.get(),
|
||||
start, end, cpids=cpids)
|
||||
|
||||
@handle_pattern(r'^playlistsearch "(?P<tag>[^"]+)" "(?P<needle>[^"]+)"$')
|
||||
@handle_pattern(r'^playlistsearch (?P<tag>\S+) "(?P<needle>[^"]+)"$')
|
||||
def playlistsearch(frontend, tag, needle):
|
||||
@handle_request(r'^playlistsearch "(?P<tag>[^"]+)" "(?P<needle>[^"]+)"$')
|
||||
@handle_request(r'^playlistsearch (?P<tag>\S+) "(?P<needle>[^"]+)"$')
|
||||
def playlistsearch(context, tag, needle):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
|
||||
@ -294,9 +294,9 @@ def playlistsearch(frontend, tag, needle):
|
||||
"""
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
@handle_pattern(r'^plchanges (?P<version>-?\d+)$')
|
||||
@handle_pattern(r'^plchanges "(?P<version>-?\d+)"$')
|
||||
def plchanges(frontend, version):
|
||||
@handle_request(r'^plchanges (?P<version>-?\d+)$')
|
||||
@handle_request(r'^plchanges "(?P<version>-?\d+)"$')
|
||||
def plchanges(context, version):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
|
||||
@ -312,14 +312,14 @@ def plchanges(frontend, version):
|
||||
- Calls ``plchanges "-1"`` two times per second to get the entire playlist.
|
||||
"""
|
||||
# XXX Naive implementation that returns all tracks as changed
|
||||
if int(version) < frontend.backend.current_playlist.version:
|
||||
if int(version) < context.backend.current_playlist.version:
|
||||
cpids = [ct[0] for ct in
|
||||
frontend.backend.current_playlist.cp_tracks.get()]
|
||||
context.backend.current_playlist.cp_tracks.get()]
|
||||
return tracks_to_mpd_format(
|
||||
frontend.backend.current_playlist.tracks.get(), cpids=cpids)
|
||||
context.backend.current_playlist.tracks.get(), cpids=cpids)
|
||||
|
||||
@handle_pattern(r'^plchangesposid "(?P<version>\d+)"$')
|
||||
def plchangesposid(frontend, version):
|
||||
@handle_request(r'^plchangesposid "(?P<version>\d+)"$')
|
||||
def plchangesposid(context, version):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
|
||||
@ -333,17 +333,17 @@ def plchangesposid(frontend, version):
|
||||
``playlistlength`` returned by status command.
|
||||
"""
|
||||
# XXX Naive implementation that returns all tracks as changed
|
||||
if int(version) != frontend.backend.current_playlist.version.get():
|
||||
if int(version) != context.backend.current_playlist.version.get():
|
||||
result = []
|
||||
for (position, (cpid, _)) in enumerate(
|
||||
frontend.backend.current_playlist.cp_tracks.get()):
|
||||
context.backend.current_playlist.cp_tracks.get()):
|
||||
result.append((u'cpos', position))
|
||||
result.append((u'Id', cpid))
|
||||
return result
|
||||
|
||||
@handle_pattern(r'^shuffle$')
|
||||
@handle_pattern(r'^shuffle "(?P<start>\d+):(?P<end>\d+)*"$')
|
||||
def shuffle(frontend, start=None, end=None):
|
||||
@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:*
|
||||
|
||||
@ -356,10 +356,10 @@ def shuffle(frontend, start=None, end=None):
|
||||
start = int(start)
|
||||
if end is not None:
|
||||
end = int(end)
|
||||
frontend.backend.current_playlist.shuffle(start, end)
|
||||
context.backend.current_playlist.shuffle(start, end)
|
||||
|
||||
@handle_pattern(r'^swap "(?P<songpos1>\d+)" "(?P<songpos2>\d+)"$')
|
||||
def swap(frontend, songpos1, songpos2):
|
||||
@handle_request(r'^swap "(?P<songpos1>\d+)" "(?P<songpos2>\d+)"$')
|
||||
def swap(context, songpos1, songpos2):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
|
||||
@ -369,18 +369,18 @@ def swap(frontend, songpos1, songpos2):
|
||||
"""
|
||||
songpos1 = int(songpos1)
|
||||
songpos2 = int(songpos2)
|
||||
tracks = frontend.backend.current_playlist.tracks.get()
|
||||
tracks = context.backend.current_playlist.tracks.get()
|
||||
song1 = tracks[songpos1]
|
||||
song2 = tracks[songpos2]
|
||||
del tracks[songpos1]
|
||||
tracks.insert(songpos1, song2)
|
||||
del tracks[songpos2]
|
||||
tracks.insert(songpos2, song1)
|
||||
frontend.backend.current_playlist.clear()
|
||||
frontend.backend.current_playlist.append(tracks)
|
||||
context.backend.current_playlist.clear()
|
||||
context.backend.current_playlist.append(tracks)
|
||||
|
||||
@handle_pattern(r'^swapid "(?P<cpid1>\d+)" "(?P<cpid2>\d+)"$')
|
||||
def swapid(frontend, cpid1, cpid2):
|
||||
@handle_request(r'^swapid "(?P<cpid1>\d+)" "(?P<cpid2>\d+)"$')
|
||||
def swapid(context, cpid1, cpid2):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
|
||||
@ -390,9 +390,9 @@ def swapid(frontend, cpid1, cpid2):
|
||||
"""
|
||||
cpid1 = int(cpid1)
|
||||
cpid2 = int(cpid2)
|
||||
cp_track1 = frontend.backend.current_playlist.get(cpid=cpid1).get()
|
||||
cp_track2 = frontend.backend.current_playlist.get(cpid=cpid2).get()
|
||||
cp_tracks = frontend.backend.current_playlist.cp_tracks.get()
|
||||
cp_track1 = context.backend.current_playlist.get(cpid=cpid1).get()
|
||||
cp_track2 = context.backend.current_playlist.get(cpid=cpid2).get()
|
||||
cp_tracks = context.backend.current_playlist.cp_tracks.get()
|
||||
position1 = cp_tracks.index(cp_track1)
|
||||
position2 = cp_tracks.index(cp_track2)
|
||||
swap(frontend, position1, position2)
|
||||
swap(context, position1, position2)
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
from mopidy.frontends.mpd.protocol import handle_pattern
|
||||
from mopidy.frontends.mpd.protocol import handle_request
|
||||
|
||||
@handle_pattern(r'^$')
|
||||
def empty(frontend):
|
||||
@handle_request(r'^$')
|
||||
def empty(context):
|
||||
"""The original MPD server returns ``OK`` on an empty request."""
|
||||
pass
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import re
|
||||
import shlex
|
||||
|
||||
from mopidy.frontends.mpd.protocol import handle_pattern, stored_playlists
|
||||
from mopidy.frontends.mpd.protocol import handle_request, stored_playlists
|
||||
from mopidy.frontends.mpd.exceptions import MpdArgError, MpdNotImplemented
|
||||
|
||||
def _build_query(mpd_query):
|
||||
@ -28,8 +28,8 @@ def _build_query(mpd_query):
|
||||
query[field] = [what]
|
||||
return query
|
||||
|
||||
@handle_pattern(r'^count "(?P<tag>[^"]+)" "(?P<needle>[^"]*)"$')
|
||||
def count(frontend, tag, needle):
|
||||
@handle_request(r'^count "(?P<tag>[^"]+)" "(?P<needle>[^"]*)"$')
|
||||
def count(context, tag, needle):
|
||||
"""
|
||||
*musicpd.org, music database section:*
|
||||
|
||||
@ -40,10 +40,10 @@ def count(frontend, tag, needle):
|
||||
"""
|
||||
return [('songs', 0), ('playtime', 0)] # TODO
|
||||
|
||||
@handle_pattern(r'^find '
|
||||
@handle_request(r'^find '
|
||||
r'(?P<mpd_query>("?([Aa]lbum|[Aa]rtist|[Dd]ate|[Ff]ilename|'
|
||||
r'[Tt]itle|[Aa]ny)"? "[^"]+"\s?)+)$')
|
||||
def find(frontend, mpd_query):
|
||||
def find(context, mpd_query):
|
||||
"""
|
||||
*musicpd.org, music database section:*
|
||||
|
||||
@ -68,12 +68,12 @@ def find(frontend, mpd_query):
|
||||
- also uses the search type "date".
|
||||
"""
|
||||
query = _build_query(mpd_query)
|
||||
return frontend.backend.library.find_exact(**query).get().mpd_format()
|
||||
return context.backend.library.find_exact(**query).get().mpd_format()
|
||||
|
||||
@handle_pattern(r'^findadd '
|
||||
@handle_request(r'^findadd '
|
||||
r'(?P<query>("?([Aa]lbum|[Aa]rtist|[Ff]ilename|[Tt]itle|[Aa]ny)"? '
|
||||
'"[^"]+"\s?)+)$')
|
||||
def findadd(frontend, query):
|
||||
def findadd(context, query):
|
||||
"""
|
||||
*musicpd.org, music database section:*
|
||||
|
||||
@ -84,11 +84,11 @@ def findadd(frontend, query):
|
||||
``WHAT`` is what to find.
|
||||
"""
|
||||
# TODO Add result to current playlist
|
||||
#result = frontend.find(query)
|
||||
#result = context.find(query)
|
||||
|
||||
@handle_pattern(r'^list "?(?P<field>([Aa]rtist|[Aa]lbum|[Dd]ate|[Gg]enre))"?'
|
||||
@handle_request(r'^list "?(?P<field>([Aa]rtist|[Aa]lbum|[Dd]ate|[Gg]enre))"?'
|
||||
'( (?P<mpd_query>.*))?$')
|
||||
def list_(frontend, field, mpd_query=None):
|
||||
def list_(context, field, mpd_query=None):
|
||||
"""
|
||||
*musicpd.org, music database section:*
|
||||
|
||||
@ -175,11 +175,11 @@ def list_(frontend, field, mpd_query=None):
|
||||
field = field.lower()
|
||||
query = _list_build_query(field, mpd_query)
|
||||
if field == u'artist':
|
||||
return _list_artist(frontend, query)
|
||||
return _list_artist(context, query)
|
||||
elif field == u'album':
|
||||
return _list_album(frontend, query)
|
||||
return _list_album(context, query)
|
||||
elif field == u'date':
|
||||
return _list_date(frontend, query)
|
||||
return _list_date(context, query)
|
||||
elif field == u'genre':
|
||||
pass # TODO We don't have genre in our internal data structures yet
|
||||
|
||||
@ -213,32 +213,32 @@ def _list_build_query(field, mpd_query):
|
||||
else:
|
||||
raise MpdArgError(u'not able to parse args', command=u'list')
|
||||
|
||||
def _list_artist(frontend, query):
|
||||
def _list_artist(context, query):
|
||||
artists = set()
|
||||
playlist = frontend.backend.library.find_exact(**query).get()
|
||||
playlist = context.backend.library.find_exact(**query).get()
|
||||
for track in playlist.tracks:
|
||||
for artist in track.artists:
|
||||
artists.add((u'Artist', artist.name))
|
||||
return artists
|
||||
|
||||
def _list_album(frontend, query):
|
||||
def _list_album(context, query):
|
||||
albums = set()
|
||||
playlist = frontend.backend.library.find_exact(**query).get()
|
||||
playlist = context.backend.library.find_exact(**query).get()
|
||||
for track in playlist.tracks:
|
||||
if track.album is not None:
|
||||
albums.add((u'Album', track.album.name))
|
||||
return albums
|
||||
|
||||
def _list_date(frontend, query):
|
||||
def _list_date(context, query):
|
||||
dates = set()
|
||||
playlist = frontend.backend.library.find_exact(**query).get()
|
||||
playlist = context.backend.library.find_exact(**query).get()
|
||||
for track in playlist.tracks:
|
||||
if track.date is not None:
|
||||
dates.add((u'Date', track.date.strftime('%Y-%m-%d')))
|
||||
return dates
|
||||
|
||||
@handle_pattern(r'^listall "(?P<uri>[^"]+)"')
|
||||
def listall(frontend, uri):
|
||||
@handle_request(r'^listall "(?P<uri>[^"]+)"')
|
||||
def listall(context, uri):
|
||||
"""
|
||||
*musicpd.org, music database section:*
|
||||
|
||||
@ -248,8 +248,8 @@ def listall(frontend, uri):
|
||||
"""
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
@handle_pattern(r'^listallinfo "(?P<uri>[^"]+)"')
|
||||
def listallinfo(frontend, uri):
|
||||
@handle_request(r'^listallinfo "(?P<uri>[^"]+)"')
|
||||
def listallinfo(context, uri):
|
||||
"""
|
||||
*musicpd.org, music database section:*
|
||||
|
||||
@ -260,9 +260,9 @@ def listallinfo(frontend, uri):
|
||||
"""
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
@handle_pattern(r'^lsinfo$')
|
||||
@handle_pattern(r'^lsinfo "(?P<uri>[^"]*)"$')
|
||||
def lsinfo(frontend, uri=None):
|
||||
@handle_request(r'^lsinfo$')
|
||||
@handle_request(r'^lsinfo "(?P<uri>[^"]*)"$')
|
||||
def lsinfo(context, uri=None):
|
||||
"""
|
||||
*musicpd.org, music database section:*
|
||||
|
||||
@ -279,11 +279,11 @@ def lsinfo(frontend, uri=None):
|
||||
""``, and ``lsinfo "/"``.
|
||||
"""
|
||||
if uri is None or uri == u'/' or uri == u'':
|
||||
return stored_playlists.listplaylists(frontend)
|
||||
return stored_playlists.listplaylists(context)
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
@handle_pattern(r'^rescan( "(?P<uri>[^"]+)")*$')
|
||||
def rescan(frontend, uri=None):
|
||||
@handle_request(r'^rescan( "(?P<uri>[^"]+)")*$')
|
||||
def rescan(context, uri=None):
|
||||
"""
|
||||
*musicpd.org, music database section:*
|
||||
|
||||
@ -291,12 +291,12 @@ def rescan(frontend, uri=None):
|
||||
|
||||
Same as ``update``, but also rescans unmodified files.
|
||||
"""
|
||||
return update(frontend, uri, rescan_unmodified_files=True)
|
||||
return update(context, uri, rescan_unmodified_files=True)
|
||||
|
||||
@handle_pattern(r'^search '
|
||||
@handle_request(r'^search '
|
||||
r'(?P<mpd_query>("?([Aa]lbum|[Aa]rtist|[Dd]ate|[Ff]ilename|'
|
||||
r'[Tt]itle|[Aa]ny)"? "[^"]+"\s?)+)$')
|
||||
def search(frontend, mpd_query):
|
||||
def search(context, mpd_query):
|
||||
"""
|
||||
*musicpd.org, music database section:*
|
||||
|
||||
@ -324,10 +324,10 @@ def search(frontend, mpd_query):
|
||||
- also uses the search type "date".
|
||||
"""
|
||||
query = _build_query(mpd_query)
|
||||
return frontend.backend.library.search(**query).get().mpd_format()
|
||||
return context.backend.library.search(**query).get().mpd_format()
|
||||
|
||||
@handle_pattern(r'^update( "(?P<uri>[^"]+)")*$')
|
||||
def update(frontend, uri=None, rescan_unmodified_files=False):
|
||||
@handle_request(r'^update( "(?P<uri>[^"]+)")*$')
|
||||
def update(context, uri=None, rescan_unmodified_files=False):
|
||||
"""
|
||||
*musicpd.org, music database section:*
|
||||
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
from mopidy.backends.base import PlaybackController
|
||||
from mopidy.frontends.mpd.protocol import handle_pattern
|
||||
from mopidy.frontends.mpd.protocol import handle_request
|
||||
from mopidy.frontends.mpd.exceptions import (MpdArgError, MpdNoExistError,
|
||||
MpdNotImplemented)
|
||||
|
||||
@handle_pattern(r'^consume (?P<state>[01])$')
|
||||
@handle_pattern(r'^consume "(?P<state>[01])"$')
|
||||
def consume(frontend, state):
|
||||
@handle_request(r'^consume (?P<state>[01])$')
|
||||
@handle_request(r'^consume "(?P<state>[01])"$')
|
||||
def consume(context, state):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
|
||||
@ -16,12 +16,12 @@ def consume(frontend, state):
|
||||
playlist.
|
||||
"""
|
||||
if int(state):
|
||||
frontend.backend.playback.consume = True
|
||||
context.backend.playback.consume = True
|
||||
else:
|
||||
frontend.backend.playback.consume = False
|
||||
context.backend.playback.consume = False
|
||||
|
||||
@handle_pattern(r'^crossfade "(?P<seconds>\d+)"$')
|
||||
def crossfade(frontend, seconds):
|
||||
@handle_request(r'^crossfade "(?P<seconds>\d+)"$')
|
||||
def crossfade(context, seconds):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
|
||||
@ -32,8 +32,8 @@ def crossfade(frontend, seconds):
|
||||
seconds = int(seconds)
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
@handle_pattern(r'^next$')
|
||||
def next_(frontend):
|
||||
@handle_request(r'^next$')
|
||||
def next_(context):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
|
||||
@ -87,11 +87,11 @@ def next_(frontend):
|
||||
order as the first time.
|
||||
|
||||
"""
|
||||
return frontend.backend.playback.next().get()
|
||||
return context.backend.playback.next().get()
|
||||
|
||||
@handle_pattern(r'^pause$')
|
||||
@handle_pattern(r'^pause "(?P<state>[01])"$')
|
||||
def pause(frontend, state=None):
|
||||
@handle_request(r'^pause$')
|
||||
@handle_request(r'^pause "(?P<state>[01])"$')
|
||||
def pause(context, state=None):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
|
||||
@ -104,28 +104,28 @@ def pause(frontend, state=None):
|
||||
- Calls ``pause`` without any arguments to toogle pause.
|
||||
"""
|
||||
if state is None:
|
||||
if (frontend.backend.playback.state.get() ==
|
||||
if (context.backend.playback.state.get() ==
|
||||
PlaybackController.PLAYING):
|
||||
frontend.backend.playback.pause()
|
||||
elif (frontend.backend.playback.state.get() ==
|
||||
context.backend.playback.pause()
|
||||
elif (context.backend.playback.state.get() ==
|
||||
PlaybackController.PAUSED):
|
||||
frontend.backend.playback.resume()
|
||||
context.backend.playback.resume()
|
||||
elif int(state):
|
||||
frontend.backend.playback.pause()
|
||||
context.backend.playback.pause()
|
||||
else:
|
||||
frontend.backend.playback.resume()
|
||||
context.backend.playback.resume()
|
||||
|
||||
@handle_pattern(r'^play$')
|
||||
def play(frontend):
|
||||
@handle_request(r'^play$')
|
||||
def play(context):
|
||||
"""
|
||||
The original MPD server resumes from the paused state on ``play``
|
||||
without arguments.
|
||||
"""
|
||||
return frontend.backend.playback.play().get()
|
||||
return context.backend.playback.play().get()
|
||||
|
||||
@handle_pattern(r'^playid "(?P<cpid>\d+)"$')
|
||||
@handle_pattern(r'^playid "(?P<cpid>-1)"$')
|
||||
def playid(frontend, cpid):
|
||||
@handle_request(r'^playid "(?P<cpid>\d+)"$')
|
||||
@handle_request(r'^playid "(?P<cpid>-1)"$')
|
||||
def playid(context, cpid):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
|
||||
@ -144,16 +144,16 @@ def playid(frontend, cpid):
|
||||
"""
|
||||
cpid = int(cpid)
|
||||
if cpid == -1:
|
||||
return _play_minus_one(frontend)
|
||||
return _play_minus_one(context)
|
||||
try:
|
||||
cp_track = frontend.backend.current_playlist.get(cpid=cpid).get()
|
||||
return frontend.backend.playback.play(cp_track).get()
|
||||
cp_track = context.backend.current_playlist.get(cpid=cpid).get()
|
||||
return context.backend.playback.play(cp_track).get()
|
||||
except LookupError:
|
||||
raise MpdNoExistError(u'No such song', command=u'playid')
|
||||
|
||||
@handle_pattern(r'^play (?P<songpos>-?\d+)$')
|
||||
@handle_pattern(r'^play "(?P<songpos>-?\d+)"$')
|
||||
def playpos(frontend, songpos):
|
||||
@handle_request(r'^play (?P<songpos>-?\d+)$')
|
||||
@handle_request(r'^play "(?P<songpos>-?\d+)"$')
|
||||
def playpos(context, songpos):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
|
||||
@ -176,29 +176,29 @@ def playpos(frontend, songpos):
|
||||
"""
|
||||
songpos = int(songpos)
|
||||
if songpos == -1:
|
||||
return _play_minus_one(frontend)
|
||||
return _play_minus_one(context)
|
||||
try:
|
||||
cp_track = frontend.backend.current_playlist.cp_tracks.get()[songpos]
|
||||
return frontend.backend.playback.play(cp_track).get()
|
||||
cp_track = context.backend.current_playlist.cp_tracks.get()[songpos]
|
||||
return context.backend.playback.play(cp_track).get()
|
||||
except IndexError:
|
||||
raise MpdArgError(u'Bad song index', command=u'play')
|
||||
|
||||
def _play_minus_one(frontend):
|
||||
if (frontend.backend.playback.state.get() == PlaybackController.PLAYING):
|
||||
def _play_minus_one(context):
|
||||
if (context.backend.playback.state.get() == PlaybackController.PLAYING):
|
||||
return # Nothing to do
|
||||
elif (frontend.backend.playback.state.get() == PlaybackController.PAUSED):
|
||||
return frontend.backend.playback.resume().get()
|
||||
elif frontend.backend.playback.current_cp_track.get() is not None:
|
||||
cp_track = frontend.backend.playback.current_cp_track.get()
|
||||
return frontend.backend.playback.play(cp_track).get()
|
||||
elif frontend.backend.current_playlist.cp_tracks.get():
|
||||
cp_track = frontend.backend.current_playlist.cp_tracks.get()[0]
|
||||
return frontend.backend.playback.play(cp_track).get()
|
||||
elif (context.backend.playback.state.get() == PlaybackController.PAUSED):
|
||||
return context.backend.playback.resume().get()
|
||||
elif context.backend.playback.current_cp_track.get() is not None:
|
||||
cp_track = context.backend.playback.current_cp_track.get()
|
||||
return context.backend.playback.play(cp_track).get()
|
||||
elif context.backend.current_playlist.cp_tracks.get():
|
||||
cp_track = context.backend.current_playlist.cp_tracks.get()[0]
|
||||
return context.backend.playback.play(cp_track).get()
|
||||
else:
|
||||
return # Fail silently
|
||||
|
||||
@handle_pattern(r'^previous$')
|
||||
def previous(frontend):
|
||||
@handle_request(r'^previous$')
|
||||
def previous(context):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
|
||||
@ -241,11 +241,11 @@ def previous(frontend):
|
||||
``previous`` should do a seek to time position 0.
|
||||
|
||||
"""
|
||||
return frontend.backend.playback.previous().get()
|
||||
return context.backend.playback.previous().get()
|
||||
|
||||
@handle_pattern(r'^random (?P<state>[01])$')
|
||||
@handle_pattern(r'^random "(?P<state>[01])"$')
|
||||
def random(frontend, state):
|
||||
@handle_request(r'^random (?P<state>[01])$')
|
||||
@handle_request(r'^random "(?P<state>[01])"$')
|
||||
def random(context, state):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
|
||||
@ -254,13 +254,13 @@ def random(frontend, state):
|
||||
Sets random state to ``STATE``, ``STATE`` should be 0 or 1.
|
||||
"""
|
||||
if int(state):
|
||||
frontend.backend.playback.random = True
|
||||
context.backend.playback.random = True
|
||||
else:
|
||||
frontend.backend.playback.random = False
|
||||
context.backend.playback.random = False
|
||||
|
||||
@handle_pattern(r'^repeat (?P<state>[01])$')
|
||||
@handle_pattern(r'^repeat "(?P<state>[01])"$')
|
||||
def repeat(frontend, state):
|
||||
@handle_request(r'^repeat (?P<state>[01])$')
|
||||
@handle_request(r'^repeat "(?P<state>[01])"$')
|
||||
def repeat(context, state):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
|
||||
@ -269,12 +269,12 @@ def repeat(frontend, state):
|
||||
Sets repeat state to ``STATE``, ``STATE`` should be 0 or 1.
|
||||
"""
|
||||
if int(state):
|
||||
frontend.backend.playback.repeat = True
|
||||
context.backend.playback.repeat = True
|
||||
else:
|
||||
frontend.backend.playback.repeat = False
|
||||
context.backend.playback.repeat = False
|
||||
|
||||
@handle_pattern(r'^replay_gain_mode "(?P<mode>(off|track|album))"$')
|
||||
def replay_gain_mode(frontend, mode):
|
||||
@handle_request(r'^replay_gain_mode "(?P<mode>(off|track|album))"$')
|
||||
def replay_gain_mode(context, mode):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
|
||||
@ -289,8 +289,8 @@ def replay_gain_mode(frontend, mode):
|
||||
"""
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
@handle_pattern(r'^replay_gain_status$')
|
||||
def replay_gain_status(frontend):
|
||||
@handle_request(r'^replay_gain_status$')
|
||||
def replay_gain_status(context):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
|
||||
@ -301,9 +301,9 @@ def replay_gain_status(frontend):
|
||||
"""
|
||||
return u'off' # TODO
|
||||
|
||||
@handle_pattern(r'^seek (?P<songpos>\d+) (?P<seconds>\d+)$')
|
||||
@handle_pattern(r'^seek "(?P<songpos>\d+)" "(?P<seconds>\d+)"$')
|
||||
def seek(frontend, songpos, seconds):
|
||||
@handle_request(r'^seek (?P<songpos>\d+) (?P<seconds>\d+)$')
|
||||
@handle_request(r'^seek "(?P<songpos>\d+)" "(?P<seconds>\d+)"$')
|
||||
def seek(context, songpos, seconds):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
|
||||
@ -316,12 +316,12 @@ def seek(frontend, songpos, seconds):
|
||||
|
||||
- issues ``seek 1 120`` without quotes around the arguments.
|
||||
"""
|
||||
if frontend.backend.playback.current_playlist_position != songpos:
|
||||
playpos(frontend, songpos)
|
||||
frontend.backend.playback.seek(int(seconds) * 1000)
|
||||
if context.backend.playback.current_playlist_position != songpos:
|
||||
playpos(context, songpos)
|
||||
context.backend.playback.seek(int(seconds) * 1000)
|
||||
|
||||
@handle_pattern(r'^seekid "(?P<cpid>\d+)" "(?P<seconds>\d+)"$')
|
||||
def seekid(frontend, cpid, seconds):
|
||||
@handle_request(r'^seekid "(?P<cpid>\d+)" "(?P<seconds>\d+)"$')
|
||||
def seekid(context, cpid, seconds):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
|
||||
@ -329,13 +329,13 @@ def seekid(frontend, cpid, seconds):
|
||||
|
||||
Seeks to the position ``TIME`` (in seconds) of song ``SONGID``.
|
||||
"""
|
||||
if frontend.backend.playback.current_cpid != cpid:
|
||||
playid(frontend, cpid)
|
||||
frontend.backend.playback.seek(int(seconds) * 1000)
|
||||
if context.backend.playback.current_cpid != cpid:
|
||||
playid(context, cpid)
|
||||
context.backend.playback.seek(int(seconds) * 1000)
|
||||
|
||||
@handle_pattern(r'^setvol (?P<volume>[-+]*\d+)$')
|
||||
@handle_pattern(r'^setvol "(?P<volume>[-+]*\d+)"$')
|
||||
def setvol(frontend, volume):
|
||||
@handle_request(r'^setvol (?P<volume>[-+]*\d+)$')
|
||||
@handle_request(r'^setvol "(?P<volume>[-+]*\d+)"$')
|
||||
def setvol(context, volume):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
|
||||
@ -352,11 +352,11 @@ def setvol(frontend, volume):
|
||||
volume = 0
|
||||
if volume > 100:
|
||||
volume = 100
|
||||
frontend.mixer.volume = volume
|
||||
context.mixer.volume = volume
|
||||
|
||||
@handle_pattern(r'^single (?P<state>[01])$')
|
||||
@handle_pattern(r'^single "(?P<state>[01])"$')
|
||||
def single(frontend, state):
|
||||
@handle_request(r'^single (?P<state>[01])$')
|
||||
@handle_request(r'^single "(?P<state>[01])"$')
|
||||
def single(context, state):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
|
||||
@ -367,12 +367,12 @@ def single(frontend, state):
|
||||
song is repeated if the ``repeat`` mode is enabled.
|
||||
"""
|
||||
if int(state):
|
||||
frontend.backend.playback.single = True
|
||||
context.backend.playback.single = True
|
||||
else:
|
||||
frontend.backend.playback.single = False
|
||||
context.backend.playback.single = False
|
||||
|
||||
@handle_pattern(r'^stop$')
|
||||
def stop(frontend):
|
||||
@handle_request(r'^stop$')
|
||||
def stop(context):
|
||||
"""
|
||||
*musicpd.org, playback section:*
|
||||
|
||||
@ -380,4 +380,4 @@ def stop(frontend):
|
||||
|
||||
Stops playing.
|
||||
"""
|
||||
frontend.backend.playback.stop()
|
||||
context.backend.playback.stop()
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
from mopidy.frontends.mpd.protocol import handle_pattern, mpd_commands
|
||||
from mopidy.frontends.mpd.protocol import handle_request, mpd_commands
|
||||
from mopidy.frontends.mpd.exceptions import MpdNotImplemented
|
||||
|
||||
@handle_pattern(r'^commands$')
|
||||
def commands(frontend):
|
||||
@handle_request(r'^commands$', auth_required=False)
|
||||
def commands(context):
|
||||
"""
|
||||
*musicpd.org, reflection section:*
|
||||
|
||||
@ -10,25 +10,34 @@ def commands(frontend):
|
||||
|
||||
Shows which commands the current user has access to.
|
||||
"""
|
||||
# FIXME When password auth is turned on and the client is not
|
||||
# authenticated, 'commands' should list only the commands the client does
|
||||
# have access to. To implement this we need access to the session object to
|
||||
# check if the client is authenticated or not.
|
||||
if context.dispatcher.authenticated:
|
||||
command_names = [command.name for command in mpd_commands]
|
||||
else:
|
||||
command_names = [command.name for command in mpd_commands
|
||||
if not command.auth_required]
|
||||
|
||||
sorted_commands = sorted(list(mpd_commands))
|
||||
# No permission to use
|
||||
if 'kill' in command_names:
|
||||
command_names.remove('kill')
|
||||
|
||||
# Not shown by MPD in its command list
|
||||
sorted_commands.remove('command_list_begin')
|
||||
sorted_commands.remove('command_list_ok_begin')
|
||||
sorted_commands.remove('command_list_end')
|
||||
sorted_commands.remove('idle')
|
||||
sorted_commands.remove('noidle')
|
||||
sorted_commands.remove('sticker')
|
||||
if 'command_list_begin' in command_names:
|
||||
command_names.remove('command_list_begin')
|
||||
if 'command_list_ok_begin' in command_names:
|
||||
command_names.remove('command_list_ok_begin')
|
||||
if 'command_list_end' in command_names:
|
||||
command_names.remove('command_list_end')
|
||||
if 'idle' in command_names:
|
||||
command_names.remove('idle')
|
||||
if 'noidle' in command_names:
|
||||
command_names.remove('noidle')
|
||||
if 'sticker' in command_names:
|
||||
command_names.remove('sticker')
|
||||
|
||||
return [('command', c) for c in sorted_commands]
|
||||
return [('command', command_name) for command_name in sorted(command_names)]
|
||||
|
||||
@handle_pattern(r'^decoders$')
|
||||
def decoders(frontend):
|
||||
@handle_request(r'^decoders$')
|
||||
def decoders(context):
|
||||
"""
|
||||
*musicpd.org, reflection section:*
|
||||
|
||||
@ -46,8 +55,8 @@ def decoders(frontend):
|
||||
"""
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
@handle_pattern(r'^notcommands$')
|
||||
def notcommands(frontend):
|
||||
@handle_request(r'^notcommands$', auth_required=False)
|
||||
def notcommands(context):
|
||||
"""
|
||||
*musicpd.org, reflection section:*
|
||||
|
||||
@ -55,14 +64,19 @@ def notcommands(frontend):
|
||||
|
||||
Shows which commands the current user does not have access to.
|
||||
"""
|
||||
# FIXME When password auth is turned on and the client is not
|
||||
# authenticated, 'notcommands' should list all the commands the client does
|
||||
# not have access to. To implement this we need access to the session
|
||||
# object to check if the client is authenticated or not.
|
||||
pass
|
||||
if context.dispatcher.authenticated:
|
||||
command_names = []
|
||||
else:
|
||||
command_names = [command.name for command in mpd_commands
|
||||
if command.auth_required]
|
||||
|
||||
@handle_pattern(r'^tagtypes$')
|
||||
def tagtypes(frontend):
|
||||
# No permission to use
|
||||
command_names.append('kill')
|
||||
|
||||
return [('command', command_name) for command_name in sorted(command_names)]
|
||||
|
||||
@handle_request(r'^tagtypes$')
|
||||
def tagtypes(context):
|
||||
"""
|
||||
*musicpd.org, reflection section:*
|
||||
|
||||
@ -72,8 +86,8 @@ def tagtypes(frontend):
|
||||
"""
|
||||
pass # TODO
|
||||
|
||||
@handle_pattern(r'^urlhandlers$')
|
||||
def urlhandlers(frontend):
|
||||
@handle_request(r'^urlhandlers$')
|
||||
def urlhandlers(context):
|
||||
"""
|
||||
*musicpd.org, reflection section:*
|
||||
|
||||
@ -81,4 +95,4 @@ def urlhandlers(frontend):
|
||||
|
||||
Gets a list of available URL handlers.
|
||||
"""
|
||||
return [(u'handler', uri) for uri in frontend.backend.uri_handlers.get()]
|
||||
return [(u'handler', uri) for uri in context.backend.uri_handlers.get()]
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import pykka.future
|
||||
|
||||
from mopidy.backends.base import PlaybackController
|
||||
from mopidy.frontends.mpd.protocol import handle_pattern
|
||||
from mopidy.frontends.mpd.protocol import handle_request
|
||||
from mopidy.frontends.mpd.exceptions import MpdNotImplemented
|
||||
|
||||
@handle_pattern(r'^clearerror$')
|
||||
def clearerror(frontend):
|
||||
@handle_request(r'^clearerror$')
|
||||
def clearerror(context):
|
||||
"""
|
||||
*musicpd.org, status section:*
|
||||
|
||||
@ -14,8 +16,8 @@ def clearerror(frontend):
|
||||
"""
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
@handle_pattern(r'^currentsong$')
|
||||
def currentsong(frontend):
|
||||
@handle_request(r'^currentsong$')
|
||||
def currentsong(context):
|
||||
"""
|
||||
*musicpd.org, status section:*
|
||||
|
||||
@ -24,15 +26,15 @@ def currentsong(frontend):
|
||||
Displays the song info of the current song (same song that is
|
||||
identified in status).
|
||||
"""
|
||||
current_cp_track = frontend.backend.playback.current_cp_track.get()
|
||||
current_cp_track = context.backend.playback.current_cp_track.get()
|
||||
if current_cp_track is not None:
|
||||
return current_cp_track[1].mpd_format(
|
||||
position=frontend.backend.playback.current_playlist_position.get(),
|
||||
cpid=current_cp_track[0])
|
||||
return current_cp_track.track.mpd_format(
|
||||
position=context.backend.playback.current_playlist_position.get(),
|
||||
cpid=current_cp_track.cpid)
|
||||
|
||||
@handle_pattern(r'^idle$')
|
||||
@handle_pattern(r'^idle (?P<subsystems>.+)$')
|
||||
def idle(frontend, subsystems=None):
|
||||
@handle_request(r'^idle$')
|
||||
@handle_request(r'^idle (?P<subsystems>.+)$')
|
||||
def idle(context, subsystems=None):
|
||||
"""
|
||||
*musicpd.org, status section:*
|
||||
|
||||
@ -67,13 +69,13 @@ def idle(frontend, subsystems=None):
|
||||
"""
|
||||
pass # TODO
|
||||
|
||||
@handle_pattern(r'^noidle$')
|
||||
def noidle(frontend):
|
||||
@handle_request(r'^noidle$')
|
||||
def noidle(context):
|
||||
"""See :meth:`_status_idle`."""
|
||||
pass # TODO
|
||||
|
||||
@handle_pattern(r'^stats$')
|
||||
def stats(frontend):
|
||||
@handle_request(r'^stats$')
|
||||
def stats(context):
|
||||
"""
|
||||
*musicpd.org, status section:*
|
||||
|
||||
@ -98,8 +100,8 @@ def stats(frontend):
|
||||
'playtime': 0, # TODO
|
||||
}
|
||||
|
||||
@handle_pattern(r'^status$')
|
||||
def status(frontend):
|
||||
@handle_request(r'^status$')
|
||||
def status(context):
|
||||
"""
|
||||
*musicpd.org, status section:*
|
||||
|
||||
@ -130,65 +132,80 @@ def status(frontend):
|
||||
- ``updatings_db``: job id
|
||||
- ``error``: if there is an error, returns message here
|
||||
"""
|
||||
futures = {
|
||||
'current_playlist.tracks': context.backend.current_playlist.tracks,
|
||||
'current_playlist.version': context.backend.current_playlist.version,
|
||||
'mixer.volume': context.mixer.volume,
|
||||
'playback.consume': context.backend.playback.consume,
|
||||
'playback.random': context.backend.playback.random,
|
||||
'playback.repeat': context.backend.playback.repeat,
|
||||
'playback.single': context.backend.playback.single,
|
||||
'playback.state': context.backend.playback.state,
|
||||
'playback.current_cp_track': context.backend.playback.current_cp_track,
|
||||
'playback.current_playlist_position':
|
||||
context.backend.playback.current_playlist_position,
|
||||
'playback.time_position': context.backend.playback.time_position,
|
||||
}
|
||||
pykka.future.get_all(futures.values())
|
||||
result = [
|
||||
('volume', _status_volume(frontend)),
|
||||
('repeat', _status_repeat(frontend)),
|
||||
('random', _status_random(frontend)),
|
||||
('single', _status_single(frontend)),
|
||||
('consume', _status_consume(frontend)),
|
||||
('playlist', _status_playlist_version(frontend)),
|
||||
('playlistlength', _status_playlist_length(frontend)),
|
||||
('xfade', _status_xfade(frontend)),
|
||||
('state', _status_state(frontend)),
|
||||
('volume', _status_volume(futures)),
|
||||
('repeat', _status_repeat(futures)),
|
||||
('random', _status_random(futures)),
|
||||
('single', _status_single(futures)),
|
||||
('consume', _status_consume(futures)),
|
||||
('playlist', _status_playlist_version(futures)),
|
||||
('playlistlength', _status_playlist_length(futures)),
|
||||
('xfade', _status_xfade(futures)),
|
||||
('state', _status_state(futures)),
|
||||
]
|
||||
if frontend.backend.playback.current_track.get() is not None:
|
||||
result.append(('song', _status_songpos(frontend)))
|
||||
result.append(('songid', _status_songid(frontend)))
|
||||
if frontend.backend.playback.state.get() in (PlaybackController.PLAYING,
|
||||
if futures['playback.current_cp_track'].get() is not None:
|
||||
result.append(('song', _status_songpos(futures)))
|
||||
result.append(('songid', _status_songid(futures)))
|
||||
if futures['playback.state'].get() in (PlaybackController.PLAYING,
|
||||
PlaybackController.PAUSED):
|
||||
result.append(('time', _status_time(frontend)))
|
||||
result.append(('elapsed', _status_time_elapsed(frontend)))
|
||||
result.append(('bitrate', _status_bitrate(frontend)))
|
||||
result.append(('time', _status_time(futures)))
|
||||
result.append(('elapsed', _status_time_elapsed(futures)))
|
||||
result.append(('bitrate', _status_bitrate(futures)))
|
||||
return result
|
||||
|
||||
def _status_bitrate(frontend):
|
||||
current_track = frontend.backend.playback.current_track.get()
|
||||
if current_track is not None:
|
||||
return current_track.bitrate
|
||||
def _status_bitrate(futures):
|
||||
current_cp_track = futures['playback.current_cp_track'].get()
|
||||
if current_cp_track is not None:
|
||||
return current_cp_track.track.bitrate
|
||||
|
||||
def _status_consume(frontend):
|
||||
if frontend.backend.playback.consume.get():
|
||||
def _status_consume(futures):
|
||||
if futures['playback.consume'].get():
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
def _status_playlist_length(frontend):
|
||||
return len(frontend.backend.current_playlist.tracks.get())
|
||||
def _status_playlist_length(futures):
|
||||
return len(futures['current_playlist.tracks'].get())
|
||||
|
||||
def _status_playlist_version(frontend):
|
||||
return frontend.backend.current_playlist.version.get()
|
||||
def _status_playlist_version(futures):
|
||||
return futures['current_playlist.version'].get()
|
||||
|
||||
def _status_random(frontend):
|
||||
return int(frontend.backend.playback.random.get())
|
||||
def _status_random(futures):
|
||||
return int(futures['playback.random'].get())
|
||||
|
||||
def _status_repeat(frontend):
|
||||
return int(frontend.backend.playback.repeat.get())
|
||||
def _status_repeat(futures):
|
||||
return int(futures['playback.repeat'].get())
|
||||
|
||||
def _status_single(frontend):
|
||||
return int(frontend.backend.playback.single.get())
|
||||
def _status_single(futures):
|
||||
return int(futures['playback.single'].get())
|
||||
|
||||
def _status_songid(frontend):
|
||||
current_cpid = frontend.backend.playback.current_cpid.get()
|
||||
if current_cpid is not None:
|
||||
return current_cpid
|
||||
def _status_songid(futures):
|
||||
current_cp_track = futures['playback.current_cp_track'].get()
|
||||
if current_cp_track is not None:
|
||||
return current_cp_track.cpid
|
||||
else:
|
||||
return _status_songpos(frontend)
|
||||
return _status_songpos(futures)
|
||||
|
||||
def _status_songpos(frontend):
|
||||
return frontend.backend.playback.current_playlist_position.get()
|
||||
def _status_songpos(futures):
|
||||
return futures['playback.current_playlist_position'].get()
|
||||
|
||||
def _status_state(frontend):
|
||||
state = frontend.backend.playback.state.get()
|
||||
def _status_state(futures):
|
||||
state = futures['playback.state'].get()
|
||||
if state == PlaybackController.PLAYING:
|
||||
return u'play'
|
||||
elif state == PlaybackController.STOPPED:
|
||||
@ -196,28 +213,28 @@ def _status_state(frontend):
|
||||
elif state == PlaybackController.PAUSED:
|
||||
return u'pause'
|
||||
|
||||
def _status_time(frontend):
|
||||
return u'%s:%s' % (_status_time_elapsed(frontend) // 1000,
|
||||
_status_time_total(frontend) // 1000)
|
||||
def _status_time(futures):
|
||||
return u'%s:%s' % (_status_time_elapsed(futures) // 1000,
|
||||
_status_time_total(futures) // 1000)
|
||||
|
||||
def _status_time_elapsed(frontend):
|
||||
return frontend.backend.playback.time_position.get()
|
||||
def _status_time_elapsed(futures):
|
||||
return futures['playback.time_position'].get()
|
||||
|
||||
def _status_time_total(frontend):
|
||||
current_track = frontend.backend.playback.current_track.get()
|
||||
if current_track is None:
|
||||
def _status_time_total(futures):
|
||||
current_cp_track = futures['playback.current_cp_track'].get()
|
||||
if current_cp_track is None:
|
||||
return 0
|
||||
elif current_track.length is None:
|
||||
elif current_cp_track.track.length is None:
|
||||
return 0
|
||||
else:
|
||||
return current_track.length
|
||||
return current_cp_track.track.length
|
||||
|
||||
def _status_volume(frontend):
|
||||
volume = frontend.mixer.volume.get()
|
||||
def _status_volume(futures):
|
||||
volume = futures['mixer.volume'].get()
|
||||
if volume is not None:
|
||||
return volume
|
||||
else:
|
||||
return 0
|
||||
|
||||
def _status_xfade(frontend):
|
||||
return 0 # TODO
|
||||
def _status_xfade(futures):
|
||||
return 0 # Not supported
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
from mopidy.frontends.mpd.protocol import handle_pattern
|
||||
from mopidy.frontends.mpd.protocol import handle_request
|
||||
from mopidy.frontends.mpd.exceptions import MpdNotImplemented
|
||||
|
||||
@handle_pattern(r'^sticker delete "(?P<field>[^"]+)" '
|
||||
@handle_request(r'^sticker delete "(?P<field>[^"]+)" '
|
||||
r'"(?P<uri>[^"]+)"( "(?P<name>[^"]+)")*$')
|
||||
def sticker_delete(frontend, field, uri, name=None):
|
||||
def sticker_delete(context, field, uri, name=None):
|
||||
"""
|
||||
*musicpd.org, sticker section:*
|
||||
|
||||
@ -14,9 +14,9 @@ def sticker_delete(frontend, field, uri, name=None):
|
||||
"""
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
@handle_pattern(r'^sticker find "(?P<field>[^"]+)" "(?P<uri>[^"]+)" '
|
||||
@handle_request(r'^sticker find "(?P<field>[^"]+)" "(?P<uri>[^"]+)" '
|
||||
r'"(?P<name>[^"]+)"$')
|
||||
def sticker_find(frontend, field, uri, name):
|
||||
def sticker_find(context, field, uri, name):
|
||||
"""
|
||||
*musicpd.org, sticker section:*
|
||||
|
||||
@ -28,9 +28,9 @@ def sticker_find(frontend, field, uri, name):
|
||||
"""
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
@handle_pattern(r'^sticker get "(?P<field>[^"]+)" "(?P<uri>[^"]+)" '
|
||||
@handle_request(r'^sticker get "(?P<field>[^"]+)" "(?P<uri>[^"]+)" '
|
||||
r'"(?P<name>[^"]+)"$')
|
||||
def sticker_get(frontend, field, uri, name):
|
||||
def sticker_get(context, field, uri, name):
|
||||
"""
|
||||
*musicpd.org, sticker section:*
|
||||
|
||||
@ -40,8 +40,8 @@ def sticker_get(frontend, field, uri, name):
|
||||
"""
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
@handle_pattern(r'^sticker list "(?P<field>[^"]+)" "(?P<uri>[^"]+)"$')
|
||||
def sticker_list(frontend, field, uri):
|
||||
@handle_request(r'^sticker list "(?P<field>[^"]+)" "(?P<uri>[^"]+)"$')
|
||||
def sticker_list(context, field, uri):
|
||||
"""
|
||||
*musicpd.org, sticker section:*
|
||||
|
||||
@ -51,9 +51,9 @@ def sticker_list(frontend, field, uri):
|
||||
"""
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
@handle_pattern(r'^sticker set "(?P<field>[^"]+)" "(?P<uri>[^"]+)" '
|
||||
@handle_request(r'^sticker set "(?P<field>[^"]+)" "(?P<uri>[^"]+)" '
|
||||
r'"(?P<name>[^"]+)" "(?P<value>[^"]+)"$')
|
||||
def sticker_set(frontend, field, uri, name, value):
|
||||
def sticker_set(context, field, uri, name, value):
|
||||
"""
|
||||
*musicpd.org, sticker section:*
|
||||
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import datetime as dt
|
||||
|
||||
from mopidy.frontends.mpd.protocol import handle_pattern
|
||||
from mopidy.frontends.mpd.protocol import handle_request
|
||||
from mopidy.frontends.mpd.exceptions import MpdNoExistError, MpdNotImplemented
|
||||
|
||||
@handle_pattern(r'^listplaylist "(?P<name>[^"]+)"$')
|
||||
def listplaylist(frontend, name):
|
||||
@handle_request(r'^listplaylist "(?P<name>[^"]+)"$')
|
||||
def listplaylist(context, name):
|
||||
"""
|
||||
*musicpd.org, stored playlists section:*
|
||||
|
||||
@ -19,13 +19,13 @@ def listplaylist(frontend, name):
|
||||
file: relative/path/to/file3.mp3
|
||||
"""
|
||||
try:
|
||||
playlist = frontend.backend.stored_playlists.get(name=name).get()
|
||||
playlist = context.backend.stored_playlists.get(name=name).get()
|
||||
return ['file: %s' % t.uri for t in playlist.tracks]
|
||||
except LookupError:
|
||||
raise MpdNoExistError(u'No such playlist', command=u'listplaylist')
|
||||
|
||||
@handle_pattern(r'^listplaylistinfo "(?P<name>[^"]+)"$')
|
||||
def listplaylistinfo(frontend, name):
|
||||
@handle_request(r'^listplaylistinfo "(?P<name>[^"]+)"$')
|
||||
def listplaylistinfo(context, name):
|
||||
"""
|
||||
*musicpd.org, stored playlists section:*
|
||||
|
||||
@ -39,14 +39,14 @@ def listplaylistinfo(frontend, name):
|
||||
Album, Artist, Track
|
||||
"""
|
||||
try:
|
||||
playlist = frontend.backend.stored_playlists.get(name=name).get()
|
||||
playlist = context.backend.stored_playlists.get(name=name).get()
|
||||
return playlist.mpd_format()
|
||||
except LookupError:
|
||||
raise MpdNoExistError(
|
||||
u'No such playlist', command=u'listplaylistinfo')
|
||||
|
||||
@handle_pattern(r'^listplaylists$')
|
||||
def listplaylists(frontend):
|
||||
@handle_request(r'^listplaylists$')
|
||||
def listplaylists(context):
|
||||
"""
|
||||
*musicpd.org, stored playlists section:*
|
||||
|
||||
@ -67,7 +67,7 @@ def listplaylists(frontend):
|
||||
Last-Modified: 2010-02-06T02:11:08Z
|
||||
"""
|
||||
result = []
|
||||
for playlist in frontend.backend.stored_playlists.playlists.get():
|
||||
for playlist in context.backend.stored_playlists.playlists.get():
|
||||
result.append((u'playlist', playlist.name))
|
||||
last_modified = (playlist.last_modified or
|
||||
dt.datetime.now()).isoformat()
|
||||
@ -79,8 +79,8 @@ def listplaylists(frontend):
|
||||
result.append((u'Last-Modified', last_modified))
|
||||
return result
|
||||
|
||||
@handle_pattern(r'^load "(?P<name>[^"]+)"$')
|
||||
def load(frontend, name):
|
||||
@handle_request(r'^load "(?P<name>[^"]+)"$')
|
||||
def load(context, name):
|
||||
"""
|
||||
*musicpd.org, stored playlists section:*
|
||||
|
||||
@ -93,13 +93,13 @@ def load(frontend, name):
|
||||
- ``load`` appends the given playlist to the current playlist.
|
||||
"""
|
||||
try:
|
||||
playlist = frontend.backend.stored_playlists.get(name=name).get()
|
||||
frontend.backend.current_playlist.append(playlist.tracks)
|
||||
playlist = context.backend.stored_playlists.get(name=name).get()
|
||||
context.backend.current_playlist.append(playlist.tracks)
|
||||
except LookupError:
|
||||
raise MpdNoExistError(u'No such playlist', command=u'load')
|
||||
|
||||
@handle_pattern(r'^playlistadd "(?P<name>[^"]+)" "(?P<uri>[^"]+)"$')
|
||||
def playlistadd(frontend, name, uri):
|
||||
@handle_request(r'^playlistadd "(?P<name>[^"]+)" "(?P<uri>[^"]+)"$')
|
||||
def playlistadd(context, name, uri):
|
||||
"""
|
||||
*musicpd.org, stored playlists section:*
|
||||
|
||||
@ -111,8 +111,8 @@ def playlistadd(frontend, name, uri):
|
||||
"""
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
@handle_pattern(r'^playlistclear "(?P<name>[^"]+)"$')
|
||||
def playlistclear(frontend, name):
|
||||
@handle_request(r'^playlistclear "(?P<name>[^"]+)"$')
|
||||
def playlistclear(context, name):
|
||||
"""
|
||||
*musicpd.org, stored playlists section:*
|
||||
|
||||
@ -122,8 +122,8 @@ def playlistclear(frontend, name):
|
||||
"""
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
@handle_pattern(r'^playlistdelete "(?P<name>[^"]+)" "(?P<songpos>\d+)"$')
|
||||
def playlistdelete(frontend, name, songpos):
|
||||
@handle_request(r'^playlistdelete "(?P<name>[^"]+)" "(?P<songpos>\d+)"$')
|
||||
def playlistdelete(context, name, songpos):
|
||||
"""
|
||||
*musicpd.org, stored playlists section:*
|
||||
|
||||
@ -133,9 +133,9 @@ def playlistdelete(frontend, name, songpos):
|
||||
"""
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
@handle_pattern(r'^playlistmove "(?P<name>[^"]+)" '
|
||||
@handle_request(r'^playlistmove "(?P<name>[^"]+)" '
|
||||
r'"(?P<from_pos>\d+)" "(?P<to_pos>\d+)"$')
|
||||
def playlistmove(frontend, name, from_pos, to_pos):
|
||||
def playlistmove(context, name, from_pos, to_pos):
|
||||
"""
|
||||
*musicpd.org, stored playlists section:*
|
||||
|
||||
@ -152,8 +152,8 @@ def playlistmove(frontend, name, from_pos, to_pos):
|
||||
"""
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
@handle_pattern(r'^rename "(?P<old_name>[^"]+)" "(?P<new_name>[^"]+)"$')
|
||||
def rename(frontend, old_name, new_name):
|
||||
@handle_request(r'^rename "(?P<old_name>[^"]+)" "(?P<new_name>[^"]+)"$')
|
||||
def rename(context, old_name, new_name):
|
||||
"""
|
||||
*musicpd.org, stored playlists section:*
|
||||
|
||||
@ -163,8 +163,8 @@ def rename(frontend, old_name, new_name):
|
||||
"""
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
@handle_pattern(r'^rm "(?P<name>[^"]+)"$')
|
||||
def rm(frontend, name):
|
||||
@handle_request(r'^rm "(?P<name>[^"]+)"$')
|
||||
def rm(context, name):
|
||||
"""
|
||||
*musicpd.org, stored playlists section:*
|
||||
|
||||
@ -174,8 +174,8 @@ def rm(frontend, name):
|
||||
"""
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
@handle_pattern(r'^save "(?P<name>[^"]+)"$')
|
||||
def save(frontend, name):
|
||||
@handle_request(r'^save "(?P<name>[^"]+)"$')
|
||||
def save(context, name):
|
||||
"""
|
||||
*musicpd.org, stored playlists section:*
|
||||
|
||||
|
||||
@ -31,16 +31,17 @@ class MpdServer(asyncore.dispatcher):
|
||||
self._format_hostname(settings.MPD_SERVER_HOSTNAME),
|
||||
settings.MPD_SERVER_PORT)
|
||||
except IOError, e:
|
||||
logger.error(u'MPD server startup failed: %s' % str(e).decode('utf-8'))
|
||||
logger.error(u'MPD server startup failed: %s' %
|
||||
str(e).decode('utf-8'))
|
||||
sys.exit(1)
|
||||
|
||||
def handle_accept(self):
|
||||
"""Handle new client connection."""
|
||||
"""Called by asyncore when a new client connects."""
|
||||
(client_socket, client_socket_address) = self.accept()
|
||||
logger.info(u'MPD client connection from [%s]:%s',
|
||||
client_socket_address[0], client_socket_address[1])
|
||||
MpdSession(self, client_socket, client_socket_address).start()
|
||||
MpdSession(self, client_socket, client_socket_address)
|
||||
|
||||
def handle_close(self):
|
||||
"""Handle end of client connection."""
|
||||
"""Called by asyncore when the socket is closed."""
|
||||
self.close()
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import asynchat
|
||||
import logging
|
||||
|
||||
from mopidy import settings
|
||||
from mopidy.frontends.mpd.dispatcher import MpdDispatcher
|
||||
from mopidy.frontends.mpd.protocol import ENCODING, LINE_TERMINATOR, VERSION
|
||||
from mopidy.utils.log import indent
|
||||
@ -22,70 +21,38 @@ class MpdSession(asynchat.async_chat):
|
||||
self.input_buffer = []
|
||||
self.authenticated = False
|
||||
self.set_terminator(LINE_TERMINATOR.encode(ENCODING))
|
||||
self.dispatcher = MpdDispatcher()
|
||||
|
||||
def start(self):
|
||||
"""Start a new client session."""
|
||||
self.send_response(u'OK MPD %s' % VERSION)
|
||||
self.dispatcher = MpdDispatcher(session=self)
|
||||
self.send_response([u'OK MPD %s' % VERSION])
|
||||
|
||||
def collect_incoming_data(self, data):
|
||||
"""Collect incoming data into buffer until a terminator is found."""
|
||||
"""Called by asynchat when new data arrives."""
|
||||
self.input_buffer.append(data)
|
||||
|
||||
def found_terminator(self):
|
||||
"""Handle request when a terminator is found."""
|
||||
"""Called by asynchat when a terminator is found in incoming data."""
|
||||
data = ''.join(self.input_buffer).strip()
|
||||
self.input_buffer = []
|
||||
try:
|
||||
request = data.decode(ENCODING)
|
||||
logger.debug(u'Input from [%s]:%s: %s', self.client_address,
|
||||
self.client_port, indent(request))
|
||||
self.handle_request(request)
|
||||
self.send_response(self.handle_request(data))
|
||||
except UnicodeDecodeError as e:
|
||||
logger.warning(u'Received invalid data: %s', e)
|
||||
|
||||
def handle_request(self, request):
|
||||
"""Handle request by sending it to the MPD frontend."""
|
||||
if not self.authenticated:
|
||||
(self.authenticated, response) = self.check_password(request)
|
||||
if response is not None:
|
||||
self.send_response(response)
|
||||
return
|
||||
response = self.dispatcher.handle_request(request)
|
||||
"""Handle the request using the MPD command handlers."""
|
||||
request = request.decode(ENCODING)
|
||||
logger.debug(u'Request from [%s]:%s: %s', self.client_address,
|
||||
self.client_port, indent(request))
|
||||
return self.dispatcher.handle_request(request)
|
||||
|
||||
def send_response(self, response):
|
||||
"""
|
||||
Format a response from the MPD command handlers and send it to the
|
||||
client.
|
||||
"""
|
||||
if response is not None:
|
||||
self.handle_response(response)
|
||||
|
||||
def handle_response(self, response):
|
||||
"""Handle response from the MPD frontend."""
|
||||
self.send_response(LINE_TERMINATOR.join(response))
|
||||
|
||||
def send_response(self, output):
|
||||
"""Send a response to the client."""
|
||||
logger.debug(u'Output to [%s]:%s: %s', self.client_address,
|
||||
self.client_port, indent(output))
|
||||
output = u'%s%s' % (output, LINE_TERMINATOR)
|
||||
data = output.encode(ENCODING)
|
||||
self.push(data)
|
||||
|
||||
def check_password(self, request):
|
||||
"""
|
||||
Takes any request and tries to authenticate the client using it.
|
||||
|
||||
:rtype: a two-tuple containing (is_authenticated, response_message). If
|
||||
the response_message is :class:`None`, normal processing should
|
||||
continue, even though the client may not be authenticated.
|
||||
"""
|
||||
if settings.MPD_SERVER_PASSWORD is None:
|
||||
return (True, None)
|
||||
command = request.split(' ')[0]
|
||||
if command == 'password':
|
||||
if request == 'password "%s"' % settings.MPD_SERVER_PASSWORD:
|
||||
return (True, u'OK')
|
||||
else:
|
||||
return (False, u'ACK [3@0] {password} incorrect password')
|
||||
if command in ('close', 'commands', 'notcommands', 'ping'):
|
||||
return (False, None)
|
||||
else:
|
||||
return (False,
|
||||
u'ACK [4@0] {%(c)s} you don\'t have permission for "%(c)s"' %
|
||||
{'c': command})
|
||||
response = LINE_TERMINATOR.join(response)
|
||||
logger.debug(u'Response to [%s]:%s: %s', self.client_address,
|
||||
self.client_port, indent(response))
|
||||
response = u'%s%s' % (response, LINE_TERMINATOR)
|
||||
data = response.encode(ENCODING)
|
||||
self.push(data)
|
||||
|
||||
@ -36,7 +36,6 @@ class GStreamer(ThreadingActor):
|
||||
def __init__(self):
|
||||
self._pipeline = None
|
||||
self._source = None
|
||||
self._taginject = None
|
||||
self._tee = None
|
||||
self._uridecodebin = None
|
||||
self._volume = None
|
||||
@ -56,13 +55,11 @@ class GStreamer(ThreadingActor):
|
||||
'uridecodebin name=uri',
|
||||
'audioconvert name=convert',
|
||||
'volume name=volume',
|
||||
'taginject name=inject',
|
||||
'tee name=tee'])
|
||||
|
||||
logger.debug(u'Setting up base GStreamer pipeline: %s', description)
|
||||
|
||||
self._pipeline = gst.parse_launch(description)
|
||||
self._taginject = self._pipeline.get_by_name('inject')
|
||||
self._tee = self._pipeline.get_by_name('tee')
|
||||
self._volume = self._pipeline.get_by_name('volume')
|
||||
self._uridecodebin = self._pipeline.get_by_name('uri')
|
||||
@ -277,13 +274,18 @@ class GStreamer(ThreadingActor):
|
||||
:param track: the current track
|
||||
:type track: :class:`mopidy.modes.Track`
|
||||
"""
|
||||
# FIXME what if we want to unset taginject tags?
|
||||
tags = u'artist="%(artist)s",title="%(title)s"' % {
|
||||
'artist': u', '.join([a.name for a in track.artists]),
|
||||
'title': track.name,
|
||||
}
|
||||
logger.debug('Setting tags to: %s', tags)
|
||||
self._taginject.set_property('tags', tags)
|
||||
taglist = gst.TagList()
|
||||
artists = [a for a in (track.artists or []) if a.name]
|
||||
|
||||
if artists:
|
||||
taglist[gst.TAG_ARTIST] = u', '.join([a.name for a in artists])
|
||||
if track.name:
|
||||
taglist[gst.TAG_TITLE] = track.name
|
||||
if track.album and track.album.name:
|
||||
taglist[gst.TAG_ALBUM] = track.album.name
|
||||
|
||||
event = gst.event_new_tag(taglist)
|
||||
self._pipeline.send_event(event)
|
||||
|
||||
def connect_output(self, output):
|
||||
"""
|
||||
|
||||
@ -51,9 +51,9 @@ class AlsaMixer(ThreadingActor, BaseMixer):
|
||||
return [settings.MIXER_ALSA_CONTROL]
|
||||
return [u'Master', u'PCM']
|
||||
|
||||
def _get_volume(self):
|
||||
def get_volume(self):
|
||||
# FIXME does not seem to see external volume changes.
|
||||
return self._mixer.getvolume()[0]
|
||||
|
||||
def _set_volume(self, volume):
|
||||
def set_volume(self, volume):
|
||||
self._mixer.setvolume(volume)
|
||||
|
||||
@ -17,9 +17,10 @@ class BaseMixer(object):
|
||||
Integer in range [0, 100]. :class:`None` if unknown. Values below 0 is
|
||||
equal to 0. Values above 100 is equal to 100.
|
||||
"""
|
||||
if self._get_volume() is None:
|
||||
volume = self.get_volume()
|
||||
if volume is None:
|
||||
return None
|
||||
return int(self._get_volume() / self.amplification_factor)
|
||||
return int(volume / self.amplification_factor)
|
||||
|
||||
@volume.setter
|
||||
def volume(self, volume):
|
||||
@ -28,9 +29,9 @@ class BaseMixer(object):
|
||||
volume = 0
|
||||
elif volume > 100:
|
||||
volume = 100
|
||||
self._set_volume(volume)
|
||||
self.set_volume(volume)
|
||||
|
||||
def _get_volume(self):
|
||||
def get_volume(self):
|
||||
"""
|
||||
Return volume as integer in range [0, 100]. :class:`None` if unknown.
|
||||
|
||||
@ -38,7 +39,7 @@ class BaseMixer(object):
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def _set_volume(self, volume):
|
||||
def set_volume(self, volume):
|
||||
"""
|
||||
Set volume as integer in range [0, 100].
|
||||
|
||||
|
||||
@ -35,14 +35,14 @@ class DenonMixer(ThreadingActor, BaseMixer):
|
||||
from serial import Serial
|
||||
self._device = Serial(port=settings.MIXER_EXT_PORT, timeout=0.2)
|
||||
|
||||
def _get_volume(self):
|
||||
def get_volume(self):
|
||||
self._ensure_open_device()
|
||||
self._device.write('MV?\r')
|
||||
vol = str(self._device.readline()[2:4])
|
||||
logger.debug(u'_get_volume() = %s' % vol)
|
||||
return self._levels.index(vol)
|
||||
|
||||
def _set_volume(self, volume):
|
||||
def set_volume(self, volume):
|
||||
# Clamp according to Denon-spec
|
||||
if volume > 99:
|
||||
volume = 99
|
||||
|
||||
@ -8,8 +8,8 @@ class DummyMixer(ThreadingActor, BaseMixer):
|
||||
def __init__(self):
|
||||
self._volume = None
|
||||
|
||||
def _get_volume(self):
|
||||
def get_volume(self):
|
||||
return self._volume
|
||||
|
||||
def _set_volume(self, volume):
|
||||
def set_volume(self, volume):
|
||||
self._volume = volume
|
||||
|
||||
@ -15,8 +15,8 @@ class GStreamerSoftwareMixer(ThreadingActor, BaseMixer):
|
||||
assert len(output_refs) == 1, 'Expected exactly one running output.'
|
||||
self.output = output_refs[0].proxy()
|
||||
|
||||
def _get_volume(self):
|
||||
def get_volume(self):
|
||||
return self.output.get_volume().get()
|
||||
|
||||
def _set_volume(self, volume):
|
||||
def set_volume(self, volume):
|
||||
self.output.set_volume(volume).get()
|
||||
|
||||
@ -40,10 +40,10 @@ class NadMixer(ThreadingActor, BaseMixer):
|
||||
self._volume_cache = None
|
||||
self._nad_talker = NadTalker.start().proxy()
|
||||
|
||||
def _get_volume(self):
|
||||
def get_volume(self):
|
||||
return self._volume_cache
|
||||
|
||||
def _set_volume(self, volume):
|
||||
def set_volume(self, volume):
|
||||
self._volume_cache = volume
|
||||
self._nad_talker.set_volume(volume)
|
||||
|
||||
|
||||
@ -28,7 +28,7 @@ class OsaMixer(ThreadingActor, BaseMixer):
|
||||
and self._last_update is not None
|
||||
and (int(time.time() - self._last_update) < self.CACHE_TTL))
|
||||
|
||||
def _get_volume(self):
|
||||
def get_volume(self):
|
||||
if not self._valid_cache():
|
||||
try:
|
||||
self._cache = int(Popen(
|
||||
@ -40,7 +40,7 @@ class OsaMixer(ThreadingActor, BaseMixer):
|
||||
self._last_update = int(time.time())
|
||||
return self._cache
|
||||
|
||||
def _set_volume(self, volume):
|
||||
def set_volume(self, volume):
|
||||
Popen(['osascript', '-e', 'set volume output volume %d' % volume])
|
||||
self._cache = volume
|
||||
self._last_update = int(time.time())
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
from mopidy.frontends.mpd import translator
|
||||
from collections import namedtuple
|
||||
|
||||
class ImmutableObject(object):
|
||||
"""
|
||||
@ -129,6 +129,9 @@ class Album(ImmutableObject):
|
||||
super(Album, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
CpTrack = namedtuple('CpTrack', ['cpid', 'track'])
|
||||
|
||||
|
||||
class Track(ImmutableObject):
|
||||
"""
|
||||
:param uri: track URI
|
||||
@ -183,6 +186,7 @@ class Track(ImmutableObject):
|
||||
super(Track, self).__init__(*args, **kwargs)
|
||||
|
||||
def mpd_format(self, *args, **kwargs):
|
||||
from mopidy.frontends.mpd import translator
|
||||
return translator.track_to_mpd_format(self, *args, **kwargs)
|
||||
|
||||
|
||||
@ -222,4 +226,5 @@ class Playlist(ImmutableObject):
|
||||
return len(self.tracks)
|
||||
|
||||
def mpd_format(self, *args, **kwargs):
|
||||
from mopidy.frontends.mpd import translator
|
||||
return translator.playlist_to_mpd_format(self, *args, **kwargs)
|
||||
|
||||
@ -4,6 +4,8 @@ import threading
|
||||
import gobject
|
||||
gobject.threads_init()
|
||||
|
||||
from pykka import ActorDeadError
|
||||
|
||||
from mopidy import SettingsError
|
||||
|
||||
logger = logging.getLogger('mopidy.utils.process')
|
||||
@ -21,26 +23,19 @@ class BaseThread(threading.Thread):
|
||||
self.run_inside_try()
|
||||
except KeyboardInterrupt:
|
||||
logger.info(u'Interrupted by user')
|
||||
self.exit(0, u'Interrupted by user')
|
||||
except SettingsError as e:
|
||||
logger.error(e.message)
|
||||
self.exit(1, u'Settings error')
|
||||
except ImportError as e:
|
||||
logger.error(e)
|
||||
self.exit(2, u'Import error')
|
||||
except ActorDeadError as e:
|
||||
logger.warning(e)
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
self.exit(3, u'Unknown error')
|
||||
logger.debug(u'%s: Exiting thread', self.name)
|
||||
|
||||
def run_inside_try(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def destroy(self):
|
||||
pass
|
||||
|
||||
def exit(self, status=0, reason=None):
|
||||
self.destroy()
|
||||
|
||||
|
||||
class GObjectEventThread(BaseThread):
|
||||
"""
|
||||
|
||||
@ -23,14 +23,14 @@ class CurrentPlaylistControllerTest(object):
|
||||
cp_track = self.controller.add(track)
|
||||
self.assertEqual(track, self.controller.tracks[-1])
|
||||
self.assertEqual(cp_track, self.controller.cp_tracks[-1])
|
||||
self.assertEqual(track, cp_track[1])
|
||||
self.assertEqual(track, cp_track.track)
|
||||
|
||||
def test_add_at_position(self):
|
||||
for track in self.tracks[:-1]:
|
||||
cp_track = self.controller.add(track, 0)
|
||||
self.assertEqual(track, self.controller.tracks[0])
|
||||
self.assertEqual(cp_track, self.controller.cp_tracks[0])
|
||||
self.assertEqual(track, cp_track[1])
|
||||
self.assertEqual(track, cp_track.track)
|
||||
|
||||
@populate_playlist
|
||||
def test_add_at_position_outside_of_playlist(self):
|
||||
@ -40,12 +40,12 @@ class CurrentPlaylistControllerTest(object):
|
||||
@populate_playlist
|
||||
def test_get_by_cpid(self):
|
||||
cp_track = self.controller.cp_tracks[1]
|
||||
self.assertEqual(cp_track, self.controller.get(cpid=cp_track[0]))
|
||||
self.assertEqual(cp_track, self.controller.get(cpid=cp_track.cpid))
|
||||
|
||||
@populate_playlist
|
||||
def test_get_by_uri(self):
|
||||
cp_track = self.controller.cp_tracks[1]
|
||||
self.assertEqual(cp_track, self.controller.get(uri=cp_track[1].uri))
|
||||
self.assertEqual(cp_track, self.controller.get(uri=cp_track.track.uri))
|
||||
|
||||
@populate_playlist
|
||||
def test_get_by_uri_raises_error_for_invalid_uri(self):
|
||||
|
||||
@ -1,29 +1,29 @@
|
||||
import unittest
|
||||
|
||||
from mopidy.backends.dummy import DummyBackend
|
||||
from mopidy.frontends.mpd import dispatcher
|
||||
from mopidy.frontends.mpd.dispatcher import MpdDispatcher
|
||||
from mopidy.mixers.dummy import DummyMixer
|
||||
|
||||
class AudioOutputHandlerTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.b = DummyBackend.start().proxy()
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.h = dispatcher.MpdDispatcher()
|
||||
self.dispatcher = MpdDispatcher()
|
||||
|
||||
def tearDown(self):
|
||||
self.b.stop().get()
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
|
||||
def test_enableoutput(self):
|
||||
result = self.h.handle_request(u'enableoutput "0"')
|
||||
result = self.dispatcher.handle_request(u'enableoutput "0"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_disableoutput(self):
|
||||
result = self.h.handle_request(u'disableoutput "0"')
|
||||
result = self.dispatcher.handle_request(u'disableoutput "0"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_outputs(self):
|
||||
result = self.h.handle_request(u'outputs')
|
||||
result = self.dispatcher.handle_request(u'outputs')
|
||||
self.assert_(u'outputid: 0' in result)
|
||||
self.assert_(u'outputname: None' in result)
|
||||
self.assert_(u'outputenabled: 1' in result)
|
||||
|
||||
63
tests/frontends/mpd/authentication_test.py
Normal file
63
tests/frontends/mpd/authentication_test.py
Normal file
@ -0,0 +1,63 @@
|
||||
import mock
|
||||
import unittest
|
||||
|
||||
from mopidy import settings
|
||||
from mopidy.frontends.mpd.dispatcher import MpdDispatcher
|
||||
from mopidy.frontends.mpd.session import MpdSession
|
||||
|
||||
class AuthenticationTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.session = mock.Mock(spec=MpdSession)
|
||||
self.dispatcher = MpdDispatcher(session=self.session)
|
||||
|
||||
def tearDown(self):
|
||||
settings.runtime.clear()
|
||||
|
||||
def test_authentication_with_valid_password_is_accepted(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'topsecret'
|
||||
response = self.dispatcher.handle_request(u'password "topsecret"')
|
||||
self.assertTrue(self.dispatcher.authenticated)
|
||||
self.assert_(u'OK' in response)
|
||||
|
||||
def test_authentication_with_invalid_password_is_not_accepted(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'topsecret'
|
||||
response = self.dispatcher.handle_request(u'password "secret"')
|
||||
self.assertFalse(self.dispatcher.authenticated)
|
||||
self.assert_(u'ACK [3@0] {password} incorrect password' in response)
|
||||
|
||||
def test_authentication_with_anything_when_password_check_turned_off(self):
|
||||
settings.MPD_SERVER_PASSWORD = None
|
||||
response = self.dispatcher.handle_request(u'any request at all')
|
||||
self.assertTrue(self.dispatcher.authenticated)
|
||||
self.assert_('ACK [5@0] {} unknown command "any"' in response)
|
||||
|
||||
def test_anything_when_not_authenticated_should_fail(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'topsecret'
|
||||
response = self.dispatcher.handle_request(u'any request at all')
|
||||
self.assertFalse(self.dispatcher.authenticated)
|
||||
self.assert_(
|
||||
u'ACK [4@0] {any} you don\'t have permission for "any"' in response)
|
||||
|
||||
def test_close_is_allowed_without_authentication(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'topsecret'
|
||||
response = self.dispatcher.handle_request(u'close')
|
||||
self.assertFalse(self.dispatcher.authenticated)
|
||||
self.assert_(u'OK' in response)
|
||||
|
||||
def test_commands_is_allowed_without_authentication(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'topsecret'
|
||||
response = self.dispatcher.handle_request(u'commands')
|
||||
self.assertFalse(self.dispatcher.authenticated)
|
||||
self.assert_(u'OK' in response)
|
||||
|
||||
def test_notcommands_is_allowed_without_authentication(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'topsecret'
|
||||
response = self.dispatcher.handle_request(u'notcommands')
|
||||
self.assertFalse(self.dispatcher.authenticated)
|
||||
self.assert_(u'OK' in response)
|
||||
|
||||
def test_ping_is_allowed_without_authentication(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'topsecret'
|
||||
response = self.dispatcher.handle_request(u'ping')
|
||||
self.assertFalse(self.dispatcher.authenticated)
|
||||
self.assert_(u'OK' in response)
|
||||
@ -8,55 +8,56 @@ class CommandListsTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.b = DummyBackend.start().proxy()
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.h = dispatcher.MpdDispatcher()
|
||||
self.dispatcher = dispatcher.MpdDispatcher()
|
||||
|
||||
def tearDown(self):
|
||||
self.b.stop().get()
|
||||
self.mixer.stop().get()
|
||||
|
||||
def test_command_list_begin(self):
|
||||
result = self.h.handle_request(u'command_list_begin')
|
||||
self.assert_(result is None)
|
||||
result = self.dispatcher.handle_request(u'command_list_begin')
|
||||
self.assertEquals(result, [])
|
||||
|
||||
def test_command_list_end(self):
|
||||
self.h.handle_request(u'command_list_begin')
|
||||
result = self.h.handle_request(u'command_list_end')
|
||||
self.dispatcher.handle_request(u'command_list_begin')
|
||||
result = self.dispatcher.handle_request(u'command_list_end')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_command_list_end_without_start_first_is_an_unknown_command(self):
|
||||
result = self.h.handle_request(u'command_list_end')
|
||||
result = self.dispatcher.handle_request(u'command_list_end')
|
||||
self.assertEquals(result[0],
|
||||
u'ACK [5@0] {} unknown command "command_list_end"')
|
||||
|
||||
def test_command_list_with_ping(self):
|
||||
self.h.handle_request(u'command_list_begin')
|
||||
self.assertEqual([], self.h.command_list)
|
||||
self.assertEqual(False, self.h.command_list_ok)
|
||||
self.h.handle_request(u'ping')
|
||||
self.assert_(u'ping' in self.h.command_list)
|
||||
result = self.h.handle_request(u'command_list_end')
|
||||
self.dispatcher.handle_request(u'command_list_begin')
|
||||
self.assertEqual([], self.dispatcher.command_list)
|
||||
self.assertEqual(False, self.dispatcher.command_list_ok)
|
||||
self.dispatcher.handle_request(u'ping')
|
||||
self.assert_(u'ping' in self.dispatcher.command_list)
|
||||
result = self.dispatcher.handle_request(u'command_list_end')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(False, self.h.command_list)
|
||||
self.assertEqual(False, self.dispatcher.command_list)
|
||||
|
||||
def test_command_list_with_error_returns_ack_with_correct_index(self):
|
||||
self.h.handle_request(u'command_list_begin')
|
||||
self.h.handle_request(u'play') # Known command
|
||||
self.h.handle_request(u'paly') # Unknown command
|
||||
result = self.h.handle_request(u'command_list_end')
|
||||
self.dispatcher.handle_request(u'command_list_begin')
|
||||
self.dispatcher.handle_request(u'play') # Known command
|
||||
self.dispatcher.handle_request(u'paly') # Unknown command
|
||||
result = self.dispatcher.handle_request(u'command_list_end')
|
||||
self.assertEqual(len(result), 1, result)
|
||||
self.assertEqual(result[0], u'ACK [5@1] {} unknown command "paly"')
|
||||
|
||||
def test_command_list_ok_begin(self):
|
||||
result = self.h.handle_request(u'command_list_ok_begin')
|
||||
self.assert_(result is None)
|
||||
result = self.dispatcher.handle_request(u'command_list_ok_begin')
|
||||
self.assertEquals(result, [])
|
||||
|
||||
def test_command_list_ok_with_ping(self):
|
||||
self.h.handle_request(u'command_list_ok_begin')
|
||||
self.assertEqual([], self.h.command_list)
|
||||
self.assertEqual(True, self.h.command_list_ok)
|
||||
self.h.handle_request(u'ping')
|
||||
self.assert_(u'ping' in self.h.command_list)
|
||||
result = self.h.handle_request(u'command_list_end')
|
||||
self.dispatcher.handle_request(u'command_list_ok_begin')
|
||||
self.assertEqual([], self.dispatcher.command_list)
|
||||
self.assertEqual(True, self.dispatcher.command_list_ok)
|
||||
self.dispatcher.handle_request(u'ping')
|
||||
self.assert_(u'ping' in self.dispatcher.command_list)
|
||||
result = self.dispatcher.handle_request(u'command_list_end')
|
||||
self.assert_(u'list_OK' in result)
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(False, self.h.command_list)
|
||||
self.assertEqual(False, self.h.command_list_ok)
|
||||
self.assertEqual(False, self.dispatcher.command_list)
|
||||
self.assertEqual(False, self.dispatcher.command_list_ok)
|
||||
|
||||
@ -1,48 +1,53 @@
|
||||
import mock
|
||||
import unittest
|
||||
|
||||
from mopidy import settings
|
||||
from mopidy.backends.dummy import DummyBackend
|
||||
from mopidy.frontends.mpd import dispatcher
|
||||
from mopidy.frontends.mpd.dispatcher import MpdDispatcher
|
||||
from mopidy.frontends.mpd.session import MpdSession
|
||||
from mopidy.mixers.dummy import DummyMixer
|
||||
|
||||
class ConnectionHandlerTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.b = DummyBackend.start().proxy()
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.h = dispatcher.MpdDispatcher()
|
||||
self.session = mock.Mock(spec=MpdSession)
|
||||
self.dispatcher = MpdDispatcher(session=self.session)
|
||||
|
||||
def tearDown(self):
|
||||
self.b.stop().get()
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
settings.runtime.clear()
|
||||
|
||||
def test_close(self):
|
||||
result = self.h.handle_request(u'close')
|
||||
def test_close_closes_the_client_connection(self):
|
||||
result = self.dispatcher.handle_request(u'close')
|
||||
self.assert_(self.session.close.called,
|
||||
u'Should call close() on MpdSession')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_empty_request(self):
|
||||
result = self.h.handle_request(u'')
|
||||
result = self.dispatcher.handle_request(u'')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_kill(self):
|
||||
result = self.h.handle_request(u'kill')
|
||||
self.assert_(u'OK' in result)
|
||||
result = self.dispatcher.handle_request(u'kill')
|
||||
self.assert_(u'ACK [4@0] {kill} you don\'t have permission for "kill"' in result)
|
||||
|
||||
def test_valid_password_is_accepted(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'topsecret'
|
||||
result = self.h.handle_request(u'password "topsecret"')
|
||||
result = self.dispatcher.handle_request(u'password "topsecret"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_invalid_password_is_not_accepted(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'topsecret'
|
||||
result = self.h.handle_request(u'password "secret"')
|
||||
result = self.dispatcher.handle_request(u'password "secret"')
|
||||
self.assert_(u'ACK [3@0] {password} incorrect password' in result)
|
||||
|
||||
def test_any_password_is_not_accepted_when_password_check_turned_off(self):
|
||||
settings.MPD_SERVER_PASSWORD = None
|
||||
result = self.h.handle_request(u'password "secret"')
|
||||
result = self.dispatcher.handle_request(u'password "secret"')
|
||||
self.assert_(u'ACK [3@0] {password} incorrect password' in result)
|
||||
|
||||
def test_ping(self):
|
||||
result = self.h.handle_request(u'ping')
|
||||
result = self.dispatcher.handle_request(u'ping')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
@ -1,160 +1,160 @@
|
||||
import unittest
|
||||
|
||||
from mopidy.backends.dummy import DummyBackend
|
||||
from mopidy.frontends.mpd import dispatcher
|
||||
from mopidy.frontends.mpd.dispatcher import MpdDispatcher
|
||||
from mopidy.mixers.dummy import DummyMixer
|
||||
from mopidy.models import Track
|
||||
|
||||
class CurrentPlaylistHandlerTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.b = DummyBackend.start().proxy()
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.h = dispatcher.MpdDispatcher()
|
||||
self.dispatcher = MpdDispatcher()
|
||||
|
||||
def tearDown(self):
|
||||
self.b.stop().get()
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
|
||||
def test_add(self):
|
||||
needle = Track(uri='dummy://foo')
|
||||
self.b.library.provider.dummy_library = [
|
||||
self.backend.library.provider.dummy_library = [
|
||||
Track(), Track(), needle, Track()]
|
||||
self.b.current_playlist.append(
|
||||
self.backend.current_playlist.append(
|
||||
[Track(), Track(), Track(), Track(), Track()])
|
||||
self.assertEqual(len(self.b.current_playlist.tracks.get()), 5)
|
||||
result = self.h.handle_request(u'add "dummy://foo"')
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 5)
|
||||
result = self.dispatcher.handle_request(u'add "dummy://foo"')
|
||||
self.assertEqual(len(result), 1)
|
||||
self.assertEqual(result[0], u'OK')
|
||||
self.assertEqual(len(self.b.current_playlist.tracks.get()), 6)
|
||||
self.assertEqual(self.b.current_playlist.tracks.get()[5], needle)
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 6)
|
||||
self.assertEqual(self.backend.current_playlist.tracks.get()[5], needle)
|
||||
|
||||
def test_add_with_uri_not_found_in_library_should_ack(self):
|
||||
result = self.h.handle_request(u'add "dummy://foo"')
|
||||
result = self.dispatcher.handle_request(u'add "dummy://foo"')
|
||||
self.assertEqual(result[0],
|
||||
u'ACK [50@0] {add} directory or file not found')
|
||||
|
||||
def test_add_with_empty_uri_should_add_all_known_tracks_and_ok(self):
|
||||
result = self.h.handle_request(u'add ""')
|
||||
result = self.dispatcher.handle_request(u'add ""')
|
||||
# TODO check that we add all tracks (we currently don't)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_addid_without_songpos(self):
|
||||
needle = Track(uri='dummy://foo')
|
||||
self.b.library.provider.dummy_library = [
|
||||
self.backend.library.provider.dummy_library = [
|
||||
Track(), Track(), needle, Track()]
|
||||
self.b.current_playlist.append(
|
||||
self.backend.current_playlist.append(
|
||||
[Track(), Track(), Track(), Track(), Track()])
|
||||
self.assertEqual(len(self.b.current_playlist.tracks.get()), 5)
|
||||
result = self.h.handle_request(u'addid "dummy://foo"')
|
||||
self.assertEqual(len(self.b.current_playlist.tracks.get()), 6)
|
||||
self.assertEqual(self.b.current_playlist.tracks.get()[5], needle)
|
||||
self.assert_(u'Id: %d' % self.b.current_playlist.cp_tracks.get()[5][0]
|
||||
in result)
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 5)
|
||||
result = self.dispatcher.handle_request(u'addid "dummy://foo"')
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 6)
|
||||
self.assertEqual(self.backend.current_playlist.tracks.get()[5], needle)
|
||||
self.assert_(u'Id: %d' %
|
||||
self.backend.current_playlist.cp_tracks.get()[5][0] in result)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_addid_with_empty_uri_acks(self):
|
||||
result = self.h.handle_request(u'addid ""')
|
||||
result = self.dispatcher.handle_request(u'addid ""')
|
||||
self.assertEqual(result[0], u'ACK [50@0] {addid} No such song')
|
||||
|
||||
def test_addid_with_songpos(self):
|
||||
needle = Track(uri='dummy://foo')
|
||||
self.b.library.provider.dummy_library = [
|
||||
self.backend.library.provider.dummy_library = [
|
||||
Track(), Track(), needle, Track()]
|
||||
self.b.current_playlist.append(
|
||||
self.backend.current_playlist.append(
|
||||
[Track(), Track(), Track(), Track(), Track()])
|
||||
self.assertEqual(len(self.b.current_playlist.tracks.get()), 5)
|
||||
result = self.h.handle_request(u'addid "dummy://foo" "3"')
|
||||
self.assertEqual(len(self.b.current_playlist.tracks.get()), 6)
|
||||
self.assertEqual(self.b.current_playlist.tracks.get()[3], needle)
|
||||
self.assert_(u'Id: %d' % self.b.current_playlist.cp_tracks.get()[3][0]
|
||||
in result)
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 5)
|
||||
result = self.dispatcher.handle_request(u'addid "dummy://foo" "3"')
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 6)
|
||||
self.assertEqual(self.backend.current_playlist.tracks.get()[3], needle)
|
||||
self.assert_(u'Id: %d' %
|
||||
self.backend.current_playlist.cp_tracks.get()[3][0] in result)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_addid_with_songpos_out_of_bounds_should_ack(self):
|
||||
needle = Track(uri='dummy://foo')
|
||||
self.b.library.provider.dummy_library = [
|
||||
self.backend.library.provider.dummy_library = [
|
||||
Track(), Track(), needle, Track()]
|
||||
self.b.current_playlist.append(
|
||||
self.backend.current_playlist.append(
|
||||
[Track(), Track(), Track(), Track(), Track()])
|
||||
self.assertEqual(len(self.b.current_playlist.tracks.get()), 5)
|
||||
result = self.h.handle_request(u'addid "dummy://foo" "6"')
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 5)
|
||||
result = self.dispatcher.handle_request(u'addid "dummy://foo" "6"')
|
||||
self.assertEqual(result[0], u'ACK [2@0] {addid} Bad song index')
|
||||
|
||||
def test_addid_with_uri_not_found_in_library_should_ack(self):
|
||||
result = self.h.handle_request(u'addid "dummy://foo"')
|
||||
result = self.dispatcher.handle_request(u'addid "dummy://foo"')
|
||||
self.assertEqual(result[0], u'ACK [50@0] {addid} No such song')
|
||||
|
||||
def test_clear(self):
|
||||
self.b.current_playlist.append(
|
||||
self.backend.current_playlist.append(
|
||||
[Track(), Track(), Track(), Track(), Track()])
|
||||
self.assertEqual(len(self.b.current_playlist.tracks.get()), 5)
|
||||
result = self.h.handle_request(u'clear')
|
||||
self.assertEqual(len(self.b.current_playlist.tracks.get()), 0)
|
||||
self.assertEqual(self.b.playback.current_track.get(), None)
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 5)
|
||||
result = self.dispatcher.handle_request(u'clear')
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 0)
|
||||
self.assertEqual(self.backend.playback.current_track.get(), None)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_delete_songpos(self):
|
||||
self.b.current_playlist.append(
|
||||
self.backend.current_playlist.append(
|
||||
[Track(), Track(), Track(), Track(), Track()])
|
||||
self.assertEqual(len(self.b.current_playlist.tracks.get()), 5)
|
||||
result = self.h.handle_request(u'delete "%d"' %
|
||||
self.b.current_playlist.cp_tracks.get()[2][0])
|
||||
self.assertEqual(len(self.b.current_playlist.tracks.get()), 4)
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 5)
|
||||
result = self.dispatcher.handle_request(u'delete "%d"' %
|
||||
self.backend.current_playlist.cp_tracks.get()[2][0])
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 4)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_delete_songpos_out_of_bounds(self):
|
||||
self.b.current_playlist.append(
|
||||
self.backend.current_playlist.append(
|
||||
[Track(), Track(), Track(), Track(), Track()])
|
||||
self.assertEqual(len(self.b.current_playlist.tracks.get()), 5)
|
||||
result = self.h.handle_request(u'delete "5"')
|
||||
self.assertEqual(len(self.b.current_playlist.tracks.get()), 5)
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 5)
|
||||
result = self.dispatcher.handle_request(u'delete "5"')
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 5)
|
||||
self.assertEqual(result[0], u'ACK [2@0] {delete} Bad song index')
|
||||
|
||||
def test_delete_open_range(self):
|
||||
self.b.current_playlist.append(
|
||||
self.backend.current_playlist.append(
|
||||
[Track(), Track(), Track(), Track(), Track()])
|
||||
self.assertEqual(len(self.b.current_playlist.tracks.get()), 5)
|
||||
result = self.h.handle_request(u'delete "1:"')
|
||||
self.assertEqual(len(self.b.current_playlist.tracks.get()), 1)
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 5)
|
||||
result = self.dispatcher.handle_request(u'delete "1:"')
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 1)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_delete_closed_range(self):
|
||||
self.b.current_playlist.append(
|
||||
self.backend.current_playlist.append(
|
||||
[Track(), Track(), Track(), Track(), Track()])
|
||||
self.assertEqual(len(self.b.current_playlist.tracks.get()), 5)
|
||||
result = self.h.handle_request(u'delete "1:3"')
|
||||
self.assertEqual(len(self.b.current_playlist.tracks.get()), 3)
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 5)
|
||||
result = self.dispatcher.handle_request(u'delete "1:3"')
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 3)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_delete_range_out_of_bounds(self):
|
||||
self.b.current_playlist.append(
|
||||
self.backend.current_playlist.append(
|
||||
[Track(), Track(), Track(), Track(), Track()])
|
||||
self.assertEqual(len(self.b.current_playlist.tracks.get()), 5)
|
||||
result = self.h.handle_request(u'delete "5:7"')
|
||||
self.assertEqual(len(self.b.current_playlist.tracks.get()), 5)
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 5)
|
||||
result = self.dispatcher.handle_request(u'delete "5:7"')
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 5)
|
||||
self.assertEqual(result[0], u'ACK [2@0] {delete} Bad song index')
|
||||
|
||||
def test_deleteid(self):
|
||||
self.b.current_playlist.append([Track(), Track()])
|
||||
self.assertEqual(len(self.b.current_playlist.tracks.get()), 2)
|
||||
result = self.h.handle_request(u'deleteid "1"')
|
||||
self.assertEqual(len(self.b.current_playlist.tracks.get()), 1)
|
||||
self.backend.current_playlist.append([Track(), Track()])
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 2)
|
||||
result = self.dispatcher.handle_request(u'deleteid "1"')
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 1)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_deleteid_does_not_exist(self):
|
||||
self.b.current_playlist.append([Track(), Track()])
|
||||
self.assertEqual(len(self.b.current_playlist.tracks.get()), 2)
|
||||
result = self.h.handle_request(u'deleteid "12345"')
|
||||
self.assertEqual(len(self.b.current_playlist.tracks.get()), 2)
|
||||
self.backend.current_playlist.append([Track(), Track()])
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 2)
|
||||
result = self.dispatcher.handle_request(u'deleteid "12345"')
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 2)
|
||||
self.assertEqual(result[0], u'ACK [50@0] {deleteid} No such song')
|
||||
|
||||
def test_move_songpos(self):
|
||||
self.b.current_playlist.append([
|
||||
self.backend.current_playlist.append([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
result = self.h.handle_request(u'move "1" "0"')
|
||||
tracks = self.b.current_playlist.tracks.get()
|
||||
result = self.dispatcher.handle_request(u'move "1" "0"')
|
||||
tracks = self.backend.current_playlist.tracks.get()
|
||||
self.assertEqual(tracks[0].name, 'b')
|
||||
self.assertEqual(tracks[1].name, 'a')
|
||||
self.assertEqual(tracks[2].name, 'c')
|
||||
@ -164,12 +164,12 @@ class CurrentPlaylistHandlerTest(unittest.TestCase):
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_move_open_range(self):
|
||||
self.b.current_playlist.append([
|
||||
self.backend.current_playlist.append([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
result = self.h.handle_request(u'move "2:" "0"')
|
||||
tracks = self.b.current_playlist.tracks.get()
|
||||
result = self.dispatcher.handle_request(u'move "2:" "0"')
|
||||
tracks = self.backend.current_playlist.tracks.get()
|
||||
self.assertEqual(tracks[0].name, 'c')
|
||||
self.assertEqual(tracks[1].name, 'd')
|
||||
self.assertEqual(tracks[2].name, 'e')
|
||||
@ -179,12 +179,12 @@ class CurrentPlaylistHandlerTest(unittest.TestCase):
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_move_closed_range(self):
|
||||
self.b.current_playlist.append([
|
||||
self.backend.current_playlist.append([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
result = self.h.handle_request(u'move "1:3" "0"')
|
||||
tracks = self.b.current_playlist.tracks.get()
|
||||
result = self.dispatcher.handle_request(u'move "1:3" "0"')
|
||||
tracks = self.backend.current_playlist.tracks.get()
|
||||
self.assertEqual(tracks[0].name, 'b')
|
||||
self.assertEqual(tracks[1].name, 'c')
|
||||
self.assertEqual(tracks[2].name, 'a')
|
||||
@ -194,12 +194,12 @@ class CurrentPlaylistHandlerTest(unittest.TestCase):
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_moveid(self):
|
||||
self.b.current_playlist.append([
|
||||
self.backend.current_playlist.append([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
result = self.h.handle_request(u'moveid "4" "2"')
|
||||
tracks = self.b.current_playlist.tracks.get()
|
||||
result = self.dispatcher.handle_request(u'moveid "4" "2"')
|
||||
tracks = self.backend.current_playlist.tracks.get()
|
||||
self.assertEqual(tracks[0].name, 'a')
|
||||
self.assertEqual(tracks[1].name, 'b')
|
||||
self.assertEqual(tracks[2].name, 'e')
|
||||
@ -209,30 +209,30 @@ class CurrentPlaylistHandlerTest(unittest.TestCase):
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_playlist_returns_same_as_playlistinfo(self):
|
||||
playlist_result = self.h.handle_request(u'playlist')
|
||||
playlistinfo_result = self.h.handle_request(u'playlistinfo')
|
||||
playlist_result = self.dispatcher.handle_request(u'playlist')
|
||||
playlistinfo_result = self.dispatcher.handle_request(u'playlistinfo')
|
||||
self.assertEqual(playlist_result, playlistinfo_result)
|
||||
|
||||
def test_playlistfind(self):
|
||||
result = self.h.handle_request(u'playlistfind "tag" "needle"')
|
||||
result = self.dispatcher.handle_request(u'playlistfind "tag" "needle"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_playlistfind_by_filename_not_in_current_playlist(self):
|
||||
result = self.h.handle_request(
|
||||
result = self.dispatcher.handle_request(
|
||||
u'playlistfind "filename" "file:///dev/null"')
|
||||
self.assertEqual(len(result), 1)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_playlistfind_by_filename_without_quotes(self):
|
||||
result = self.h.handle_request(
|
||||
result = self.dispatcher.handle_request(
|
||||
u'playlistfind filename "file:///dev/null"')
|
||||
self.assertEqual(len(result), 1)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_playlistfind_by_filename_in_current_playlist(self):
|
||||
self.b.current_playlist.append([
|
||||
self.backend.current_playlist.append([
|
||||
Track(uri='file:///exists')])
|
||||
result = self.h.handle_request(
|
||||
result = self.dispatcher.handle_request(
|
||||
u'playlistfind filename "file:///exists"')
|
||||
self.assert_(u'file: file:///exists' in result)
|
||||
self.assert_(u'Id: 0' in result)
|
||||
@ -240,15 +240,15 @@ class CurrentPlaylistHandlerTest(unittest.TestCase):
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_playlistid_without_songid(self):
|
||||
self.b.current_playlist.append([Track(name='a'), Track(name='b')])
|
||||
result = self.h.handle_request(u'playlistid')
|
||||
self.backend.current_playlist.append([Track(name='a'), Track(name='b')])
|
||||
result = self.dispatcher.handle_request(u'playlistid')
|
||||
self.assert_(u'Title: a' in result)
|
||||
self.assert_(u'Title: b' in result)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_playlistid_with_songid(self):
|
||||
self.b.current_playlist.append([Track(name='a'), Track(name='b')])
|
||||
result = self.h.handle_request(u'playlistid "1"')
|
||||
self.backend.current_playlist.append([Track(name='a'), Track(name='b')])
|
||||
result = self.dispatcher.handle_request(u'playlistid "1"')
|
||||
self.assert_(u'Title: a' not in result)
|
||||
self.assert_(u'Id: 0' not in result)
|
||||
self.assert_(u'Title: b' in result)
|
||||
@ -256,16 +256,16 @@ class CurrentPlaylistHandlerTest(unittest.TestCase):
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_playlistid_with_not_existing_songid_fails(self):
|
||||
self.b.current_playlist.append([Track(name='a'), Track(name='b')])
|
||||
result = self.h.handle_request(u'playlistid "25"')
|
||||
self.backend.current_playlist.append([Track(name='a'), Track(name='b')])
|
||||
result = self.dispatcher.handle_request(u'playlistid "25"')
|
||||
self.assertEqual(result[0], u'ACK [50@0] {playlistid} No such song')
|
||||
|
||||
def test_playlistinfo_without_songpos_or_range(self):
|
||||
self.b.current_playlist.append([
|
||||
self.backend.current_playlist.append([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
result = self.h.handle_request(u'playlistinfo')
|
||||
result = self.dispatcher.handle_request(u'playlistinfo')
|
||||
self.assert_(u'Title: a' in result)
|
||||
self.assert_(u'Title: b' in result)
|
||||
self.assert_(u'Title: c' in result)
|
||||
@ -275,11 +275,11 @@ class CurrentPlaylistHandlerTest(unittest.TestCase):
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_playlistinfo_with_songpos(self):
|
||||
self.b.current_playlist.append([
|
||||
self.backend.current_playlist.append([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
result = self.h.handle_request(u'playlistinfo "4"')
|
||||
result = self.dispatcher.handle_request(u'playlistinfo "4"')
|
||||
self.assert_(u'Title: a' not in result)
|
||||
self.assert_(u'Title: b' not in result)
|
||||
self.assert_(u'Title: c' not in result)
|
||||
@ -289,16 +289,16 @@ class CurrentPlaylistHandlerTest(unittest.TestCase):
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_playlistinfo_with_negative_songpos_same_as_playlistinfo(self):
|
||||
result1 = self.h.handle_request(u'playlistinfo "-1"')
|
||||
result2 = self.h.handle_request(u'playlistinfo')
|
||||
result1 = self.dispatcher.handle_request(u'playlistinfo "-1"')
|
||||
result2 = self.dispatcher.handle_request(u'playlistinfo')
|
||||
self.assertEqual(result1, result2)
|
||||
|
||||
def test_playlistinfo_with_open_range(self):
|
||||
self.b.current_playlist.append([
|
||||
self.backend.current_playlist.append([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
result = self.h.handle_request(u'playlistinfo "2:"')
|
||||
result = self.dispatcher.handle_request(u'playlistinfo "2:"')
|
||||
self.assert_(u'Title: a' not in result)
|
||||
self.assert_(u'Title: b' not in result)
|
||||
self.assert_(u'Title: c' in result)
|
||||
@ -308,11 +308,11 @@ class CurrentPlaylistHandlerTest(unittest.TestCase):
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_playlistinfo_with_closed_range(self):
|
||||
self.b.current_playlist.append([
|
||||
self.backend.current_playlist.append([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
result = self.h.handle_request(u'playlistinfo "2:4"')
|
||||
result = self.dispatcher.handle_request(u'playlistinfo "2:4"')
|
||||
self.assert_(u'Title: a' not in result)
|
||||
self.assert_(u'Title: b' not in result)
|
||||
self.assert_(u'Title: c' in result)
|
||||
@ -322,52 +322,53 @@ class CurrentPlaylistHandlerTest(unittest.TestCase):
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_playlistinfo_with_too_high_start_of_range_returns_arg_error(self):
|
||||
result = self.h.handle_request(u'playlistinfo "10:20"')
|
||||
result = self.dispatcher.handle_request(u'playlistinfo "10:20"')
|
||||
self.assert_(u'ACK [2@0] {playlistinfo} Bad song index' in result)
|
||||
|
||||
def test_playlistinfo_with_too_high_end_of_range_returns_ok(self):
|
||||
result = self.h.handle_request(u'playlistinfo "0:20"')
|
||||
result = self.dispatcher.handle_request(u'playlistinfo "0:20"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_playlistsearch(self):
|
||||
result = self.h.handle_request(u'playlistsearch "any" "needle"')
|
||||
result = self.dispatcher.handle_request(
|
||||
u'playlistsearch "any" "needle"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_playlistsearch_without_quotes(self):
|
||||
result = self.h.handle_request(u'playlistsearch any "needle"')
|
||||
result = self.dispatcher.handle_request(u'playlistsearch any "needle"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_plchanges(self):
|
||||
self.b.current_playlist.append(
|
||||
self.backend.current_playlist.append(
|
||||
[Track(name='a'), Track(name='b'), Track(name='c')])
|
||||
result = self.h.handle_request(u'plchanges "0"')
|
||||
result = self.dispatcher.handle_request(u'plchanges "0"')
|
||||
self.assert_(u'Title: a' in result)
|
||||
self.assert_(u'Title: b' in result)
|
||||
self.assert_(u'Title: c' in result)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_plchanges_with_minus_one_returns_entire_playlist(self):
|
||||
self.b.current_playlist.append(
|
||||
self.backend.current_playlist.append(
|
||||
[Track(name='a'), Track(name='b'), Track(name='c')])
|
||||
result = self.h.handle_request(u'plchanges "-1"')
|
||||
result = self.dispatcher.handle_request(u'plchanges "-1"')
|
||||
self.assert_(u'Title: a' in result)
|
||||
self.assert_(u'Title: b' in result)
|
||||
self.assert_(u'Title: c' in result)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_plchanges_without_quotes_works(self):
|
||||
self.b.current_playlist.append(
|
||||
self.backend.current_playlist.append(
|
||||
[Track(name='a'), Track(name='b'), Track(name='c')])
|
||||
result = self.h.handle_request(u'plchanges 0')
|
||||
result = self.dispatcher.handle_request(u'plchanges 0')
|
||||
self.assert_(u'Title: a' in result)
|
||||
self.assert_(u'Title: b' in result)
|
||||
self.assert_(u'Title: c' in result)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_plchangesposid(self):
|
||||
self.b.current_playlist.append([Track(), Track(), Track()])
|
||||
result = self.h.handle_request(u'plchangesposid "0"')
|
||||
cp_tracks = self.b.current_playlist.cp_tracks.get()
|
||||
self.backend.current_playlist.append([Track(), Track(), Track()])
|
||||
result = self.dispatcher.handle_request(u'plchangesposid "0"')
|
||||
cp_tracks = self.backend.current_playlist.cp_tracks.get()
|
||||
self.assert_(u'cpos: 0' in result)
|
||||
self.assert_(u'Id: %d' % cp_tracks[0][0]
|
||||
in result)
|
||||
@ -380,24 +381,24 @@ class CurrentPlaylistHandlerTest(unittest.TestCase):
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_shuffle_without_range(self):
|
||||
self.b.current_playlist.append([
|
||||
self.backend.current_playlist.append([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
version = self.b.current_playlist.version.get()
|
||||
result = self.h.handle_request(u'shuffle')
|
||||
self.assert_(version < self.b.current_playlist.version.get())
|
||||
version = self.backend.current_playlist.version.get()
|
||||
result = self.dispatcher.handle_request(u'shuffle')
|
||||
self.assert_(version < self.backend.current_playlist.version.get())
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_shuffle_with_open_range(self):
|
||||
self.b.current_playlist.append([
|
||||
self.backend.current_playlist.append([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
version = self.b.current_playlist.version.get()
|
||||
result = self.h.handle_request(u'shuffle "4:"')
|
||||
self.assert_(version < self.b.current_playlist.version.get())
|
||||
tracks = self.b.current_playlist.tracks.get()
|
||||
version = self.backend.current_playlist.version.get()
|
||||
result = self.dispatcher.handle_request(u'shuffle "4:"')
|
||||
self.assert_(version < self.backend.current_playlist.version.get())
|
||||
tracks = self.backend.current_playlist.tracks.get()
|
||||
self.assertEqual(tracks[0].name, 'a')
|
||||
self.assertEqual(tracks[1].name, 'b')
|
||||
self.assertEqual(tracks[2].name, 'c')
|
||||
@ -405,14 +406,14 @@ class CurrentPlaylistHandlerTest(unittest.TestCase):
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_shuffle_with_closed_range(self):
|
||||
self.b.current_playlist.append([
|
||||
self.backend.current_playlist.append([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
version = self.b.current_playlist.version.get()
|
||||
result = self.h.handle_request(u'shuffle "1:3"')
|
||||
self.assert_(version < self.b.current_playlist.version.get())
|
||||
tracks = self.b.current_playlist.tracks.get()
|
||||
version = self.backend.current_playlist.version.get()
|
||||
result = self.dispatcher.handle_request(u'shuffle "1:3"')
|
||||
self.assert_(version < self.backend.current_playlist.version.get())
|
||||
tracks = self.backend.current_playlist.tracks.get()
|
||||
self.assertEqual(tracks[0].name, 'a')
|
||||
self.assertEqual(tracks[3].name, 'd')
|
||||
self.assertEqual(tracks[4].name, 'e')
|
||||
@ -420,12 +421,12 @@ class CurrentPlaylistHandlerTest(unittest.TestCase):
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_swap(self):
|
||||
self.b.current_playlist.append([
|
||||
self.backend.current_playlist.append([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
result = self.h.handle_request(u'swap "1" "4"')
|
||||
tracks = self.b.current_playlist.tracks.get()
|
||||
result = self.dispatcher.handle_request(u'swap "1" "4"')
|
||||
tracks = self.backend.current_playlist.tracks.get()
|
||||
self.assertEqual(tracks[0].name, 'a')
|
||||
self.assertEqual(tracks[1].name, 'e')
|
||||
self.assertEqual(tracks[2].name, 'c')
|
||||
@ -435,12 +436,12 @@ class CurrentPlaylistHandlerTest(unittest.TestCase):
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_swapid(self):
|
||||
self.b.current_playlist.append([
|
||||
self.backend.current_playlist.append([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
result = self.h.handle_request(u'swapid "1" "4"')
|
||||
tracks = self.b.current_playlist.tracks.get()
|
||||
result = self.dispatcher.handle_request(u'swapid "1" "4"')
|
||||
tracks = self.backend.current_playlist.tracks.get()
|
||||
self.assertEqual(tracks[0].name, 'a')
|
||||
self.assertEqual(tracks[1].name, 'e')
|
||||
self.assertEqual(tracks[2].name, 'c')
|
||||
|
||||
@ -1,33 +1,33 @@
|
||||
import unittest
|
||||
|
||||
from mopidy.backends.dummy import DummyBackend
|
||||
from mopidy.frontends.mpd import dispatcher
|
||||
from mopidy.frontends.mpd.dispatcher import MpdDispatcher
|
||||
from mopidy.frontends.mpd.exceptions import MpdAckError
|
||||
from mopidy.frontends.mpd.protocol import request_handlers, handle_pattern
|
||||
from mopidy.frontends.mpd.protocol import request_handlers, handle_request
|
||||
from mopidy.mixers.dummy import DummyMixer
|
||||
|
||||
class MpdDispatcherTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.b = DummyBackend.start().proxy()
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.h = dispatcher.MpdDispatcher()
|
||||
self.dispatcher = MpdDispatcher()
|
||||
|
||||
def tearDown(self):
|
||||
self.b.stop().get()
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
|
||||
def test_register_same_pattern_twice_fails(self):
|
||||
func = lambda: None
|
||||
try:
|
||||
handle_pattern('a pattern')(func)
|
||||
handle_pattern('a pattern')(func)
|
||||
handle_request('a pattern')(func)
|
||||
handle_request('a pattern')(func)
|
||||
self.fail('Registering a pattern twice shoulde raise ValueError')
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
def test_finding_handler_for_unknown_command_raises_exception(self):
|
||||
try:
|
||||
self.h.find_handler('an_unknown_command with args')
|
||||
self.dispatcher._find_handler('an_unknown_command with args')
|
||||
self.fail('Should raise exception')
|
||||
except MpdAckError as e:
|
||||
self.assertEqual(e.get_mpd_ack(),
|
||||
@ -37,18 +37,18 @@ class MpdDispatcherTest(unittest.TestCase):
|
||||
expected_handler = lambda x: None
|
||||
request_handlers['known_command (?P<arg1>.+)'] = \
|
||||
expected_handler
|
||||
(handler, kwargs) = self.h.find_handler('known_command an_arg')
|
||||
(handler, kwargs) = self.dispatcher._find_handler('known_command an_arg')
|
||||
self.assertEqual(handler, expected_handler)
|
||||
self.assert_('arg1' in kwargs)
|
||||
self.assertEqual(kwargs['arg1'], 'an_arg')
|
||||
|
||||
def test_handling_unknown_request_yields_error(self):
|
||||
result = self.h.handle_request('an unhandled request')
|
||||
result = self.dispatcher.handle_request('an unhandled request')
|
||||
self.assertEqual(result[0], u'ACK [5@0] {} unknown command "an"')
|
||||
|
||||
def test_handling_known_request(self):
|
||||
expected = 'magic'
|
||||
request_handlers['known request'] = lambda x: expected
|
||||
result = self.h.handle_request('known request')
|
||||
result = self.dispatcher.handle_request('known request')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assert_(expected in result)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import unittest
|
||||
|
||||
from mopidy.frontends.mpd.exceptions import (MpdAckError, MpdUnknownCommand,
|
||||
MpdNotImplemented)
|
||||
from mopidy.frontends.mpd.exceptions import (MpdAckError, MpdPermissionError,
|
||||
MpdUnknownCommand, MpdSystemError, MpdNotImplemented)
|
||||
|
||||
class MpdExceptionsTest(unittest.TestCase):
|
||||
def test_key_error_wrapped_in_mpd_ack_error(self):
|
||||
@ -25,10 +25,9 @@ class MpdExceptionsTest(unittest.TestCase):
|
||||
|
||||
def test_get_mpd_ack_with_values(self):
|
||||
try:
|
||||
raise MpdAckError('A description', error_code=6, index=7,
|
||||
command='foo')
|
||||
raise MpdAckError('A description', index=7, command='foo')
|
||||
except MpdAckError as e:
|
||||
self.assertEqual(e.get_mpd_ack(), u'ACK [6@7] {foo} A description')
|
||||
self.assertEqual(e.get_mpd_ack(), u'ACK [0@7] {foo} A description')
|
||||
|
||||
def test_mpd_unknown_command(self):
|
||||
try:
|
||||
@ -36,3 +35,17 @@ class MpdExceptionsTest(unittest.TestCase):
|
||||
except MpdAckError as e:
|
||||
self.assertEqual(e.get_mpd_ack(),
|
||||
u'ACK [5@0] {} unknown command "play"')
|
||||
|
||||
def test_mpd_system_error(self):
|
||||
try:
|
||||
raise MpdSystemError('foo')
|
||||
except MpdSystemError as e:
|
||||
self.assertEqual(e.get_mpd_ack(),
|
||||
u'ACK [52@0] {} foo')
|
||||
|
||||
def test_mpd_permission_error(self):
|
||||
try:
|
||||
raise MpdPermissionError(command='foo')
|
||||
except MpdPermissionError as e:
|
||||
self.assertEqual(e.get_mpd_ack(),
|
||||
u'ACK [4@0] {foo} you don\'t have permission for "foo"')
|
||||
|
||||
@ -1,390 +1,412 @@
|
||||
import unittest
|
||||
|
||||
from mopidy.backends.dummy import DummyBackend
|
||||
from mopidy.frontends.mpd import dispatcher
|
||||
from mopidy.frontends.mpd.dispatcher import MpdDispatcher
|
||||
from mopidy.mixers.dummy import DummyMixer
|
||||
|
||||
class MusicDatabaseHandlerTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.b = DummyBackend.start().proxy()
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.h = dispatcher.MpdDispatcher()
|
||||
self.dispatcher = MpdDispatcher()
|
||||
|
||||
def tearDown(self):
|
||||
self.b.stop().get()
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
|
||||
def test_count(self):
|
||||
result = self.h.handle_request(u'count "tag" "needle"')
|
||||
result = self.dispatcher.handle_request(u'count "tag" "needle"')
|
||||
self.assert_(u'songs: 0' in result)
|
||||
self.assert_(u'playtime: 0' in result)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_findadd(self):
|
||||
result = self.h.handle_request(u'findadd "album" "what"')
|
||||
result = self.dispatcher.handle_request(u'findadd "album" "what"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_listall(self):
|
||||
result = self.h.handle_request(u'listall "file:///dev/urandom"')
|
||||
result = self.dispatcher.handle_request(
|
||||
u'listall "file:///dev/urandom"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_listallinfo(self):
|
||||
result = self.h.handle_request(u'listallinfo "file:///dev/urandom"')
|
||||
result = self.dispatcher.handle_request(
|
||||
u'listallinfo "file:///dev/urandom"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_lsinfo_without_path_returns_same_as_listplaylists(self):
|
||||
lsinfo_result = self.h.handle_request(u'lsinfo')
|
||||
listplaylists_result = self.h.handle_request(u'listplaylists')
|
||||
lsinfo_result = self.dispatcher.handle_request(u'lsinfo')
|
||||
listplaylists_result = self.dispatcher.handle_request(u'listplaylists')
|
||||
self.assertEqual(lsinfo_result, listplaylists_result)
|
||||
|
||||
def test_lsinfo_with_empty_path_returns_same_as_listplaylists(self):
|
||||
lsinfo_result = self.h.handle_request(u'lsinfo ""')
|
||||
listplaylists_result = self.h.handle_request(u'listplaylists')
|
||||
lsinfo_result = self.dispatcher.handle_request(u'lsinfo ""')
|
||||
listplaylists_result = self.dispatcher.handle_request(u'listplaylists')
|
||||
self.assertEqual(lsinfo_result, listplaylists_result)
|
||||
|
||||
def test_lsinfo_for_root_returns_same_as_listplaylists(self):
|
||||
lsinfo_result = self.h.handle_request(u'lsinfo "/"')
|
||||
listplaylists_result = self.h.handle_request(u'listplaylists')
|
||||
lsinfo_result = self.dispatcher.handle_request(u'lsinfo "/"')
|
||||
listplaylists_result = self.dispatcher.handle_request(u'listplaylists')
|
||||
self.assertEqual(lsinfo_result, listplaylists_result)
|
||||
|
||||
def test_update_without_uri(self):
|
||||
result = self.h.handle_request(u'update')
|
||||
result = self.dispatcher.handle_request(u'update')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assert_(u'updating_db: 0' in result)
|
||||
|
||||
def test_update_with_uri(self):
|
||||
result = self.h.handle_request(u'update "file:///dev/urandom"')
|
||||
result = self.dispatcher.handle_request(u'update "file:///dev/urandom"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assert_(u'updating_db: 0' in result)
|
||||
|
||||
def test_rescan_without_uri(self):
|
||||
result = self.h.handle_request(u'rescan')
|
||||
result = self.dispatcher.handle_request(u'rescan')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assert_(u'updating_db: 0' in result)
|
||||
|
||||
def test_rescan_with_uri(self):
|
||||
result = self.h.handle_request(u'rescan "file:///dev/urandom"')
|
||||
result = self.dispatcher.handle_request(u'rescan "file:///dev/urandom"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assert_(u'updating_db: 0' in result)
|
||||
|
||||
|
||||
class MusicDatabaseFindTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.b = DummyBackend.start().proxy()
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.h = dispatcher.MpdDispatcher()
|
||||
self.dispatcher = MpdDispatcher()
|
||||
|
||||
def tearDown(self):
|
||||
self.b.stop().get()
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
|
||||
def test_find_album(self):
|
||||
result = self.h.handle_request(u'find "album" "what"')
|
||||
result = self.dispatcher.handle_request(u'find "album" "what"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_find_album_without_quotes(self):
|
||||
result = self.h.handle_request(u'find album "what"')
|
||||
result = self.dispatcher.handle_request(u'find album "what"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_find_artist(self):
|
||||
result = self.h.handle_request(u'find "artist" "what"')
|
||||
result = self.dispatcher.handle_request(u'find "artist" "what"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_find_artist_without_quotes(self):
|
||||
result = self.h.handle_request(u'find artist "what"')
|
||||
result = self.dispatcher.handle_request(u'find artist "what"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_find_title(self):
|
||||
result = self.h.handle_request(u'find "title" "what"')
|
||||
result = self.dispatcher.handle_request(u'find "title" "what"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_find_title_without_quotes(self):
|
||||
result = self.h.handle_request(u'find title "what"')
|
||||
result = self.dispatcher.handle_request(u'find title "what"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_find_date(self):
|
||||
result = self.h.handle_request(u'find "date" "2002-01-01"')
|
||||
result = self.dispatcher.handle_request(u'find "date" "2002-01-01"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_find_date_without_quotes(self):
|
||||
result = self.h.handle_request(u'find date "2002-01-01"')
|
||||
result = self.dispatcher.handle_request(u'find date "2002-01-01"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_find_date_with_capital_d_and_incomplete_date(self):
|
||||
result = self.h.handle_request(u'find Date "2005"')
|
||||
result = self.dispatcher.handle_request(u'find Date "2005"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_find_else_should_fail(self):
|
||||
|
||||
result = self.h.handle_request(u'find "somethingelse" "what"')
|
||||
result = self.dispatcher.handle_request(u'find "somethingelse" "what"')
|
||||
self.assertEqual(result[0], u'ACK [2@0] {find} incorrect arguments')
|
||||
|
||||
def test_find_album_and_artist(self):
|
||||
result = self.h.handle_request(
|
||||
result = self.dispatcher.handle_request(
|
||||
u'find album "album_what" artist "artist_what"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
|
||||
class MusicDatabaseListTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.b = DummyBackend.start().proxy()
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.h = dispatcher.MpdDispatcher()
|
||||
self.dispatcher = MpdDispatcher()
|
||||
|
||||
def tearDown(self):
|
||||
self.b.stop().get()
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
|
||||
def test_list_foo_returns_ack(self):
|
||||
result = self.h.handle_request(u'list "foo"')
|
||||
result = self.dispatcher.handle_request(u'list "foo"')
|
||||
self.assertEqual(result[0],
|
||||
u'ACK [2@0] {list} incorrect arguments')
|
||||
|
||||
### Artist
|
||||
|
||||
def test_list_artist_with_quotes(self):
|
||||
result = self.h.handle_request(u'list "artist"')
|
||||
result = self.dispatcher.handle_request(u'list "artist"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_artist_without_quotes(self):
|
||||
result = self.h.handle_request(u'list artist')
|
||||
result = self.dispatcher.handle_request(u'list artist')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_artist_without_quotes_and_capitalized(self):
|
||||
result = self.h.handle_request(u'list Artist')
|
||||
result = self.dispatcher.handle_request(u'list Artist')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_artist_with_query_of_one_token(self):
|
||||
result = self.h.handle_request(u'list "artist" "anartist"')
|
||||
result = self.dispatcher.handle_request(u'list "artist" "anartist"')
|
||||
self.assertEqual(result[0],
|
||||
u'ACK [2@0] {list} should be "Album" for 3 arguments')
|
||||
|
||||
def test_list_artist_with_unknown_field_in_query_returns_ack(self):
|
||||
result = self.h.handle_request(u'list "artist" "foo" "bar"')
|
||||
result = self.dispatcher.handle_request(u'list "artist" "foo" "bar"')
|
||||
self.assertEqual(result[0],
|
||||
u'ACK [2@0] {list} not able to parse args')
|
||||
|
||||
def test_list_artist_by_artist(self):
|
||||
result = self.h.handle_request(u'list "artist" "artist" "anartist"')
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "artist" "artist" "anartist"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_artist_by_album(self):
|
||||
result = self.h.handle_request(u'list "artist" "album" "analbum"')
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "artist" "album" "analbum"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_artist_by_full_date(self):
|
||||
result = self.h.handle_request(u'list "artist" "date" "2001-01-01"')
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "artist" "date" "2001-01-01"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_artist_by_year(self):
|
||||
result = self.h.handle_request(u'list "artist" "date" "2001"')
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "artist" "date" "2001"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_artist_by_genre(self):
|
||||
result = self.h.handle_request(u'list "artist" "genre" "agenre"')
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "artist" "genre" "agenre"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_artist_by_artist_and_album(self):
|
||||
result = self.h.handle_request(
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "artist" "artist" "anartist" "album" "analbum"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
### Album
|
||||
|
||||
def test_list_album_with_quotes(self):
|
||||
result = self.h.handle_request(u'list "album"')
|
||||
result = self.dispatcher.handle_request(u'list "album"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_album_without_quotes(self):
|
||||
result = self.h.handle_request(u'list album')
|
||||
result = self.dispatcher.handle_request(u'list album')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_album_without_quotes_and_capitalized(self):
|
||||
result = self.h.handle_request(u'list Album')
|
||||
result = self.dispatcher.handle_request(u'list Album')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_album_with_artist_name(self):
|
||||
result = self.h.handle_request(u'list "album" "anartist"')
|
||||
result = self.dispatcher.handle_request(u'list "album" "anartist"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_album_by_artist(self):
|
||||
result = self.h.handle_request(u'list "album" "artist" "anartist"')
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "album" "artist" "anartist"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_album_by_album(self):
|
||||
result = self.h.handle_request(u'list "album" "album" "analbum"')
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "album" "album" "analbum"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_album_by_full_date(self):
|
||||
result = self.h.handle_request(u'list "album" "date" "2001-01-01"')
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "album" "date" "2001-01-01"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_album_by_year(self):
|
||||
result = self.h.handle_request(u'list "album" "date" "2001"')
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "album" "date" "2001"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_album_by_genre(self):
|
||||
result = self.h.handle_request(u'list "album" "genre" "agenre"')
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "album" "genre" "agenre"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_album_by_artist_and_album(self):
|
||||
result = self.h.handle_request(
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "album" "artist" "anartist" "album" "analbum"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
### Date
|
||||
|
||||
def test_list_date_with_quotes(self):
|
||||
result = self.h.handle_request(u'list "date"')
|
||||
result = self.dispatcher.handle_request(u'list "date"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_date_without_quotes(self):
|
||||
result = self.h.handle_request(u'list date')
|
||||
result = self.dispatcher.handle_request(u'list date')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_date_without_quotes_and_capitalized(self):
|
||||
result = self.h.handle_request(u'list Date')
|
||||
result = self.dispatcher.handle_request(u'list Date')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_date_with_query_of_one_token(self):
|
||||
result = self.h.handle_request(u'list "date" "anartist"')
|
||||
result = self.dispatcher.handle_request(u'list "date" "anartist"')
|
||||
self.assertEqual(result[0],
|
||||
u'ACK [2@0] {list} should be "Album" for 3 arguments')
|
||||
|
||||
def test_list_date_by_artist(self):
|
||||
result = self.h.handle_request(u'list "date" "artist" "anartist"')
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "date" "artist" "anartist"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_date_by_album(self):
|
||||
result = self.h.handle_request(u'list "date" "album" "analbum"')
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "date" "album" "analbum"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_date_by_full_date(self):
|
||||
result = self.h.handle_request(u'list "date" "date" "2001-01-01"')
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "date" "date" "2001-01-01"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_date_by_year(self):
|
||||
result = self.h.handle_request(u'list "date" "date" "2001"')
|
||||
result = self.dispatcher.handle_request(u'list "date" "date" "2001"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_date_by_genre(self):
|
||||
result = self.h.handle_request(u'list "date" "genre" "agenre"')
|
||||
result = self.dispatcher.handle_request(u'list "date" "genre" "agenre"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_date_by_artist_and_album(self):
|
||||
result = self.h.handle_request(
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "date" "artist" "anartist" "album" "analbum"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
### Genre
|
||||
|
||||
def test_list_genre_with_quotes(self):
|
||||
result = self.h.handle_request(u'list "genre"')
|
||||
result = self.dispatcher.handle_request(u'list "genre"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_genre_without_quotes(self):
|
||||
result = self.h.handle_request(u'list genre')
|
||||
result = self.dispatcher.handle_request(u'list genre')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_genre_without_quotes_and_capitalized(self):
|
||||
result = self.h.handle_request(u'list Genre')
|
||||
result = self.dispatcher.handle_request(u'list Genre')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_genre_with_query_of_one_token(self):
|
||||
result = self.h.handle_request(u'list "genre" "anartist"')
|
||||
result = self.dispatcher.handle_request(u'list "genre" "anartist"')
|
||||
self.assertEqual(result[0],
|
||||
u'ACK [2@0] {list} should be "Album" for 3 arguments')
|
||||
|
||||
def test_list_genre_by_artist(self):
|
||||
result = self.h.handle_request(u'list "genre" "artist" "anartist"')
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "genre" "artist" "anartist"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_genre_by_album(self):
|
||||
result = self.h.handle_request(u'list "genre" "album" "analbum"')
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "genre" "album" "analbum"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_genre_by_full_date(self):
|
||||
result = self.h.handle_request(u'list "genre" "date" "2001-01-01"')
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "genre" "date" "2001-01-01"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_genre_by_year(self):
|
||||
result = self.h.handle_request(u'list "genre" "date" "2001"')
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "genre" "date" "2001"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_genre_by_genre(self):
|
||||
result = self.h.handle_request(u'list "genre" "genre" "agenre"')
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "genre" "genre" "agenre"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_genre_by_artist_and_album(self):
|
||||
result = self.h.handle_request(
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "genre" "artist" "anartist" "album" "analbum"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
|
||||
class MusicDatabaseSearchTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.b = DummyBackend.start().proxy()
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.h = dispatcher.MpdDispatcher()
|
||||
self.dispatcher = MpdDispatcher()
|
||||
|
||||
def tearDown(self):
|
||||
self.b.stop().get()
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
|
||||
def test_search_album(self):
|
||||
result = self.h.handle_request(u'search "album" "analbum"')
|
||||
result = self.dispatcher.handle_request(u'search "album" "analbum"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_search_album_without_quotes(self):
|
||||
result = self.h.handle_request(u'search album "analbum"')
|
||||
result = self.dispatcher.handle_request(u'search album "analbum"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_search_artist(self):
|
||||
result = self.h.handle_request(u'search "artist" "anartist"')
|
||||
result = self.dispatcher.handle_request(u'search "artist" "anartist"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_search_artist_without_quotes(self):
|
||||
result = self.h.handle_request(u'search artist "anartist"')
|
||||
result = self.dispatcher.handle_request(u'search artist "anartist"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_search_filename(self):
|
||||
result = self.h.handle_request(u'search "filename" "afilename"')
|
||||
result = self.dispatcher.handle_request(
|
||||
u'search "filename" "afilename"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_search_filename_without_quotes(self):
|
||||
result = self.h.handle_request(u'search filename "afilename"')
|
||||
result = self.dispatcher.handle_request(u'search filename "afilename"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_search_title(self):
|
||||
result = self.h.handle_request(u'search "title" "atitle"')
|
||||
result = self.dispatcher.handle_request(u'search "title" "atitle"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_search_title_without_quotes(self):
|
||||
result = self.h.handle_request(u'search title "atitle"')
|
||||
result = self.dispatcher.handle_request(u'search title "atitle"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_search_any(self):
|
||||
result = self.h.handle_request(u'search "any" "anything"')
|
||||
result = self.dispatcher.handle_request(u'search "any" "anything"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_search_any_without_quotes(self):
|
||||
result = self.h.handle_request(u'search any "anything"')
|
||||
result = self.dispatcher.handle_request(u'search any "anything"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_search_date(self):
|
||||
result = self.h.handle_request(u'search "date" "2002-01-01"')
|
||||
result = self.dispatcher.handle_request(u'search "date" "2002-01-01"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_search_date_without_quotes(self):
|
||||
result = self.h.handle_request(u'search date "2002-01-01"')
|
||||
result = self.dispatcher.handle_request(u'search date "2002-01-01"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_search_date_with_capital_d_and_incomplete_date(self):
|
||||
result = self.h.handle_request(u'search Date "2005"')
|
||||
result = self.dispatcher.handle_request(u'search Date "2005"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_search_else_should_fail(self):
|
||||
result = self.h.handle_request(u'search "sometype" "something"')
|
||||
result = self.dispatcher.handle_request(
|
||||
u'search "sometype" "something"')
|
||||
self.assertEqual(result[0], u'ACK [2@0] {search} incorrect arguments')
|
||||
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ import unittest
|
||||
|
||||
from mopidy.backends.base import PlaybackController
|
||||
from mopidy.backends.dummy import DummyBackend
|
||||
from mopidy.frontends.mpd import dispatcher
|
||||
from mopidy.frontends.mpd.dispatcher import MpdDispatcher
|
||||
from mopidy.mixers.dummy import DummyMixer
|
||||
from mopidy.models import Track
|
||||
|
||||
@ -14,393 +14,378 @@ STOPPED = PlaybackController.STOPPED
|
||||
|
||||
class PlaybackOptionsHandlerTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.b = DummyBackend.start().proxy()
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.h = dispatcher.MpdDispatcher()
|
||||
self.dispatcher = MpdDispatcher()
|
||||
|
||||
def tearDown(self):
|
||||
self.b.stop().get()
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
|
||||
def test_consume_off(self):
|
||||
result = self.h.handle_request(u'consume "0"')
|
||||
self.assertFalse(self.b.playback.consume.get())
|
||||
result = self.dispatcher.handle_request(u'consume "0"')
|
||||
self.assertFalse(self.backend.playback.consume.get())
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_consume_off_without_quotes(self):
|
||||
result = self.h.handle_request(u'consume 0')
|
||||
self.assertFalse(self.b.playback.consume.get())
|
||||
result = self.dispatcher.handle_request(u'consume 0')
|
||||
self.assertFalse(self.backend.playback.consume.get())
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_consume_on(self):
|
||||
result = self.h.handle_request(u'consume "1"')
|
||||
self.assertTrue(self.b.playback.consume.get())
|
||||
result = self.dispatcher.handle_request(u'consume "1"')
|
||||
self.assertTrue(self.backend.playback.consume.get())
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_consume_on_without_quotes(self):
|
||||
result = self.h.handle_request(u'consume 1')
|
||||
self.assertTrue(self.b.playback.consume.get())
|
||||
result = self.dispatcher.handle_request(u'consume 1')
|
||||
self.assertTrue(self.backend.playback.consume.get())
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_crossfade(self):
|
||||
result = self.h.handle_request(u'crossfade "10"')
|
||||
result = self.dispatcher.handle_request(u'crossfade "10"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_random_off(self):
|
||||
result = self.h.handle_request(u'random "0"')
|
||||
self.assertFalse(self.b.playback.random.get())
|
||||
result = self.dispatcher.handle_request(u'random "0"')
|
||||
self.assertFalse(self.backend.playback.random.get())
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_random_off_without_quotes(self):
|
||||
result = self.h.handle_request(u'random 0')
|
||||
self.assertFalse(self.b.playback.random.get())
|
||||
result = self.dispatcher.handle_request(u'random 0')
|
||||
self.assertFalse(self.backend.playback.random.get())
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_random_on(self):
|
||||
result = self.h.handle_request(u'random "1"')
|
||||
self.assertTrue(self.b.playback.random.get())
|
||||
result = self.dispatcher.handle_request(u'random "1"')
|
||||
self.assertTrue(self.backend.playback.random.get())
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_random_on_without_quotes(self):
|
||||
result = self.h.handle_request(u'random 1')
|
||||
self.assertTrue(self.b.playback.random.get())
|
||||
result = self.dispatcher.handle_request(u'random 1')
|
||||
self.assertTrue(self.backend.playback.random.get())
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_repeat_off(self):
|
||||
result = self.h.handle_request(u'repeat "0"')
|
||||
self.assertFalse(self.b.playback.repeat.get())
|
||||
result = self.dispatcher.handle_request(u'repeat "0"')
|
||||
self.assertFalse(self.backend.playback.repeat.get())
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_repeat_off_without_quotes(self):
|
||||
result = self.h.handle_request(u'repeat 0')
|
||||
self.assertFalse(self.b.playback.repeat.get())
|
||||
result = self.dispatcher.handle_request(u'repeat 0')
|
||||
self.assertFalse(self.backend.playback.repeat.get())
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_repeat_on(self):
|
||||
result = self.h.handle_request(u'repeat "1"')
|
||||
self.assertTrue(self.b.playback.repeat.get())
|
||||
result = self.dispatcher.handle_request(u'repeat "1"')
|
||||
self.assertTrue(self.backend.playback.repeat.get())
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_repeat_on_without_quotes(self):
|
||||
result = self.h.handle_request(u'repeat 1')
|
||||
self.assertTrue(self.b.playback.repeat.get())
|
||||
result = self.dispatcher.handle_request(u'repeat 1')
|
||||
self.assertTrue(self.backend.playback.repeat.get())
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_setvol_below_min(self):
|
||||
result = self.h.handle_request(u'setvol "-10"')
|
||||
result = self.dispatcher.handle_request(u'setvol "-10"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(0, self.mixer.volume.get())
|
||||
|
||||
def test_setvol_min(self):
|
||||
result = self.h.handle_request(u'setvol "0"')
|
||||
result = self.dispatcher.handle_request(u'setvol "0"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(0, self.mixer.volume.get())
|
||||
|
||||
def test_setvol_middle(self):
|
||||
result = self.h.handle_request(u'setvol "50"')
|
||||
result = self.dispatcher.handle_request(u'setvol "50"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(50, self.mixer.volume.get())
|
||||
|
||||
def test_setvol_max(self):
|
||||
result = self.h.handle_request(u'setvol "100"')
|
||||
result = self.dispatcher.handle_request(u'setvol "100"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(100, self.mixer.volume.get())
|
||||
|
||||
def test_setvol_above_max(self):
|
||||
result = self.h.handle_request(u'setvol "110"')
|
||||
result = self.dispatcher.handle_request(u'setvol "110"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(100, self.mixer.volume.get())
|
||||
|
||||
def test_setvol_plus_is_ignored(self):
|
||||
result = self.h.handle_request(u'setvol "+10"')
|
||||
result = self.dispatcher.handle_request(u'setvol "+10"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(10, self.mixer.volume.get())
|
||||
|
||||
def test_setvol_without_quotes(self):
|
||||
result = self.h.handle_request(u'setvol 50')
|
||||
result = self.dispatcher.handle_request(u'setvol 50')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(50, self.mixer.volume.get())
|
||||
|
||||
def test_single_off(self):
|
||||
result = self.h.handle_request(u'single "0"')
|
||||
self.assertFalse(self.b.playback.single.get())
|
||||
result = self.dispatcher.handle_request(u'single "0"')
|
||||
self.assertFalse(self.backend.playback.single.get())
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_single_off_without_quotes(self):
|
||||
result = self.h.handle_request(u'single 0')
|
||||
self.assertFalse(self.b.playback.single.get())
|
||||
result = self.dispatcher.handle_request(u'single 0')
|
||||
self.assertFalse(self.backend.playback.single.get())
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_single_on(self):
|
||||
result = self.h.handle_request(u'single "1"')
|
||||
self.assertTrue(self.b.playback.single.get())
|
||||
result = self.dispatcher.handle_request(u'single "1"')
|
||||
self.assertTrue(self.backend.playback.single.get())
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_single_on_without_quotes(self):
|
||||
result = self.h.handle_request(u'single 1')
|
||||
self.assertTrue(self.b.playback.single.get())
|
||||
result = self.dispatcher.handle_request(u'single 1')
|
||||
self.assertTrue(self.backend.playback.single.get())
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_replay_gain_mode_off(self):
|
||||
result = self.h.handle_request(u'replay_gain_mode "off"')
|
||||
result = self.dispatcher.handle_request(u'replay_gain_mode "off"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_replay_gain_mode_track(self):
|
||||
result = self.h.handle_request(u'replay_gain_mode "track"')
|
||||
result = self.dispatcher.handle_request(u'replay_gain_mode "track"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_replay_gain_mode_album(self):
|
||||
result = self.h.handle_request(u'replay_gain_mode "album"')
|
||||
result = self.dispatcher.handle_request(u'replay_gain_mode "album"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_replay_gain_status_default(self):
|
||||
expected = u'off'
|
||||
result = self.h.handle_request(u'replay_gain_status')
|
||||
result = self.dispatcher.handle_request(u'replay_gain_status')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assert_(expected in result)
|
||||
|
||||
def test_replay_gain_status_off(self):
|
||||
raise SkipTest
|
||||
expected = u'off'
|
||||
self.h._replay_gain_mode(expected)
|
||||
result = self.h.handle_request(u'replay_gain_status')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assert_(expected in result)
|
||||
raise SkipTest # TODO
|
||||
|
||||
def test_replay_gain_status_track(self):
|
||||
raise SkipTest
|
||||
expected = u'track'
|
||||
self.h._replay_gain_mode(expected)
|
||||
result = self.h.handle_request(u'replay_gain_status')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assert_(expected in result)
|
||||
raise SkipTest # TODO
|
||||
|
||||
def test_replay_gain_status_album(self):
|
||||
raise SkipTest
|
||||
expected = u'album'
|
||||
self.h._replay_gain_mode(expected)
|
||||
result = self.h.handle_request(u'replay_gain_status')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assert_(expected in result)
|
||||
raise SkipTest # TODO
|
||||
|
||||
|
||||
class PlaybackControlHandlerTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.b = DummyBackend.start().proxy()
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.h = dispatcher.MpdDispatcher()
|
||||
self.dispatcher = MpdDispatcher()
|
||||
|
||||
def tearDown(self):
|
||||
self.b.stop().get()
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
|
||||
def test_next(self):
|
||||
result = self.h.handle_request(u'next')
|
||||
result = self.dispatcher.handle_request(u'next')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_pause_off(self):
|
||||
self.b.current_playlist.append([Track()])
|
||||
self.h.handle_request(u'play "0"')
|
||||
self.h.handle_request(u'pause "1"')
|
||||
result = self.h.handle_request(u'pause "0"')
|
||||
self.backend.current_playlist.append([Track()])
|
||||
self.dispatcher.handle_request(u'play "0"')
|
||||
self.dispatcher.handle_request(u'pause "1"')
|
||||
result = self.dispatcher.handle_request(u'pause "0"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(PLAYING, self.b.playback.state.get())
|
||||
self.assertEqual(PLAYING, self.backend.playback.state.get())
|
||||
|
||||
def test_pause_on(self):
|
||||
self.b.current_playlist.append([Track()])
|
||||
self.h.handle_request(u'play "0"')
|
||||
result = self.h.handle_request(u'pause "1"')
|
||||
self.backend.current_playlist.append([Track()])
|
||||
self.dispatcher.handle_request(u'play "0"')
|
||||
result = self.dispatcher.handle_request(u'pause "1"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(PAUSED, self.b.playback.state.get())
|
||||
self.assertEqual(PAUSED, self.backend.playback.state.get())
|
||||
|
||||
def test_pause_toggle(self):
|
||||
self.b.current_playlist.append([Track()])
|
||||
result = self.h.handle_request(u'play "0"')
|
||||
self.backend.current_playlist.append([Track()])
|
||||
result = self.dispatcher.handle_request(u'play "0"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(PLAYING, self.b.playback.state.get())
|
||||
result = self.h.handle_request(u'pause')
|
||||
self.assertEqual(PLAYING, self.backend.playback.state.get())
|
||||
result = self.dispatcher.handle_request(u'pause')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(PAUSED, self.b.playback.state.get())
|
||||
result = self.h.handle_request(u'pause')
|
||||
self.assertEqual(PAUSED, self.backend.playback.state.get())
|
||||
result = self.dispatcher.handle_request(u'pause')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(PLAYING, self.b.playback.state.get())
|
||||
self.assertEqual(PLAYING, self.backend.playback.state.get())
|
||||
|
||||
def test_play_without_pos(self):
|
||||
self.b.current_playlist.append([Track()])
|
||||
self.b.playback.state = PAUSED
|
||||
result = self.h.handle_request(u'play')
|
||||
self.backend.current_playlist.append([Track()])
|
||||
self.backend.playback.state = PAUSED
|
||||
result = self.dispatcher.handle_request(u'play')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(PLAYING, self.b.playback.state.get())
|
||||
self.assertEqual(PLAYING, self.backend.playback.state.get())
|
||||
|
||||
def test_play_with_pos(self):
|
||||
self.b.current_playlist.append([Track()])
|
||||
result = self.h.handle_request(u'play "0"')
|
||||
self.backend.current_playlist.append([Track()])
|
||||
result = self.dispatcher.handle_request(u'play "0"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(PLAYING, self.b.playback.state.get())
|
||||
self.assertEqual(PLAYING, self.backend.playback.state.get())
|
||||
|
||||
def test_play_with_pos_without_quotes(self):
|
||||
self.b.current_playlist.append([Track()])
|
||||
result = self.h.handle_request(u'play 0')
|
||||
self.backend.current_playlist.append([Track()])
|
||||
result = self.dispatcher.handle_request(u'play 0')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(PLAYING, self.b.playback.state.get())
|
||||
self.assertEqual(PLAYING, self.backend.playback.state.get())
|
||||
|
||||
def test_play_with_pos_out_of_bounds(self):
|
||||
self.b.current_playlist.append([])
|
||||
result = self.h.handle_request(u'play "0"')
|
||||
self.backend.current_playlist.append([])
|
||||
result = self.dispatcher.handle_request(u'play "0"')
|
||||
self.assertEqual(result[0], u'ACK [2@0] {play} Bad song index')
|
||||
self.assertEqual(STOPPED, self.b.playback.state.get())
|
||||
self.assertEqual(STOPPED, self.backend.playback.state.get())
|
||||
|
||||
def test_play_minus_one_plays_first_in_playlist_if_no_current_track(self):
|
||||
self.assertEqual(self.b.playback.current_track.get(), None)
|
||||
self.b.current_playlist.append([Track(uri='a'), Track(uri='b')])
|
||||
result = self.h.handle_request(u'play "-1"')
|
||||
self.assertEqual(self.backend.playback.current_track.get(), None)
|
||||
self.backend.current_playlist.append([Track(uri='a'), Track(uri='b')])
|
||||
result = self.dispatcher.handle_request(u'play "-1"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(PLAYING, self.b.playback.state.get())
|
||||
self.assertEqual(self.b.playback.current_track.get().uri, 'a')
|
||||
self.assertEqual(PLAYING, self.backend.playback.state.get())
|
||||
self.assertEqual(self.backend.playback.current_track.get().uri, 'a')
|
||||
|
||||
def test_play_minus_one_plays_current_track_if_current_track_is_set(self):
|
||||
self.b.current_playlist.append([Track(uri='a'), Track(uri='b')])
|
||||
self.assertEqual(self.b.playback.current_track.get(), None)
|
||||
self.b.playback.play()
|
||||
self.b.playback.next()
|
||||
self.b.playback.stop()
|
||||
self.assertNotEqual(self.b.playback.current_track.get(), None)
|
||||
result = self.h.handle_request(u'play "-1"')
|
||||
self.backend.current_playlist.append([Track(uri='a'), Track(uri='b')])
|
||||
self.assertEqual(self.backend.playback.current_track.get(), None)
|
||||
self.backend.playback.play()
|
||||
self.backend.playback.next()
|
||||
self.backend.playback.stop()
|
||||
self.assertNotEqual(self.backend.playback.current_track.get(), None)
|
||||
result = self.dispatcher.handle_request(u'play "-1"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(PLAYING, self.b.playback.state.get())
|
||||
self.assertEqual(self.b.playback.current_track.get().uri, 'b')
|
||||
self.assertEqual(PLAYING, self.backend.playback.state.get())
|
||||
self.assertEqual(self.backend.playback.current_track.get().uri, 'b')
|
||||
|
||||
def test_play_minus_one_on_empty_playlist_does_not_ack(self):
|
||||
self.b.current_playlist.clear()
|
||||
result = self.h.handle_request(u'play "-1"')
|
||||
self.backend.current_playlist.clear()
|
||||
result = self.dispatcher.handle_request(u'play "-1"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(STOPPED, self.b.playback.state.get())
|
||||
self.assertEqual(self.b.playback.current_track.get(), None)
|
||||
self.assertEqual(STOPPED, self.backend.playback.state.get())
|
||||
self.assertEqual(self.backend.playback.current_track.get(), None)
|
||||
|
||||
def test_play_minus_is_ignored_if_playing(self):
|
||||
self.b.current_playlist.append([Track(length=40000)])
|
||||
self.b.playback.seek(30000)
|
||||
self.assert_(self.b.playback.time_position.get() >= 30000)
|
||||
self.assertEquals(PLAYING, self.b.playback.state.get())
|
||||
result = self.h.handle_request(u'play "-1"')
|
||||
self.backend.current_playlist.append([Track(length=40000)])
|
||||
self.backend.playback.seek(30000)
|
||||
self.assert_(self.backend.playback.time_position.get() >= 30000)
|
||||
self.assertEquals(PLAYING, self.backend.playback.state.get())
|
||||
result = self.dispatcher.handle_request(u'play "-1"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(PLAYING, self.b.playback.state.get())
|
||||
self.assert_(self.b.playback.time_position.get() >= 30000)
|
||||
self.assertEqual(PLAYING, self.backend.playback.state.get())
|
||||
self.assert_(self.backend.playback.time_position.get() >= 30000)
|
||||
|
||||
def test_play_minus_one_resumes_if_paused(self):
|
||||
self.b.current_playlist.append([Track(length=40000)])
|
||||
self.b.playback.seek(30000)
|
||||
self.assert_(self.b.playback.time_position.get() >= 30000)
|
||||
self.assertEquals(PLAYING, self.b.playback.state.get())
|
||||
self.b.playback.pause()
|
||||
self.assertEquals(PAUSED, self.b.playback.state.get())
|
||||
result = self.h.handle_request(u'play "-1"')
|
||||
self.backend.current_playlist.append([Track(length=40000)])
|
||||
self.backend.playback.seek(30000)
|
||||
self.assert_(self.backend.playback.time_position.get() >= 30000)
|
||||
self.assertEquals(PLAYING, self.backend.playback.state.get())
|
||||
self.backend.playback.pause()
|
||||
self.assertEquals(PAUSED, self.backend.playback.state.get())
|
||||
result = self.dispatcher.handle_request(u'play "-1"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(PLAYING, self.b.playback.state.get())
|
||||
self.assert_(self.b.playback.time_position.get() >= 30000)
|
||||
self.assertEqual(PLAYING, self.backend.playback.state.get())
|
||||
self.assert_(self.backend.playback.time_position.get() >= 30000)
|
||||
|
||||
def test_playid(self):
|
||||
self.b.current_playlist.append([Track()])
|
||||
result = self.h.handle_request(u'playid "0"')
|
||||
self.backend.current_playlist.append([Track()])
|
||||
result = self.dispatcher.handle_request(u'playid "0"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(PLAYING, self.b.playback.state.get())
|
||||
self.assertEqual(PLAYING, self.backend.playback.state.get())
|
||||
|
||||
def test_playid_minus_one_plays_first_in_playlist_if_no_current_track(self):
|
||||
self.assertEqual(self.b.playback.current_track.get(), None)
|
||||
self.b.current_playlist.append([Track(uri='a'), Track(uri='b')])
|
||||
result = self.h.handle_request(u'playid "-1"')
|
||||
self.assertEqual(self.backend.playback.current_track.get(), None)
|
||||
self.backend.current_playlist.append([Track(uri='a'), Track(uri='b')])
|
||||
result = self.dispatcher.handle_request(u'playid "-1"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(PLAYING, self.b.playback.state.get())
|
||||
self.assertEqual(self.b.playback.current_track.get().uri, 'a')
|
||||
self.assertEqual(PLAYING, self.backend.playback.state.get())
|
||||
self.assertEqual(self.backend.playback.current_track.get().uri, 'a')
|
||||
|
||||
def test_playid_minus_one_plays_current_track_if_current_track_is_set(self):
|
||||
self.b.current_playlist.append([Track(uri='a'), Track(uri='b')])
|
||||
self.assertEqual(self.b.playback.current_track.get(), None)
|
||||
self.b.playback.play()
|
||||
self.b.playback.next()
|
||||
self.b.playback.stop()
|
||||
self.assertNotEqual(self.b.playback.current_track.get(), None)
|
||||
result = self.h.handle_request(u'playid "-1"')
|
||||
self.backend.current_playlist.append([Track(uri='a'), Track(uri='b')])
|
||||
self.assertEqual(self.backend.playback.current_track.get(), None)
|
||||
self.backend.playback.play()
|
||||
self.backend.playback.next()
|
||||
self.backend.playback.stop()
|
||||
self.assertNotEqual(self.backend.playback.current_track.get(), None)
|
||||
result = self.dispatcher.handle_request(u'playid "-1"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(PLAYING, self.b.playback.state.get())
|
||||
self.assertEqual(self.b.playback.current_track.get().uri, 'b')
|
||||
self.assertEqual(PLAYING, self.backend.playback.state.get())
|
||||
self.assertEqual(self.backend.playback.current_track.get().uri, 'b')
|
||||
|
||||
def test_playid_minus_one_on_empty_playlist_does_not_ack(self):
|
||||
self.b.current_playlist.clear()
|
||||
result = self.h.handle_request(u'playid "-1"')
|
||||
self.backend.current_playlist.clear()
|
||||
result = self.dispatcher.handle_request(u'playid "-1"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(STOPPED, self.b.playback.state.get())
|
||||
self.assertEqual(self.b.playback.current_track.get(), None)
|
||||
self.assertEqual(STOPPED, self.backend.playback.state.get())
|
||||
self.assertEqual(self.backend.playback.current_track.get(), None)
|
||||
|
||||
def test_playid_minus_is_ignored_if_playing(self):
|
||||
self.b.current_playlist.append([Track(length=40000)])
|
||||
self.b.playback.seek(30000)
|
||||
self.assert_(self.b.playback.time_position.get() >= 30000)
|
||||
self.assertEquals(PLAYING, self.b.playback.state.get())
|
||||
result = self.h.handle_request(u'playid "-1"')
|
||||
self.backend.current_playlist.append([Track(length=40000)])
|
||||
self.backend.playback.seek(30000)
|
||||
self.assert_(self.backend.playback.time_position.get() >= 30000)
|
||||
self.assertEquals(PLAYING, self.backend.playback.state.get())
|
||||
result = self.dispatcher.handle_request(u'playid "-1"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(PLAYING, self.b.playback.state.get())
|
||||
self.assert_(self.b.playback.time_position.get() >= 30000)
|
||||
self.assertEqual(PLAYING, self.backend.playback.state.get())
|
||||
self.assert_(self.backend.playback.time_position.get() >= 30000)
|
||||
|
||||
def test_playid_minus_one_resumes_if_paused(self):
|
||||
self.b.current_playlist.append([Track(length=40000)])
|
||||
self.b.playback.seek(30000)
|
||||
self.assert_(self.b.playback.time_position.get() >= 30000)
|
||||
self.assertEquals(PLAYING, self.b.playback.state.get())
|
||||
self.b.playback.pause()
|
||||
self.assertEquals(PAUSED, self.b.playback.state.get())
|
||||
result = self.h.handle_request(u'playid "-1"')
|
||||
self.backend.current_playlist.append([Track(length=40000)])
|
||||
self.backend.playback.seek(30000)
|
||||
self.assert_(self.backend.playback.time_position.get() >= 30000)
|
||||
self.assertEquals(PLAYING, self.backend.playback.state.get())
|
||||
self.backend.playback.pause()
|
||||
self.assertEquals(PAUSED, self.backend.playback.state.get())
|
||||
result = self.dispatcher.handle_request(u'playid "-1"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(PLAYING, self.b.playback.state.get())
|
||||
self.assert_(self.b.playback.time_position.get() >= 30000)
|
||||
self.assertEqual(PLAYING, self.backend.playback.state.get())
|
||||
self.assert_(self.backend.playback.time_position.get() >= 30000)
|
||||
|
||||
def test_playid_which_does_not_exist(self):
|
||||
self.b.current_playlist.append([Track()])
|
||||
result = self.h.handle_request(u'playid "12345"')
|
||||
self.backend.current_playlist.append([Track()])
|
||||
result = self.dispatcher.handle_request(u'playid "12345"')
|
||||
self.assertEqual(result[0], u'ACK [50@0] {playid} No such song')
|
||||
|
||||
def test_previous(self):
|
||||
result = self.h.handle_request(u'previous')
|
||||
result = self.dispatcher.handle_request(u'previous')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_seek(self):
|
||||
self.b.current_playlist.append([Track(length=40000)])
|
||||
self.h.handle_request(u'seek "0"')
|
||||
result = self.h.handle_request(u'seek "0" "30"')
|
||||
self.backend.current_playlist.append([Track(length=40000)])
|
||||
self.dispatcher.handle_request(u'seek "0"')
|
||||
result = self.dispatcher.handle_request(u'seek "0" "30"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assert_(self.b.playback.time_position >= 30000)
|
||||
self.assert_(self.backend.playback.time_position >= 30000)
|
||||
|
||||
def test_seek_with_songpos(self):
|
||||
seek_track = Track(uri='2', length=40000)
|
||||
self.b.current_playlist.append(
|
||||
self.backend.current_playlist.append(
|
||||
[Track(uri='1', length=40000), seek_track])
|
||||
result = self.h.handle_request(u'seek "1" "30"')
|
||||
result = self.dispatcher.handle_request(u'seek "1" "30"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(self.b.playback.current_track.get(), seek_track)
|
||||
self.assertEqual(self.backend.playback.current_track.get(), seek_track)
|
||||
|
||||
def test_seek_without_quotes(self):
|
||||
self.b.current_playlist.append([Track(length=40000)])
|
||||
self.h.handle_request(u'seek 0')
|
||||
result = self.h.handle_request(u'seek 0 30')
|
||||
self.backend.current_playlist.append([Track(length=40000)])
|
||||
self.dispatcher.handle_request(u'seek 0')
|
||||
result = self.dispatcher.handle_request(u'seek 0 30')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assert_(self.b.playback.time_position.get() >= 30000)
|
||||
self.assert_(self.backend.playback.time_position.get() >= 30000)
|
||||
|
||||
def test_seekid(self):
|
||||
self.b.current_playlist.append([Track(length=40000)])
|
||||
result = self.h.handle_request(u'seekid "0" "30"')
|
||||
self.backend.current_playlist.append([Track(length=40000)])
|
||||
result = self.dispatcher.handle_request(u'seekid "0" "30"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assert_(self.b.playback.time_position.get() >= 30000)
|
||||
self.assert_(self.backend.playback.time_position.get() >= 30000)
|
||||
|
||||
def test_seekid_with_cpid(self):
|
||||
seek_track = Track(uri='2', length=40000)
|
||||
self.b.current_playlist.append(
|
||||
self.backend.current_playlist.append(
|
||||
[Track(length=40000), seek_track])
|
||||
result = self.h.handle_request(u'seekid "1" "30"')
|
||||
result = self.dispatcher.handle_request(u'seekid "1" "30"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(self.b.playback.current_cpid.get(), 1)
|
||||
self.assertEqual(self.b.playback.current_track.get(), seek_track)
|
||||
self.assertEqual(self.backend.playback.current_cpid.get(), 1)
|
||||
self.assertEqual(self.backend.playback.current_track.get(), seek_track)
|
||||
|
||||
def test_stop(self):
|
||||
result = self.h.handle_request(u'stop')
|
||||
result = self.dispatcher.handle_request(u'stop')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(STOPPED, self.b.playback.state.get())
|
||||
self.assertEqual(STOPPED, self.backend.playback.state.get())
|
||||
|
||||
@ -1,25 +1,29 @@
|
||||
import unittest
|
||||
|
||||
from mopidy import settings
|
||||
from mopidy.backends.dummy import DummyBackend
|
||||
from mopidy.frontends.mpd import dispatcher
|
||||
from mopidy.frontends.mpd.dispatcher import MpdDispatcher
|
||||
from mopidy.mixers.dummy import DummyMixer
|
||||
|
||||
class ReflectionHandlerTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.b = DummyBackend.start().proxy()
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.h = dispatcher.MpdDispatcher()
|
||||
self.dispatcher = MpdDispatcher()
|
||||
|
||||
def tearDown(self):
|
||||
self.b.stop().get()
|
||||
settings.runtime.clear()
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
|
||||
def test_commands_returns_list_of_all_commands(self):
|
||||
result = self.h.handle_request(u'commands')
|
||||
result = self.dispatcher.handle_request(u'commands')
|
||||
# Check if some random commands are included
|
||||
self.assert_(u'command: commands' in result)
|
||||
self.assert_(u'command: play' in result)
|
||||
self.assert_(u'command: status' in result)
|
||||
# Check if commands you do not have access to are not present
|
||||
self.assert_(u'command: kill' not in result)
|
||||
# Check if the blacklisted commands are not present
|
||||
self.assert_(u'command: command_list_begin' not in result)
|
||||
self.assert_(u'command: command_list_ok_begin' not in result)
|
||||
@ -29,20 +33,47 @@ class ReflectionHandlerTest(unittest.TestCase):
|
||||
self.assert_(u'command: sticker' not in result)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_commands_show_less_if_auth_required_and_not_authed(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'secret'
|
||||
result = self.dispatcher.handle_request(u'commands')
|
||||
# Not requiring auth
|
||||
self.assert_(u'command: close' in result, result)
|
||||
self.assert_(u'command: commands' in result, result)
|
||||
self.assert_(u'command: notcommands' in result, result)
|
||||
self.assert_(u'command: password' in result, result)
|
||||
self.assert_(u'command: ping' in result, result)
|
||||
# Requiring auth
|
||||
self.assert_(u'command: play' not in result, result)
|
||||
self.assert_(u'command: status' not in result, result)
|
||||
|
||||
def test_decoders(self):
|
||||
result = self.h.handle_request(u'decoders')
|
||||
result = self.dispatcher.handle_request(u'decoders')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_notcommands_returns_only_ok(self):
|
||||
result = self.h.handle_request(u'notcommands')
|
||||
self.assertEqual(1, len(result))
|
||||
def test_notcommands_returns_only_kill_and_ok(self):
|
||||
result = self.dispatcher.handle_request(u'notcommands')
|
||||
self.assertEqual(2, len(result))
|
||||
self.assert_(u'command: kill' in result)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_notcommands_returns_more_if_auth_required_and_not_authed(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'secret'
|
||||
result = self.dispatcher.handle_request(u'notcommands')
|
||||
# Not requiring auth
|
||||
self.assert_(u'command: close' not in result, result)
|
||||
self.assert_(u'command: commands' not in result, result)
|
||||
self.assert_(u'command: notcommands' not in result, result)
|
||||
self.assert_(u'command: password' not in result, result)
|
||||
self.assert_(u'command: ping' not in result, result)
|
||||
# Requiring auth
|
||||
self.assert_(u'command: play' in result, result)
|
||||
self.assert_(u'command: status' in result, result)
|
||||
|
||||
def test_tagtypes(self):
|
||||
result = self.h.handle_request(u'tagtypes')
|
||||
result = self.dispatcher.handle_request(u'tagtypes')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_urlhandlers(self):
|
||||
result = self.h.handle_request(u'urlhandlers')
|
||||
result = self.dispatcher.handle_request(u'urlhandlers')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assert_(u'handler: dummy:' in result)
|
||||
|
||||
@ -21,52 +21,3 @@ class MpdSessionTest(unittest.TestCase):
|
||||
self.session.input_buffer = ['\xff']
|
||||
self.session.found_terminator()
|
||||
self.assertEqual(len(self.session.input_buffer), 0)
|
||||
|
||||
def test_authentication_with_valid_password_is_accepted(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'topsecret'
|
||||
authed, response = self.session.check_password(u'password "topsecret"')
|
||||
self.assertTrue(authed)
|
||||
self.assertEqual(u'OK', response)
|
||||
|
||||
def test_authentication_with_invalid_password_is_not_accepted(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'topsecret'
|
||||
authed, response = self.session.check_password(u'password "secret"')
|
||||
self.assertFalse(authed)
|
||||
self.assertEqual(u'ACK [3@0] {password} incorrect password', response)
|
||||
|
||||
def test_authentication_with_anything_when_password_check_turned_off(self):
|
||||
settings.MPD_SERVER_PASSWORD = None
|
||||
authed, response = self.session.check_password(u'any request at all')
|
||||
self.assertTrue(authed)
|
||||
self.assertEqual(None, response)
|
||||
|
||||
def test_anything_when_not_authenticated_should_fail(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'topsecret'
|
||||
authed, response = self.session.check_password(u'any request at all')
|
||||
self.assertFalse(authed)
|
||||
self.assertEqual(
|
||||
u'ACK [4@0] {any} you don\'t have permission for "any"', response)
|
||||
|
||||
def test_close_is_allowed_without_authentication(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'topsecret'
|
||||
authed, response = self.session.check_password(u'close')
|
||||
self.assertFalse(authed)
|
||||
self.assertEqual(None, response)
|
||||
|
||||
def test_commands_is_allowed_without_authentication(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'topsecret'
|
||||
authed, response = self.session.check_password(u'commands')
|
||||
self.assertFalse(authed)
|
||||
self.assertEqual(None, response)
|
||||
|
||||
def test_notcommands_is_allowed_without_authentication(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'topsecret'
|
||||
authed, response = self.session.check_password(u'notcommands')
|
||||
self.assertFalse(authed)
|
||||
self.assertEqual(None, response)
|
||||
|
||||
def test_ping_is_allowed_without_authentication(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'topsecret'
|
||||
authed, response = self.session.check_password(u'ping')
|
||||
self.assertFalse(authed)
|
||||
self.assertEqual(None, response)
|
||||
|
||||
@ -2,7 +2,8 @@ import unittest
|
||||
|
||||
from mopidy.backends.base import PlaybackController
|
||||
from mopidy.backends.dummy import DummyBackend
|
||||
from mopidy.frontends.mpd import dispatcher
|
||||
from mopidy.frontends.mpd.dispatcher import MpdDispatcher
|
||||
from mopidy.frontends.mpd.protocol import status
|
||||
from mopidy.mixers.dummy import DummyMixer
|
||||
from mopidy.models import Track
|
||||
|
||||
@ -12,23 +13,24 @@ STOPPED = PlaybackController.STOPPED
|
||||
|
||||
class StatusHandlerTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.b = DummyBackend.start().proxy()
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.h = dispatcher.MpdDispatcher()
|
||||
self.dispatcher = MpdDispatcher()
|
||||
self.context = self.dispatcher.context
|
||||
|
||||
def tearDown(self):
|
||||
self.b.stop().get()
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
|
||||
def test_clearerror(self):
|
||||
result = self.h.handle_request(u'clearerror')
|
||||
result = self.dispatcher.handle_request(u'clearerror')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_currentsong(self):
|
||||
track = Track()
|
||||
self.b.current_playlist.append([track])
|
||||
self.b.playback.play()
|
||||
result = self.h.handle_request(u'currentsong')
|
||||
self.backend.current_playlist.append([track])
|
||||
self.backend.playback.play()
|
||||
result = self.dispatcher.handle_request(u'currentsong')
|
||||
self.assert_(u'file: ' in result)
|
||||
self.assert_(u'Time: 0' in result)
|
||||
self.assert_(u'Artist: ' in result)
|
||||
@ -41,27 +43,27 @@ class StatusHandlerTest(unittest.TestCase):
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_currentsong_without_song(self):
|
||||
result = self.h.handle_request(u'currentsong')
|
||||
result = self.dispatcher.handle_request(u'currentsong')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_idle_without_subsystems(self):
|
||||
result = self.h.handle_request(u'idle')
|
||||
result = self.dispatcher.handle_request(u'idle')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_idle_with_subsystems(self):
|
||||
result = self.h.handle_request(u'idle database playlist')
|
||||
result = self.dispatcher.handle_request(u'idle database playlist')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_noidle(self):
|
||||
result = self.h.handle_request(u'noidle')
|
||||
result = self.dispatcher.handle_request(u'noidle')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_stats_command(self):
|
||||
result = self.h.handle_request(u'stats')
|
||||
result = self.dispatcher.handle_request(u'stats')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_stats_method(self):
|
||||
result = dispatcher.status.stats(self.h)
|
||||
result = status.stats(self.context)
|
||||
self.assert_('artists' in result)
|
||||
self.assert_(int(result['artists']) >= 0)
|
||||
self.assert_('albums' in result)
|
||||
@ -78,110 +80,110 @@ class StatusHandlerTest(unittest.TestCase):
|
||||
self.assert_(int(result['playtime']) >= 0)
|
||||
|
||||
def test_status_command(self):
|
||||
result = self.h.handle_request(u'status')
|
||||
result = self.dispatcher.handle_request(u'status')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_status_method_contains_volume_which_defaults_to_0(self):
|
||||
result = dict(dispatcher.status.status(self.h))
|
||||
result = dict(status.status(self.context))
|
||||
self.assert_('volume' in result)
|
||||
self.assertEqual(int(result['volume']), 0)
|
||||
|
||||
def test_status_method_contains_volume(self):
|
||||
self.mixer.volume = 17
|
||||
result = dict(dispatcher.status.status(self.h))
|
||||
result = dict(status.status(self.context))
|
||||
self.assert_('volume' in result)
|
||||
self.assertEqual(int(result['volume']), 17)
|
||||
|
||||
def test_status_method_contains_repeat_is_0(self):
|
||||
result = dict(dispatcher.status.status(self.h))
|
||||
result = dict(status.status(self.context))
|
||||
self.assert_('repeat' in result)
|
||||
self.assertEqual(int(result['repeat']), 0)
|
||||
|
||||
def test_status_method_contains_repeat_is_1(self):
|
||||
self.b.playback.repeat = 1
|
||||
result = dict(dispatcher.status.status(self.h))
|
||||
self.backend.playback.repeat = 1
|
||||
result = dict(status.status(self.context))
|
||||
self.assert_('repeat' in result)
|
||||
self.assertEqual(int(result['repeat']), 1)
|
||||
|
||||
def test_status_method_contains_random_is_0(self):
|
||||
result = dict(dispatcher.status.status(self.h))
|
||||
result = dict(status.status(self.context))
|
||||
self.assert_('random' in result)
|
||||
self.assertEqual(int(result['random']), 0)
|
||||
|
||||
def test_status_method_contains_random_is_1(self):
|
||||
self.b.playback.random = 1
|
||||
result = dict(dispatcher.status.status(self.h))
|
||||
self.backend.playback.random = 1
|
||||
result = dict(status.status(self.context))
|
||||
self.assert_('random' in result)
|
||||
self.assertEqual(int(result['random']), 1)
|
||||
|
||||
def test_status_method_contains_single(self):
|
||||
result = dict(dispatcher.status.status(self.h))
|
||||
result = dict(status.status(self.context))
|
||||
self.assert_('single' in result)
|
||||
self.assert_(int(result['single']) in (0, 1))
|
||||
|
||||
def test_status_method_contains_consume_is_0(self):
|
||||
result = dict(dispatcher.status.status(self.h))
|
||||
result = dict(status.status(self.context))
|
||||
self.assert_('consume' in result)
|
||||
self.assertEqual(int(result['consume']), 0)
|
||||
|
||||
def test_status_method_contains_consume_is_1(self):
|
||||
self.b.playback.consume = 1
|
||||
result = dict(dispatcher.status.status(self.h))
|
||||
self.backend.playback.consume = 1
|
||||
result = dict(status.status(self.context))
|
||||
self.assert_('consume' in result)
|
||||
self.assertEqual(int(result['consume']), 1)
|
||||
|
||||
def test_status_method_contains_playlist(self):
|
||||
result = dict(dispatcher.status.status(self.h))
|
||||
result = dict(status.status(self.context))
|
||||
self.assert_('playlist' in result)
|
||||
self.assert_(int(result['playlist']) in xrange(0, 2**31 - 1))
|
||||
|
||||
def test_status_method_contains_playlistlength(self):
|
||||
result = dict(dispatcher.status.status(self.h))
|
||||
result = dict(status.status(self.context))
|
||||
self.assert_('playlistlength' in result)
|
||||
self.assert_(int(result['playlistlength']) >= 0)
|
||||
|
||||
def test_status_method_contains_xfade(self):
|
||||
result = dict(dispatcher.status.status(self.h))
|
||||
result = dict(status.status(self.context))
|
||||
self.assert_('xfade' in result)
|
||||
self.assert_(int(result['xfade']) >= 0)
|
||||
|
||||
def test_status_method_contains_state_is_play(self):
|
||||
self.b.playback.state = PLAYING
|
||||
result = dict(dispatcher.status.status(self.h))
|
||||
self.backend.playback.state = PLAYING
|
||||
result = dict(status.status(self.context))
|
||||
self.assert_('state' in result)
|
||||
self.assertEqual(result['state'], 'play')
|
||||
|
||||
def test_status_method_contains_state_is_stop(self):
|
||||
self.b.playback.state = STOPPED
|
||||
result = dict(dispatcher.status.status(self.h))
|
||||
self.backend.playback.state = STOPPED
|
||||
result = dict(status.status(self.context))
|
||||
self.assert_('state' in result)
|
||||
self.assertEqual(result['state'], 'stop')
|
||||
|
||||
def test_status_method_contains_state_is_pause(self):
|
||||
self.b.playback.state = PLAYING
|
||||
self.b.playback.state = PAUSED
|
||||
result = dict(dispatcher.status.status(self.h))
|
||||
self.backend.playback.state = PLAYING
|
||||
self.backend.playback.state = PAUSED
|
||||
result = dict(status.status(self.context))
|
||||
self.assert_('state' in result)
|
||||
self.assertEqual(result['state'], 'pause')
|
||||
|
||||
def test_status_method_when_playlist_loaded_contains_song(self):
|
||||
self.b.current_playlist.append([Track()])
|
||||
self.b.playback.play()
|
||||
result = dict(dispatcher.status.status(self.h))
|
||||
self.backend.current_playlist.append([Track()])
|
||||
self.backend.playback.play()
|
||||
result = dict(status.status(self.context))
|
||||
self.assert_('song' in result)
|
||||
self.assert_(int(result['song']) >= 0)
|
||||
|
||||
def test_status_method_when_playlist_loaded_contains_cpid_as_songid(self):
|
||||
self.b.current_playlist.append([Track()])
|
||||
self.b.playback.play()
|
||||
result = dict(dispatcher.status.status(self.h))
|
||||
self.backend.current_playlist.append([Track()])
|
||||
self.backend.playback.play()
|
||||
result = dict(status.status(self.context))
|
||||
self.assert_('songid' in result)
|
||||
self.assertEqual(int(result['songid']), 0)
|
||||
|
||||
def test_status_method_when_playing_contains_time_with_no_length(self):
|
||||
self.b.current_playlist.append([Track(length=None)])
|
||||
self.b.playback.play()
|
||||
result = dict(dispatcher.status.status(self.h))
|
||||
self.backend.current_playlist.append([Track(length=None)])
|
||||
self.backend.playback.play()
|
||||
result = dict(status.status(self.context))
|
||||
self.assert_('time' in result)
|
||||
(position, total) = result['time'].split(':')
|
||||
position = int(position)
|
||||
@ -189,9 +191,9 @@ class StatusHandlerTest(unittest.TestCase):
|
||||
self.assert_(position <= total)
|
||||
|
||||
def test_status_method_when_playing_contains_time_with_length(self):
|
||||
self.b.current_playlist.append([Track(length=10000)])
|
||||
self.b.playback.play()
|
||||
result = dict(dispatcher.status.status(self.h))
|
||||
self.backend.current_playlist.append([Track(length=10000)])
|
||||
self.backend.playback.play()
|
||||
result = dict(status.status(self.context))
|
||||
self.assert_('time' in result)
|
||||
(position, total) = result['time'].split(':')
|
||||
position = int(position)
|
||||
@ -199,15 +201,15 @@ class StatusHandlerTest(unittest.TestCase):
|
||||
self.assert_(position <= total)
|
||||
|
||||
def test_status_method_when_playing_contains_elapsed(self):
|
||||
self.b.playback.state = PAUSED
|
||||
self.b.playback.play_time_accumulated = 59123
|
||||
result = dict(dispatcher.status.status(self.h))
|
||||
self.backend.playback.state = PAUSED
|
||||
self.backend.playback.play_time_accumulated = 59123
|
||||
result = dict(status.status(self.context))
|
||||
self.assert_('elapsed' in result)
|
||||
self.assertEqual(int(result['elapsed']), 59123)
|
||||
|
||||
def test_status_method_when_playing_contains_bitrate(self):
|
||||
self.b.current_playlist.append([Track(bitrate=320)])
|
||||
self.b.playback.play()
|
||||
result = dict(dispatcher.status.status(self.h))
|
||||
self.backend.current_playlist.append([Track(bitrate=320)])
|
||||
self.backend.playback.play()
|
||||
result = dict(status.status(self.context))
|
||||
self.assert_('bitrate' in result)
|
||||
self.assertEqual(int(result['bitrate']), 320)
|
||||
|
||||
@ -1,45 +1,45 @@
|
||||
import unittest
|
||||
|
||||
from mopidy.backends.dummy import DummyBackend
|
||||
from mopidy.frontends.mpd import dispatcher
|
||||
from mopidy.frontends.mpd.dispatcher import MpdDispatcher
|
||||
from mopidy.mixers.dummy import DummyMixer
|
||||
|
||||
class StickersHandlerTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.b = DummyBackend.start().proxy()
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.h = dispatcher.MpdDispatcher()
|
||||
self.dispatcher = MpdDispatcher()
|
||||
|
||||
def tearDown(self):
|
||||
self.b.stop().get()
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
|
||||
def test_sticker_get(self):
|
||||
result = self.h.handle_request(
|
||||
result = self.dispatcher.handle_request(
|
||||
u'sticker get "song" "file:///dev/urandom" "a_name"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_sticker_set(self):
|
||||
result = self.h.handle_request(
|
||||
result = self.dispatcher.handle_request(
|
||||
u'sticker set "song" "file:///dev/urandom" "a_name" "a_value"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_sticker_delete_with_name(self):
|
||||
result = self.h.handle_request(
|
||||
result = self.dispatcher.handle_request(
|
||||
u'sticker delete "song" "file:///dev/urandom" "a_name"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_sticker_delete_without_name(self):
|
||||
result = self.h.handle_request(
|
||||
result = self.dispatcher.handle_request(
|
||||
u'sticker delete "song" "file:///dev/urandom"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_sticker_list(self):
|
||||
result = self.h.handle_request(
|
||||
result = self.dispatcher.handle_request(
|
||||
u'sticker list "song" "file:///dev/urandom"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_sticker_find(self):
|
||||
result = self.h.handle_request(
|
||||
result = self.dispatcher.handle_request(
|
||||
u'sticker find "song" "file:///dev/urandom" "a_name"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
@ -2,64 +2,64 @@ import datetime as dt
|
||||
import unittest
|
||||
|
||||
from mopidy.backends.dummy import DummyBackend
|
||||
from mopidy.frontends.mpd import dispatcher
|
||||
from mopidy.frontends.mpd.dispatcher import MpdDispatcher
|
||||
from mopidy.mixers.dummy import DummyMixer
|
||||
from mopidy.models import Track, Playlist
|
||||
|
||||
class StoredPlaylistsHandlerTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.b = DummyBackend.start().proxy()
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.h = dispatcher.MpdDispatcher()
|
||||
self.dispatcher = MpdDispatcher()
|
||||
|
||||
def tearDown(self):
|
||||
self.b.stop().get()
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
|
||||
def test_listplaylist(self):
|
||||
self.b.stored_playlists.playlists = [
|
||||
self.backend.stored_playlists.playlists = [
|
||||
Playlist(name='name', tracks=[Track(uri='file:///dev/urandom')])]
|
||||
result = self.h.handle_request(u'listplaylist "name"')
|
||||
result = self.dispatcher.handle_request(u'listplaylist "name"')
|
||||
self.assert_(u'file: file:///dev/urandom' in result)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_listplaylist_fails_if_no_playlist_is_found(self):
|
||||
result = self.h.handle_request(u'listplaylist "name"')
|
||||
result = self.dispatcher.handle_request(u'listplaylist "name"')
|
||||
self.assertEqual(result[0],
|
||||
u'ACK [50@0] {listplaylist} No such playlist')
|
||||
|
||||
def test_listplaylistinfo(self):
|
||||
self.b.stored_playlists.playlists = [
|
||||
self.backend.stored_playlists.playlists = [
|
||||
Playlist(name='name', tracks=[Track(uri='file:///dev/urandom')])]
|
||||
result = self.h.handle_request(u'listplaylistinfo "name"')
|
||||
result = self.dispatcher.handle_request(u'listplaylistinfo "name"')
|
||||
self.assert_(u'file: file:///dev/urandom' in result)
|
||||
self.assert_(u'Track: 0' in result)
|
||||
self.assert_(u'Pos: 0' not in result)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_listplaylistinfo_fails_if_no_playlist_is_found(self):
|
||||
result = self.h.handle_request(u'listplaylistinfo "name"')
|
||||
result = self.dispatcher.handle_request(u'listplaylistinfo "name"')
|
||||
self.assertEqual(result[0],
|
||||
u'ACK [50@0] {listplaylistinfo} No such playlist')
|
||||
|
||||
def test_listplaylists(self):
|
||||
last_modified = dt.datetime(2001, 3, 17, 13, 41, 17, 12345)
|
||||
self.b.stored_playlists.playlists = [Playlist(name='a',
|
||||
self.backend.stored_playlists.playlists = [Playlist(name='a',
|
||||
last_modified=last_modified)]
|
||||
result = self.h.handle_request(u'listplaylists')
|
||||
result = self.dispatcher.handle_request(u'listplaylists')
|
||||
self.assert_(u'playlist: a' in result)
|
||||
# Date without microseconds and with time zone information
|
||||
self.assert_(u'Last-Modified: 2001-03-17T13:41:17Z' in result)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_load_known_playlist_appends_to_current_playlist(self):
|
||||
self.b.current_playlist.append([Track(uri='a'), Track(uri='b')])
|
||||
self.assertEqual(len(self.b.current_playlist.tracks.get()), 2)
|
||||
self.b.stored_playlists.playlists = [Playlist(name='A-list',
|
||||
self.backend.current_playlist.append([Track(uri='a'), Track(uri='b')])
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 2)
|
||||
self.backend.stored_playlists.playlists = [Playlist(name='A-list',
|
||||
tracks=[Track(uri='c'), Track(uri='d'), Track(uri='e')])]
|
||||
result = self.h.handle_request(u'load "A-list"')
|
||||
result = self.dispatcher.handle_request(u'load "A-list"')
|
||||
self.assert_(u'OK' in result)
|
||||
tracks = self.b.current_playlist.tracks.get()
|
||||
tracks = self.backend.current_playlist.tracks.get()
|
||||
self.assertEqual(len(tracks), 5)
|
||||
self.assertEqual(tracks[0].uri, 'a')
|
||||
self.assertEqual(tracks[1].uri, 'b')
|
||||
@ -68,35 +68,35 @@ class StoredPlaylistsHandlerTest(unittest.TestCase):
|
||||
self.assertEqual(tracks[4].uri, 'e')
|
||||
|
||||
def test_load_unknown_playlist_acks(self):
|
||||
result = self.h.handle_request(u'load "unknown playlist"')
|
||||
result = self.dispatcher.handle_request(u'load "unknown playlist"')
|
||||
self.assert_(u'ACK [50@0] {load} No such playlist' in result)
|
||||
self.assertEqual(len(self.b.current_playlist.tracks.get()), 0)
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 0)
|
||||
|
||||
def test_playlistadd(self):
|
||||
result = self.h.handle_request(
|
||||
result = self.dispatcher.handle_request(
|
||||
u'playlistadd "name" "file:///dev/urandom"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_playlistclear(self):
|
||||
result = self.h.handle_request(u'playlistclear "name"')
|
||||
result = self.dispatcher.handle_request(u'playlistclear "name"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_playlistdelete(self):
|
||||
result = self.h.handle_request(u'playlistdelete "name" "5"')
|
||||
result = self.dispatcher.handle_request(u'playlistdelete "name" "5"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_playlistmove(self):
|
||||
result = self.h.handle_request(u'playlistmove "name" "5" "10"')
|
||||
result = self.dispatcher.handle_request(u'playlistmove "name" "5" "10"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_rename(self):
|
||||
result = self.h.handle_request(u'rename "old_name" "new_name"')
|
||||
result = self.dispatcher.handle_request(u'rename "old_name" "new_name"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_rm(self):
|
||||
result = self.h.handle_request(u'rm "name"')
|
||||
result = self.dispatcher.handle_request(u'rm "name"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_save(self):
|
||||
result = self.h.handle_request(u'save "name"')
|
||||
result = self.dispatcher.handle_request(u'save "name"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import datetime as dt
|
||||
import unittest
|
||||
|
||||
from mopidy.models import Artist, Album, Track, Playlist
|
||||
from mopidy.models import Artist, Album, CpTrack, Track, Playlist
|
||||
|
||||
from tests import SkipTest
|
||||
|
||||
@ -274,6 +274,21 @@ class AlbumTest(unittest.TestCase):
|
||||
self.assertNotEqual(hash(album1), hash(album2))
|
||||
|
||||
|
||||
class CpTrackTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.cpid = 123
|
||||
self.track = Track()
|
||||
self.cp_track = CpTrack(self.cpid, self.track)
|
||||
|
||||
def test_cp_track_can_be_accessed_as_a_tuple(self):
|
||||
self.assertEqual(self.cpid, self.cp_track[0])
|
||||
self.assertEqual(self.track, self.cp_track[1])
|
||||
|
||||
def test_cp_track_can_be_accessed_by_attribute_names(self):
|
||||
self.assertEqual(self.cpid, self.cp_track.cpid)
|
||||
self.assertEqual(self.track, self.cp_track.track)
|
||||
|
||||
|
||||
class TrackTest(unittest.TestCase):
|
||||
def test_uri(self):
|
||||
uri = u'an_uri'
|
||||
|
||||
Loading…
Reference in New Issue
Block a user