Skip to content
Snippets Groups Projects
engine-core.py 6.02 KiB
#!/usr/bin/env python3.7

#
# Aura Engine (https://gitlab.servus.at/aura/engine)
#
# Copyright (C) 2017-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 os
import sys
import signal
import logging
import subprocess

from flask               import Flask
from flask_sqlalchemy    import SQLAlchemy

from modules.base.logger import AuraLogger
from modules.base.config import AuraConfig
from modules.base.utils  import SimpleUtil


config = AuraConfig()
def configure_flask():
    app.config["SQLALCHEMY_DATABASE_URI"] = config.get_database_uri()
    app.config['BABEL_DEFAULT_LOCALE'] = 'de'
    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

# FIXME Instatiate SQLAlchemy without the need for Flask
app = Flask(__name__)
configure_flask()
DB = SQLAlchemy(app)


class AuraEngine:
    """
    AuraEngine does the following:

    1. Initialize the soundsystem and scheduler
    2. Initialize Redis
    3. Start Liquidsoap in a separate thread which connects to the engine

    """
    logger = None
    config = None
    server = None
    messenger = None
    controller = None
    soundsystem = None
    scheduler = None
    lqs = None
    lqs_startup = None



    def __init__(self):
        """
        Initializes Engine Core.
        """
        self.config = config



    def startup(self, lqs_startup):
        """
        Starts Engine Core.
        """
        AuraLogger(self.config)
        self.logger = logging.getLogger("AuraEngine")

        from modules.scheduling.scheduler import AuraScheduler
        from modules.core.engine import SoundSystem
        from modules.communication.redis.adapter import ServerRedisAdapter

        # If Liquidsoap should be started automatically
        self.lqs_startup = lqs_startup

        # Check if the database has to be re-created
        if self.config.get("recreate_db") is not None:
            AuraScheduler(self.config, None, None)

        # Create scheduler and Liquidsoap communicator
        self.soundsystem = SoundSystem(self.config)
        self.scheduler = AuraScheduler(self.config, self.soundsystem, self.on_initialized)

        # Create the Redis adapter
        self.messenger = ServerRedisAdapter(self.config)
        self.messenger.scheduler = self.scheduler
        self.messenger.soundsystem = self.soundsystem

        # And finally wait for redis message / start listener thread
        self.messenger.start()



    def on_initialized(self):
        """
        Called when the engine is initialized, before the Liquidsoap connection is established."
        """
        self.logger.info(SimpleUtil.green("Engine Core initialized - Waiting for Liquidsoap connection ..."))
        if self.lqs_startup:
            self.start_lqs(False, False)
        else:
            self.logger.info(SimpleUtil.yellow("Please note, Liquidsoap needs to be started manually."))

        # from modules.communication.redis.adapter import ServerRedisAdapter
        # self.messenger = ServerRedisAdapter(self.config)
        # self.messenger.scheduler = self.scheduler
        # self.messenger.soundsystem = self.soundsystem
        # self.messenger.start()


    def start_lqs(self, debug_output, verbose_output):
        """
        Starts Liquidsoap.
        """
        lqs_path = self.config.get("liquidsoap_path")
        lqs_cwd = os.getcwd() + "/" + self.config.get("liquidsoap_working_dir")
        lqs_output = ""
        lqs_output = self.get_debug_flags(debug_output, verbose_output)

        self.lqs = subprocess.Popen([lqs_path, lqs_output, "engine.liq"], \
            cwd=lqs_cwd, \
            stdout=subprocess.PIPE, \
            shell=False)



    def get_lqs_cmd(self, debug_output, verbose_output):
        """
        Returns a shell command string to start Liquidsoap
        """
        lqs_path = self.config.get("liquidsoap_path")
        lqs_cwd = os.getcwd() + "/" + self.config.get("liquidsoap_working_dir")
        lqs_output = self.get_debug_flags(debug_output, verbose_output)
        return "(cd %s && %s %s ./engine.liq)" % (lqs_cwd, lqs_path, lqs_output)



    def get_debug_flags(self, debug_output, verbose_output):
        """
        Build Liquidssoap debug parameters.
        """
        output = ""
        if debug_output:
            output += "--debug " 
        if verbose_output:
            output += "--verbose "
        return output



    def exit_gracefully(self, signum, frame):
        """
        Shutdown of the engine. Also terminates the Liquidsoap thread.
        """
        if self.lqs:
            self.lqs.terminate()
            self.logger.info("Terminated Liquidsoap")

        if self.messenger:
            self.messenger.terminate()
        
        self.logger.info("Gracefully terminated Aura Engine!" + str(self.lqs))
        sys.exit(0)        



#
# START THE ENGINE
#


if __name__ == "__main__":        

    engine = AuraEngine()
    lqs_startup = True
    lqs_cmd = False

    signal.signal(signal.SIGINT, engine.exit_gracefully)
    signal.signal(signal.SIGTERM, engine.exit_gracefully)

    if len(sys.argv) >= 2:
        if "--without-lqs" in sys.argv:
            lqs_startup = False
        if "--get-lqs-command" in sys.argv:
            lqs_cmd = True
        if "--use-test-data" in sys.argv:
            engine.config.set("use_test_data", True)
        if "--recreate-database" in sys.argv:
            engine.config.set("recreate_db", True)

    if lqs_cmd:
        print(engine.get_lqs_cmd(True, True))
    else:
        engine.startup(lqs_startup)