diff --git a/docs/changes.rst b/docs/changes.rst
index 2347ddb0..5d2ab57d 100644
--- a/docs/changes.rst
+++ b/docs/changes.rst
@@ -8,6 +8,10 @@ This change log is used to track all major changes to Mopidy.
v0.6.0 (in development)
=======================
+**Important changes**
+
+- Pykka 0.12.3 or greater is required.
+
**Changes**
- Replace :attr:`mopidy.backends.base.Backend.uri_handlers` with
diff --git a/docs/installation/index.rst b/docs/installation/index.rst
index 5101cc84..198ac9e8 100644
--- a/docs/installation/index.rst
+++ b/docs/installation/index.rst
@@ -25,7 +25,7 @@ Otherwise, make sure you got the required dependencies installed.
- Python >= 2.6, < 3
-- `Pykka `_ >= 0.12
+- `Pykka `_ >= 0.12.3
- GStreamer >= 0.10, with Python bindings. See :doc:`gstreamer`.
diff --git a/mopidy/backends/base/playback.py b/mopidy/backends/base/playback.py
index 07e286fa..088a5ad4 100644
--- a/mopidy/backends/base/playback.py
+++ b/mopidy/backends/base/playback.py
@@ -461,32 +461,30 @@ class PlaybackController(object):
self.current_cp_track = None
def _trigger_started_playing_event(self):
- """
- Notifies implementors of :class:`mopidy.listeners.BackendListener` that
- a track has started playing.
-
- For internal use only. Should be called by the backend directly after a
- track has started playing.
- """
+ logger.debug(u'Triggering started playing event')
if self.current_track is None:
return
- for listener_ref in ActorRegistry.get_by_class(BackendListener):
- listener_ref.proxy().started_playing(track=self.current_track)
+ ActorRegistry.broadcast({
+ 'command': 'pykka_call',
+ 'attr_path': ('started_playing',),
+ 'args': [],
+ 'kwargs': {'track': self.current_track},
+ }, target_class=BackendListener)
def _trigger_stopped_playing_event(self):
- """
- Notifies implementors of :class:`mopidy.listeners.BackendListener` that
- a track has stopped playing.
-
- For internal use only. Should be called by the backend before a track
- is stopped playing, e.g. at the next, previous, and stop actions and at
- end-of-track.
- """
+ # TODO Test that this is called on next/prev/end-of-track
+ logger.debug(u'Triggering stopped playing event')
if self.current_track is None:
return
- for listener_ref in ActorRegistry.get_by_class(BackendListener):
- listener_ref.proxy().stopped_playing(
- track=self.current_track, stop_position=self.time_position)
+ ActorRegistry.broadcast({
+ 'command': 'pykka_call',
+ 'attr_path': ('stopped_playing',),
+ 'args': [],
+ 'kwargs': {
+ 'track': self.current_track,
+ 'time_position': self.time_position,
+ },
+ }, target_class=BackendListener)
class BasePlaybackProvider(object):
diff --git a/mopidy/listeners.py b/mopidy/listeners.py
index f6d1c67e..dfc5c60b 100644
--- a/mopidy/listeners.py
+++ b/mopidy/listeners.py
@@ -20,7 +20,7 @@ class BackendListener(object):
"""
pass
- def stopped_playing(self, track, stop_position):
+ def stopped_playing(self, track, time_position):
"""
Called whenever playback is stopped.
@@ -28,7 +28,7 @@ class BackendListener(object):
:param track: the track that was played before playback stopped
:type track: :class:`mopidy.models.Track`
- :param stop_position: the time position when stopped in milliseconds
- :type stop_position: int
+ :param time_position: the time position in milliseconds
+ :type time_position: int
"""
pass
diff --git a/requirements/core.txt b/requirements/core.txt
index aaae84f8..8f9da622 100644
--- a/requirements/core.txt
+++ b/requirements/core.txt
@@ -1 +1 @@
-Pykka >= 0.12
+Pykka >= 0.12.3
diff --git a/tests/backends/events_test.py b/tests/backends/events_test.py
new file mode 100644
index 00000000..44529e90
--- /dev/null
+++ b/tests/backends/events_test.py
@@ -0,0 +1,45 @@
+import threading
+import unittest
+
+from pykka.actor import ThreadingActor
+from pykka.registry import ActorRegistry
+
+from mopidy.backends.dummy import DummyBackend
+from mopidy.listeners import BackendListener
+from mopidy.models import Track
+
+class BackendEventsTest(unittest.TestCase):
+ def setUp(self):
+ self.events = {
+ 'started_playing': threading.Event(),
+ 'stopped_playing': threading.Event(),
+ }
+ self.backend = DummyBackend.start().proxy()
+ self.listener = DummyBackendListener.start(self.events).proxy()
+
+ def tearDown(self):
+ ActorRegistry.stop_all()
+
+ def test_play_sends_started_playing_event(self):
+ self.backend.current_playlist.add([Track(uri='a')])
+ self.backend.playback.play()
+ self.events['started_playing'].wait(timeout=1)
+ self.assertTrue(self.events['started_playing'].is_set())
+
+ def test_stop_sends_stopped_playing_event(self):
+ self.backend.current_playlist.add([Track(uri='a')])
+ self.backend.playback.play()
+ self.backend.playback.stop()
+ self.events['stopped_playing'].wait(timeout=1)
+ self.assertTrue(self.events['stopped_playing'].is_set())
+
+
+class DummyBackendListener(ThreadingActor, BackendListener):
+ def __init__(self, events):
+ self.events = events
+
+ def started_playing(self, track):
+ self.events['started_playing'].set()
+
+ def stopped_playing(self, track, time_position):
+ self.events['stopped_playing'].set()