From 2237e4f5a1e2e79d50485f0d1b97a5774af0ed40 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Tue, 25 Sep 2012 12:10:25 +0200 Subject: [PATCH] Move optional wall clock-based position tracking down to the playback provider --- mopidy/backends/base/playback.py | 45 +++++++++++++++++++++++++++ mopidy/backends/spotify/playback.py | 9 ++++++ mopidy/core/playback.py | 48 +++-------------------------- 3 files changed, 58 insertions(+), 44 deletions(-) diff --git a/mopidy/backends/base/playback.py b/mopidy/backends/base/playback.py index 197ba90e..b3b9959e 100644 --- a/mopidy/backends/base/playback.py +++ b/mopidy/backends/base/playback.py @@ -1,3 +1,8 @@ +import time + +from mopidy.core.playback import PlaybackState + + class BasePlaybackProvider(object): """ :param backend: the backend @@ -9,6 +14,9 @@ class BasePlaybackProvider(object): def __init__(self, backend): self.backend = backend + self._play_time_accumulated = 0 + self._play_time_started = 0 + def pause(self): """ Pause playback. @@ -95,3 +103,40 @@ class BasePlaybackProvider(object): :type volume: int [0..100] """ self.backend.audio.set_volume(volume) + + def wall_clock_based_time_position(self): + """ + Helper method that tracks track time position using the wall clock. + + To use this helper you must call the helper from your implementation of + :meth:`get_time_position` and return its return value. + + :rtype: int + """ + state = self.backend.playback.state + if state == PlaybackState.PLAYING: + time_since_started = (self._wall_time() - + self._play_time_started) + return self._play_time_accumulated + time_since_started + elif state == PlaybackState.PAUSED: + return self._play_time_accumulated + elif state == PlaybackState.STOPPED: + return 0 + + def update_play_time_on_play(self): + self._play_time_accumulated = 0 + self._play_time_started = self._wall_time() + + def update_play_time_on_pause(self): + time_since_started = self._wall_time() - self._play_time_started + self._play_time_accumulated += time_since_started + + def update_play_time_on_resume(self): + self._play_time_started = self._wall_time() + + def update_play_time_on_seek(self, time_position): + self._play_time_started = self._wall_time() + self._play_time_accumulated = time_position + + def _wall_time(self): + return int(time.time() * 1000) diff --git a/mopidy/backends/spotify/playback.py b/mopidy/backends/spotify/playback.py index 1c20da87..cd5b0689 100644 --- a/mopidy/backends/spotify/playback.py +++ b/mopidy/backends/spotify/playback.py @@ -5,8 +5,10 @@ from spotify import Link, SpotifyError from mopidy.backends.base import BasePlaybackProvider from mopidy.core import PlaybackState + logger = logging.getLogger('mopidy.backends.spotify.playback') + class SpotifyPlaybackProvider(BasePlaybackProvider): def play(self, track): if self.backend.playback.state == PlaybackState.PLAYING: @@ -38,3 +40,10 @@ class SpotifyPlaybackProvider(BasePlaybackProvider): def stop(self): self.backend.spotify.session.play(0) return super(SpotifyPlaybackProvider, self).stop() + + def get_time_position(self): + # XXX: The default implementation of get_time_position hangs/times out + # when used with the Spotify backend and GStreamer appsrc. If this can + # be resolved, we no longer need to use a wall clock based time + # position for Spotify playback. + return self.wall_clock_based_time_position() diff --git a/mopidy/core/playback.py b/mopidy/core/playback.py index 1bebd270..b32f5b62 100644 --- a/mopidy/core/playback.py +++ b/mopidy/core/playback.py @@ -1,6 +1,5 @@ import logging import random -import time from mopidy.listeners import BackendListener @@ -20,7 +19,6 @@ def option_wrapper(name, default): return property(get_option, set_option) - class PlaybackState(object): """ Enum of playback states. @@ -87,8 +85,6 @@ class PlaybackController(object): self._state = PlaybackState.STOPPED self._shuffled = [] self._first_shuffle = True - self.play_time_accumulated = 0 - self.play_time_started = 0 def _get_cpid(self, cp_track): if cp_track is None: @@ -292,48 +288,11 @@ class PlaybackController(object): self._trigger_playback_state_changed(old_state, new_state) - # FIXME play_time stuff assumes backend does not have a better way of - # handeling this stuff :/ - if (old_state in (PlaybackState.PLAYING, PlaybackState.STOPPED) - and new_state == PlaybackState.PLAYING): - self._play_time_start() - elif (old_state == PlaybackState.PLAYING - and new_state == PlaybackState.PAUSED): - self._play_time_pause() - elif (old_state == PlaybackState.PAUSED - and new_state == PlaybackState.PLAYING): - self._play_time_resume() - @property def time_position(self): """Time position in milliseconds.""" return self.provider.get_time_position() - def _wall_clock_based_time_position(): - if self.state == PlaybackState.PLAYING: - time_since_started = (self._current_wall_time - - self.play_time_started) - return self.play_time_accumulated + time_since_started - elif self.state == PlaybackState.PAUSED: - return self.play_time_accumulated - elif self.state == PlaybackState.STOPPED: - return 0 - - def _play_time_start(self): - self.play_time_accumulated = 0 - self.play_time_started = self._current_wall_time - - def _play_time_pause(self): - time_since_started = self._current_wall_time - self.play_time_started - self.play_time_accumulated += time_since_started - - def _play_time_resume(self): - self.play_time_started = self._current_wall_time - - @property - def _current_wall_time(self): - return int(time.time() * 1000) - @property def volume(self): return self.provider.get_volume() @@ -411,6 +370,7 @@ class PlaybackController(object): """Pause playback.""" if self.provider.pause(): self.state = PlaybackState.PAUSED + self.provider.update_play_time_on_pause() self._trigger_track_playback_paused() def play(self, cp_track=None, on_error_step=1): @@ -453,6 +413,7 @@ class PlaybackController(object): if self.random and self.current_cp_track in self._shuffled: self._shuffled.remove(self.current_cp_track) + self.provider.update_play_time_on_play() self._trigger_track_playback_started() def previous(self): @@ -469,6 +430,7 @@ class PlaybackController(object): """If paused, resume playing the current track.""" if self.state == PlaybackState.PAUSED and self.provider.resume(): self.state = PlaybackState.PLAYING + self.provider.update_play_time_on_resume() self._trigger_track_playback_resumed() def seek(self, time_position): @@ -493,11 +455,9 @@ class PlaybackController(object): self.next() return True - self.play_time_started = self._current_wall_time - self.play_time_accumulated = time_position - success = self.provider.seek(time_position) if success: + self.provider.update_play_time_on_seek(time_position) self._trigger_seeked(time_position) return success