diff --git a/mopidy/frontends/mpd/dispatcher.py b/mopidy/frontends/mpd/dispatcher.py index ea2fc1e8..660f82ec 100644 --- a/mopidy/frontends/mpd/dispatcher.py +++ b/mopidy/frontends/mpd/dispatcher.py @@ -20,10 +20,6 @@ from mopidy.utils import flatten logger = logging.getLogger('mopidy.frontends.mpd.dispatcher') -#: Subsystems that can be registered with idle command. -SUBSYSTEMS = ['database', 'mixer', 'options', 'output', - 'player', 'playlist', 'stored_playlist', 'update', ] - class MpdDispatcher(object): """ The MPD session feeds the MPD dispatcher with requests. The dispatcher @@ -132,59 +128,40 @@ class MpdDispatcher(object): ### Filter: idle def _idle_filter(self, request, response, filter_chain): - if re.match(r'^noidle$', request): - if not self.context.subscriptions: - return [] - self.context.subscriptions = set() - self.context.events = set() - self.context.session.connection.enable_timeout() - return [u'OK'] - - if self.context.subscriptions: + if self._is_currently_idle() and not self._is_noidle(request): + logger.debug(u'Client send us %s, only %s is allowed while in ' + 'the idle state', repr(request), repr('noidle')) self.context.session.close() return [] - if re.match(r'^idle( .+)?$', request): - for subsystem in self._extract_subsystems(request): - self.context.subscriptions.add(subsystem) + if not self._is_currently_idle() and self._is_noidle(request): + return [] - subsystems = self.context.subscriptions.intersection( - self.context.events) - if subsystems: - for subsystem in subsystems: - response.append(u'changed: %s' % subsystem) - self.context.events = set() - self.context.subscriptions = set() - response.append(u'OK') - return response - else: - self.context.session.connection.disable_timeout() - return [] + response = self._call_next_filter(request, response, filter_chain) - return self._call_next_filter(request, response, filter_chain) + if self._is_currently_idle(): + return [] + else: + return response - def _extract_subsystems(self, request): - match = re.match(r'^idle (?P.+)$', request) - if not match: - return SUBSYSTEMS - return match.groupdict()['subsystems'].split(' ') + def _is_currently_idle(self): + return bool(self.context.subscriptions) + + def _is_noidle(self, request): + return re.match(r'^noidle$', request) ### 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) and not self._is_idle(request): + 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') - def _is_idle(self, request): - return request.startswith('idle') - - ### Filter: call handler def _call_handler_filter(self, request, response, filter_chain): diff --git a/mopidy/frontends/mpd/protocol/reflection.py b/mopidy/frontends/mpd/protocol/reflection.py index 3618f5e1..dbd76034 100644 --- a/mopidy/frontends/mpd/protocol/reflection.py +++ b/mopidy/frontends/mpd/protocol/reflection.py @@ -27,10 +27,6 @@ def commands(context): 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') diff --git a/mopidy/frontends/mpd/protocol/status.py b/mopidy/frontends/mpd/protocol/status.py index 4a961e76..444ec0c2 100644 --- a/mopidy/frontends/mpd/protocol/status.py +++ b/mopidy/frontends/mpd/protocol/status.py @@ -4,6 +4,10 @@ from mopidy.backends.base import PlaybackController from mopidy.frontends.mpd.protocol import handle_request from mopidy.frontends.mpd.exceptions import MpdNotImplemented +#: Subsystems that can be registered with idle command. +SUBSYSTEMS = ['database', 'mixer', 'options', 'output', + 'player', 'playlist', 'stored_playlist', 'update', ] + @handle_request(r'^clearerror$') def clearerror(context): """ @@ -32,8 +36,8 @@ def currentsong(context): position=context.backend.playback.current_playlist_position.get(), cpid=current_cp_track.cpid) -#@handle_request(r'^idle$') -#@handle_request(r'^idle (?P.+)$') +@handle_request(r'^idle$') +@handle_request(r'^idle (?P.+)$') def idle(context, subsystems=None): """ *musicpd.org, status section:* @@ -67,12 +71,38 @@ def idle(context, subsystems=None): notifications when something changed in one of the specified subsystems. """ - pass # TODO -#@handle_request(r'^noidle$') + if subsystems: + subsystems = subsystems.split() + else: + subsystems = SUBSYSTEMS + + for subsystem in subsystems: + context.subscriptions.add(subsystem) + + active = context.subscriptions.intersection(context.events) + if not active: + context.session.connection.disable_timeout() + return + + response = [] + context.events = set() + context.subscriptions = set() + + for subsystem in active: + response.append(u'changed: %s' % subsystem) + response.append(u'OK') + + return response + +@handle_request(r'^noidle$') def noidle(context): """See :meth:`_status_idle`.""" - pass # TODO + if not context.subscriptions: + return + context.subscriptions = set() + context.events = set() + context.session.connection.enable_timeout() @handle_request(r'^stats$') def stats(context): diff --git a/tests/frontends/mpd/protocol/idle_test.py b/tests/frontends/mpd/protocol/idle_test.py index 3efb0cb1..0f56cd61 100644 --- a/tests/frontends/mpd/protocol/idle_test.py +++ b/tests/frontends/mpd/protocol/idle_test.py @@ -1,6 +1,6 @@ from mock import patch -from mopidy.frontends.mpd.dispatcher import SUBSYSTEMS +from mopidy.frontends.mpd.protocol.status import SUBSYSTEMS from mopidy.models import Track from tests.frontends.mpd import protocol