diff --git a/modules/base/utils.py b/modules/base/utils.py index e6a33efeae7701bbdd12754726c51c4ed93c3df9..474e053f94119285c93ea780063102bd4b31488b 100644 --- a/modules/base/utils.py +++ b/modules/base/utils.py @@ -179,6 +179,34 @@ class SimpleUtil: return datetime.datetime.fromtimestamp(timestamp).strftime('%H:%M:%S') + @staticmethod + def nano_to_seconds(nanoseconds): + """ + Converts nano-seconds to senconds + + Args: + (Integer) nanoseconds + + Returns: + (Float): seconds + """ + return float(nanoseconds / 1000000000) + + + @staticmethod + def seconds_to_nano(seconds): + """ + Converts senconds to nano-seconds + + Args: + (Integer) seconds + + Returns: + (Float): nanoseconds + """ + return int(seconds * 1000000000) + + @staticmethod def timestamp(date_and_time=None): """ diff --git a/modules/scheduling/calendar.py b/modules/scheduling/calendar.py index f920fa55f4c44efbcf19a06f249a05d4dc4ead38..8d565888badb59fde18e7ad268a40b1b0fafe265 100644 --- a/modules/scheduling/calendar.py +++ b/modules/scheduling/calendar.py @@ -27,7 +27,7 @@ import logging from datetime import datetime from modules.scheduling.types import PlaylistType -from modules.base.utils import SimpleUtil +from modules.base.utils import SimpleUtil as SU from modules.base.models import Schedule, Playlist, PlaylistEntry, PlaylistEntryMetaData from modules.scheduling.calender_fetcher import CalendarFetcher @@ -87,7 +87,7 @@ class AuraCalendarService(threading.Thread): Schedule ([]): An arrar of retrieved schedules passed via `self.queue` """ result = [] - now_unix = SimpleUtil.timestamp() + now_unix = SU.timestamp() scheduling_window_start = self.config.get("scheduling_window_start") try: @@ -121,8 +121,9 @@ class AuraCalendarService(threading.Thread): self.logger.info("Deleted local schedule #%s from database" % local_schedule.schedule_id) else: - msg = "Schedule #%s has been deleted remotely. Since the scheduling window has already started, it won't be deleted locally." % local_schedule.schedule_id - self.logger.warn(SimpleUtil.red(msg)) + msg = "Schedule #%s has been deleted remotely. Since the scheduling window has already started, it won't be deleted locally." % \ + local_schedule.schedule_id + self.logger.error(SU.red(msg)) # Process fetched schedules for schedule in fetched_schedule_data: @@ -243,8 +244,7 @@ class AuraCalendarService(threading.Thread): entry_num = 0 time_marker = playlist_db.start_unix - # If existing playlist entries are beyond the count of the new playlist entries, - # then delete those first + self.expand_entry_duration(schedule_db, fetched_playlist) self.delete_orphaned_entries(playlist_db, fetched_playlist) for entry in fetched_playlist["entries"]: @@ -257,16 +257,7 @@ class AuraCalendarService(threading.Thread): entry_db.entry_start = datetime.fromtimestamp(time_marker) entry_db.artificial_playlist_id = playlist_db.artificial_id entry_db.entry_num = entry_num - - if "file" in entry and "duration" in entry["file"]: - # Convert nano-seconds to seconds - duration = int(float(entry["file"]["duration"]) / 1000000000) - else: - # No duration means it's playing until the end of the schedule - self.logger.debug("Missing duration - setting duration of entry to end-of-schedule") - duration = SimpleUtil.timestamp(schedule_db.schedule_end) - time_marker - - entry_db.duration = duration + entry_db.duration = SU.nano_to_seconds(entry["duration"]) if "uri" in entry: # FIXME Refactor mix of uri/filename/file/source @@ -281,7 +272,7 @@ class AuraCalendarService(threading.Thread): self.store_playlist_entry_metadata(entry_db, entry["file"]["metadata"]) entry_num = entry_num + 1 - time_marker += duration + time_marker += entry_db.duration @@ -299,10 +290,41 @@ class AuraCalendarService(threading.Thread): for entry_num in range(new_last_idx, existing_last_idx+1, 1): PlaylistEntry.delete_entry(playlist_db.artificial_id, entry_num) - self.logger.info(SimpleUtil.yellow("Deleted playlist entry %s:%s" % (playlist_db.artificial_id, entry_num))) + self.logger.info(SU.yellow("Deleted playlist entry %s:%s" % (playlist_db.artificial_id, entry_num))) entry_num += 1 + def expand_entry_duration(self, schedule_db, fetched_playlist): + """ + If some playlist entry doesn't have a duration assigned, its duration is expanded to the + remaining duration of the playlist (= schedule duration minus playlist entries with duration). + If there's more than one entry without duration, such entries are removed from the playlist. + """ + total_seconds = (schedule_db.schedule_end - schedule_db.schedule_start).total_seconds() + total_duration = SU.seconds_to_nano(total_seconds) + actual_duration = 0 + missing_duration = [] + idx = 0 + + for entry in fetched_playlist["entries"]: + if not "duration" in entry: + missing_duration.append(idx) + else: + actual_duration += entry["duration"] + idx += 1 + + if len(missing_duration) == 1: + fetched_playlist["entries"][missing_duration[0]]["duration"] = total_duration - actual_duration + self.logger.info("Expanded duration of playlist entry #%s:%s" % (fetched_playlist["id"], missing_duration[0])) + + elif len(missing_duration) > 1: + # This case should actually never happen, as TANK doesn't allow more than one entry w/o duration anymore + for i in reversed(missing_duration[1:-1]): + self.logger.error(SU.red("Deleted Playlist Entry without duration: %s" % \ + str(fetched_playlist["entries"][i]))) + del fetched_playlist["entries"][i] + + def store_playlist_entry_metadata(self, entry_db, metadata): """ diff --git a/modules/scheduling/calender_fetcher.py b/modules/scheduling/calender_fetcher.py index 087dbec888de377b1688cca69d0b83913930585c..d942ca41db2526c2ac1d5b1fad494816bd9780ec 100644 --- a/modules/scheduling/calender_fetcher.py +++ b/modules/scheduling/calender_fetcher.py @@ -125,7 +125,6 @@ class CalendarFetcher: try: self.logger.debug("Fetch schedules from Steering API...") response = requests.get(self.steering_calendar_url, data=None, headers=headers) - self.logger.debug("Steering API response: %s" % response.status_code) if not response.status_code == 200: self.logger.critical(SU.red("HTTP Status: %s | Schedules could not be fetched! Response: %s" % \ (str(response.status_code), response.text))) @@ -209,7 +208,6 @@ class CalendarFetcher: try: self.logger.debug("Fetch playlist from Tank API...") response = requests.get(url, data=None, headers=headers) - self.logger.info("Tank API response: %s" % response.status_code) if not response.status_code == 200: self.logger.critical(SU.red("HTTP Status: %s | Playlist #%s could not be fetched or is not available! Response: %s" % \ (str(response.status_code), str(playlist_id), response.text)))