Newer
Older
#
# 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/>.
from models import PlayLog, PlayLogSchema, TrackSchema, ActivityLog, HealthHistory, HealthHistorySchema
class NodeType(Enum):
"""
Types of API Server deployment models.
"""
MAIN = "main"
SYNC = "sync"
"""
config = None
logger = None
node_type = None
sync_host = None
main_hosts = None
def __init__(self, config, logger):
self.config = config
self.logger = logger
node_type = NodeType.MAIN
host_id = config.get("host_id")
if host_id == 0:
node_type = NodeType.SYNC
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)
else:
self.node_type = NodeType.MAIN
self.sync_host = config.get("sync_host")
if not self.sync_host:
else:
msg = "Pushing data to '%s'" % (self.sync_host)
source = ActivityLog.get_active_source()
if source: self.active_source = source.source_number
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.info("Active source: %s" % self.active_source)
# Init Sync API endpoints
self.api_playlog = self.config.get("sync_api_store_playlog")
self.api_healthlog = self.config.get("sync_api_store_healthlog")
self.logger.info("Running in '%s' mode. %s." % (self.node_type, msg))
def current_track(self):
"""
Retrieves the currently playing track.
Returns:
"""
track = PlayLog.select_current()
return track_schema.dump(track)
page (Integer): The number of the page to return
size (Integer): The numbers of items to return
if not size or size > 50 or size < 1: size = 20
tracklist = PlayLog.paginate(page, size, None, None, False)
tracklist_schema = TrackSchema(many=True)
return tracklist_schema.dump(tracklist)
def list_playlog(self, page=None, size=None, from_time=None, to_time=None, skip_synced=False):
"""
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
from_time (datetime): Optionally, get entries after this timestamp (e.g. "2020-08-29T09:12:33.001Z")
from_time (datetime): Optionally, get entries before this timestamp (e.g. "2020-08-29T09:12:33.001Z")
skip_synced (Boolean): Optionally, don't return entries which have been posted directly before (sync node only)
tracklist = PlayLog.paginate(page, size, from_time, to_time, skip_synced)
tracklist_schema = TrackSchema(many=True)
return tracklist_schema.dump(tracklist)
"""
Stores the passed playlog entry.
Args:
data (Object): The data to store in the playlog
# 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 and self.api_playlog:
playlog_schema = PlayLogSchema()
json_playlog = playlog_schema.dump(playlog)
try:
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")
Retrieves the dataset required to render the studio clock.
Sets the active source (engine1, engine2, other) identified by its source number.
if self.active_source != source_number:
self.active_source = source_number
Retrieves number of the currently active source (engine1, engine2, other)
Retrieves the most recent health info of the requested source
source_number (Integer): Number of the play-out source
health = HealthHistory.get_latest_entry(source_number)
health_schema = HealthHistorySchema()
return health_schema.dump(health)
source_number (Integer): Number of the play-out source
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
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):
healthlog = HealthHistory(data.log_source, data.is_healthy, data.health_info)
healthlog.save()
self.logger.debug("Stored health info for '%s'" % healthlog.log_source)
# Main Node: Push to Sync Node, if enabled
if self.node_type == NodeType.MAIN and self.sync_host and self.api_healthlog:
health_schema = HealthHistorySchema()
json_healthlog = health_schema.dump(healthlog)
try:
api_url = self.sync_host + api_healthlog + "/" + data.log_source
r = requests.post(api_url, json=json_healthlog)
if r.status_code == 200:
self.logger.info("Successfully pushed healthlog for source '%s' to '%s'" % (healthlog.log_source, self.sync_host))
playlog.is_synced = True
playlog.save()
else:
self.logger.error("Error while pushing healthlog 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 healthlog sent from an inactive source")