Split xPlaybackController into xPlaybackController and xPlaybackProvider
This commit is contained in:
parent
62ae85d05a
commit
704b5517e1
@ -6,7 +6,8 @@ import time
|
||||
from mopidy import settings
|
||||
from mopidy.backends.base.current_playlist import BaseCurrentPlaylistController
|
||||
from mopidy.backends.base.library import BaseLibraryController
|
||||
from mopidy.backends.base.playback import BasePlaybackController
|
||||
from mopidy.backends.base.playback import (BasePlaybackController,
|
||||
BasePlaybackProvider)
|
||||
from mopidy.backends.base.stored_playlists import BaseStoredPlaylistsController
|
||||
from mopidy.frontends.mpd import translator
|
||||
from mopidy.models import Playlist
|
||||
|
||||
@ -6,8 +6,10 @@ logger = logging.getLogger('mopidy.backends.base')
|
||||
|
||||
class BasePlaybackController(object):
|
||||
"""
|
||||
:param backend: backend the controller is a part of
|
||||
:param backend: the backend
|
||||
:type backend: :class:`BaseBackend`
|
||||
:param provider: provider the controller should use
|
||||
:type provider: instance of :class:`BasePlaybackProvider`
|
||||
"""
|
||||
|
||||
# pylint: disable = R0902
|
||||
@ -54,8 +56,9 @@ class BasePlaybackController(object):
|
||||
#: Playback continues after current song.
|
||||
single = False
|
||||
|
||||
def __init__(self, backend):
|
||||
def __init__(self, backend, provider):
|
||||
self.backend = backend
|
||||
self.provider = provider
|
||||
self._state = self.STOPPED
|
||||
self._shuffled = []
|
||||
self._first_shuffle = True
|
||||
@ -353,18 +356,9 @@ class BasePlaybackController(object):
|
||||
|
||||
def pause(self):
|
||||
"""Pause playback."""
|
||||
if self.state == self.PLAYING and self._pause():
|
||||
if self.state == self.PLAYING and self.provider.pause():
|
||||
self.state = self.PAUSED
|
||||
|
||||
def _pause(self):
|
||||
"""
|
||||
To be overridden by subclass. Implement your backend's pause
|
||||
functionality here.
|
||||
|
||||
:rtype: :class:`True` if successful, else :class:`False`
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def play(self, cp_track=None, on_error_step=1):
|
||||
"""
|
||||
Play the given track, or if the given track is :class:`None`, play the
|
||||
@ -391,7 +385,7 @@ class BasePlaybackController(object):
|
||||
self.state = self.STOPPED
|
||||
self.current_cp_track = cp_track
|
||||
self.state = self.PLAYING
|
||||
if not self._play(cp_track[1]):
|
||||
if not self.provider.play(cp_track[1]):
|
||||
# Track is not playable
|
||||
if self.random and self._shuffled:
|
||||
self._shuffled.remove(cp_track)
|
||||
@ -405,18 +399,6 @@ class BasePlaybackController(object):
|
||||
|
||||
self._trigger_started_playing_event()
|
||||
|
||||
def _play(self, track):
|
||||
"""
|
||||
To be overridden by subclass. Implement your backend's play
|
||||
functionality here.
|
||||
|
||||
:param track: the track to play
|
||||
:type track: :class:`mopidy.models.Track`
|
||||
:rtype: :class:`True` if successful, else :class:`False`
|
||||
"""
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
def previous(self):
|
||||
"""Play the previous track."""
|
||||
if self.cp_track_at_previous is None:
|
||||
@ -428,18 +410,9 @@ class BasePlaybackController(object):
|
||||
|
||||
def resume(self):
|
||||
"""If paused, resume playing the current track."""
|
||||
if self.state == self.PAUSED and self._resume():
|
||||
if self.state == self.PAUSED and self.provider.resume():
|
||||
self.state = self.PLAYING
|
||||
|
||||
def _resume(self):
|
||||
"""
|
||||
To be overridden by subclass. Implement your backend's resume
|
||||
functionality here.
|
||||
|
||||
:rtype: :class:`True` if successful, else :class:`False`
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def seek(self, time_position):
|
||||
"""
|
||||
Seeks to time position given in milliseconds.
|
||||
@ -465,18 +438,7 @@ class BasePlaybackController(object):
|
||||
self._play_time_started = self._current_wall_time
|
||||
self._play_time_accumulated = time_position
|
||||
|
||||
return self._seek(time_position)
|
||||
|
||||
def _seek(self, time_position):
|
||||
"""
|
||||
To be overridden by subclass. Implement your backend's seek
|
||||
functionality here.
|
||||
|
||||
:param time_position: time position in milliseconds
|
||||
:type time_position: int
|
||||
:rtype: :class:`True` if successful, else :class:`False`
|
||||
"""
|
||||
raise NotImplementedError
|
||||
return self.provider.seek(time_position)
|
||||
|
||||
def stop(self, clear_current_track=False):
|
||||
"""
|
||||
@ -489,20 +451,11 @@ class BasePlaybackController(object):
|
||||
if self.state == self.STOPPED:
|
||||
return
|
||||
self._trigger_stopped_playing_event()
|
||||
if self._stop():
|
||||
if self.provider.stop():
|
||||
self.state = self.STOPPED
|
||||
if clear_current_track:
|
||||
self.current_cp_track = None
|
||||
|
||||
def _stop(self):
|
||||
"""
|
||||
To be overridden by subclass. Implement your backend's stop
|
||||
functionality here.
|
||||
|
||||
:rtype: :class:`True` if successful, else :class:`False`
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def _trigger_started_playing_event(self):
|
||||
"""
|
||||
Notifies frontends that a track has started playing.
|
||||
@ -532,3 +485,62 @@ class BasePlaybackController(object):
|
||||
'track': self.current_track,
|
||||
'stop_position': self.time_position,
|
||||
})
|
||||
|
||||
|
||||
class BasePlaybackProvider(object):
|
||||
"""
|
||||
:param backend: the backend
|
||||
:type backend: :class:`BaseBackend`
|
||||
"""
|
||||
|
||||
def __init__(self, backend):
|
||||
self.backend = backend
|
||||
|
||||
def pause(self):
|
||||
"""
|
||||
To be overridden by subclass. Implement your backend's pause
|
||||
functionality here.
|
||||
|
||||
:rtype: :class:`True` if successful, else :class:`False`
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def play(self, track):
|
||||
"""
|
||||
To be overridden by subclass. Implement your backend's play
|
||||
functionality here.
|
||||
|
||||
:param track: the track to play
|
||||
:type track: :class:`mopidy.models.Track`
|
||||
:rtype: :class:`True` if successful, else :class:`False`
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def resume(self):
|
||||
"""
|
||||
To be overridden by subclass. Implement your backend's resume
|
||||
functionality here.
|
||||
|
||||
:rtype: :class:`True` if successful, else :class:`False`
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def seek(self, time_position):
|
||||
"""
|
||||
To be overridden by subclass. Implement your backend's seek
|
||||
functionality here.
|
||||
|
||||
:param time_position: time position in milliseconds
|
||||
:type time_position: int
|
||||
:rtype: :class:`True` if successful, else :class:`False`
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def stop(self):
|
||||
"""
|
||||
To be overridden by subclass. Implement your backend's stop
|
||||
functionality here.
|
||||
|
||||
:rtype: :class:`True` if successful, else :class:`False`
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
from mopidy.backends.base import (BaseBackend, BaseCurrentPlaylistController,
|
||||
BasePlaybackController, BaseLibraryController,
|
||||
BasePlaybackController, BasePlaybackProvider, BaseLibraryController,
|
||||
BaseStoredPlaylistsController)
|
||||
from mopidy.models import Playlist
|
||||
|
||||
@ -13,10 +13,17 @@ class DummyBackend(BaseBackend):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(DummyBackend, self).__init__(*args, **kwargs)
|
||||
|
||||
self.current_playlist = DummyCurrentPlaylistController(backend=self)
|
||||
|
||||
self.library = DummyLibraryController(backend=self)
|
||||
self.playback = DummyPlaybackController(backend=self)
|
||||
|
||||
playback_provider = DummyPlaybackProvider(backend=self)
|
||||
self.playback = DummyPlaybackController(backend=self,
|
||||
provider=playback_provider)
|
||||
|
||||
self.stored_playlists = DummyStoredPlaylistsController(backend=self)
|
||||
|
||||
self.uri_handlers = [u'dummy:']
|
||||
|
||||
|
||||
@ -43,30 +50,6 @@ class DummyLibraryController(BaseLibraryController):
|
||||
|
||||
|
||||
class DummyPlaybackController(BasePlaybackController):
|
||||
def _next(self, track):
|
||||
"""Pass None as track to force failure"""
|
||||
return track is not None
|
||||
|
||||
def _pause(self):
|
||||
return True
|
||||
|
||||
def _play(self, track):
|
||||
"""Pass None as track to force failure"""
|
||||
return track is not None
|
||||
|
||||
def _previous(self, track):
|
||||
"""Pass None as track to force failure"""
|
||||
return track is not None
|
||||
|
||||
def _resume(self):
|
||||
return True
|
||||
|
||||
def _seek(self, time_position):
|
||||
return True
|
||||
|
||||
def _stop(self):
|
||||
return True
|
||||
|
||||
def _trigger_started_playing_event(self):
|
||||
pass # noop
|
||||
|
||||
@ -74,6 +57,24 @@ class DummyPlaybackController(BasePlaybackController):
|
||||
pass # noop
|
||||
|
||||
|
||||
class DummyPlaybackProvider(BasePlaybackProvider):
|
||||
def pause(self):
|
||||
return True
|
||||
|
||||
def play(self, track):
|
||||
"""Pass None as track to force failure"""
|
||||
return track is not None
|
||||
|
||||
def resume(self):
|
||||
return True
|
||||
|
||||
def seek(self, time_position):
|
||||
return True
|
||||
|
||||
def stop(self):
|
||||
return True
|
||||
|
||||
|
||||
class DummyStoredPlaylistsController(BaseStoredPlaylistsController):
|
||||
_playlists = []
|
||||
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import logging
|
||||
|
||||
from mopidy import settings
|
||||
from mopidy.backends.base import BaseBackend, BaseCurrentPlaylistController
|
||||
from mopidy.backends.base import (BaseBackend, BaseCurrentPlaylistController,
|
||||
BasePlaybackController)
|
||||
|
||||
logger = logging.getLogger('mopidy.backends.libspotify')
|
||||
|
||||
@ -34,17 +35,24 @@ class LibspotifyBackend(BaseBackend):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
from .library import LibspotifyLibraryController
|
||||
from .playback import LibspotifyPlaybackController
|
||||
from .playback import LibspotifyPlaybackProvider
|
||||
from .stored_playlists import LibspotifyStoredPlaylistsController
|
||||
|
||||
super(LibspotifyBackend, self).__init__(*args, **kwargs)
|
||||
|
||||
self.current_playlist = BaseCurrentPlaylistController(backend=self)
|
||||
|
||||
self.library = LibspotifyLibraryController(backend=self)
|
||||
self.playback = LibspotifyPlaybackController(backend=self)
|
||||
|
||||
playback_provider = LibspotifyPlaybackProvider(backend=self)
|
||||
self.playback = BasePlaybackController(backend=self,
|
||||
provider=playback_provider)
|
||||
|
||||
self.stored_playlists = LibspotifyStoredPlaylistsController(
|
||||
backend=self)
|
||||
|
||||
self.uri_handlers = [u'spotify:', u'http://open.spotify.com/']
|
||||
|
||||
self.spotify = self._connect()
|
||||
|
||||
def _connect(self):
|
||||
|
||||
@ -2,17 +2,17 @@ import logging
|
||||
|
||||
from spotify import Link, SpotifyError
|
||||
|
||||
from mopidy.backends.base import BasePlaybackController
|
||||
from mopidy.backends.base import BasePlaybackProvider
|
||||
|
||||
logger = logging.getLogger('mopidy.backends.libspotify.playback')
|
||||
|
||||
class LibspotifyPlaybackController(BasePlaybackController):
|
||||
def _pause(self):
|
||||
class LibspotifyPlaybackProvider(BasePlaybackProvider):
|
||||
def pause(self):
|
||||
return self.backend.output.set_state('PAUSED')
|
||||
|
||||
def _play(self, track):
|
||||
def play(self, track):
|
||||
self.backend.output.set_state('READY')
|
||||
if self.state == self.PLAYING:
|
||||
if self.backend.playback.state == self.backend.playback.PLAYING:
|
||||
self.backend.spotify.session.play(0)
|
||||
if track.uri is None:
|
||||
return False
|
||||
@ -26,16 +26,16 @@ class LibspotifyPlaybackController(BasePlaybackController):
|
||||
logger.warning('Play %s failed: %s', track.uri, e)
|
||||
return False
|
||||
|
||||
def _resume(self):
|
||||
return self._seek(self.time_position)
|
||||
def resume(self):
|
||||
return self.seek(self.backend.playback.time_position)
|
||||
|
||||
def _seek(self, time_position):
|
||||
def seek(self, time_position):
|
||||
self.backend.output.set_state('READY')
|
||||
self.backend.spotify.session.seek(time_position)
|
||||
self.backend.output.set_state('PLAYING')
|
||||
return True
|
||||
|
||||
def _stop(self):
|
||||
def stop(self):
|
||||
result = self.backend.output.set_state('READY')
|
||||
self.backend.spotify.session.play(0)
|
||||
return result
|
||||
|
||||
@ -5,9 +5,10 @@ import os
|
||||
import shutil
|
||||
|
||||
from mopidy import settings
|
||||
from mopidy.backends.base import (BaseBackend, BaseLibraryController,
|
||||
BaseStoredPlaylistsController, BaseCurrentPlaylistController,
|
||||
BasePlaybackController)
|
||||
from mopidy.backends.base import (BaseBackend,
|
||||
BaseCurrentPlaylistController, BaseLibraryController,
|
||||
BasePlaybackController, BasePlaybackProvider,
|
||||
BaseStoredPlaylistsController)
|
||||
from mopidy.models import Playlist, Track, Album
|
||||
from mopidy.utils.process import pickle_connection
|
||||
|
||||
@ -31,41 +32,51 @@ class LocalBackend(BaseBackend):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(LocalBackend, self).__init__(*args, **kwargs)
|
||||
|
||||
self.library = LocalLibraryController(self)
|
||||
self.stored_playlists = LocalStoredPlaylistsController(self)
|
||||
self.current_playlist = BaseCurrentPlaylistController(self)
|
||||
self.playback = LocalPlaybackController(self)
|
||||
self.library = LocalLibraryController(backend=self)
|
||||
|
||||
self.stored_playlists = LocalStoredPlaylistsController(backend=self)
|
||||
|
||||
self.current_playlist = BaseCurrentPlaylistController(backend=self)
|
||||
|
||||
playback_provider = LocalPlaybackProvider(backend=self)
|
||||
self.playback = LocalPlaybackController(backend=self,
|
||||
provider=playback_provider)
|
||||
|
||||
self.uri_handlers = [u'file://']
|
||||
|
||||
|
||||
class LocalPlaybackController(BasePlaybackController):
|
||||
def __init__(self, backend):
|
||||
super(LocalPlaybackController, self).__init__(backend)
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(LocalPlaybackController, self).__init__(*args, **kwargs)
|
||||
|
||||
# XXX Why do we call stop()? Is it to set GStreamer state to 'READY'?
|
||||
self.stop()
|
||||
|
||||
def _play(self, track):
|
||||
return self.backend.output.play_uri(track.uri)
|
||||
|
||||
def _stop(self):
|
||||
return self.backend.output.set_state('READY')
|
||||
|
||||
def _pause(self):
|
||||
return self.backend.output.set_state('PAUSED')
|
||||
|
||||
def _resume(self):
|
||||
return self.backend.output.set_state('PLAYING')
|
||||
|
||||
def _seek(self, time_position):
|
||||
return self.backend.output.set_position(time_position)
|
||||
|
||||
@property
|
||||
def time_position(self):
|
||||
return self.backend.output.get_position()
|
||||
|
||||
|
||||
class LocalPlaybackProvider(BasePlaybackProvider):
|
||||
def pause(self):
|
||||
return self.backend.output.set_state('PAUSED')
|
||||
|
||||
def play(self, track):
|
||||
return self.backend.output.play_uri(track.uri)
|
||||
|
||||
def resume(self):
|
||||
return self.backend.output.set_state('PLAYING')
|
||||
|
||||
def seek(self, time_position):
|
||||
return self.backend.output.set_position(time_position)
|
||||
|
||||
def stop(self):
|
||||
return self.backend.output.set_state('READY')
|
||||
|
||||
|
||||
class LocalStoredPlaylistsController(BaseStoredPlaylistsController):
|
||||
def __init__(self, *args):
|
||||
super(LocalStoredPlaylistsController, self).__init__(*args)
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(LocalStoredPlaylistsController, self).__init__(*args, **kwargs)
|
||||
self._folder = os.path.expanduser(settings.LOCAL_PLAYLIST_FOLDER)
|
||||
self.refresh()
|
||||
|
||||
@ -137,8 +148,8 @@ class LocalStoredPlaylistsController(BaseStoredPlaylistsController):
|
||||
|
||||
|
||||
class LocalLibraryController(BaseLibraryController):
|
||||
def __init__(self, backend):
|
||||
super(LocalLibraryController, self).__init__(backend)
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(LocalLibraryController, self).__init__(*args, **kwargs)
|
||||
self._uri_mapping = {}
|
||||
self.refresh()
|
||||
|
||||
|
||||
@ -104,8 +104,8 @@ class BasePlaybackControllerTest(object):
|
||||
|
||||
@populate_playlist
|
||||
def test_play_skips_to_next_track_on_failure(self):
|
||||
# If _play() returns False, it is a failure.
|
||||
self.playback._play = lambda track: track != self.tracks[0]
|
||||
# If provider.play() returns False, it is a failure.
|
||||
self.playback.provider.play = lambda track: track != self.tracks[0]
|
||||
self.playback.play()
|
||||
self.assertNotEqual(self.playback.current_track, self.tracks[0])
|
||||
self.assertEqual(self.playback.current_track, self.tracks[1])
|
||||
@ -164,8 +164,8 @@ class BasePlaybackControllerTest(object):
|
||||
|
||||
@populate_playlist
|
||||
def test_previous_skips_to_previous_track_on_failure(self):
|
||||
# If _play() returns False, it is a failure.
|
||||
self.playback._play = lambda track: track != self.tracks[1]
|
||||
# If provider.play() returns False, it is a failure.
|
||||
self.playback.provider.play = lambda track: track != self.tracks[1]
|
||||
self.playback.play(self.current_playlist.cp_tracks[2])
|
||||
self.assertEqual(self.playback.current_track, self.tracks[2])
|
||||
self.playback.previous()
|
||||
@ -228,8 +228,8 @@ class BasePlaybackControllerTest(object):
|
||||
|
||||
@populate_playlist
|
||||
def test_next_skips_to_next_track_on_failure(self):
|
||||
# If _play() returns False, it is a failure.
|
||||
self.playback._play = lambda track: track != self.tracks[1]
|
||||
# If provider.play() returns False, it is a failure.
|
||||
self.playback.provider.play = lambda track: track != self.tracks[1]
|
||||
self.playback.play()
|
||||
self.assertEqual(self.playback.current_track, self.tracks[0])
|
||||
self.playback.next()
|
||||
@ -364,8 +364,8 @@ class BasePlaybackControllerTest(object):
|
||||
|
||||
@populate_playlist
|
||||
def test_end_of_track_skips_to_next_track_on_failure(self):
|
||||
# If _play() returns False, it is a failure.
|
||||
self.playback._play = lambda track: track != self.tracks[1]
|
||||
# If provider.play() returns False, it is a failure.
|
||||
self.playback.provider.play = lambda track: track != self.tracks[1]
|
||||
self.playback.play()
|
||||
self.assertEqual(self.playback.current_track, self.tracks[0])
|
||||
self.playback.on_end_of_track()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user