From 4d4a2566c3d1839d9ebeb49bfab9a7be240f8a4f Mon Sep 17 00:00:00 2001 From: David Trattnig <david.trattnig@o94.at> Date: Fri, 27 Nov 2020 16:07:36 +0100 Subject: [PATCH] Default playlist handling. #52 --- src/scheduling/api.py | 54 +++++++++++++++++++++++++------------ src/scheduling/fallback.py | 5 ++-- src/scheduling/programme.py | 46 ++++++++++++++++++++++++++----- src/scheduling/scheduler.py | 9 ++++--- 4 files changed, 84 insertions(+), 30 deletions(-) diff --git a/src/scheduling/api.py b/src/scheduling/api.py index dfc99573..f8831408 100644 --- a/src/scheduling/api.py +++ b/src/scheduling/api.py @@ -119,11 +119,22 @@ class ApiFetcher(threading.Thread): self.logger.critical(SU.red("No timeslots fetched from API!")) return None + for timeslot in self.fetched_timeslot_data: + + # FIXME Workaround until https://gitlab.servus.at/aura/steering/-/issues/54 is implemented + if "schedule_fallback_id" in timeslot: + timeslot["default_schedule_playlist_id"] = timeslot["schedule_fallback_id"] + timeslot["schedule_fallback_id"] = None + if "show_fallback_id" in timeslot: + timeslot["default_show_playlist_id"] = timeslot["show_fallback_id"] + timeslot["show_fallback_id"] = None + self.logger.debug("Fetching playlists from TANK") self.fetch_playlists() try: for timeslot in self.fetched_timeslot_data: + # Skip timeslot if no start or end is given if "start" not in timeslot: self.logger.warning("No start of timeslot given. Skipping timeslot: " + str(timeslot)) @@ -131,13 +142,6 @@ class ApiFetcher(threading.Thread): if "end" not in timeslot: self.logger.warning("No end of timeslot given. Skipping timeslot: " + str(timeslot)) timeslot = None - if "playlist" not in timeslot \ - and "show_fallback" not in timeslot \ - and "schedule_fallback" not in timeslot \ - and "station_fallback" not in timeslot: - - self.logger.warning("No playlist for timeslot given. Skipping timeslot: " + str(timeslot)) - timeslot = None if timeslot: return_data.append(timeslot) @@ -191,18 +195,22 @@ class ApiFetcher(threading.Thread): try: for timeslot in self.fetched_timeslot_data: - # Get IDs of playlists + # Get IDs of specific, default and fallback playlists playlist_id = self.get_playlist_id(timeslot, "playlist_id") - schedule_fallback_id = self.get_playlist_id(timeslot, "schedule_fallback_id") + default_schedule_playlist_id = self.get_playlist_id(timeslot, "default_schedule_playlist_id") + default_show_playlist_id = self.get_playlist_id(timeslot, "default_show_playlist_id") + schedule_fallback_id = self.get_playlist_id(timeslot, "schedule_fallback_id") show_fallback_id = self.get_playlist_id(timeslot, "show_fallback_id") station_fallback_id = self.get_playlist_id(timeslot, "station_fallback_id") - # Retrieve playlist and the fallback playlists for every timeslot. + # Retrieve playlist, default and the fallback playlists for every timeslot. # If a playlist (like station_fallback) is already fetched, it is not fetched again but reused - timeslot["playlist"] = self.fetch_playlist(playlist_id, fetched_entries) - timeslot["schedule_fallback"] = self.fetch_playlist(schedule_fallback_id, fetched_entries) - timeslot["show_fallback"] = self.fetch_playlist(show_fallback_id, fetched_entries) - timeslot["station_fallback"] = self.fetch_playlist(station_fallback_id, fetched_entries) + timeslot["playlist"] = self.fetch_playlist(playlist_id, fetched_entries) + timeslot["default_schedule_playlist"] = self.fetch_playlist(default_schedule_playlist_id, fetched_entries) + timeslot["default_show_playlist"] = self.fetch_playlist(default_show_playlist_id, fetched_entries) + timeslot["schedule_fallback"] = self.fetch_playlist(schedule_fallback_id, fetched_entries) + timeslot["show_fallback"] = self.fetch_playlist(show_fallback_id, fetched_entries) + timeslot["station_fallback"] = self.fetch_playlist(station_fallback_id, fetched_entries) except Exception as e: self.logger.error("Error while fetching playlists from API endpoints: " + str(e), e) @@ -214,14 +222,15 @@ class ApiFetcher(threading.Thread): Fetches the playlist for a given timeslot. Args: - id_name (String): The type of playlist to fetch (e.g. normal vs. fallback) - fetched_playlists ([]): Previously fetched playlists to avoid re-fetching + playlist_id (String): The ID of the playlist + fetched_playlists ([dict]): Previously fetched playlists to avoid re-fetching Returns: - (Playlist): Playlist of type `id_name` + (Playlist): Playlist for `playlist_id` """ if not playlist_id: return None + playlist = None url = self.tank_playlist_url.replace("${ID}", playlist_id) headers = { @@ -255,7 +264,17 @@ class ApiFetcher(threading.Thread): def get_playlist_id(self, timeslot, id_name): """ Extracts the playlist ID for a given playlist (fallback) type. + + Args: + timeslot (dict): The timeslot dictionary + id_name (String): The dictionary key holding the playlist ID + + Returns: + (Integer): The playlist ID """ + if not id_name in timeslot: + return None + playlist_id = str(timeslot[id_name]) if not playlist_id or playlist_id == "None": self.logger.debug("No value defined for '%s' in timeslot '#%s'" % (id_name, timeslot["id"])) @@ -264,6 +283,7 @@ class ApiFetcher(threading.Thread): return playlist_id + def polish_timeslots(self, timeslots): """ Removes all timeslots which are not relevant for further processing, diff --git a/src/scheduling/fallback.py b/src/scheduling/fallback.py index a0e6b482..2f4e7c67 100644 --- a/src/scheduling/fallback.py +++ b/src/scheduling/fallback.py @@ -201,11 +201,10 @@ class FallbackManager: Returns: (FallbackType, Playlist) """ - planned_playlist = None fallback_type = None + planned_playlist = self.engine.scheduler.programme.get_current_playlist(timeslot) - if self.validate_playlist(timeslot, "playlist"): - planned_playlist = timeslot.playlist + if planned_playlist: fallback_type = FallbackType.NONE else: (fallback_type, planned_playlist) = self.get_fallback_playlist(timeslot) diff --git a/src/scheduling/programme.py b/src/scheduling/programme.py index 0104de7e..109900b5 100644 --- a/src/scheduling/programme.py +++ b/src/scheduling/programme.py @@ -117,9 +117,9 @@ class ProgrammeService(): return None # Check for scheduled playlist - current_playlist = current_timeslot.playlist + current_playlist = self.get_current_playlist(current_timeslot) if not current_playlist: - msg = "There's no playlist assigned to the current timeslot. Most likely a fallback will make things okay again." + msg = "There's no (default) playlist assigned to the current timeslot. Most likely a fallback will make things okay again." self.logger.warning(SU.red(msg)) return None @@ -161,6 +161,25 @@ class ProgrammeService(): + def get_current_playlist(self, timeslot): + """ + Retrieves the playlist to be scheduled. If no specific playlist is assigned, + the default schedule or show playlist is returned. This method does not + respect any defined fallback playlists. + + Returns: + (FallbackType, Playlist): The currently assigned playlist + """ + playlist = timeslot.playlist + if not playlist: + playlist = timeslot.default_schedule_playlist + if not playlist: + playlist = timeslot.default_show_playlist + + return playlist + + + def get_next_timeslots(self, max_count=0): """ Retrieves the timeslots to be played after the current one. @@ -274,6 +293,10 @@ class ProgrammeStore(): # Store assigned playlists self.store_playlist(timeslot_db, timeslot_db.playlist_id, timeslot["playlist"]) + if timeslot_db.default_schedule_playlist_id: + self.store_playlist(timeslot_db, timeslot_db.default_schedule_playlist_id, timeslot["default_schedule_playlist"]) + if timeslot_db.default_show_playlist_id: + self.store_playlist(timeslot_db, timeslot_db.default_show_playlist_id, timeslot["default_show_playlist"]) if timeslot_db.schedule_fallback_id: self.store_playlist(timeslot_db, timeslot_db.schedule_fallback_id, timeslot["schedule_fallback"]) if timeslot_db.show_fallback_id: @@ -351,9 +374,18 @@ class ProgrammeStore(): timeslot_db.topic = timeslot["show_topics"] timeslot_db.musicfocus = timeslot["show_musicfocus"] timeslot_db.playlist_id = timeslot["playlist_id"] - timeslot_db.schedule_fallback_id = timeslot["schedule_fallback_id"] - timeslot_db.show_fallback_id = timeslot["show_fallback_id"] - timeslot_db.station_fallback_id = timeslot["station_fallback_id"] + + # Optional API properties + if "default_schedule_playlist_id" in timeslot: + timeslot_db.default_schedule_playlist_id = timeslot["default_schedule_playlist_id"] + if "default_show_playlist_id" in timeslot: + timeslot_db.default_show_playlist_id = timeslot["default_show_playlist_id"] + if "schedule_fallback_id" in timeslot: + timeslot_db.schedule_fallback_id = timeslot["schedule_fallback_id"] + if "show_fallback_id" in timeslot: + timeslot_db.show_fallback_id = timeslot["show_fallback_id"] + if "station_fallback_id" in timeslot: + timeslot_db.station_fallback_id = timeslot["station_fallback_id"] timeslot_db.store(add=havetoadd, commit=True) return timeslot_db @@ -415,8 +447,9 @@ class ProgrammeStore(): entry_db.entry_num = entry_num entry_db.duration = SU.nano_to_seconds(entry["duration"]) + # FIXME Refactor mix of uri/filename/file/source + if "uri" in entry: - # FIXME Refactor mix of uri/filename/file/source entry_db.uri = entry["uri"] entry_db.source = entry["uri"] if "filename" in entry: @@ -450,6 +483,7 @@ class ProgrammeStore(): entry_num += 1 + def expand_entry_duration(self, timeslot_db, fetched_playlist): """ If some playlist entry doesn't have a duration assigned, its duration is expanded to the diff --git a/src/scheduling/scheduler.py b/src/scheduling/scheduler.py index b32b3f30..931e9184 100644 --- a/src/scheduling/scheduler.py +++ b/src/scheduling/scheduler.py @@ -121,7 +121,7 @@ class AuraScheduler(threading.Thread): # - # EVENTS + # EVENT HANDLERS # @@ -262,8 +262,9 @@ class AuraScheduler(threading.Thread): # 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) + playlist = self.programme.get_current_playlist(next_timeslot) + if playlist: + self.queue_playlist_entries(next_timeslot, playlist.entries, False, True) self.logger.info(SU.green("Finished queuing programme.")) @@ -278,7 +279,7 @@ class AuraScheduler(threading.Thread): # Queue the (rest of the) currently playing timeslot upon startup if current_timeslot: - current_playlist = current_timeslot.playlist + current_playlist = self.programme.get_current_playlist(current_timeslot) if current_playlist: active_entry = self.programme.get_current_entry() -- GitLab