Merged SleepTimer patch

Merged SleepTimer patch from
d28715fb84

For support from Webclient required SleepTimer patch for Mopidy-MusicBox-Webclient
d5b6649fee
This commit is contained in:
Andrey Perminov 2019-07-02 14:09:33 -07:00
parent 76395522d0
commit 22bd607338
6 changed files with 177 additions and 0 deletions

View File

@ -9,3 +9,4 @@ from .mixer import MixerController
from .playback import PlaybackController, PlaybackState from .playback import PlaybackController, PlaybackState
from .playlists import PlaylistsController from .playlists import PlaylistsController
from .tracklist import TracklistController from .tracklist import TracklistController
from .sleeptimer import SleepTimerController

View File

@ -17,6 +17,7 @@ from mopidy.core.mixer import MixerController
from mopidy.core.playback import PlaybackController from mopidy.core.playback import PlaybackController
from mopidy.core.playlists import PlaylistsController from mopidy.core.playlists import PlaylistsController
from mopidy.core.tracklist import TracklistController from mopidy.core.tracklist import TracklistController
from mopidy.core.sleeptimer import SleepTimerController
from mopidy.internal import path, storage, validation, versioning from mopidy.internal import path, storage, validation, versioning
from mopidy.internal.deprecation import deprecated_property from mopidy.internal.deprecation import deprecated_property
from mopidy.internal.models import CoreState from mopidy.internal.models import CoreState
@ -44,6 +45,9 @@ class Core(
playlists = None playlists = None
"""An instance of :class:`~mopidy.core.PlaylistsController`""" """An instance of :class:`~mopidy.core.PlaylistsController`"""
sleeptimer = None
"""An instance of :class: ~mopidy.core.SleepTimerController`."""
tracklist = None tracklist = None
"""An instance of :class:`~mopidy.core.TracklistController`""" """An instance of :class:`~mopidy.core.TracklistController`"""
@ -61,6 +65,7 @@ class Core(
audio=audio, backends=self.backends, core=self) audio=audio, backends=self.backends, core=self)
self.playlists = PlaylistsController(backends=self.backends, core=self) self.playlists = PlaylistsController(backends=self.backends, core=self)
self.tracklist = TracklistController(core=self) self.tracklist = TracklistController(core=self)
self.sleeptimer = SleepTimerController(playback=self.playback, core=self)
self.audio = audio self.audio = audio

View File

@ -187,3 +187,40 @@ class CoreListener(listener.Listener):
:type title: string :type title: string
""" """
pass pass
def sleeptimer_started(self, was_running, duration, seconds_left):
"""
Called whenever the sleeptimer is started
*MAY* be implemented by actor.
:param was_running: indicates if the timer has been restarted while it was already running i.e. the end time has changed
:type was_running: boolean
:param duration: the length of time in seconds until the sleep timer will expire and stop playback
:type duration: int
:param seconds_left: the number of seconds left until the the sleep timer expire. may be slightly different to duration because of datetime calc rounding etc
:type seconds_left: float
"""
pass
def sleeptimer_tick(self, seconds_left):
"""
Called roughly every 0.5 seconds when the sleeptimer is active
*MAY* be implemented by actor.
:param seconds_left: the number of seconds left until the the sleep timer expire
:type seconds_left: float
"""
pass
def sleeptimer_expired(self):
"""
Called whenever the sleeptimer has reached the end time nd stopped playback
*MAY* be implemented by actor.
"""
pass
def sleeptimer_cancelled(self):
"""
Called whenever the sleeptimer is running and is cancelled
*MAY* be implemented by actor.
"""
pass

128
mopidy/core/sleeptimer.py Normal file
View File

