Merged SleepTimer patch
Merged SleepTimer patch fromd28715fb84For support from Webclient required SleepTimer patch for Mopidy-MusicBox-Webclientd5b6649fee
This commit is contained in:
parent
76395522d0
commit
22bd607338
@ -9,3 +9,4 @@ from .mixer import MixerController
|
||||
from .playback import PlaybackController, PlaybackState
|
||||
from .playlists import PlaylistsController
|
||||
from .tracklist import TracklistController
|
||||
from .sleeptimer import SleepTimerController
|
||||
|
||||
@ -17,6 +17,7 @@ from mopidy.core.mixer import MixerController
|
||||
from mopidy.core.playback import PlaybackController
|
||||
from mopidy.core.playlists import PlaylistsController
|
||||
from mopidy.core.tracklist import TracklistController
|
||||
from mopidy.core.sleeptimer import SleepTimerController
|
||||
from mopidy.internal import path, storage, validation, versioning
|
||||
from mopidy.internal.deprecation import deprecated_property
|
||||
from mopidy.internal.models import CoreState
|
||||
@ -44,6 +45,9 @@ class Core(
|
||||
playlists = None
|
||||
"""An instance of :class:`~mopidy.core.PlaylistsController`"""
|
||||
|
||||
sleeptimer = None
|
||||
"""An instance of :class: ~mopidy.core.SleepTimerController`."""
|
||||
|
||||
tracklist = None
|
||||
"""An instance of :class:`~mopidy.core.TracklistController`"""
|
||||
|
||||
@ -61,6 +65,7 @@ class Core(
|
||||
audio=audio, backends=self.backends, core=self)
|
||||
self.playlists = PlaylistsController(backends=self.backends, core=self)
|
||||
self.tracklist = TracklistController(core=self)
|
||||
self.sleeptimer = SleepTimerController(playback=self.playback, core=self)
|
||||
|
||||
self.audio = audio
|
||||
|
||||
|
||||
@ -187,3 +187,40 @@ class CoreListener(listener.Listener):
|
||||
:type title: string
|
||||
"""
|
||||
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
128
mopidy/core/sleeptimer.py
Normal 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)
|
||||
@ -59,6 +59,7 @@ def make_jsonrpc_wrapper(core_actor):
|
||||
'core.playback': core.PlaybackController,
|
||||
'core.playlists': core.PlaylistsController,
|
||||
'core.tracklist': core.TracklistController,
|
||||
'core.sleeptimer': core.SleepTimerController,
|
||||
})
|
||||
return jsonrpc.JsonRpcWrapper(
|
||||
objects={
|
||||
@ -71,6 +72,7 @@ def make_jsonrpc_wrapper(core_actor):
|
||||
'core.playback': core_actor.playback,
|
||||
'core.playlists': core_actor.playlists,
|
||||
'core.tracklist': core_actor.tracklist,
|
||||
'core.sleeptimer': core_actor.sleeptimer,
|
||||
},
|
||||
decoders=[models.model_json_decoder],
|
||||
encoders=[models.ModelJSONEncoder]
|
||||
|
||||
@ -26,6 +26,10 @@ _CORE_EVENTS_TO_IDLE_SUBSYSTEMS = {
|
||||
'mute_changed': 'output',
|
||||
'seeked': 'player',
|
||||
'stream_title_changed': 'playlist',
|
||||
'sleeptimer_started': None,
|
||||
'sleeptimer_expired': None,
|
||||
'sleeptimer_tick': None,
|
||||
'sleeptimer_cancelled': None,
|
||||
}
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user