-
Notifications
You must be signed in to change notification settings - Fork 2
Add an autosave feature. State can saved frequently #6
Conversation
I just thought about writing this kind of "autosave-on-demand" for myself, but came across this conversation at the right time. How about adding some "dirty" state set through event handlers (like mute_changed, playback_state_changed...) and only then doing the store to SD card? |
The "dirty" state sounds good. I also thinked about this, but the interval based save was easier to implement :-) Config something like this:
P.S. I used it with armbian on a Orange Pi Zero => great item: small, cheap, WLAN and LAN Intrerface, stable, good audio interface. |
@Race666: Thank you for the nicely made PR. Highly appreciated. My own plan would have been to use the mopidy events to save the state with: state = self.store_state()
self.write_state(state, self.statefile) This would implement the 'dirty' state approach and at the same time get rid of the Configuration options would be a list of states, where changes in this state should be tracked and saved, e.g. NB: There should be a 'delay' in the saving the I have not found the time to do it, despite its ‘easiness’ to implement … What do you think about this approach? |
@sphh I agree with you, (Configurable) Events are the best approach. I see attaching to events is simple to define such a function with the name of the event:
I think it should be possible to set the "dirty state" on tracklist or playback events. A state save on a mixer change, is in my opinion, not necessary. |
interval -> events
first approach interval -> events
interval -> events
@sphh Check updated PR :-) |
Thanks, @Race666. That's what I had in mind!! A couple of points:
|
Individual events -> on_event
Individual events -> on_event
Individual events -> on_event
Individual events -> on_event
@sphh Changed to on_event. But not sure where is the best place to store const
|
Sorry for being a nuisance! I do like the feature to limit the autosave events!! This should definitely be configurable!!
I would have thought, something like that is enough: def on_event(self, event, **kwargs):
logger.debug("Event %s args: %s",str(event),str(kwargs))
if event in self.config['autosave_events']:
now = time.time()
if (now - self._last_autosave) < self.config['autosave_min_interval']:
logger.info("Skip autosave")
else:
logger.info("Autosave ({0})".format(save_event))
state = self.store_state()
self.write_state(state, self.statefile)
self._last_autosave = now |
Parameter autosave_min_interval added, compact code.
Parameter autosave_min_interval added, compact code.
Parameter autosave_min_interval added, compact code.
@sphh Written compact code and added autosave_min_interval |
Thanks, @Race666. That looks good. Some more questions:
|
|
@mikiair: Looks like you know my own code better than I!! You are absolutely right regarding the log level. After looking through my own code I would very much prefer a You are also absolutely right regarding the Regarding you most important point: You are absolutely right. Well spotted and shame on me for not having seen it: The state is only saved with the next event. Solution? Either do not care about writing too often (the only problem I could see here is the Or start a timer thread on the event and save the state whenever it finishes. (Should a new event fired in the interval before the timer has expired, restart the timer from 0?) I would very much prefer the first option (and write a warning regarding the volume). I could also imagine, that the volume is only autosaved in steps of |
@sphh: Thanks! Fortunately, it is not too much code, and I like Python for its 'cleanliness' more and more!
|
Ok, if there is a universal request for not writing too often, I can see two options:
The question still remains: How should it behave, if new events are fired and the timer has not expired yet:
It might be true, that compared to other programming languages, it does not look too complex, but still it adds complexity to the code! As promised above, here is a controllable timer: import threading
import queue
import enum
# Could be replaced by strings ...
class TimerCommand(enum.Enum):
STOP = enum.auto()
SET = enum.auto()
CANCEL = enum.auto()
class ResetTimer(threading.Thread):
def __init__(self, timeout, callback, *args, **kwargs):
super().__init__()
self.timeout = timeout
self.callback = callback
self.args = args
self.kwargs = kwargs
# The queue to control the timer
self.queue = queue.SimpleQueue()
def stop(self):
"""Stop and exit the timer."""
self.queue.put(TimerCommand.STOP)
def set(self, timeout=None):
if timeout is not None:
self.timeout = timeout
self.queue.put(TimerCommand.SET)
def cancel(self):
self.queue.put(TimerCommand.CANCEL)
def run(self):
timeout = None
while True:
try:
cmd = self.queue.get(block=True, timeout=timeout)
except queue.Empty:
# Timed out
timeout = None
self.callback(*self.args, **self.kwargs)
else:
if cmd == TimerCommand.STOP:
# Exit thread
return
elif cmd == TimerCommand.SET:
timeout = self.timeout
elif cmd == TimerCommand.CANCEL:
timeout = None
else:
raise ValueError(
f'Unknown command received. '
f'Got: {cmd}') |
The question still remains: How should it behave, if new events are fired and the timer has not expired yet: Should the timer be reset to 0. => On_event |
Yes, resetting the timer to 0 on each event could delay the data storage again and again. Consider the (unlikely) case that some condition leads to an infinite or unusual long sequence of events --> status data will never be written to SD.
And maybe it could help to have an optimized or adapted version of the |
@mikiair: You are absolutely right. I just wanted to know, which behaviour you expect from such a feature. Thanks, @Race666 and @mikiair. I took the liberty to implement this feature taking bits from @Race666, from this discussion and my own ideas. I hope, you don't mind! Please have a look at the @mikiair: Now that we have a persistent timer, we should also have a persistent state variable. That is not implemented. I might do that next, if no one beats me to it. |
@sphh Thats ok 👍 Hope I could try it tommorrow. Thanks for the expedient discussion and implementing this feature! |
With the latest commit, the Please give everything a thorough check! It was not that heavily tested … |
Great! Thanks for the rapid implementation! |
Happy to hear, that it works so far. Please observe it a while and please tell me your experience with it. Thanks! |
@sphh Working fine and stable:-) . A delay of 10secs before the file is written is ok. Thank you! |
Great to hear that it works. Whenever 10 s does not work for you, you know, that you can change the delay in the configuration file … (option @Race666: Thanks for get this starting and for your PRs, which gave me a good idea what and how to implement it! |
Closed, because the final implementation can be found in https://github.com/sphh/mopidy-autoplay/tree/autosave (which is already merged into |
I also need an autosave feature for mopidy as @kokosowy #4
This PR implements such a feature, but not sure if programming is sufficent for mopidy and your code quality standard :-)
I need this function because mopidy runs on an device which is simply powered off instead of shutting down the operating system cleanly.