diff --git a/src/scheduling/scheduler.py b/src/scheduling/scheduler.py index 5e72546d088e1fee4002ead0b724576ec684a114..731ce57daa5bdcad8a573d25c3c436e64b2b18c3 100644 --- a/src/scheduling/scheduler.py +++ b/src/scheduling/scheduler.py @@ -23,8 +23,6 @@ import threading import time import sqlalchemy -from operator import attrgetter -from datetime import datetime, timedelta from src.base.config import AuraConfig from src.base.utils import SimpleUtil as SU @@ -41,103 +39,6 @@ from src.scheduling.programme import Programme -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): - self.logger.info(SU.cyan(f"=== on_timeslot_start('{timeslot}') ===")) - self.engine.event_dispatcher.on_timeslot_start(timeslot) - - def do_end_timeslot(timeslot): - self.logger.info(SU.cyan(f"=== on_timeslot_end('{timeslot}') ===")) - self.engine.event_dispatcher.on_timeslot_end(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) - - - - - - class AuraScheduler(threading.Thread): """ Aura Scheduler Class @@ -497,3 +398,118 @@ class AuraScheduler(threading.Thread): self.logger.info("Shutting down scheduler ...") + + +# +# EngineExecutor Commands +# + + +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 + + 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, self.do_start_timeslot, timeslot) + EngineExecutor("TIMESLOT", self, start_fade_out, self.do_end_timeslot, timeslot) + + + def do_start_timeslot(self, timeslot): + """ + Initiates the start of the timeslot. + """ + self.logger.info(SU.cyan(f"=== on_timeslot_start('{timeslot}') ===")) + self.engine.event_dispatcher.on_timeslot_start(timeslot) + + + def do_end_timeslot(self, timeslot): + """ + Initiates the end of the timeslot. + """ + self.logger.info(SU.cyan(f"=== on_timeslot_end('{timeslot}') ===")) + self.engine.event_dispatcher.on_timeslot_end(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?")) + + + +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 + + start_preload = entries[0].start_unix - self.config.get("preload_offset") + start_play = entries[0].start_unix + super().__init__("PRELOAD", None, start_preload, self.do_preload, entries) + EngineExecutor("PLAY", self, start_play, self.do_play, entries) + + + def do_preload(self, entries): + """ + Preload the 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(self, entries): + """ + Play the 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(self.engine.scheduler.timeslot_renderer.get_ascii_timeslots()) + + + +