From 4729605a6b6579e07ccee37bc3808ab0be3714e9 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Fri, 19 Mar 2010 21:11:42 +0100 Subject: [PATCH 01/14] Add process ID to log format --- mopidy/settings/default.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mopidy/settings/default.py b/mopidy/settings/default.py index 7fd41fa6..7fdb9f14 100644 --- a/mopidy/settings/default.py +++ b/mopidy/settings/default.py @@ -23,7 +23,7 @@ BACKENDS = ( #: The log format used on the console. See #: http://docs.python.org/library/logging.html#formatter-objects for details on #: the format. -CONSOLE_LOG_FORMAT = u'%(levelname)-8s %(asctime)s [%(threadName)s] %(name)s\n %(message)s' +CONSOLE_LOG_FORMAT = u'%(levelname)-8s %(asctime)s [%(process)d:%(threadName)s] %(name)s\n %(message)s' #: Sound mixer to use. See :mod:`mopidy.mixers` for all available mixers. #: From 34b7f679f569f7c885c0dff86b3a8331ff2ecbd5 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Fri, 19 Mar 2010 21:55:17 +0100 Subject: [PATCH 02/14] Move mopidy.__main__._get_class to mopidy.get_class --- mopidy/__init__.py | 12 ++++++++++++ mopidy/__main__.py | 14 +++----------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/mopidy/__init__.py b/mopidy/__init__.py index 176bef6a..89605726 100644 --- a/mopidy/__init__.py +++ b/mopidy/__init__.py @@ -1,11 +1,23 @@ +import logging + from mopidy import settings as raw_settings +logger = logging.getLogger('mopidy') + def get_version(): return u'0.1.dev' def get_mpd_protocol_version(): return u'0.16.0' +def get_class(name): + module_name = name[:name.rindex('.')] + class_name = name[name.rindex('.') + 1:] + logger.info('Loading: %s from %s', class_name, module_name) + module = __import__(module_name, globals(), locals(), [class_name], -1) + class_object = getattr(module, class_name) + return class_object + class SettingsError(Exception): pass diff --git a/mopidy/__main__.py b/mopidy/__main__.py index d2cf09a7..0ffb0391 100644 --- a/mopidy/__main__.py +++ b/mopidy/__main__.py @@ -6,15 +6,15 @@ import sys sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../'))) -from mopidy import settings, SettingsError +from mopidy import get_class, settings, SettingsError from mopidy.mpd.server import MpdServer logger = logging.getLogger('mopidy') def main(): _setup_logging(2) - mixer = _get_class(settings.MIXER)() - backend = _get_class(settings.BACKENDS[0])(mixer=mixer) + mixer = get_class(settings.MIXER)() + backend = get_class(settings.BACKENDS[0])(mixer=mixer) MpdServer(backend=backend) asyncore.loop() @@ -30,14 +30,6 @@ def _setup_logging(verbosity_level): level=level, ) -def _get_class(name): - module_name = name[:name.rindex('.')] - class_name = name[name.rindex('.') + 1:] - logger.info('Loading: %s from %s', class_name, module_name) - module = __import__(module_name, globals(), locals(), [class_name], -1) - class_object = getattr(module, class_name) - return class_object - if __name__ == '__main__': try: main() From be9614da32a3c626d2a8e434a43d411d30451f7f Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Fri, 19 Mar 2010 21:55:41 +0100 Subject: [PATCH 03/14] Add todo list for multiprocessing branch --- mopidy/__main__.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mopidy/__main__.py b/mopidy/__main__.py index 0ffb0391..111c0b5e 100644 --- a/mopidy/__main__.py +++ b/mopidy/__main__.py @@ -13,6 +13,15 @@ logger = logging.getLogger('mopidy') def main(): _setup_logging(2) + + # multiprocessing branch plan + # --------------------------- + # + # TODO Init backend in new Process (named core?) + # TODO Init mixer from backend + # TODO Init MpdHandler from backend/core + # TODO Init MpdServer in MainThread or in new Process? + mixer = get_class(settings.MIXER)() backend = get_class(settings.BACKENDS[0])(mixer=mixer) MpdServer(backend=backend) From 194f0e543edb1598b88f9f20562a92c52a5f0122 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Fri, 19 Mar 2010 22:05:43 +0100 Subject: [PATCH 04/14] Move mixer initialization from main() into backend --- mopidy/__main__.py | 4 +--- mopidy/backends/__init__.py | 5 +++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/mopidy/__main__.py b/mopidy/__main__.py index 111c0b5e..07f571ce 100644 --- a/mopidy/__main__.py +++ b/mopidy/__main__.py @@ -18,12 +18,10 @@ def main(): # --------------------------- # # TODO Init backend in new Process (named core?) - # TODO Init mixer from backend # TODO Init MpdHandler from backend/core # TODO Init MpdServer in MainThread or in new Process? - mixer = get_class(settings.MIXER)() - backend = get_class(settings.BACKENDS[0])(mixer=mixer) + backend = get_class(settings.BACKENDS[0])() MpdServer(backend=backend) asyncore.loop() diff --git a/mopidy/backends/__init__.py b/mopidy/backends/__init__.py index 93b74498..280458a4 100644 --- a/mopidy/backends/__init__.py +++ b/mopidy/backends/__init__.py @@ -3,13 +3,14 @@ import logging import random import time +from mopidy import get_class, settings from mopidy.models import Playlist logger = logging.getLogger('backends.base') class BaseBackend(object): - def __init__(self, mixer=None): - self.mixer = mixer + def __init__(self): + self.mixer = get_class(settings.MIXER)() #: The current playlist controller. An instance of #: :class:`BaseCurrentPlaylistController`. From feda5db67f8bcba08999eccc9fefec8e9628f82f Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Fri, 19 Mar 2010 22:52:49 +0100 Subject: [PATCH 05/14] Move backend init into new CoreProcess --- mopidy/__main__.py | 19 +++++++++++++++---- mopidy/core.py | 23 +++++++++++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 mopidy/core.py diff --git a/mopidy/__main__.py b/mopidy/__main__.py index 07f571ce..84b1d6a5 100644 --- a/mopidy/__main__.py +++ b/mopidy/__main__.py @@ -1,5 +1,6 @@ import asyncore import logging +from multiprocessing import Queue import os import sys @@ -7,6 +8,7 @@ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../'))) from mopidy import get_class, settings, SettingsError +from mopidy.core import CoreProcess from mopidy.mpd.server import MpdServer logger = logging.getLogger('mopidy') @@ -17,13 +19,22 @@ def main(): # multiprocessing branch plan # --------------------------- # - # TODO Init backend in new Process (named core?) # TODO Init MpdHandler from backend/core # TODO Init MpdServer in MainThread or in new Process? - backend = get_class(settings.BACKENDS[0])() - MpdServer(backend=backend) - asyncore.loop() + main_queue = Queue() + core_queue = Queue() + server_queue = Queue() + core = CoreProcess(core_queue=core_queue, + main_queue=main_queue, server_queue=server_queue) + core.start() + while True: + message = main_queue.get() + if message['command'] == 'core_ready': + MpdServer(backend=None) + asyncore.loop() + else: + logger.warning(u'Cannot handle message: %s', message) def _setup_logging(verbosity_level): if verbosity_level == 0: diff --git a/mopidy/core.py b/mopidy/core.py new file mode 100644 index 00000000..bd48c818 --- /dev/null +++ b/mopidy/core.py @@ -0,0 +1,23 @@ +import logging +from multiprocessing import Process, Queue + +from mopidy import get_class, settings + +logger = logging.getLogger('mopidy.core') + +class CoreProcess(Process): + def __init__(self, core_queue=None, main_queue=None, server_queue=None): + Process.__init__(self) + self.queue = core_queue + self.main_queue = main_queue + self.server_queue = server_queue + + def run(self): + self._setup() + while True: + message = self.queue.get() + # TODO Do something with the message + + def _setup(self): + self.backend = get_class(settings.BACKENDS[0])() + self.main_queue.put({'command': 'core_ready'}) From d9a71ca6f9d7a215328682c4b0aad5642f6bd9cc Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Fri, 19 Mar 2010 23:07:06 +0100 Subject: [PATCH 06/14] Make mixer injectable again to make the tests happy --- mopidy/backends/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/mopidy/backends/__init__.py b/mopidy/backends/__init__.py index 280458a4..d20b6c50 100644 --- a/mopidy/backends/__init__.py +++ b/mopidy/backends/__init__.py @@ -9,8 +9,11 @@ from mopidy.models import Playlist logger = logging.getLogger('backends.base') class BaseBackend(object): - def __init__(self): - self.mixer = get_class(settings.MIXER)() + def __init__(self, mixer=None): + if mixer is not None: + self.mixer = mixer + else: + self.mixer = get_class(settings.MIXER)() #: The current playlist controller. An instance of #: :class:`BaseCurrentPlaylistController`. From 65fcfbfae9ef1a68d324aea932f983f7edd00cdf Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 20 Mar 2010 00:34:27 +0100 Subject: [PATCH 07/14] Add util functions for pickling and unpickling multiprocessing.Connection --- mopidy/__init__.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mopidy/__init__.py b/mopidy/__init__.py index 89605726..5770716f 100644 --- a/mopidy/__init__.py +++ b/mopidy/__init__.py @@ -1,4 +1,6 @@ import logging +from multiprocessing.reduction import reduce_connection +import pickle from mopidy import settings as raw_settings @@ -18,6 +20,16 @@ def get_class(name): class_object = getattr(module, class_name) return class_object +def pickle_connection(connection): + return pickle.dumps(reduce_connection(connection)) + +def unpickle_connection(pickled_connection): + # From http://stackoverflow.com/questions/1446004 + unpickled = pickle.loads(pickled_connection) + func = unpickled[0] + args = unpickled[1] + return func(*args) + class SettingsError(Exception): pass From af4d1f702e607c290bd1f8e2ca5302c5c737ceba Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 20 Mar 2010 00:35:50 +0100 Subject: [PATCH 08/14] Add FRONTEND setting --- mopidy/settings/default.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mopidy/settings/default.py b/mopidy/settings/default.py index 7fdb9f14..9f9cdded 100644 --- a/mopidy/settings/default.py +++ b/mopidy/settings/default.py @@ -25,6 +25,11 @@ BACKENDS = ( #: the format. CONSOLE_LOG_FORMAT = u'%(levelname)-8s %(asctime)s [%(process)d:%(threadName)s] %(name)s\n %(message)s' +#: Protocol frontend to use. Default:: +#: +#: FRONTEND = u'mopidy.mpd.handler.MpdHandler' +FRONTEND = u'mopidy.mpd.handler.MpdHandler' + #: Sound mixer to use. See :mod:`mopidy.mixers` for all available mixers. #: #: Default on Linux:: From 0e0a9e67dd0fb6028fc56617bb8ac4e536180dd4 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 20 Mar 2010 00:36:43 +0100 Subject: [PATCH 09/14] Move MpdHandler usage from MpdSession to CoreProcess --- mopidy/__main__.py | 3 +-- mopidy/core.py | 23 +++++++++++++---------- mopidy/mpd/server.py | 6 +++--- mopidy/mpd/session.py | 18 ++++++++++++------ 4 files changed, 29 insertions(+), 21 deletions(-) diff --git a/mopidy/__main__.py b/mopidy/__main__.py index 84b1d6a5..ecd82216 100644 --- a/mopidy/__main__.py +++ b/mopidy/__main__.py @@ -19,7 +19,6 @@ def main(): # multiprocessing branch plan # --------------------------- # - # TODO Init MpdHandler from backend/core # TODO Init MpdServer in MainThread or in new Process? main_queue = Queue() @@ -31,7 +30,7 @@ def main(): while True: message = main_queue.get() if message['command'] == 'core_ready': - MpdServer(backend=None) + MpdServer(core_queue=core_queue) asyncore.loop() else: logger.warning(u'Cannot handle message: %s', message) diff --git a/mopidy/core.py b/mopidy/core.py index bd48c818..bb1674ed 100644 --- a/mopidy/core.py +++ b/mopidy/core.py @@ -1,23 +1,26 @@ import logging -from multiprocessing import Process, Queue +import multiprocessing -from mopidy import get_class, settings +from mopidy import get_class, settings, unpickle_connection logger = logging.getLogger('mopidy.core') -class CoreProcess(Process): +class CoreProcess(multiprocessing.Process): def __init__(self, core_queue=None, main_queue=None, server_queue=None): - Process.__init__(self) + multiprocessing.Process.__init__(self) self.queue = core_queue self.main_queue = main_queue self.server_queue = server_queue def run(self): - self._setup() + backend = get_class(settings.BACKENDS[0])() + frontend = get_class(settings.FRONTEND)(backend=backend) + self.main_queue.put({'command': 'core_ready'}) while True: message = self.queue.get() - # TODO Do something with the message - - def _setup(self): - self.backend = get_class(settings.BACKENDS[0])() - self.main_queue.put({'command': 'core_ready'}) + if message['command'] == 'mpd_request': + response = frontend.handle_request(message['request']) + connection = unpickle_connection(message['reply_to']) + connection.send(response) + else: + logger.warning(u'Cannot handle message: %s', message) diff --git a/mopidy/mpd/server.py b/mopidy/mpd/server.py index e0173574..270f1f6b 100644 --- a/mopidy/mpd/server.py +++ b/mopidy/mpd/server.py @@ -10,10 +10,10 @@ from mopidy.mpd.session import MpdSession logger = logging.getLogger(u'mpd.server') class MpdServer(asyncore.dispatcher): - def __init__(self, session_class=MpdSession, backend=None): + def __init__(self, session_class=MpdSession, core_queue=None): asyncore.dispatcher.__init__(self) self.session_class = session_class - self.backend = backend + self.core_queue = core_queue self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.set_reuse_addr() self.bind((settings.MPD_SERVER_HOSTNAME, settings.MPD_SERVER_PORT)) @@ -26,7 +26,7 @@ class MpdServer(asyncore.dispatcher): (client_socket, client_address) = self.accept() logger.info(u'Connection from: [%s]:%s', *client_address) self.session_class(self, client_socket, client_address, - backend=self.backend) + core_queue=self.core_queue) def handle_close(self): self.close() diff --git a/mopidy/mpd/session.py b/mopidy/mpd/session.py index 19d8c1c5..4c9bb0c5 100644 --- a/mopidy/mpd/session.py +++ b/mopidy/mpd/session.py @@ -1,9 +1,9 @@ import asynchat import logging +import multiprocessing -from mopidy import get_mpd_protocol_version +from mopidy import get_mpd_protocol_version, pickle_connection from mopidy.mpd import MpdAckError -from mopidy.mpd.handler import MpdHandler logger = logging.getLogger(u'mpd.session') @@ -22,14 +22,13 @@ def indent(string, places=4, linebreak=LINE_TERMINATOR): return result class MpdSession(asynchat.async_chat): - def __init__(self, server, client_socket, client_address, backend, - handler_class=MpdHandler): + def __init__(self, server, client_socket, client_address, core_queue): asynchat.async_chat.__init__(self, sock=client_socket) self.server = server self.client_address = client_address + self.core_queue = core_queue self.input_buffer = [] self.set_terminator(LINE_TERMINATOR.encode(ENCODING)) - self.handler = handler_class(session=self, backend=backend) self.send_response(u'OK MPD %s' % get_mpd_protocol_version()) def do_close(self): @@ -51,7 +50,14 @@ class MpdSession(asynchat.async_chat): def handle_request(self, input): try: - response = self.handler.handle_request(input) + my_end, other_end = multiprocessing.Pipe() + self.core_queue.put({ + 'command': 'mpd_request', + 'request': input, + 'reply_to': pickle_connection(other_end), + }) + my_end.poll(None) + response = my_end.recv() if response is not None: self.handle_response(response) except MpdAckError, e: From 1faecdf496ecf909e043a79a41b6c92a982d0051 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 20 Mar 2010 01:05:33 +0100 Subject: [PATCH 10/14] Add SERVER setting. Remove MPD_ prefix from existing server settings. --- mopidy/mpd/server.py | 4 ++-- mopidy/settings/default.py | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/mopidy/mpd/server.py b/mopidy/mpd/server.py index 270f1f6b..a61e2aa8 100644 --- a/mopidy/mpd/server.py +++ b/mopidy/mpd/server.py @@ -16,11 +16,11 @@ class MpdServer(asyncore.dispatcher): self.core_queue = core_queue self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.set_reuse_addr() - self.bind((settings.MPD_SERVER_HOSTNAME, settings.MPD_SERVER_PORT)) + self.bind((settings.SERVER_HOSTNAME, settings.SERVER_PORT)) self.listen(1) self.started_at = int(time.time()) logger.info(u'Please connect to %s port %s using an MPD client.', - settings.MPD_SERVER_HOSTNAME, settings.MPD_SERVER_PORT) + settings.SERVER_HOSTNAME, settings.SERVER_PORT) def handle_accept(self): (client_socket, client_address) = self.accept() diff --git a/mopidy/settings/default.py b/mopidy/settings/default.py index 9f9cdded..874c2f87 100644 --- a/mopidy/settings/default.py +++ b/mopidy/settings/default.py @@ -70,16 +70,21 @@ MIXER_EXT_SPEAKERS_A = None #: *Default:* :class:`None`. MIXER_EXT_SPEAKERS_B = None +#: Server to use. Default:: +#: +#: SERVER = u'mopidy.mpd.server.MpdServer' +SERVER = u'mopidy.mpd.server.MpdServer' + #: Which address Mopidy should bind to. Examples: #: #: ``localhost`` #: Listens only on the loopback interface. *Default.* #: ``0.0.0.0`` #: Listens on all interfaces. -MPD_SERVER_HOSTNAME = u'localhost' +SERVER_HOSTNAME = u'localhost' #: Which TCP port Mopidy should listen to. *Default: 6600* -MPD_SERVER_PORT = 6600 +SERVER_PORT = 6600 #: Your Spotify Premium username. Used by all Spotify backends. SPOTIFY_USERNAME = u'' From e5be3ea6407f56229f826aa4b77d106fb6e9c8fa Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 20 Mar 2010 01:06:22 +0100 Subject: [PATCH 11/14] Remove plan for server in own process. Remove redundant blocking of server until CoreProcess is ready. --- mopidy/__main__.py | 27 ++++++--------------------- mopidy/core.py | 9 +++------ 2 files changed, 9 insertions(+), 27 deletions(-) diff --git a/mopidy/__main__.py b/mopidy/__main__.py index ecd82216..0666d99b 100644 --- a/mopidy/__main__.py +++ b/mopidy/__main__.py @@ -1,6 +1,6 @@ import asyncore import logging -from multiprocessing import Queue +import multiprocessing import os import sys @@ -9,31 +9,16 @@ sys.path.insert(0, from mopidy import get_class, settings, SettingsError from mopidy.core import CoreProcess -from mopidy.mpd.server import MpdServer -logger = logging.getLogger('mopidy') +logger = logging.getLogger('mopidy.main') def main(): _setup_logging(2) - - # multiprocessing branch plan - # --------------------------- - # - # TODO Init MpdServer in MainThread or in new Process? - - main_queue = Queue() - core_queue = Queue() - server_queue = Queue() - core = CoreProcess(core_queue=core_queue, - main_queue=main_queue, server_queue=server_queue) + core_queue = multiprocessing.Queue() + core = CoreProcess(core_queue) core.start() - while True: - message = main_queue.get() - if message['command'] == 'core_ready': - MpdServer(core_queue=core_queue) - asyncore.loop() - else: - logger.warning(u'Cannot handle message: %s', message) + get_class(settings.SERVER)(core_queue=core_queue) + asyncore.loop() def _setup_logging(verbosity_level): if verbosity_level == 0: diff --git a/mopidy/core.py b/mopidy/core.py index bb1674ed..374945d4 100644 --- a/mopidy/core.py +++ b/mopidy/core.py @@ -6,18 +6,15 @@ from mopidy import get_class, settings, unpickle_connection logger = logging.getLogger('mopidy.core') class CoreProcess(multiprocessing.Process): - def __init__(self, core_queue=None, main_queue=None, server_queue=None): + def __init__(self, core_queue): multiprocessing.Process.__init__(self) - self.queue = core_queue - self.main_queue = main_queue - self.server_queue = server_queue + self.core_queue = core_queue def run(self): backend = get_class(settings.BACKENDS[0])() frontend = get_class(settings.FRONTEND)(backend=backend) - self.main_queue.put({'command': 'core_ready'}) while True: - message = self.queue.get() + message = self.core_queue.get() if message['command'] == 'mpd_request': response = frontend.handle_request(message['request']) connection = unpickle_connection(message['reply_to']) From aa8b67a327a487ec694535714e352177cfb568c1 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 20 Mar 2010 01:20:21 +0100 Subject: [PATCH 12/14] Make core_queue available for backends --- mopidy/backends/__init__.py | 7 ++++++- mopidy/core.py | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/mopidy/backends/__init__.py b/mopidy/backends/__init__.py index d20b6c50..14b3a40b 100644 --- a/mopidy/backends/__init__.py +++ b/mopidy/backends/__init__.py @@ -9,12 +9,17 @@ from mopidy.models import Playlist logger = logging.getLogger('backends.base') class BaseBackend(object): - def __init__(self, mixer=None): + def __init__(self, core_queue=None, mixer=None): + self.core_queue = core_queue if mixer is not None: self.mixer = mixer else: self.mixer = get_class(settings.MIXER)() + #: A :class:`multiprocessing.Queue` which can be used by e.g. library + #: callbacks to send messages to the core. + core_queue = None + #: The current playlist controller. An instance of #: :class:`BaseCurrentPlaylistController`. current_playlist = None diff --git a/mopidy/core.py b/mopidy/core.py index 374945d4..54d1e37b 100644 --- a/mopidy/core.py +++ b/mopidy/core.py @@ -11,7 +11,7 @@ class CoreProcess(multiprocessing.Process): self.core_queue = core_queue def run(self): - backend = get_class(settings.BACKENDS[0])() + backend = get_class(settings.BACKENDS[0])(core_queue=self.core_queue) frontend = get_class(settings.FRONTEND)(backend=backend) while True: message = self.core_queue.get() From be769652823b667630d5290f648ccbd3ce7678b2 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 20 Mar 2010 01:20:51 +0100 Subject: [PATCH 13/14] Add end_of_track supportto despotify --- mopidy/backends/__init__.py | 4 ++++ mopidy/backends/despotify.py | 6 ++++-- mopidy/core.py | 2 ++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/mopidy/backends/__init__.py b/mopidy/backends/__init__.py index 14b3a40b..f5317ac6 100644 --- a/mopidy/backends/__init__.py +++ b/mopidy/backends/__init__.py @@ -388,6 +388,10 @@ class BasePlaybackController(object): def volume(self, volume): self.backend.mixer.volume = volume + def end_of_track_callback(self): + """Tell the playback controller that end of track is reached.""" + self.next() + def new_playlist_loaded_callback(self): """Tell the playback controller that a new playlist has been loaded.""" self.current_track = None diff --git a/mopidy/backends/despotify.py b/mopidy/backends/despotify.py index 5bd3552b..11ad45c5 100644 --- a/mopidy/backends/despotify.py +++ b/mopidy/backends/despotify.py @@ -53,7 +53,8 @@ class DespotifyBackend(BaseBackend): logger.info(u'Connecting to Spotify') return DespotifySessionManager( settings.SPOTIFY_USERNAME.encode(ENCODING), - settings.SPOTIFY_PASSWORD.encode(ENCODING)) + settings.SPOTIFY_PASSWORD.encode(ENCODING), + core_queue=self.core_queue) class DespotifyCurrentPlaylistController(BaseCurrentPlaylistController): @@ -164,9 +165,10 @@ class DespotifySessionManager(spytify.Spytify): def __init__(self, *args, **kwargs): kwargs['callback'] = self.callback + self.core_queue = kwargs.pop('core_queue') super(DespotifySessionManager, self).__init__(*args, **kwargs) def callback(self, signal, data): if signal == self.DESPOTIFY_END_OF_PLAYLIST: logger.debug('Despotify signalled end of playlist') - # TODO Ask backend to play next track + self.core_queue.put({'command': 'end_of_track'}) diff --git a/mopidy/core.py b/mopidy/core.py index 54d1e37b..acb3b984 100644 --- a/mopidy/core.py +++ b/mopidy/core.py @@ -19,5 +19,7 @@ class CoreProcess(multiprocessing.Process): response = frontend.handle_request(message['request']) connection = unpickle_connection(message['reply_to']) connection.send(response) + elif message['command'] == 'end_of_track': + backend.playback.end_of_track_callback() else: logger.warning(u'Cannot handle message: %s', message) From dda49dde43162a0e3a4603d44b42d505d9f2bfc5 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 20 Mar 2010 01:28:17 +0100 Subject: [PATCH 14/14] If no next track, stop at end of track. --- mopidy/backends/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mopidy/backends/__init__.py b/mopidy/backends/__init__.py index f5317ac6..16f41b36 100644 --- a/mopidy/backends/__init__.py +++ b/mopidy/backends/__init__.py @@ -390,7 +390,10 @@ class BasePlaybackController(object): def end_of_track_callback(self): """Tell the playback controller that end of track is reached.""" - self.next() + if self.next_track is not None: + self.next() + else: + self.stop() def new_playlist_loaded_callback(self): """Tell the playback controller that a new playlist has been loaded."""