From 5e4704990a6f5fef0e4f635a1ae9de968f763677 Mon Sep 17 00:00:00 2001 From: Chris Pastl <chris@crispybits.app> Date: Tue, 15 Aug 2023 23:13:40 +0200 Subject: [PATCH] refactor: adapt config --- config/engine.yaml | 129 +++++++++++++++++ src/aura_engine/app.py | 2 +- src/aura_engine/base/config.py | 182 ++++++++++++------------ src/aura_engine/base/logger.py | 6 +- src/aura_engine/core/channels.py | 2 +- src/aura_engine/core/client.py | 8 +- src/aura_engine/core/mixer.py | 6 +- src/aura_engine/engine.py | 12 +- src/aura_engine/events.py | 2 +- src/aura_engine/plugins/clock.py | 10 +- src/aura_engine/plugins/monitor.py | 36 +++-- src/aura_engine/scheduling/api.py | 12 +- src/aura_engine/scheduling/programme.py | 10 +- src/aura_engine/scheduling/scheduler.py | 16 +-- tests/test_config.py | 33 ++--- tests/test_logger.py | 2 +- tests/test_simple_api.py | 2 +- tests/test_simple_api_cached.py | 4 +- 18 files changed, 305 insertions(+), 169 deletions(-) create mode 100644 config/engine.yaml diff --git a/config/engine.yaml b/config/engine.yaml new file mode 100644 index 00000000..8924a38c --- /dev/null +++ b/config/engine.yaml @@ -0,0 +1,129 @@ +############################################## +# Engine Configuration # +############################################## + +general: + log: + # Directory where the log file resides + directory: logs + # Possible values: debug, info, warning, error, critical + level: info + + # Path to the engine-core socket directory relative to the engine project root + socket_dir: ../engine-core/socket + # Directory to store temporary data + cache_dir: ./.cache + + # Details for the Station Fallback + fallback: + show_name: Random Music + show_id: -1 + +monitoring: + mail: + # Mail server credentials for sending email notifications (Admin and Programme Coordination) + host: mail.your-radio.org + port: 587 + user: aura@subsquare.at + pwd: ---SECRET--PASSWORD--- + + coordinator: + # Set to "true" if you want to notify programme-coordinators about about fallback situations, otherwise "false" + enabled: false + # If you want to address multiple programme-coordinators separate their emails by space + mail: programme-coordinator@your-radio.org + + admin: + # Set to "true" if you want to notify admins about incidents, otherwise "false" + enabled: false + # If you want to address multiple administrators separate their emails by space + mail: david@subsquare.at + + # The FROM email address used when sending + from: monitoring@aura.engine + # A subject prefix allows applying filter rules in your mail client + subject_prefix: "[AURA Engine]" # default: [AURA Engine] + + heartbeat: + # Seconds how often the vitality of Engine Core should be checked (default: 1) + frequency: 1 + # Host where heartbeat is sent to (disabled if empty string) + host: "" + # Some UDP port + port: 43334 + +api: + ## STEERING ## + steering: + # The URL to get the health status + status: http://localhost:8000/api/v1/ + # The URL to get the Calendar via Steering + calendar: http://localhost:8000/api/v1/playout + + ## TANK ## + tank: + # The session name which is used to authenticate against Tank + session: engine + # The secret which is used to authenticate against Tank + secret: rather-secret + # The URL to get the health status + status: http://localhost:8040/healthz + # The URL to get playlist details via Tank + playlist: http://localhost:8040/api/v1/playlists/${ID} + + ## ENGINE-API ## + engine: + # Engine ID (1 or 2) + number: 1 + # Engine API availability check + status: http://localhost:8008/api/v1/ui/ + # Engine API endpoint to store playlogs + store_playlog: http://localhost:8008/api/v1/playlog + # Engine API endpoint to store clock information + store_clock: http://localhost:8008/api/v1/clock + # Engine API endpoint to store health information + store_health: http://localhost:8008/api/v1/source/health/${ENGINE_NUMBER} + +scheduler: + # Database settings: Use 'postgresql', 'sqlite' or 'mysql'. In case of SQLite the "db_name" is the name of the file. + db: + type: sqlite + name: aura_engine + user: aura_engine + pwd: ---SECRET--PASSWORD--- + host: localhost + charset: utf8 + + # Base path as seen by "engine-core", not accessed by "engine"; this is required to construct the absolute audio file path (check "Audio Store" in the docs) + # Either provide an absolute base path or a relative one starting in the `engine` directory. In case of `engine-core` running in docker use `/var/audio/source` + audio: + source_folder: ../engine-core/audio/source + source_extension: .flac + # Folder holding M3U Playlists to be scheduled in form of Engine Playlists (similar as audio source folder above) + playlist_folder: ../engine-core/audio/playlist + # Offset in seconds how long it takes for Liquidsoap to actually execute a scheduler command; Crucial to keep things in sync + engine_latency_offset: 0.5 + + # How often should the calendar be fetched in seconds. This determines the time of the last changes applied, before a specific show is aired + fetching_frequency: 30 + # The scheduling window defines when the entries of each timeslot are queued for play-out. The windows start at (timeslot.start - window_start) seconds + # and ends at (timeslot.end - window.end) seconds. Its also worth noting, that timeslots can only be deleted before the start of the window. + scheduling_window_start: 60 + scheduling_window_end: 60 + # How many seconds before the actual schedule time the entry should be pre-loaded. Note to provide enough timeout for + # contents which take longer to load (big files, bad connectivity to streams etc.). If the planned start time is in + # the past the offset is ignored and the entry is played as soon as possible + preload_offset: 15 + + # Sometimes it might take longer to get a stream connected. Here you can define a viable length. + # But note, that this may affect the preloading time (see `preload_offset`), hence affecting the + # overall playout, its delays and possible fallbacks + input_stream: + retry_delay: 1 + max_retries: 10 + buffer: 3.0 + + # Fade duration when selecting another mixer input (seconds) + fade: + in_time: 1.5 + out_time: 1.5 diff --git a/src/aura_engine/app.py b/src/aura_engine/app.py index c5fdec90..52765e31 100755 --- a/src/aura_engine/app.py +++ b/src/aura_engine/app.py @@ -64,7 +64,7 @@ class EngineRunner: """ Constructor. """ - self.config = config + self.config = config.config AuraLogger(self.config) self.logger = logging.getLogger("engine") self.engine = Engine() diff --git a/src/aura_engine/base/config.py b/src/aura_engine/base/config.py index 72211f94..d95fe7ee 100644 --- a/src/aura_engine/base/config.py +++ b/src/aura_engine/base/config.py @@ -25,9 +25,68 @@ import logging import os import os.path import sys -from configparser import ConfigParser from pathlib import Path +import confuse + +template = { + "general": { + "log": { + "directory": str, + "level": confuse.OneOf(["debug", "info", "warning", "error", "critical"]), + }, + "socket_dir": str, + "cache_dir": str, + "fallback": {"show_name": str, "show_id": int}, + }, + "monitoring": { + "mail": { + "host": str, + "port": int, + "user": str, + "pwd": str, + "from": str, + "subject_prefix": str, + "coordinator": {"enabled": bool, "mail": str}, + "admin": {"enabled": bool, "mail": str}, + }, + "heartbeat": {"host": str, "port": int, "frequency": int}, + }, + "api": { + "steering": {"status": str, "calendar": str}, + "tank": {"session": str, "secret": str, "status": str, "playlist": str}, + "engine": { + "number": int, + "status": str, + "store_playlog": str, + "store_clock": str, + "store_health": str, + }, + }, + "scheduler": { + "db": { + "type": confuse.OneOf(["postgresql", "mysql", "sqlite"]), + "name": str, + "user": str, + "pwd": str, + "host": str, + "charset": str, + }, + "audio": { + "source_folder": str, + "source_extension": str, + "playlist_folder": str, + "engine_latency_offset": float, + }, + "fetching_frequency": int, + "scheduling_window_start": int, + "scheduling_window_end": int, + "preload_offset": int, + "input_stream": {"retry_delay": int, "max_retries": int, "buffer": float}, + "fade": {"in_time": float, "out_time": float}, + }, +} + class AuraConfig: """ @@ -35,99 +94,62 @@ class AuraConfig: """ instance = None - ini_path = "" + yaml_path = "" + confuse_config = None + config = None # points to a validated config (hopefully later) logger = None - def __init__(self, ini_path="/etc/aura/engine.ini"): + def __init__(self, yaml_path="/etc/aura/engine.yaml"): """ - Initialize the configuration, defaults to `/etc/aura/engine.ini`. + Initialize the configuration, defaults to `/etc/aura/engine.yaml`. - If this file doesn't exist it uses `./config/engine.ini` from + If this file doesn't exist it uses `./config/engine.yaml` from the project directory. Args: - ini_path(String): The path to the configuration file `engine.ini` + yaml_path(String): The path to the configuration file `engine.yaml` """ self.logger = logging.getLogger("engine") - config_file = Path(ini_path) + config_file = Path(yaml_path) project_root = Path(__file__).parent.parent.parent.parent.absolute() if not config_file.is_file(): - ini_path = f"{project_root}/config/engine.ini" + yaml_path = f"{project_root}/config/engine.yaml" + + self.yaml_path = yaml_path + print(f"Using configuration at: {yaml_path}") - self.ini_path = ini_path + self.confuse_config = confuse.Configuration("engine") + self.confuse_config.set_file(yaml_path) self.load_config() - AuraConfig.instance = self # Defaults - self.set("install_dir", os.path.realpath(project_root)) - self.set("config_dir", os.path.dirname(ini_path)) - print(f"Using configuration at: {ini_path}") + self.confuse_config["install_dir"].set(os.path.realpath(project_root)) + self.confuse_config["config_dir"].set(os.path.dirname(yaml_path)) + + AuraConfig.instance = self def init_version(self, version: dict): """ Read and set the component version from VERSION file in project root. """ - self.set("version_control", version.get("control")) - self.set("version_core", version.get("core")) - self.set("version_liquidsoap", version.get("liquidsoap")) - - @staticmethod - def config(): - """ - Retrieve the global instances of the configuration. - """ - return AuraConfig.instance - - def set(self, key, value): - """ - Set specific config property. - - Args: - key (String): key - default (*): value - - """ - try: - self.__dict__[key] = int(value) - except ValueError: - self.__dict__[key] = str(value) - - def get(self, key, default=None): - """ - Get for some specific config property. - - Args: - key (String): key - default (*): value - - """ - if key not in self.__dict__: - if default: - self.set(key, default) - else: - self.logger.warning( - "Key " + key + " not found in configfile " + self.ini_path + "!" - ) - return None - - value = self.__dict__[key] - if value and isinstance(value, str): - value = os.path.expandvars(value) - return value + self.confuse_config["version_control"].set(version.get("control")) + self.confuse_config["version_core"].set(version.get("core")) + self.confuse_config["version_liquidsoap"].set(version.get("liquidsoap")) def get_database_uri(self): """ Retrieve the database connection string. """ - db_name = str(self.get("db_name")) - db_type = str(self.get("db_type")) + db_cfg = self.config.scheduler.db + db_name = db_cfg.name + db_type = db_cfg.type if db_type in {"mysql", "postgresql"}: - db_user = str(self.get("db_user")) - db_pass = str(self.get("db_pass")) - db_host = str(self.get("db_host")) - db_charset = self.get("db_charset", "utf8") + db_user = db_cfg.user + db_pass = db_cfg.pwd + db_host = db_cfg.host + db_charset = db_cfg.charset if db_type == "mysql": return f"mysql://{db_user}:{db_pass}@{db_host}/{db_name}?charset={db_charset}" else: @@ -145,27 +167,11 @@ class AuraConfig: """ Set config defaults and load settings from file. """ - if not os.path.isfile(self.ini_path): - self.logger.critical(self.ini_path + " not found :(") + if not os.path.isfile(self.yaml_path): + self.logger.critical(self.yaml_path + " not found :(") sys.exit(1) - # Read the file - f = open(self.ini_path, "r") - ini_str = f.read() - f.close() - - # Parse the values - config_parser = ConfigParser() - try: - config_parser.read_string(ini_str) - except Exception as e: - self.logger.critical("Cannot read " + self.ini_path + "! Reason: " + str(e)) - sys.exit(0) - - for section in config_parser.sections(): - for key, value in config_parser.items(section): - v = config_parser.get(section, key).replace('"', "").strip() - self.set(key, v) + self.config = self.confuse_config.get(template) def to_abs_path(self, path): """ @@ -176,16 +182,16 @@ class AuraConfig: if path.startswith("/"): return path else: - return self.get("install_dir") + "/" + path + return self.confuse_config["install_dir"].get() + "/" + path def abs_audio_store_path(self): """ Return the absolute path to the audio store, based on the `audio_source_folder` setting. """ - return self.to_abs_path(self.get("audio_source_folder")) + return self.to_abs_path(self.config.scheduler.audio.source_folder) def abs_playlist_path(self): """ Return the absolute path to the playlist folder. """ - return self.to_abs_path(self.get("audio_playlist_folder")) + return self.to_abs_path(self.config.scheduler.audio.playlist_folder) diff --git a/src/aura_engine/base/logger.py b/src/aura_engine/base/logger.py index a86ebc7d..e0bc3933 100644 --- a/src/aura_engine/base/logger.py +++ b/src/aura_engine/base/logger.py @@ -53,7 +53,7 @@ class AuraLogger: """ Retrieve the configured log level (default=INFO). """ - lvl = self.config.get("log_level") + lvl = self.config.general.log.level mapping = { "debug": logging.DEBUG, "info": logging.INFO, @@ -83,7 +83,9 @@ class AuraLogger: if not self.logger.hasHandlers(): # create file handler for logger - file_handler = logging.FileHandler(self.config.get("log_dir") + "/" + name + ".log") + file_handler = logging.FileHandler( + self.config.general.log.directory + "/" + name + ".log" + ) file_handler.setLevel(lvl) # create stream handler for logger diff --git a/src/aura_engine/core/channels.py b/src/aura_engine/core/channels.py index 6336bf12..b2554bbd 100644 --- a/src/aura_engine/core/channels.py +++ b/src/aura_engine/core/channels.py @@ -148,7 +148,7 @@ class GenericChannel: mixer (Mixer): The mixer instance """ - self.config = AuraConfig.config() + self.config = AuraConfig.config().config self.logger = logging.getLogger("engine") self.mixer = mixer self.name = channel_name diff --git a/src/aura_engine/core/client.py b/src/aura_engine/core/client.py index f2482b17..a4e53373 100644 --- a/src/aura_engine/core/client.py +++ b/src/aura_engine/core/client.py @@ -50,7 +50,7 @@ class CoreClient: Initialize the client. """ self.logger = logging.getLogger("engine") - self.config = AuraConfig.config() + self.config = AuraConfig.instance.config self.event_dispatcher = event_dispatcher self.conn = CoreConnection() @@ -153,7 +153,7 @@ class CoreClient: @private """ - if self.config.get("log_level") == "debug": + if self.config.general.log_level == "debug": cmds = CoreClient.skip_log_commands base_cmd = command.split(" ")[0] if not base_cmd.startswith(cmds): @@ -236,8 +236,8 @@ class CoreConnection: Initialize the connection. """ self.logger = logging.getLogger("engine") - config = AuraConfig.config() - socket_path = config.get("socket_dir") + "/engine.sock" + config = AuraConfig.config().config + socket_path = config.config.general.socket_dir + "/engine.sock" self.socket_path = config.to_abs_path(socket_path) self.logger.debug(f"Using socket at '{self.socket_path}'") diff --git a/src/aura_engine/core/mixer.py b/src/aura_engine/core/mixer.py index 83022296..ca933fce 100644 --- a/src/aura_engine/core/mixer.py +++ b/src/aura_engine/core/mixer.py @@ -63,7 +63,7 @@ class Mixer: client (PlayoutClient): The client for controlling playout """ - self.config = AuraConfig.config() + self.config = AuraConfig.instance.config self.logger = logging.getLogger("engine") self.mixer_id = mixer_id self.client = client @@ -234,7 +234,7 @@ class Mixer: self.logger.info(msg) return - fade_in_time = float(self.config.get("fade_in_time")) + fade_in_time = self.config.scheduler.fade_in_time if fade_in_time > 0: self.fade_in_active = True @@ -274,7 +274,7 @@ class Mixer: self.logger.info(msg) return - fade_out_time = float(self.config.get("fade_out_time")) + fade_out_time = self.config.scheduler.fade_out_time if fade_out_time > 0: step = abs(fade_out_time) / current_volume diff --git a/src/aura_engine/engine.py b/src/aura_engine/engine.py index 20c29439..8cd0273c 100644 --- a/src/aura_engine/engine.py +++ b/src/aura_engine/engine.py @@ -66,8 +66,8 @@ class Engine: raise Exception("Engine is already running!") Engine.instance = self self.logger = logging.getLogger("engine") - self.config = AuraConfig.config() - Engine.engine_time_offset = float(self.config.get("engine_latency_offset")) + self.config = AuraConfig.instance.config + Engine.engine_time_offset = self.config.scheduler.audio.engine_latency_offset def start(self): """ @@ -125,7 +125,7 @@ class Engine: def dispatch_fallback_event(): timeslot = self.scheduler.timetable.get_current_timeslot() - fallback_show_name = self.config.get("fallback_show_name") + fallback_show_name = self.config.general.fallback_show_name self.event_dispatcher.on_fallback_active(timeslot, fallback_show_name) # Initialize state @@ -155,8 +155,8 @@ class Engine: Update the config of playout with the current values. """ playout_config = { - "fallback_show_id": int(self.config.get("fallback_show_id")), - "fallback_show_name": self.config.get("fallback_show_name"), + "fallback_show_id": self.config.general.fallback_show_id, + "fallback_show_name": self.config.general.fallback_show_name, } json_config = json.dumps(playout_config, ensure_ascii=False) response = self.playout.set_config(json_config) @@ -261,7 +261,7 @@ class Player: event_dispatcher (EventDispather): Dispatcher for issuing events """ - self.config = AuraConfig.config() + self.config = AuraConfig.config().config self.logger = logging.getLogger("engine") self.event_dispatcher = event_dispatcher self.resource_map = ResourceMapping() diff --git a/src/aura_engine/events.py b/src/aura_engine/events.py index 86d463e4..5a8e387f 100644 --- a/src/aura_engine/events.py +++ b/src/aura_engine/events.py @@ -85,7 +85,7 @@ class EngineEventDispatcher: """ self.subscriber_registry = dict() self.logger = logging.getLogger("engine") - self.config = AuraConfig.config() + self.config = AuraConfig.instance.config self.engine = engine binding = self.attach(AuraMonitor) diff --git a/src/aura_engine/plugins/clock.py b/src/aura_engine/plugins/clock.py index 02a81005..4bec67ed 100644 --- a/src/aura_engine/plugins/clock.py +++ b/src/aura_engine/plugins/clock.py @@ -45,7 +45,7 @@ class ClockInfoHandler: Initialize. """ self.logger = logging.getLogger("engine") - self.config = AuraConfig.config() + self.config = AuraConfig.config().config self.api = SimpleRestApi() self.engine = engine @@ -69,8 +69,8 @@ class ClockInfoHandler: return self.logger.info(f"Fallback '{fallback_name}' activated, clock update required") - fallback_show_id = self.config.get("fallback_show_id") - fallback_show_name = self.config.get("fallback_show_name") + fallback_show_id = self.config.general.fallback_show_id + fallback_show_name = self.config.general.fallback_show_name # Interpolate timeslot-less slot # TODO start time to be calculated based on previous timeslot (future station logic) @@ -124,12 +124,12 @@ class ClockInfoHandler: built_upcoming.append(self.build_timeslot(upcoming_timeslot)) data = { - "engineSource": self.config.get("api_engine_number"), + "engineSource": self.config.api.engine.number, "currentTimeslot": self.build_timeslot(active_timeslot, active_type), "upcomingTimeslots": built_upcoming, "plannedPlaylist": self.build_playlist(active_playlist), } - url = self.config.get("api_engine_store_clock") + url = self.config.api.engine.store_clock self.logger.info(f"PUT clock info to '{url}': \n{data}") self.api.put(url, data=data) diff --git a/src/aura_engine/plugins/monitor.py b/src/aura_engine/plugins/monitor.py index c5e9bb39..4f29f068 100644 --- a/src/aura_engine/plugins/monitor.py +++ b/src/aura_engine/plugins/monitor.py @@ -100,9 +100,9 @@ class AuraMonitor: # Heartbeat settings self.heartbeat_running = False - self.heartbeat_server = self.config.get("heartbeat_server") - self.heartbeat_port = self.config.get("heartbeat_port") - self.heartbeat_frequency = self.config.get("heartbeat_frequency") + self.heartbeat_server = self.config.monitoring.heartbeat.host + self.heartbeat_port = self.config.monitoring.heartbeat.port + self.heartbeat_frequency = self.config.monitoring.heartbeat.frequency self.heartbeat_socket = socket(AF_INET, SOCK_DGRAM) self.engine_id = self.get_engine_id() @@ -197,8 +197,8 @@ class AuraMonitor: body["isHealthy"] = is_healthy body["details"] = json.dumps(data, default=str) json_data = json.dumps(body, default=str) - url = self.config.get("api_engine_store_health") - url = url.replace("${ENGINE_NUMBER}", str(self.config.get("api_engine_number"))) + url = self.config.api.engine.store_health + url = url.replace("${ENGINE_NUMBER}", str(self.config.api.engine.number)) headers = {"content-type": "application/json"} response = requests.Response() response.status_code = 404 @@ -230,28 +230,26 @@ class AuraMonitor: Request the current status of all components. """ self.engine.init_version() - ctrl_version = self.config.get("version_control") - core_version = self.config.get("version_core") - liq_version = self.config.get("version_liquidsoap") + ctrl_version = self.config.version.control + core_version = self.config.version.core + liq_version = self.config.version.liquidsoap self.status["engine"]["version"] = ctrl_version self.status["lqs"]["version"] = {"core": core_version, "liquidsoap": liq_version} self.status["lqs"]["outputs"] = self.engine.player.mixer.get_outputs() self.status["lqs"]["mixer"] = self.engine.player.mixer.get_inputs() - self.status["api"]["steering"]["url"] = self.config.get("api_steering_status") + self.status["api"]["steering"]["url"] = self.config.api.steering.status self.status["api"]["steering"]["available"] = self.validate_url_connection( - self.config.get("api_steering_status") + self.config.api.steering.status ) - self.status["api"]["tank"]["url"] = self.config.get("api_tank_status") + self.status["api"]["tank"]["url"] = self.config.api.tank.status self.status["api"]["tank"]["available"] = self.validate_url_connection( - self.config.get("api_tank_status") + self.config.api.tank.status ) - self.status["api"]["tank"]["status"] = self.get_url_response( - self.config.get("api_tank_status") - ) - self.status["api"]["engine"]["url"] = self.config.get("api_engine_status") + self.status["api"]["tank"]["status"] = self.get_url_response(self.config.api.tank.status) + self.status["api"]["engine"]["url"] = self.config.api.engine.status self.status["api"]["engine"]["available"] = self.validate_url_connection( - self.config.get("api_engine_status") + self.config.api.engine.status ) self.update_vitality_status() @@ -278,7 +276,7 @@ class AuraMonitor: """ if self.has_valid_status(True): # Always check status, but only send heartbeat if wanted so - if self.config.get("heartbeat_server") != "": + if self.config.monitoring.heartbeat.server != "": self.heartbeat_socket.sendto( str.encode("OK"), (self.heartbeat_server, self.heartbeat_port) ) @@ -306,7 +304,7 @@ class AuraMonitor: {"engine_id": self.engine_id, "status": status} ) - heartbeat_frq = self.config.get("heartbeat_frequency", 1) + heartbeat_frq = self.config.monitoring.heartbeat.frequency # default: 1 if int(heartbeat_frq or 0) < 1: heartbeat_frq = 1 threading.Timer(heartbeat_frq, self.heartbeat).start() diff --git a/src/aura_engine/scheduling/api.py b/src/aura_engine/scheduling/api.py index 0fbe806c..10ccd979 100644 --- a/src/aura_engine/scheduling/api.py +++ b/src/aura_engine/scheduling/api.py @@ -66,12 +66,12 @@ class ApiFetcher(threading.Thread): """ Initialize the API Fetcher. """ - self.config = AuraConfig.config() + self.config = AuraConfig.instance.config self.logger = logging.getLogger("engine") - cache_location = self.config.get("cache_dir") + cache_location = self.config.general.cache_dir self.api = SimpleCachedRestApi(SimpleRestApi(), cache_location) - self.url_api_timeslots = self.config.get("api_steering_calendar") - self.url_api_playlist = self.config.get("api_tank_playlist") + self.url_api_timeslots = self.config.api.steering.calendar + self.url_api_playlist = self.config.api.tank.playlist self.queue = queue.Queue() self.stop_event = threading.Event() @@ -82,8 +82,8 @@ class ApiFetcher(threading.Thread): ApiFetcher.MODEL_PLID_SHOW: ApiFetcher.API_PLID_SHOW, } - tank_session = self.config.get("api_tank_session") - tank_secret = self.config.get("api_tank_secret") + tank_session = self.config.api.tank.session + tank_secret = self.config.api.tank.secret self.tank_headers = { "Authorization": f"Bearer {tank_session}:{tank_secret}", "content-type": "application/json", diff --git a/src/aura_engine/scheduling/programme.py b/src/aura_engine/scheduling/programme.py index a16287ed..24ab8fd0 100644 --- a/src/aura_engine/scheduling/programme.py +++ b/src/aura_engine/scheduling/programme.py @@ -50,7 +50,7 @@ class ProgrammeService: programme_store = None def __init__(self): - self.config = AuraConfig.config() + self.config = AuraConfig.config().config self.logger = logging.getLogger("engine") self.programme_store = ProgrammeStore() @@ -213,8 +213,8 @@ class ProgrammeService: Check if the timeslot is within the scheduling window. """ now_unix = Engine.engine_time() - window_start = self.config.get("scheduling_window_start") - window_end = self.config.get("scheduling_window_end") + window_start = self.config.scheduler.scheduling_window_start + window_end = self.config.scheduler.scheduling_window_end if ( timeslot.start_unix - window_start < now_unix @@ -247,7 +247,7 @@ class ProgrammeStore: m3u_processor = None def __init__(self): - self.config = AuraConfig.config() + self.config = AuraConfig.instance.config self.logger = logging.getLogger("engine") self.m3u_processor = M3UPlaylistProcessor() @@ -312,7 +312,7 @@ class ProgrammeStore: """ now_unix = SU.timestamp() - scheduling_window_start = self.config.get("scheduling_window_start") + scheduling_window_start = self.config.scheduler.scheduling_window_start local_timeslots = Timeslot.get_timeslots(datetime.now()) for local_timeslot in local_timeslots: diff --git a/src/aura_engine/scheduling/scheduler.py b/src/aura_engine/scheduling/scheduler.py index 92a902ab..0313ae64 100644 --- a/src/aura_engine/scheduling/scheduler.py +++ b/src/aura_engine/scheduling/scheduler.py @@ -65,7 +65,7 @@ class AuraScheduler(threading.Thread): is_engine_ready: bool = None def __init__(self, engine): - self.config = AuraConfig.config() + self.config = AuraConfig.config().config self.logger = logging.getLogger("engine") self.timetable = ProgrammeService() self.timetable_renderer = TimetableRenderer(self) @@ -101,7 +101,7 @@ class AuraScheduler(threading.Thread): while not self.exit_event.is_set(): try: self.config.load_config() - seconds_to_wait = int(self.config.get("fetching_frequency")) + seconds_to_wait = self.config.scheduler.fetching_frequency msg = f"== start fetching new timeslots (every {seconds_to_wait} seconds) ==" self.logger.info(SU.cyan(msg)) @@ -392,8 +392,8 @@ class AuraScheduler(threading.Thread): now_unix = Engine.engine_time() len_before = len(timeslots) - window_start = self.config.get("scheduling_window_start") - window_end = self.config.get("scheduling_window_end") + window_start = self.config.scheduler.scheduling_window_start + window_end = self.config.scheduler.scheduling_window_end timeslots = list( filter( lambda t: (t.start_unix - window_start) < now_unix @@ -445,7 +445,7 @@ class TimeslotCommand(EngineExecutor): self.engine = engine now_unix = SU.timestamp() - fade_out_time = float(self.config.get("fade_out_time")) + fade_out_time = self.config.scheduler.fade_out_time start_fade_in = timeslot.start_unix - now_unix start_fade_out = timeslot.end_unix - now_unix - fade_out_time self.logger.debug( @@ -511,7 +511,7 @@ class PlayCommand(EngineExecutor): """ engine: Engine = None - config: AuraConfig = None + config = None def __init__(self, engine: Engine, entries): """Initialize the play command. @@ -520,10 +520,10 @@ class PlayCommand(EngineExecutor): engine (Engine): The engine entries (PlaylistEntry): One or more playlist entries to be started """ - self.config = AuraConfig() + self.config = AuraConfig.instance.config self.engine = engine - preload_offset = self.config.get("preload_offset") + preload_offset = self.config.scheduler.preload_offset start_preload = entries[0].start_unix - preload_offset start_play = entries[0].start_unix msg = ( diff --git a/tests/test_config.py b/tests/test_config.py index 0d7c7413..e262270c 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -39,30 +39,31 @@ class TestConfig(unittest.TestCase): print(self._testMethodName) # Check if config is available - self.assertIsNotNone(self.config.ini_path) + self.assertIsNotNone(self.config.yaml_path) # Check if "install_dir" is a valid directory (is evaluated at runtime) - self.assertTrue(os.path.isdir(self.config.get("install_dir"))) + self.assertTrue(os.path.isdir(self.config.confuse_config["install_dir"].get())) + + # Reference to confuse config + cfg = self.config.config # Check API Urls - self.assertTrue(validators.url(self.config.get("api_steering_status"))) - self.assertTrue(validators.url(self.config.get("api_steering_calendar"))) - self.assertTrue(validators.url(self.config.get("api_tank_status"))) - tank_playlist_url = self.config.get("api_tank_playlist").replace("${ID}", "1") + self.assertTrue(validators.url(cfg.api.steering.status)) + self.assertTrue(validators.url(cfg.api.steering.calendar)) + self.assertTrue(validators.url(cfg.api.tank.status)) + tank_playlist_url = cfg.api.tank.playlist.replace("${ID}", "1") self.assertTrue(validators.url(tank_playlist_url)) - self.assertTrue(validators.url(self.config.get("api_engine_status"))) - self.assertTrue(validators.url(self.config.get("api_engine_store_playlog"))) - self.assertTrue(validators.url(self.config.get("api_engine_store_clock"))) - engine_health_url = self.config.get("api_engine_store_health").replace( - "${ENGINE_NUMBER}", "1" - ) + self.assertTrue(validators.url(cfg.api.engine.status)) + self.assertTrue(validators.url(cfg.api.engine.store_playlog)) + self.assertTrue(validators.url(cfg.api.engine.store_clock)) + engine_health_url = cfg.api.engine.store_health.replace("${ENGINE_NUMBER}", "1") self.assertTrue(validators.url(engine_health_url)) # Check if database settings are set - self.assertIsNotNone(self.config.get("db_user")) - self.assertIsNotNone(self.config.get("db_pass")) - self.assertIsNotNone(self.config.get("db_name")) - self.assertIsNotNone(self.config.get("db_host")) + self.assertIsNotNone(cfg.scheduler.db.user) + self.assertIsNotNone(cfg.scheduler.db.pwd) + self.assertIsNotNone(cfg.scheduler.db.name) + self.assertIsNotNone(cfg.scheduler.db.host) if __name__ == "__main__": diff --git a/tests/test_logger.py b/tests/test_logger.py index ac532bd8..4344b55c 100644 --- a/tests/test_logger.py +++ b/tests/test_logger.py @@ -31,7 +31,7 @@ class TestLogger(unittest.TestCase): aura_logger = None def setUp(self): - self.config = AuraConfig() + self.config = AuraConfig().config self.aura_logger = AuraLogger(self.config) def test_logger(self): diff --git a/tests/test_simple_api.py b/tests/test_simple_api.py index 12aa24c4..05cbd9f5 100644 --- a/tests/test_simple_api.py +++ b/tests/test_simple_api.py @@ -101,7 +101,7 @@ class TestApi(unittest.TestCase): print(self._testMethodName) # Check if config is available - self.assertIsNotNone(self.config.ini_path) + self.assertIsNotNone(self.config.yaml_path) def test_clean_dict(self): print(self._testMethodName) diff --git a/tests/test_simple_api_cached.py b/tests/test_simple_api_cached.py index e1168b55..6c96c5c8 100644 --- a/tests/test_simple_api_cached.py +++ b/tests/test_simple_api_cached.py @@ -63,7 +63,7 @@ class TestCachedApi(unittest.TestCase): def setUp(self): self.config = AuraConfig() - cache_location = self.config.get("cache_dir") + cache_location = self.config.config.general.cache_dir self.api = SimpleCachedRestApi(SimpleRestApi(), cache_location) # @@ -74,7 +74,7 @@ class TestCachedApi(unittest.TestCase): print(self._testMethodName) # Check if config is available - self.assertIsNotNone(self.config.ini_path) + self.assertIsNotNone(self.config.yaml_path) def test_build_filename(self): url = "https://dashboard.aura.radio/steering/api/v1/playout" -- GitLab