Merge branch 'develop' of https://github.com/mopidy/mopidy into fix/310-persist-mopidy-state-between-runs
This commit is contained in:
commit
bc981355bc
@ -95,7 +95,7 @@ MPD frontend
|
|||||||
|
|
||||||
- Start ``songid`` counting at 1 instead of 0 to match the original MPD server.
|
- Start ``songid`` counting at 1 instead of 0 to match the original MPD server.
|
||||||
|
|
||||||
- Idle events are now emitted on ``seekeded`` events. This fix means that
|
- Idle events are now emitted on ``seeked`` events. This fix means that
|
||||||
clients relying on ``idle`` events now get notified about seeks.
|
clients relying on ``idle`` events now get notified about seeks.
|
||||||
(Fixes: :issue:`1331` :issue:`1347`)
|
(Fixes: :issue:`1331` :issue:`1347`)
|
||||||
|
|
||||||
|
|||||||
29
docs/conf.py
29
docs/conf.py
@ -15,7 +15,6 @@ sys.path.insert(0, os.path.abspath(os.path.dirname(__file__) + '/../'))
|
|||||||
|
|
||||||
|
|
||||||
class Mock(object):
|
class Mock(object):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -27,39 +26,21 @@ class Mock(object):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
if name in ('__file__', '__path__'):
|
if name == 'get_system_config_dirs': # GLib.get_system_config_dirs()
|
||||||
return '/dev/null'
|
return list
|
||||||
elif name == 'get_system_config_dirs':
|
elif name == 'get_user_config_dir': # GLib.get_user_config_dir()
|
||||||
# glib.get_system_config_dirs()
|
|
||||||
return tuple
|
|
||||||
elif name == 'get_user_config_dir':
|
|
||||||
# glib.get_user_config_dir()
|
|
||||||
return str
|
return str
|
||||||
elif (name[0] == name[0].upper() and
|
|
||||||
# gst.Caps
|
|
||||||
not name.startswith('Caps') and
|
|
||||||
# gst.PadTemplate
|
|
||||||
not name.startswith('PadTemplate') and
|
|
||||||
# dbus.String()
|
|
||||||
not name == 'String'):
|
|
||||||
return type(name, (), {})
|
|
||||||
else:
|
else:
|
||||||
return Mock()
|
return Mock()
|
||||||
|
|
||||||
|
|
||||||
MOCK_MODULES = [
|
MOCK_MODULES = [
|
||||||
'dbus',
|
'dbus',
|
||||||
'dbus.mainloop',
|
'dbus.mainloop',
|
||||||
'dbus.mainloop.glib',
|
'dbus.mainloop.glib',
|
||||||
'dbus.service',
|
'dbus.service',
|
||||||
'glib',
|
'mopidy.internal.gi',
|
||||||
'gobject',
|
|
||||||
'gst',
|
|
||||||
'gst.pbutils',
|
|
||||||
'pygst',
|
|
||||||
'pykka',
|
'pykka',
|
||||||
'pykka.actor',
|
|
||||||
'pykka.future',
|
|
||||||
'pykka.registry',
|
|
||||||
]
|
]
|
||||||
for mod_name in MOCK_MODULES:
|
for mod_name in MOCK_MODULES:
|
||||||
sys.modules[mod_name] = Mock()
|
sys.modules[mod_name] = Mock()
|
||||||
|
|||||||
@ -7,14 +7,13 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from gi.repository import GLib, GObject
|
|
||||||
|
|
||||||
import pykka
|
import pykka
|
||||||
|
|
||||||
from mopidy import config as config_lib, exceptions
|
from mopidy import config as config_lib, exceptions
|
||||||
from mopidy.audio import Audio
|
from mopidy.audio import Audio
|
||||||
from mopidy.core import Core
|
from mopidy.core import Core
|
||||||
from mopidy.internal import deps, process, timer, versioning
|
from mopidy.internal import deps, process, timer, versioning
|
||||||
|
from mopidy.internal.gi import GLib, GObject
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|||||||
@ -627,7 +627,7 @@ class TracklistController(object):
|
|||||||
|
|
||||||
def _mark_played(self, tl_track):
|
def _mark_played(self, tl_track):
|
||||||
"""Internal method for :class:`mopidy.core.PlaybackController`."""
|
"""Internal method for :class:`mopidy.core.PlaybackController`."""
|
||||||
if self.consume and tl_track is not None:
|
if self.get_consume() and tl_track is not None:
|
||||||
self.remove({'tlid': [tl_track.tlid]})
|
self.remove({'tlid': [tl_track.tlid]})
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|||||||
@ -7,8 +7,7 @@ import textwrap
|
|||||||
try:
|
try:
|
||||||
import gi
|
import gi
|
||||||
gi.require_version('Gst', '1.0')
|
gi.require_version('Gst', '1.0')
|
||||||
gi.require_version('GstPbutils', '1.0')
|
from gi.repository import GLib, GObject, Gst
|
||||||
from gi.repository import GLib, GObject, Gst, GstPbutils
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
print(textwrap.dedent("""
|
print(textwrap.dedent("""
|
||||||
ERROR: A GObject Python package was not found.
|
ERROR: A GObject Python package was not found.
|
||||||
@ -22,7 +21,9 @@ except ImportError:
|
|||||||
"""))
|
"""))
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
Gst.is_initialized() or Gst.init()
|
Gst.init([])
|
||||||
|
gi.require_version('GstPbutils', '1.0')
|
||||||
|
from gi.repository import GstPbutils
|
||||||
|
|
||||||
|
|
||||||
REQUIRED_GST_VERSION = (1, 2, 3)
|
REQUIRED_GST_VERSION = (1, 2, 3)
|
||||||
|
|||||||
@ -7,11 +7,10 @@ import socket
|
|||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
from gi.repository import GObject
|
|
||||||
|
|
||||||
import pykka
|
import pykka
|
||||||
|
|
||||||
from mopidy.internal import encoding
|
from mopidy.internal import encoding
|
||||||
|
from mopidy.internal.gi import GObject
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|||||||
@ -4,8 +4,7 @@ import logging
|
|||||||
import signal
|
import signal
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
from pykka import ActorDeadError
|
import pykka
|
||||||
from pykka.registry import ActorRegistry
|
|
||||||
|
|
||||||
from mopidy.compat import thread
|
from mopidy.compat import thread
|
||||||
|
|
||||||
@ -31,14 +30,14 @@ def exit_handler(signum, frame):
|
|||||||
|
|
||||||
|
|
||||||
def stop_actors_by_class(klass):
|
def stop_actors_by_class(klass):
|
||||||
actors = ActorRegistry.get_by_class(klass)
|
actors = pykka.ActorRegistry.get_by_class(klass)
|
||||||
logger.debug('Stopping %d instance(s) of %s', len(actors), klass.__name__)
|
logger.debug('Stopping %d instance(s) of %s', len(actors), klass.__name__)
|
||||||
for actor in actors:
|
for actor in actors:
|
||||||
actor.stop()
|
actor.stop()
|
||||||
|
|
||||||
|
|
||||||
def stop_remaining_actors():
|
def stop_remaining_actors():
|
||||||
num_actors = len(ActorRegistry.get_all())
|
num_actors = len(pykka.ActorRegistry.get_all())
|
||||||
while num_actors:
|
while num_actors:
|
||||||
logger.error(
|
logger.error(
|
||||||
'There are actor threads still running, this is probably a bug')
|
'There are actor threads still running, this is probably a bug')
|
||||||
@ -47,8 +46,8 @@ def stop_remaining_actors():
|
|||||||
num_actors, threading.active_count() - num_actors,
|
num_actors, threading.active_count() - num_actors,
|
||||||
', '.join([t.name for t in threading.enumerate()]))
|
', '.join([t.name for t in threading.enumerate()]))
|
||||||
logger.debug('Stopping %d actor(s)...', num_actors)
|
logger.debug('Stopping %d actor(s)...', num_actors)
|
||||||
ActorRegistry.stop_all()
|
pykka.ActorRegistry.stop_all()
|
||||||
num_actors = len(ActorRegistry.get_all())
|
num_actors = len(pykka.ActorRegistry.get_all())
|
||||||
logger.debug('All actors stopped.')
|
logger.debug('All actors stopped.')
|
||||||
|
|
||||||
|
|
||||||
@ -67,7 +66,7 @@ class BaseThread(threading.Thread):
|
|||||||
logger.info('Interrupted by user')
|
logger.info('Interrupted by user')
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
except ActorDeadError as e:
|
except pykka.ActorDeadError as e:
|
||||||
logger.warning(e)
|
logger.warning(e)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(e)
|
logger.exception(e)
|
||||||
|
|||||||
@ -232,21 +232,6 @@ class TestPreviousHandling(BaseTest):
|
|||||||
self.assertIn(tl_tracks[1], self.core.tracklist.tl_tracks)
|
self.assertIn(tl_tracks[1], self.core.tracklist.tl_tracks)
|
||||||
|
|
||||||
|
|
||||||
class TestPlayUnknownHandling(BaseTest):
|
|
||||||
|
|
||||||
tracks = [Track(uri='unknown:a', length=1234),
|
|
||||||
Track(uri='dummy:b', length=1234)]
|
|
||||||
|
|
||||||
# TODO: move to UnplayableTest?
|
|
||||||
def test_play_skips_to_next_on_track_without_playback_backend(self):
|
|
||||||
self.core.playback.play()
|
|
||||||
|
|
||||||
self.replay_events()
|
|
||||||
|
|
||||||
current_track = self.core.playback.get_current_track()
|
|
||||||
self.assertEqual(current_track, self.tracks[1])
|
|
||||||
|
|
||||||
|
|
||||||
class OnAboutToFinishTest(BaseTest):
|
class OnAboutToFinishTest(BaseTest):
|
||||||
|
|
||||||
def test_on_about_to_finish_keeps_finished_track_in_tracklist(self):
|
def test_on_about_to_finish_keeps_finished_track_in_tracklist(self):
|
||||||
@ -622,14 +607,26 @@ class EventEmissionTest(BaseTest):
|
|||||||
listener_mock.send.mock_calls)
|
listener_mock.send.mock_calls)
|
||||||
|
|
||||||
|
|
||||||
class UnplayableURITest(BaseTest):
|
class TestUnplayableURI(BaseTest):
|
||||||
|
|
||||||
|
tracks = [
|
||||||
|
Track(uri='unplayable://'),
|
||||||
|
Track(uri='dummy:b'),
|
||||||
|
]
|
||||||
|
|
||||||
def setUp(self): # noqa: N802
|
def setUp(self): # noqa: N802
|
||||||
super(UnplayableURITest, self).setUp()
|
super(TestUnplayableURI, self).setUp()
|
||||||
self.core.tracklist.clear()
|
tl_tracks = self.core.tracklist.get_tl_tracks()
|
||||||
tl_tracks = self.core.tracklist.add([Track(uri='unplayable://')])
|
|
||||||
self.core.playback._set_current_tl_track(tl_tracks[0])
|
self.core.playback._set_current_tl_track(tl_tracks[0])
|
||||||
|
|
||||||
|
def test_play_skips_to_next_if_track_is_unplayable(self):
|
||||||
|
self.core.playback.play()
|
||||||
|
|
||||||
|
self.replay_events()
|
||||||
|
|
||||||
|
current_track = self.core.playback.get_current_track()
|
||||||
|
self.assertEqual(current_track, self.tracks[1])
|
||||||
|
|
||||||
def test_pause_changes_state_even_if_track_is_unplayable(self):
|
def test_pause_changes_state_even_if_track_is_unplayable(self):
|
||||||
self.core.playback.pause()
|
self.core.playback.pause()
|
||||||
self.assertEqual(self.core.playback.state, core.PlaybackState.PAUSED)
|
self.assertEqual(self.core.playback.state, core.PlaybackState.PAUSED)
|
||||||
@ -769,7 +766,7 @@ class TestStream(BaseTest):
|
|||||||
self.assertEqual(self.playback.get_stream_title(), None)
|
self.assertEqual(self.playback.get_stream_title(), None)
|
||||||
|
|
||||||
|
|
||||||
class BackendSelectionTest(unittest.TestCase):
|
class TestBackendSelection(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self): # noqa: N802
|
def setUp(self): # noqa: N802
|
||||||
config = {
|
config = {
|
||||||
@ -918,7 +915,7 @@ class BackendSelectionTest(unittest.TestCase):
|
|||||||
self.playback2.get_time_position.assert_called_once_with()
|
self.playback2.get_time_position.assert_called_once_with()
|
||||||
|
|
||||||
|
|
||||||
class CorePlaybackWithOldBackendTest(unittest.TestCase):
|
class TestCorePlaybackWithOldBackend(unittest.TestCase):
|
||||||
|
|
||||||
def test_type_error_from_old_backend_does_not_crash_core(self):
|
def test_type_error_from_old_backend_does_not_crash_core(self):
|
||||||
config = {
|
config = {
|
||||||
@ -941,7 +938,7 @@ class CorePlaybackWithOldBackendTest(unittest.TestCase):
|
|||||||
b.playback.play.assert_called_once_with()
|
b.playback.play.assert_called_once_with()
|
||||||
|
|
||||||
|
|
||||||
class Bug1177RegressionTest(unittest.TestCase):
|
class TestBug1177Regression(unittest.TestCase):
|
||||||
def test(self):
|
def test(self):
|
||||||
config = {
|
config = {
|
||||||
'core': {
|
'core': {
|
||||||
|
|||||||
@ -5,13 +5,12 @@ import logging
|
|||||||
import socket
|
import socket
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from gi.repository import GObject
|
|
||||||
|
|
||||||
from mock import Mock, call, patch, sentinel
|
from mock import Mock, call, patch, sentinel
|
||||||
|
|
||||||
import pykka
|
import pykka
|
||||||
|
|
||||||
from mopidy.internal import network
|
from mopidy.internal import network
|
||||||
|
from mopidy.internal.gi import GObject
|
||||||
|
|
||||||
from tests import any_int, any_unicode
|
from tests import any_int, any_unicode
|
||||||
|
|
||||||
|
|||||||
@ -4,11 +4,10 @@ import errno
|
|||||||
import socket
|
import socket
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from gi.repository import GObject
|
|
||||||
|
|
||||||
from mock import Mock, patch, sentinel
|
from mock import Mock, patch, sentinel
|
||||||
|
|
||||||
from mopidy.internal import network
|
from mopidy.internal import network
|
||||||
|
from mopidy.internal.gi import GObject
|
||||||
|
|
||||||
from tests import any_int
|
from tests import any_int
|
||||||
|
|
||||||
|
|||||||
@ -7,10 +7,9 @@ import shutil
|
|||||||
import tempfile
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from gi.repository import GLib
|
|
||||||
|
|
||||||
from mopidy import compat, exceptions
|
from mopidy import compat, exceptions
|
||||||
from mopidy.internal import path
|
from mopidy.internal import path
|
||||||
|
from mopidy.internal.gi import GLib
|
||||||
|
|
||||||
import tests
|
import tests
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user