diff --git a/mopidy/backends/spotify/playback.py b/mopidy/backends/spotify/playback.py index 61696bd8..94d57f56 100644 --- a/mopidy/backends/spotify/playback.py +++ b/mopidy/backends/spotify/playback.py @@ -14,12 +14,10 @@ class SpotifyPlaybackProvider(BasePlaybackProvider): def __init__(self, *args, **kwargs): super(SpotifyPlaybackProvider, self).__init__(*args, **kwargs) - self._play_time_accumulated = 0 - self._play_time_started = 0 + self._timer = TrackPositionTimer() def pause(self): - time_since_started = self._wall_time() - self._play_time_started - self._play_time_accumulated += time_since_started + self._timer.pause() return super(SpotifyPlaybackProvider, self).pause() @@ -27,38 +25,42 @@ class SpotifyPlaybackProvider(BasePlaybackProvider): if track.uri is None: return False - self._play_time_accumulated = 0 - self._play_time_started = self._wall_time() - try: self.backend.spotify.session.load( Link.from_string(track.uri).as_track()) self.backend.spotify.session.play(1) + self.backend.audio.prepare_change() self.backend.audio.set_uri('appsrc://') self.backend.audio.start_playback() self.backend.audio.set_metadata(track) + + self._timer.play() + return True except SpotifyError as e: logger.info('Playback of %s failed: %s', track.uri, e) return False def resume(self): - self._play_time_started = self._wall_time() - return self.seek(self.backend.playback.time_position) + time_position = self.get_time_position() + + self._timer.resume() + + return self.seek(time_position) def seek(self, time_position): - self._play_time_started = self._wall_time() - self._play_time_accumulated = time_position - self.backend.audio.prepare_change() self.backend.spotify.session.seek(time_position) self.backend.audio.start_playback() + self._timer.seek(time_position) + return True def stop(self): self.backend.spotify.session.play(0) + return super(SpotifyPlaybackProvider, self).stop() def get_time_position(self): @@ -66,14 +68,46 @@ class SpotifyPlaybackProvider(BasePlaybackProvider): # 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. - 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 self._timer.get_time_position() + + +class TrackPositionTimer(object): + """ + Keeps track of time position in a track using the wall clock and playback + events. + + To not introduce a reverse dependency on the playback controller, this + class keeps track of playback state itself. + """ + + def __init__(self): + self._state = PlaybackState.STOPPED + self._accumulated = 0 + self._started = 0 + + def play(self): + self._state = PlaybackState.PLAYING + self._accumulated = 0 + self._started = self._wall_time() + + def pause(self): + self._state = PlaybackState.PAUSED + self._accumulated += self._wall_time() - self._started + + def resume(self): + self._state = PlaybackState.PLAYING + + def seek(self, time_position): + self._started = self._wall_time() + self._accumulated = time_position + + def get_time_position(self): + if self._state == PlaybackState.PLAYING: + time_since_started = self._wall_time() - self._started + return self._accumulated + time_since_started + elif self._state == PlaybackState.PAUSED: + return self._accumulated + elif self._state == PlaybackState.STOPPED: return 0 def _wall_time(self):