# # Aura Engine API (https://gitlab.servus.at/aura/engine-api) # # Copyright (C) 2020 - The Aura Engine Team. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import datetime import requests from enum import Enum from models import PlayLog, PlayLogSchema, TrackSchema class NodeType(Enum): """ Types of API Server deployment models. """ MAIN = "main" SYNC = "sync" class ApiService(): """ Service handling for API actions. """ config = None logger = None node_type = None sync_host = None main_hosts = None active_source = None def __init__(self, config, logger): """ Initialize Service. """ self.config = config self.logger = logger # Evaluate deployment mode node_type = NodeType.MAIN host_id = config.get("host_id") if host_id == 0: node_type = NodeType.SYNC # Configured as Sync node if not node_type == NodeType.MAIN: self.node_type = NodeType.SYNC self.main_hosts = [ config.get("main_host_1"), config.get("main_host_2") ] if not self.main_hosts[0] and not self.main_hosts[1]: self.logger.warn("Not a single main host defined. Be aware what you are doing.") msg = "No sync possible as no host nodes are configured" else: msg = "Syncing data of hosts '%s'" % (self.main_hosts) # Configured as Main node else: self.node_type = NodeType.MAIN self.sync_host = config.get("sync_host") if not self.sync_host: msg = "No child node for synchronization defined" else: msg = "Pushing data to '%s'" % (self.sync_host) # Set active source self.active_source = ActivityLog.get_active_source() if not self.active_source: if self.node_type == NodeType.MAIN: source_number = self.config.get("host_id") else: source_number = self.config.get("default_source") self.set_active_source(source_number) self.logger("Active source: %s" % self.active_source) self.logger.info("Running in '%s' mode. %s." % (self.node_type, msg)) def current_track(self): """ Retrieves the currently playing track. Returns: (PlayLogEntry) """ track = PlayLog.select_current() track_schema = TrackSchema() return track_schema.dump(track) def list_tracks(self, page=None, size=None): """ Lists track-service entries with pagination. Args: page (Integer): The number of the page to return size (Integer): The numbers of items to return Returns: (List[PlayLogEntry]) """ if not size or size > 50 or size < 1: size = 20 tracklist = PlayLog.paginate(page, size, None) tracklist_schema = TrackSchema(many=True) return tracklist_schema.dump(tracklist) def list_playlog(self, page=None, size=None, since_time=None): """ Get paginated playlog entries for since the given timestamp. Args: page (Integer): The number of items to skip before starting to collect the result set size (Integer): The numbers of items to return per page since_time (datetime): Optionally, get entries after this timestamp (e.g. "2020-08-29T09:12:33.001Z") Returns: (List[PlayLogEntry]) """ tracklist = PlayLog.paginate(page, size, since_time) tracklist_schema = TrackSchema(many=True) return tracklist_schema.dump(tracklist) def store_playlog(self, data): """ Stores the passed playlog entry. Returns: (PlayLogEntry) """ if not data.log_source: data.log_source = self.host_id # Main Node: Alway log entry, independed of the source # Sync Node: Only log entry when it's coming from an active source if self.node_type == NodeType.MAIN or \ (self.node_type == NodeType.SYNC and data.log_source == self.active_source): playlog = PlayLog(data) playlog.save() self.logger.debug("Stored playlog for '%s'" % playlog.track_start) # Main Node: Push to Sync Node, if enabled if self.node_type == NodeType.MAIN and self.sync_host: playlog_schema = PlayLogSchema() json_playlog = playlog_schema.dump(playlog) try: api_url = self.sync_host + self.config.get("sync_api_store_playlog") r = requests.post(api_url, json=json_playlog) if r.status_code == 200: self.logger.info("Successfully pushed playlog for '%s' to '%s'" % (playlog.track_start, self.sync_host)) playlog.is_synced = True playlog.save() else: self.logger.error("Error while pushing playlog to sync-node: " + r.json()) except Exception as e: self.logger.error("Error while posting to sync-node API '%s'!" % (api_url), e) else: self.logger.info("Ditching playlog sent from an inactive source") def clock_info(self): """ Retrieves the dataset required to render the studio clock. """ return "studio clock data" def set_active_source(self, source_number): """ Sets the active source (engine1, engine2, other) identified by its source number. Args: source_number (Integer): Number of the engine """ if self.active_source > 0: if self.active_source != source_number: self.active_source = source_number activity_log = ActivityLog(active_source) activity_log.save() def get_active_source(self): """ Retrieves number of the currently active source (engine1, engine2, other) Returns: (Integer) """ return self.active_source def get_source_health(self, source_number): """ Retrieves the most recent health info of the requested source Args: source_number (Integer): Number of the play-out source Returns: (HealthHistoryEntry) """ return 'do some magic!' def log_source_health(self, source_number): """ Logs another health entry for the given source Args: source_number (Integer): Number of the play-out source """ return 'do some magic!'