From 6eafd4ebfd074fd9bc3f53e8b0bce378ea1e9f80 Mon Sep 17 00:00:00 2001 From: David Trattnig <david.trattnig@o94.at> Date: Wed, 18 Nov 2020 19:17:48 +0100 Subject: [PATCH] Scheduler cmds inherit from EngineExecutor. #41 --- src/core/client/connector.py | 2 +- src/core/engine.py | 15 +- src/core/events.py | 7 +- src/scheduling/calendar.py | 4 +- src/scheduling/fallback.py | 13 +- src/scheduling/models.py | 51 ++--- src/scheduling/programme.py | 2 +- src/scheduling/scheduler.py | 430 ++++++++--------------------------- 8 files changed, 145 insertions(+), 379 deletions(-) diff --git a/src/core/client/connector.py b/src/core/client/connector.py index fc0ea7af..59665758 100644 --- a/src/core/client/connector.py +++ b/src/core/client/connector.py @@ -88,7 +88,7 @@ class PlayerConnector(): "queue_clear", "playlist_uri_set", "playlist_uri_clear", - "stream_set_ur", + "stream_set_url", "stream_start", "stream_stop", "stream_status", diff --git a/src/core/engine.py b/src/core/engine.py index 52aed5bc..69c69dae 100644 --- a/src/core/engine.py +++ b/src/core/engine.py @@ -341,10 +341,7 @@ class Player: self.connector.disable_transaction() Thread(target=clean_up).start() - # Filesystem meta-changes trigger the event via Liquidsoap, so only - # issue event for LIVE and STREAM: - if not entry.channel in ChannelType.QUEUE.channels: - self.event_dispatcher.on_play(entry) + self.event_dispatcher.on_play(entry) @@ -386,6 +383,7 @@ class Player: self.event_dispatcher.on_fallback_updated(entries) + def stop_fallback_playlist(self): """ Performs a fade-out and clears any scheduled fallback playlist. @@ -548,7 +546,14 @@ class Player: self.connector.disable_transaction() # If successful, Liquidsoap returns a resource ID of the queued track - return int(result) >= 0 + resource_id = -1 + try: + resource_id = int(result) + except ValueError: + self.logger.error(SU.red("Got an invalid resource ID: '%s'" % result)) + return False + + return resource_id >= 0 diff --git a/src/core/events.py b/src/core/events.py index ee0255d3..74aca24b 100644 --- a/src/core/events.py +++ b/src/core/events.py @@ -220,15 +220,18 @@ class EngineEventDispatcher(): def on_play(self, entry): """ - Event Handler which is called by the engine when some entry is actually playing. + Event Handler which is called by the engine when some play command to Liquidsoap is issued. + This does not indiciate that Liquidsoap started playing actually, only that the command has + been issued. To get the metadata update issued by Liquidsoap use `on_metadata` instead. Args: source (String): The `PlaylistEntry` object """ def func(self, entry): self.logger.debug("on_play(..)") - # Assign timestamp for play time + # Assign timestamp indicating start play time. Use the actual playtime when possible. entry.entry_start_actual = datetime.datetime.now() + self.scheduler.on_play(entry) self.call_event("on_play", entry) thread = Thread(target = func, args = (self, entry)) diff --git a/src/scheduling/calendar.py b/src/scheduling/calendar.py index cffa0961..3d847011 100644 --- a/src/scheduling/calendar.py +++ b/src/scheduling/calendar.py @@ -100,7 +100,7 @@ class AuraCalendarService(threading.Thread): return # Check if existing timeslots have been deleted - local_timeslots = Timeslot.select_programme(datetime.now()) + local_timeslots = Timeslot.get_timeslots(datetime.now()) for local_timeslot in local_timeslots: # Only allow deletion of timeslots which are deleted before the start of the scheduling window @@ -169,7 +169,7 @@ class AuraCalendarService(threading.Thread): Args: timeslot (Timeslot): The timeslot """ - timeslot_db = Timeslot.select_show_on_datetime(timeslot["start"]) + timeslot_db = Timeslot.for_datetime(timeslot["start"]) havetoadd = False if not timeslot_db: diff --git a/src/scheduling/fallback.py b/src/scheduling/fallback.py index cc6bde81..1dec3da8 100644 --- a/src/scheduling/fallback.py +++ b/src/scheduling/fallback.py @@ -26,7 +26,7 @@ from datetime import timedelta from src.base.config import AuraConfig from src.base.utils import SimpleUtil as SU -from src.core.resources import ResourceClass +from src.core.resources import ResourceClass, ResourceUtil from src.core.channels import Channel from src.core.control import EngineExecutor @@ -113,7 +113,12 @@ class FallbackManager: Args: source (String): The `PlaylistEntry` object - """ + """ + content_class = ResourceUtil.get_content_class(entry.get_content_type()) + if content_class == ResourceClass.FILE: + # Files are handled by "on_metadata" called via Liquidsoap + return + self.update_fallback_state(entry.channel) @@ -306,5 +311,5 @@ class FallbackCommand(EngineExecutor): end_time_offset = int(float(AuraConfig.config().get("fade_out_time")) / 2 * 1000 * -1) end_time = SU.timestamp(timeslot.timeslot_end + timedelta(milliseconds=end_time_offset)) self.logger.info(f"Starting fade-out of scheduled fallback with an offset of {end_time_offset} milliseconds at {end_time}") - child_timer = EngineExecutor("FALLBACK", None, end_time, do_stop, None) - super().__init__("FALLBACK", child_timer, timeslot.start_unix, do_play, entries) \ No newline at end of file + super().__init__("FALLBACK", None, timeslot.start_unix, do_play, entries) + EngineExecutor("FALLBACK", self, end_time, do_stop, None) \ No newline at end of file diff --git a/src/scheduling/models.py b/src/scheduling/models.py index 90ab2f58..f0f751e9 100644 --- a/src/scheduling/models.py +++ b/src/scheduling/models.py @@ -167,16 +167,23 @@ class Timeslot(DB.Model, AuraDatabaseModel): musicfocus = Column(String(256)) is_repetition = Column(Boolean()) - fadeouttimer = None # Used to fade-out the timeslot, even when entries are longer + # Transients + active_entry = None @staticmethod - def select_show_on_datetime(date_time): + def for_datetime(date_time): + """ + Select a timeslot at the given datetime. + + Args: + date_time (datetime): date and time when the timeslot starts + """ return DB.session.query(Timeslot).filter(Timeslot.timeslot_start == date_time).first() @staticmethod - def select_programme(date_from=datetime.date.today()): + def get_timeslots(date_from=datetime.date.today()): """ Select all timeslots starting from `date_from` or from today if no parameter is passed. @@ -189,34 +196,26 @@ class Timeslot(DB.Model, AuraDatabaseModel): """ timeslots = DB.session.query(Timeslot).\ filter(Timeslot.timeslot_start >= date_from).\ - order_by(Timeslot.timeslot_start).all() - + order_by(Timeslot.timeslot_start).all() return timeslots - @staticmethod - def select_upcoming(n): + def set_active_entry(self, entry): """ - Selects the (`n`) upcoming timeslots. + Sets the currently playing entry. + + Args: + entry (PlaylistEntry): The entry playing right now """ - now = datetime.datetime.now() - DB.session.commit() # Required since independend session is used. - timeslots = DB.session.query(Timeslot).\ - filter(Timeslot.timeslot_start > str(now)).\ - order_by(Timeslot.timeslot_start.asc()).limit(n).all() - - return timeslots + self.active_entry = entry - def has_queued_entries(self): + def get_recent_entry(self): """ - Checks if entries of this timeslot have been queued at the engine. + Retrieves the most recent played or currently playing entry. This is used to fade-out + the timeslot, when there is no other entry is following the current one. """ - #TODO Make logic more transparent - if hasattr(self, "queued_entries"): - if len(self.queued_entries) > 0: - return True - return False + return self.active_entry @hybrid_property @@ -332,9 +331,7 @@ class Playlist(DB.Model, AuraDatabaseModel): """ playlist = None playlists = DB.session.query(Playlist).filter(Playlist.timeslot_start == start_date).all() - # FIXME There are unknown issues with the native SQL query by ID - # playlists = DB.session.query(Playlist).filter(Playlist.timeslot_start == datetime and Playlist.playlist_id == playlist_id).all() - + for p in playlists: if p.playlist_id == playlist_id: playlist = p @@ -450,13 +447,11 @@ class PlaylistEntry(DB.Model, AuraDatabaseModel): source = Column(String(1024)) entry_start = Column(DateTime) + # Transients entry_start_actual = None # Assigned when the entry is actually played channel = None # Assigned when entry is actually played queue_state = None # Assigned when entry is about to be queued status = None # Assigned when state changes - switchtimer = None - loadtimer = None - fadeouttimer = None @staticmethod diff --git a/src/scheduling/programme.py b/src/scheduling/programme.py index c8620342..bb477eb1 100644 --- a/src/scheduling/programme.py +++ b/src/scheduling/programme.py @@ -104,7 +104,7 @@ class Programme(): them via `self.enable_entries(..)`. After that, the current message queue is printed to the console. """ - self.programme = Timeslot.select_programme() + self.programme = Timeslot.get_timeslots() if not self.programme: self.logger.critical(SU.red("Could not load programme from database. We are in big trouble my friend!")) diff --git a/src/scheduling/scheduler.py b/src/scheduling/scheduler.py index 762324f4..5e72546d 100644 --- a/src/scheduling/scheduler.py +++ b/src/scheduling/scheduler.py @@ -28,7 +28,7 @@ from datetime import datetime, timedelta from src.base.config import AuraConfig from src.base.utils import SimpleUtil as SU -from src.scheduling.models import AuraDatabaseModel, Timeslot, Playlist +from src.scheduling.models import AuraDatabaseModel, Playlist from src.base.exceptions import NoActiveTimeslotException, LoadSourceException from src.core.control import EngineExecutor from src.core.engine import Engine @@ -46,14 +46,17 @@ class TimeslotCommand(EngineExecutor): Command for triggering start and end of timeslot events. """ engine = None + config = None def __init__(self, engine, timeslot): """ Constructor Args: + engine (Engine): The engine timeslot (Timeslot): The timeslot which is starting at this time - """ + """ + self.config = AuraConfig() self.engine = engine def do_start_timeslot(timeslot): @@ -64,8 +67,73 @@ class TimeslotCommand(EngineExecutor): self.logger.info(SU.cyan(f"=== on_timeslot_end('{timeslot}') ===")) self.engine.event_dispatcher.on_timeslot_end(timeslot) - child_timer = EngineExecutor("TIMESLOT", None, timeslot.end_unix, do_end_timeslot, timeslot) - super().__init__("TIMESLOT", child_timer, timeslot.start_unix, do_start_timeslot, timeslot) + recent_entry = timeslot.get_recent_entry() + if recent_entry: + self.engine.player.stop(recent_entry, TransitionType.FADE) + else: + self.logger.warning(SU.red(f"Interestingly timeslot {timeslot} has no entry to be faded out?")) + + fade_out_time = float(self.config.get("fade_out_time")) + start_fade_out = timeslot.end_unix - fade_out_time + self.logger.info(f"Fading out timeslot in {start_fade_out} seconds at {timeslot.timeslot_end} | Timeslot: {timeslot}") + super().__init__("TIMESLOT", None, timeslot.start_unix, do_start_timeslot, timeslot) + EngineExecutor("TIMESLOT", self, start_fade_out, do_end_timeslot, timeslot) + + + +class PlayCommand(EngineExecutor): + """ + Command for triggering start and end of timeslot events. + """ + engine = None + config = None + + def __init__(self, engine, entries): + """ + Constructor + + Args: + engine (Engine): The engine + entries (PlaylistEntry): One or more playlist entries to be started + """ + self.config = AuraConfig() + self.engine = engine + + + def do_preload(entries): + try: + if entries[0].get_content_type() in ResourceClass.FILE.types: + self.logger.info(SU.cyan("=== preload_group('%s') ===" % ResourceUtil.get_entries_string(entries))) + self.engine.player.preload_group(entries, ChannelType.QUEUE) + else: + self.logger.info(SU.cyan("=== preload('%s') ===" % ResourceUtil.get_entries_string(entries))) + self.engine.player.preload(entries[0]) + except LoadSourceException as e: + self.logger.critical(SU.red("Could not preload entries %s" % ResourceUtil.get_entries_string(entries)), e) + + if entries[-1].status != EntryPlayState.READY: + self.logger.critical(SU.red("Entries didn't reach 'ready' state during preloading (Entries: %s)" % ResourceUtil.get_entries_string(entries))) + + + def do_play(entries): + self.logger.info(SU.cyan("=== play('%s') ===" % ResourceUtil.get_entries_string(entries))) + if entries[-1].status != EntryPlayState.READY: + # Let 'em play anyway ... + self.logger.critical(SU.red("PLAY: The entry/entries are not yet ready to be played (Entries: %s)" % ResourceUtil.get_entries_string(entries))) + while (entries[-1].status != EntryPlayState.READY): + self.logger.info("PLAY: Wait a little until preloading is done ...") + time.sleep(2) + + self.engine.player.play(entries[0], TransitionType.FADE) + self.logger.info(engine.scheduler.timeslot_renderer.get_ascii_timeslots()) + + + start_preload = entries[0].start_unix - self.config.get("preload_offset") + start_play = entries[0].start_unix + super().__init__("PRELOAD", None, start_preload, do_preload, entries) + EngineExecutor("PLAY", self, start_play, do_play, entries) + + @@ -158,10 +226,6 @@ class AuraScheduler(threading.Thread): self.logger.critical(SU.red(f"Unhandled error while fetching & scheduling new programme! ({str(e)})"), e) # Keep on working anyway - self.clean_timer_queue() - self.print_timer_queue() - - EngineExecutor.log_commands() self.exit_event.wait(seconds_to_wait) @@ -177,14 +241,6 @@ class AuraScheduler(threading.Thread): Called when the engine has finished booting and is ready to play. """ self.is_initialized = True - self.on_scheduler_ready() - - - - def on_scheduler_ready(self): - """ - Called when the scheduler is ready. - """ self.logger.info(self.timeslot_renderer.get_ascii_timeslots()) try: @@ -192,8 +248,24 @@ class AuraScheduler(threading.Thread): self.queue_startup_entries() except NoActiveTimeslotException: # That's not good, but keep on working... - pass + pass + + + def on_play(self, entry): + """ + Event Handler which is called by the engine when some entry is actually playing. + Ignores entries which are part of a scheduled fallback, because they handle their + stuff by themselves. + Args: + source (String): The `PlaylistEntry` object + """ + if entry.channel in ChannelType.FALLBACK_QUEUE.channels: + return + + if entry.playlist and entry.playlist.timeslot: + timeslot = entry.playlist.timeslot + timeslot.set_active_entry(entry) # @@ -206,7 +278,7 @@ class AuraScheduler(threading.Thread): Returns the current programme. """ return self.programme - + def play_active_entry(self): """ @@ -223,11 +295,7 @@ class AuraScheduler(threading.Thread): if active_timeslot: # Create command timer to indicate the start of the timeslot TimeslotCommand(self.engine, active_timeslot) - self.fallback.queue_fallback_playlist(active_timeslot) - # Queue the fade-out of the timeslot - if not active_timeslot.fadeouttimer: - self.queue_end_of_timeslot(active_timeslot, True) active_entry = self.programme.get_current_entry() if not active_entry: @@ -246,8 +314,7 @@ class AuraScheduler(threading.Thread): self.logger.info("The FFWD [>>] range exceeds the length of the entry. Drink some tea and wait for the sound of the next entry.") else: # Preload and play active entry - self.engine.player.preload(active_entry) - self.engine.player.play(active_entry, TransitionType.FADE) + PlayCommand(self.engine, [active_entry]) # Fast-forward to the scheduled position if seconds_to_seek > 0: @@ -266,41 +333,13 @@ class AuraScheduler(threading.Thread): or active_entry.get_content_type() in ResourceClass.LIVE.types: # Preload and play active entry - self.engine.player.preload(active_entry) - self.engine.player.play(active_entry, TransitionType.FADE) + PlayCommand(self.engine, [active_entry]) else: self.logger.critical("Unknown Entry Type: %s" % active_entry) - def print_timer_queue(self): - """ - Prints the current timer queue i.e. playlists in the queue to be played. - """ - message_queue = "" - messages = sorted(self.message_timer, key=attrgetter('diff')) - if not messages: - self.logger.warning(SU.red("There's nothing in the Timer Queue!")) - else: - for msg in messages: - message_queue += str(msg)+"\n" - - self.logger.info("Timer queue: \n" + message_queue) - - - - def clean_timer_queue(self): - """ - Removes inactive timers from the queue. - """ - len_before = len(self.message_timer) - self.message_timer[:] = [m for m in self.message_timer if m.is_alive()] - len_after = len(self.message_timer) - self.logger.debug("Removed %s finished timer objects from queue" % (len_before - len_after)) - - - def get_active_playlist(self): """ Retrieves the currently playing playlist. @@ -329,17 +368,12 @@ class AuraScheduler(threading.Thread): for next_timeslot in timeslots: # Create command timer to indicate the start of the timeslot TimeslotCommand(self.engine, next_timeslot) - # Schedule any available fallback playlist self.fallback.queue_fallback_playlist(next_timeslot) if next_timeslot.playlist: self.queue_playlist_entries(next_timeslot, next_timeslot.playlist.entries, False, True) - # Queue the fade-out of the timeslot - if not next_timeslot.fadeouttimer: - self.queue_end_of_timeslot(next_timeslot, True) - self.logger.info(SU.green("Finished queuing programme.")) @@ -357,24 +391,11 @@ class AuraScheduler(threading.Thread): if current_playlist: active_entry = self.programme.get_current_entry() - - # Finished entries - for entry in current_playlist.entries: - if active_entry == entry: - break - - # Entry currently being played if active_entry: - # Queue open entries for current playlist rest_of_playlist = active_entry.get_next_entries(True) self.queue_playlist_entries(current_timeslot, rest_of_playlist, False, True) - # Store them for later reference - current_timeslot.queued_entries = [active_entry] - if rest_of_playlist: - current_timeslot.queued_entries.append(rest_of_playlist) - def queue_playlist_entries(self, timeslot, entries, fade_in, fade_out): @@ -430,225 +451,18 @@ class AuraScheduler(threading.Thread): self.logger.info("Built %s entry group(s)" % len(entry_groups)) # Timeslot function calls - do_queue_timeslot_end = False if len(entries) > 0 and len(entry_groups) > 0: for entries in entry_groups: if not isinstance(entries, list): raise ValueError("Invalid Entry Group: %s" % str(entries)) - # Create timers for each entry group - self.set_entries_timer(entries, fade_in, fade_out) - - # Store them for later reference - timeslot.queued_entries = entries - + # Create command timers for each entry group + PlayCommand(self.engine, entries) else: self.logger.warn(SU.red("Nothing to schedule ...")) - def set_entries_timer(self, entries, fade_in, fade_out): - """ - Creates timer for loading and playing one or multiple entries. Existing timers are - updated. - - Args: - entries ([]): List of multiple filesystem entries, or a single entry of other types - """ - play_timer = self.is_something_planned_at_time(entries[0].start_unix) - now_unix = Engine.engine_time() - diff = entries[0].start_unix - now_unix - - # Play function to be called by timer - def do_play(entries): - self.logger.info(SU.cyan("=== play('%s') ===" % ResourceUtil.get_entries_string(entries))) - transition_type = TransitionType.INSTANT - if fade_in: - transition_type = TransitionType.FADE - - if entries[-1].status != EntryPlayState.READY: - self.logger.critical(SU.red("PLAY: For some reason the entry/entries are not yet ready to be played (Entries: %s)" % ResourceUtil.get_entries_string(entries))) - # Let 'em play anyway ... - - self.engine.player.play(entries[0], transition_type) - self.logger.info(self.timeslot_renderer.get_ascii_timeslots()) - - - if play_timer: - # Check if the Playlist IDs are different - if self.have_entries_changed(play_timer, entries): - # If not, stop and remove the old timer, create a new one - self.stop_timer(play_timer) - else: - # If the playlist entries do not differ => reuse the old timer and do nothing - self.logger.debug("Playlist Entry %s is already scheduled - no new timer created." % ResourceUtil.get_entries_string(entries)) - return - - # If nothing is planned at given time, create a new timer - (entries[0].switchtimer, entries[0].loadtimer) = self.create_timer(diff, do_play, entries, switcher=True) - - - - def have_entries_changed(self, timer, new_entries): - """ - Checks if the new entries and playlists are matching the existing queued ones, - or if they should be updated. - - Args: - timer (CallFunctionTimer): The timer holding queued entries - new_entries ([PlaylistEntry]): The possibly updated entries - - Returns: - (Boolean): `True` if it has changed - """ - old_entries = timer.entries - - if old_entries[0].playlist and new_entries[0].playlist: - if old_entries[0].playlist.playlist_id != new_entries[0].playlist.playlist_id: - return True - if len(old_entries) != len(new_entries): - return True - - for old_entry, new_entry in zip(old_entries, new_entries): - if old_entry.source != new_entry.source: - return True - - return False - - - - def queue_end_of_timeslot(self, timeslot, fade_out): - """ - Queues a engine action to stop/fade-out the given timeslot. - - Args: - timeslot (PlaylistEntry): The timeslot - fade_out (Boolean): If the timeslot should be faded-out - """ - timeslot_end = timeslot.timeslot_end - timeslot_end_unix = timeslot.end_unix - now_unix = Engine.engine_time() - fade_out_time = 0 - - # Stop function to be called when timeslot ends - def do_stop(timeslot): - if timeslot.has_queued_entries(): - last_entry = timeslot.queued_entries[-1] - self.logger.info(SU.cyan("=== stop('%s') ===" % str(last_entry.playlist.timeslot))) - transition_type = TransitionType.INSTANT - if fade_out: - transition_type = TransitionType.FADE - self.engine.player.stop(last_entry, transition_type) - - if fade_out == True: - fade_out_time = int(round(float(self.config.get("fade_out_time")))) #TODO Use float - - # Stop any existing fade-out timer - if timeslot.fadeouttimer: - timeslot.fadeouttimer.cancel() - self.message_timer.remove(timeslot.fadeouttimer) - - # Create timer to fade-out - start_fade_out = timeslot_end_unix - now_unix - fade_out_time - # last_entry = timeslot.queued_entries[-1] - timeslot.fadeouttimer = self.create_timer(start_fade_out, do_stop, timeslot, fadeout=True) - - self.logger.info("Fading out timeslot in %s seconds at %s | Timeslot: %s" % (str(start_fade_out), str(timeslot_end), timeslot)) - - - - def is_something_planned_at_time(self, given_time): - """ - Checks for existing timers at the given time. - """ - for t in self.message_timer: - if t.fadein or t.switcher: - if t.entries[0].start_unix == given_time: - return t - return False - - - - def create_timer(self, diff, func, param, fadein=False, fadeout=False, switcher=False): - """ - Creates a new timer for timed execution of mixer commands. - - Args: - diff (Integer): The difference in seconds from now, when the call should happen - func (Function): The function to call - param ([]): A timeslot or list of entries - - Returns: - (CallFunctionTimer, CallFunctionTimer): In case of a "switch" command, the switch and preload timer is returned - (CallFunctionTimer): In all other cases only the timer for the command is returned - """ - if not fadein and not fadeout and not switcher or fadein and fadeout or fadein and switcher or fadeout and switcher: - raise ValueError("You have to call me with either fadein=true, fadeout=true or switcher=True") - if not isinstance(param, list) and not isinstance(param, Timeslot): - raise ValueError("No list of entries nor timeslot passed!") - - t = CallFunctionTimer(diff=diff, func=func, param=param, fadein=fadein, fadeout=fadeout, switcher=switcher) - self.message_timer.append(t) - t.start() - - if switcher: - # Preload function to be called by timer - def do_preload(entries): - try: - if entries[0].get_content_type() in ResourceClass.FILE.types: - self.logger.info(SU.cyan("=== preload_group('%s') ===" % ResourceUtil.get_entries_string(entries))) - self.engine.player.preload_group(entries, ChannelType.QUEUE) - else: - self.logger.info(SU.cyan("=== preload('%s') ===" % ResourceUtil.get_entries_string(entries))) - self.engine.player.preload(entries[0]) - except LoadSourceException as e: - self.logger.critical(SU.red("Could not preload entries %s" % ResourceUtil.get_entries_string(entries)), e) - - if entries[-1].status != EntryPlayState.READY: - self.logger.critical(SU.red("Entries didn't reach 'ready' state during preloading (Entries: %s)" % ResourceUtil.get_entries_string(entries))) - - loader_diff = diff - self.config.get("preload_offset") - loader = CallFunctionTimer(diff=loader_diff, func=do_preload, param=param, fadein=fadein, fadeout=fadeout, switcher=False, loader=True) - self.message_timer.append(loader) - loader.start() - return (t, loader) - else: - return t - - - - def stop_timer(self, timer): - """ - Stops the given timer. - - Args: - timer (Timer): The timer to stop. - """ - timer.cancel() - count = 1 - - for entry in timer.entries: - if entry.loadtimer is not None: - entry.loadtimer.cancel() - self.message_timer.remove(entry.loadtimer) - count += 1 - - # if timer.entries[0].fadeintimer is not None: - # timer.entries[0].fadeintimer.cancel() - # self.message_timer.remove(timer.entries[0].fadeintimer) - # count += 1 - - # if entry.fadeouttimer is not None: - # entry.fadeouttimer.cancel() - # self.message_timer.remove(entry.fadeouttimer) - # count += 1 - - # Remove it from message queue - self.message_timer.remove(timer) - self.logger.info("Stopped %s timers for: %s" % (str(count), ResourceUtil.get_entries_string(timer.entries))) - - - # FIXME Move to adequate module @staticmethod def init_database(): @@ -683,59 +497,3 @@ class AuraScheduler(threading.Thread): self.logger.info("Shutting down scheduler ...") - - -# ------------------------------------------------------------------------------------------ # -class CallFunctionTimer(threading.Timer): - logger = None - param = None - entries = None - diff = None - dt = None - fadein = False - fadeout = False - switcher = False - loader = False - - def __init__(self, diff=None, func=None, param=None, fadein=False, fadeout=False, switcher=False, loader=False): - - self.logger = logging.getLogger("AuraEngine") - self.logger.debug("Executing engine command '%s' in %s seconds..." % (str(func.__name__), str(diff))) - threading.Timer.__init__(self, diff, func, (param,)) - - if not fadein and not fadeout and not switcher and not loader \ - or fadein and fadeout \ - or fadein and switcher \ - or fadeout and switcher: - - raise Exception("You have to create me with either fadein=True, fadeout=True or switcher=True") - - self.diff = diff - self.dt = datetime.now() + timedelta(seconds=diff) - - self.func = func - self.param = param - self.entries = param # TODO Refactor since param can hold [entries] or a timeslot, depending on the timer type - self.fadein = fadein - self.fadeout = fadeout - self.switcher = switcher - self.loader = loader - - - def __str__(self): - """ - String represenation of the timer. - """ - status = "Timer (Alive: %s)" % self.is_alive() - status += " starting at " + str(self.dt) - - if self.fadein: - return status + " fading in entries '" + ResourceUtil.get_entries_string(self.entries) - elif self.fadeout: - return status + " fading out timeslot '" + str(self.param) - elif self.switcher: - return status + " switching to entries '" + ResourceUtil.get_entries_string(self.entries) - elif self.loader: - return status + " preloading entries '" + ResourceUtil.get_entries_string(self.entries) - else: - return "CORRUPTED CallFunctionTimer around! How can that be?" -- GitLab