Split xPlaybackController into xPlaybackController and xPlaybackProvider

This commit is contained in:
Stein Magnus Jodal 2010-10-26 13:25:52 +02:00
parent 62ae85d05a
commit 704b5517e1
7 changed files with 165 additions and 132 deletions

View File

@ -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

View File

@ -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

View File

@ -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 = []

View File

@ -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):

View File

@ -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

View File

@ -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()

View File

@ -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()