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 .playback import PlaybackController, PlaybackState
|
||||||
from .playlists import PlaylistsController
|
from .playlists import PlaylistsController
|
||||||
from .tracklist import TracklistController
|
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.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
|
||||||
|
|
||||||
|
|||||||
@ -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
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.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]
|
||||||
|
|||||||
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user