@ -0,0 +1,128 @@
from __future__ import absolute_import, unicode_literals
import logging
import threading
import datetime
#import gobject
import time
from mopidy.audio import PlaybackState
from mopidy.core import playback, listener
logger = logging.getLogger(__name__)
class SleepTimerController(object):
pykka_traversable = True
def __init__(self, playback, core):
logger.debug('Core.SleepTimer __init__')
self.playback = playback
self.core = core
self._cancelevent = threading.Event()
self._timer = None
self._state = SleeptimerState()
self._state.__init__()
self._timer_id = None
def get_state(self):
return {"running": self._state.running,
"duration": self._state.duration,
"seconds_left": self._get_seconds_left()}
def _get_seconds_left(self):
now = datetime.datetime.now()
time_left = self._state.timerEndTime - now
seconds_left = time_left.total_seconds()
if seconds_left < 0:
seconds_left = 0
return seconds_left
def cancel(self, notify=True):
logger.debug('Cancel')
self._cancelevent.set()
if notify:
listener.CoreListener.send(
'sleeptimer_cancelled')
return True
def start(self, duration):
old_state = self._state.running
logger.debug('Start - state = %s, duration = %d', old_state, duration)
if self._state.running:
self.cancel(False)
self._state.start(duration)
if self._timer:
self._timer.cancel()
#gobject.timeout_add(500, self._tick_handler)
self._timer=threading.Timer(1, self._tick_handler)
self._timer.start()
self._cancelevent.clear()
listener.CoreListener.send(
'sleeptimer_started',
was_running=old_state, duration=self._state.duration, seconds_left=self._get_seconds_left())
return True
def _tick_handler(self):
logger.debug('tick_handler, time left = %s', self._get_seconds_left())
if self._cancelevent.is_set():
return False
if datetime.datetime.now() > self._state.timerEndTime:
self._cancelevent.set()
if self.playback.get_state() != PlaybackState.STOPPED:
#self.playback.stop()
self.playback.pause()
listener.CoreListener.send(
'sleeptimer_expired')
self._state.clear()
return False
else:
self._timer=threading.Timer(1, self._tick_handler)
self._timer.start()
listener.CoreListener.send(
'sleeptimer_tick',
seconds_left=self._get_seconds_left())
return True
class SleeptimerState(object):
pykka_traversable = True
def __init__(self):
#self.running = False
#self.duration = 0
self.clear()
def clear(self):
self.running = False
self.timerStartTime = datetime.datetime.now()
self.timerEndTime = self.timerStartTime
self.duration = 0
def start(self, duration):
self.running = True
self.timerStartTime = datetime.datetime.now()
self.timerEndTime = self.timerStartTime + datetime.timedelta(seconds=duration)
self.duration = duration
logger.debug('SleepTimerState.start: running = %s, end time = %s', self.running, self.timerEndTime)

View File

@ -59,6 +59,7 @@ def make_jsonrpc_wrapper(core_actor):
'core.playback': core.PlaybackController, 'core.playback': core.PlaybackController,
'core.playlists': core.PlaylistsController, 'core.playlists': core.PlaylistsController,
'core.tracklist': core.TracklistController, 'core.tracklist': core.TracklistController,
'core.sleeptimer': core.SleepTimerController,
}) })
return jsonrpc.JsonRpcWrapper( return jsonrpc.JsonRpcWrapper(
objects={ objects={
@ -71,6 +72,7 @@ def make_jsonrpc_wrapper(core_actor):
'core.playback': core_actor.playback, 'core.playback': core_actor.playback,
'core.playlists': core_actor.playlists, 'core.playlists': core_actor.playlists,
'core.tracklist': core_actor.tracklist, 'core.tracklist': core_actor.tracklist,
'core.sleeptimer': core_actor.sleeptimer,
}, },
decoders=[models.model_json_decoder], decoders=[models.model_json_decoder],
encoders=[models.ModelJSONEncoder] encoders=[models.ModelJSONEncoder]

View File

@ -26,6 +26,10 @@ _CORE_EVENTS_TO_IDLE_SUBSYSTEMS = {
'mute_changed': 'output', 'mute_changed': 'output',
'seeked': 'player', 'seeked': 'player',
'stream_title_changed': 'playlist', 'stream_title_changed': 'playlist',
'sleeptimer_started': None,
'sleeptimer_expired': None,
'sleeptimer_tick': None,
'sleeptimer_cancelled': None,
} }