Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • aura/engine
  • hermannschwaerzler/engine
  • sumpfralle/aura-engine
3 results
Show changes
Showing
with 1878 additions and 0 deletions
engine-api @ ae560241
Subproject commit ae560241e5947f595a793413dfc48657807233b0
engine-clock @ 6ae833ad
Subproject commit 6ae833ad05501cfed65d54becf1f749bded1fd0d
#!/usr/bin/env python2.7
# Copyright (c) 2001, Nicola Larosa
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of the <ORGANIZATION> nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS
# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
""" PyHeartBeat server: receives and tracks UDP packets from all clients.
While the BeatLog thread logs each UDP packet in a dictionary, the main
thread periodically scans the dictionary and prints the IP addresses of the
clients that sent at least one packet during the run, but have
not sent any packet since a time longer than the definition of the timeout.
Adjust the constant parameters as needed, or call as:
PyHBServer.py [timeout [udpport]]
https://www.oreilly.com/library/view/python-cookbook/0596001673/ch10s13.html
"""
HBPORT = 43334
CHECKWAIT = 10
from socket import socket, gethostbyname, AF_INET, SOCK_DGRAM
from threading import Lock, Thread, Event
from time import time, ctime, sleep
import sys
class BeatDict:
"Manage heartbeat dictionary"
def __init__(self):
self.beatDict = {}
if __debug__:
self.beatDict['127.0.0.1'] = time( )
self.dictLock = Lock( )
def __repr__(self):
list = ''
self.dictLock.acquire( )
for key in self.beatDict.keys( ):
list = "%sIP address: %s - Last time: %s\n" % (
list, key, ctime(self.beatDict[key]))
self.dictLock.release( )
return list
def update(self, entry):
"Create or update a dictionary entry"
self.dictLock.acquire( )
self.beatDict[entry] = time( )
self.dictLock.release( )
def extractSilent(self, howPast):
"Returns a list of entries older than howPast"
silent = []
when = time( ) - howPast
self.dictLock.acquire( )
for key in self.beatDict.keys( ):
if self.beatDict[key] < when:
silent.append(key)
self.dictLock.release( )
return silent
class BeatRec(Thread):
"Receive UDP packets, log them in heartbeat dictionary"
def __init__(self, goOnEvent, updateDictFunc, port):
Thread.__init__(self)
self.goOnEvent = goOnEvent
self.updateDictFunc = updateDictFunc
self.port = port
self.recSocket = socket(AF_INET, SOCK_DGRAM)
self.recSocket.bind(('', port))
def __repr__(self):
return "Heartbeat Server on port: %d\n" % self.port
def run(self):
while self.goOnEvent.isSet( ):
if __debug__:
print "Waiting to receive..."
data, addr = self.recSocket.recvfrom(6)
if __debug__:
print "Received packet from " + `addr`
self.updateDictFunc(addr[0])
def main( ):
"Listen to the heartbeats and detect inactive clients"
global HBPORT, CHECKWAIT
if len(sys.argv)>1:
HBPORT=sys.argv[1]
if len(sys.argv)>2:
CHECKWAIT=sys.argv[2]
beatRecGoOnEvent = Event( )
beatRecGoOnEvent.set( )
beatDictObject = BeatDict( )
beatRecThread = BeatRec(beatRecGoOnEvent, beatDictObject.update, HBPORT)
if __debug__:
print beatRecThread
beatRecThread.start( )
print "PyHeartBeat server listening on port %d" % HBPORT
print "\n*** Press Ctrl-C to stop ***\n"
while 1:
try:
if __debug__:
print "Beat Dictionary"
print `beatDictObject`
silent = beatDictObject.extractSilent(CHECKWAIT)
if silent:
print "Silent clients"
print `silent`
sleep(CHECKWAIT)
except KeyboardInterrupt:
print "Exiting."
beatRecGoOnEvent.clear( )
beatRecThread.join( )
if __name__ == '__main__':
main( )
\ No newline at end of file
CREATE DATABASE aura_engine CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'aura_engine'@'localhost' IDENTIFIED BY '1234';
GRANT ALL PRIVILEGES ON aura_engine.* TO 'aura_engine'@'localhost';
FLUSH PRIVILEGES;
\ No newline at end of file
# Aura Engine Development Guide
This page gives insights on extending Aura Engine internals or through the API.
<!-- TOC -->
- [Aura Engine Development Guide](#aura-engine-development-guide)
- [AURA Componentes](#aura-componentes)
- [Engine Components](#engine-components)
- [API](#api)
- [Engine Startup Phases](#engine-startup-phases)
- [More infos for debugging](#more-infos-for-debugging)
- [Debugging Liquidsoap](#debugging-liquidsoap)
- [Tips on configuring the audo interface](#tips-on-configuring-the-audo-interface)
- [Read more](#read-more)
<!-- /TOC -->
## AURA Componentes
AURA Engine as part of the AURA Radio Suite uses an modulear architecture
based on a REST API. All external information is retrieved using JSON data-structures.
To get the basic architectural overview, visit the [Aura Meta](https://gitlab.servus.at/autoradio/meta) repository.
Starting development of engine can be quite tedious, as it requires all most all other AURA components to be up and running.
For example:
- Steering, to get the main incredient of an play-out engine: schedules (or "timeslots" in Steering terms),
which hold the actual information on playlists and their entries.
- Dashboard, to have a neat interface, being able to programm the schedules
- Tank, to get the references to audio files and other audio sources. Plus the actual files.
If you need to test and develop against the Engine's API you'll also need to get the `engine-api` project running.
For a start it's recommended to create a general `aura` project folder. In there you start cloning all the sub-projects.
After having all the sub-projects configured, and verified that they are working, take a look at the AURA `meta` project.
There's a convenience script to start all of the three main dependencies (Steering, Dashboard, Tank) all at once:
```bash
~/code/aura/meta$ ./run.sh aura local
```
## Engine Components
*...TBD...*
## API
You can find the AURA API definition here: https://gitlab.servus.at/autoradio/meta/blob/master/api-definition.md
OpenAPI definition for Engine API: https://app.swaggerhub.com/apis/AURA-Engine/engine-api/
## Engine Startup Phases
When you start Engine the following is happening:
1. Python `run.py`: Initializes `src/core/engine.py` (The virtual mixer; class for remote-controlling Liquidsoap), Scheduler
2. Python `run.py`: Start Liquidsoap.
3. Liquidsoap: When Liquidsoap finished its startup, it creates a socket file as configured in `socketdir` of `engine.ini`.
4. Python `src/core/liquidsoap/client.py`: Connects to that socket file.
5. Python `src/schedulung/scheduler.py`: Continously loads schedules from the API endpoints, stores them in the local database and starts the playout as per the schedules.
## More infos for debugging
### Debugging Liquidsoap
Connect to Liquidsoap via Telnet
telnet 127.0.0.1 1234
List available commands
help
List all available channels
list
List all input channels connected to the mixer
mixer.input
Set the volume of mixer `input 0` to `100%`
mixer.volume 0 100
Push some audio file to the filesystem `queue 0`
in_filesystem_0.push /path/to/your/file.mp3
### Tips on configuring the audo interface
Configure your audio device in the `[soundcard]` section of `engine.ini`.
You can configure up to **five** line IN and OUT stereo channels. Your hardware should
support that. When you use JACK, you will see the additional elements popping up when
viewing your connections (with e.g. Patchage).
**Pulse Audio:** When using Ubuntu, Pulse Audio is selected by default. This is convenient,
as you won't have the need to adapt any Engine setting to get audio playing initially.
**ALSA:** When you use ALSA, you will have to play around with ALSA settings. In the folder
`./src/liquidsoap` is a scipt called alsa_settings_tester.liq. You can start it
with 'liquidsoap -v --debug alsa_settings_tester.liq'. Changing and playing with
settings may help you to find correct ALSA settings.
**Jack Audio**: Install the JACK daemon and GUI:
```bash
sudo apt-get install jackd qjackctl
```
Please ensure to enable "*realtime process priority*" when installing JACK to keep latency low.
Now, you are able to configure your hardware settings using following command:
```bash
qjackctl
```
Next you need to install the JACK plugin for Liquidsoap:
```bash
sudo apt install \
liquidsoap-plugin-jack
```
## Read more
- [Overview](/README.md)
- [Installation for Development](installation-development.md)
- [Installation for Production](installation-production.md)
- [Running with Docker](running-docker.md)
- [Setup the Audio Store](docs/setup-audio-store.md)
- [Developer Guide](developer-guide.md)
- [Engine Features](engine-features.md)
- [Frequently Asked Questions (FAQ)](docs/frequently-asked-questions.md)
\ No newline at end of file
# Aura Engine Features
This page gives a more detailed overview of the Aura Engine features and how to configure them.
<!-- TOC -->
- [Aura Engine Features](#aura-engine-features)
- [Multi-channel Input (Filesystem, Stream, Analog)](#multi-channel-input-filesystem-stream-analog)
- [Multi-channel output](#multi-channel-output)
- [Analog line-out](#analog-line-out)
- [Stream to Icecast](#stream-to-icecast)
- [Scheduling](#scheduling)
- [Fallback Handling](#fallback-handling)
- [Silence Detector](#silence-detector)
- [Monitoring](#monitoring)
- [Send mails on errors and warnings](#send-mails-on-errors-and-warnings)
- [Engine Health Information via Engine API](#engine-health-information-via-engine-api)
- [Engine Heartbeat](#engine-heartbeat)
- [Logging](#logging)
- [Read more](#read-more)
<!-- /TOC -->
## Multi-channel Input (Filesystem, Stream, Analog)
It's possible to schedules playlists with music or pre-recorded show stored on the **filessystem**,
via external **streams** or live from an **analog input** in the studio. All types of sources can
be mixed in a single playlist.
> Note: Any live sources or streams not specifing a length property, are automatically expanded to
the left duration of the timeslot.
The switching between types of audio source is handled automatically. To learn more check out the
[Scheduling](### Secure Scheduling) section.
## Multi-channel output
### Analog line-out
In most scenarios it might be sufficient to broadcast via only one analog stereo output.
If needed you can configure up to five stereo pairs.
### Stream to Icecast
Engine allows to stream to multiple Icecast Servers simultaniousely. It is also sending meta information
to the streaming server, using the *Icy* protocol.
To configure your Icecast connectivity check-out the `[stream]` section in your configuration.
## Scheduling
Engine provide a scheduling functionality by polling external API endpoints frequently.
Scheduling is split into multiple phase. Below you see a timline with one schedule planned at a certain
point in time and the involved phase before:
```ascii
================= [ Scheduling Window ] ============ [ Timeslot Play-out ] ====
== (FILESYSTEM A) ========================== [ Preload ] [ Play 4 ] ====================================
== (STREAM A) ========================================== [ Preload ] [ Play 1 ] ==========================
== (LIVE 1) ====================================================== [ Preload ] [ Play 1 ] ================
== (FILESYSTEM B) ========================================================== [ Preload ] [ Play 4 ] ====
```
- **Scheduling Window**: Within the scheduling window any commands for controlling
the mixer of the soundsystem are prepared and queued.
Until the start of the window, timeslot can be added or removed via external API Endpoints
(e.g. using Steering or Dashboard). Until here any changes on the timeslot itself will be reflected
in the actual play-out. This only affects the start and end time of the "timeslot" itself.
It does not involve related playlists and their entries. Those can still be modified after the
scheduling window has started.
The start and the end of the window is defined by the start of the schedule minus
a configured amount of seconds (see `scheduling_window_start` and `scheduling_window_end`
in `engine.ini`).
During the scheduling window, the external API Endpoints are pulled continiously, to
check for updated schedules and related playlists. Also, any changes to playlists and
its entries are respected within that window (see `fetching_frequency` in `engine.ini`).
> Important: It's vital that the the scheduling window is wider than the fetching frequency.
Otherwise one fetch might never hit a scheduling window, hence not being able to schedule stuff.
- **Queuing and Pre-Loading**: Before any playlist entries of the schedule can be turned into
sound, they need to be queued and pre-loaded. Ideally the pre-loading happens somewhat before
the scheduled play-out time to avoid any delays in timing. Set the maximum time reserved for
pre-loading in your configuration (compare `preload_offset`in `engine.ini`).
> Important: The offset should not exceed the time between the end of the scheduling-window and the
start of the actual schedule playout.
- **Play-out**: Finally the actual play-out is happening. The faders of the virtual mixers are pushed
all the way up, as soon it's "time to play" for one of the pre-loaded entries.
Transitions between playlist entries with different types of sources (file, stream and analog
inputs) are performed automatically. At the end of each schedule the channel is faded-out,
no matter if the total length of the playlist entries would require a longer timeslot.
If for some reason the playout is corrupted, stopped or too silent to make any sense, then
this <u>triggers a fallback using the silence detector</u> (see chapter below).
## Fallback Handling
Engine is able to react to common community radio scenarios, like the upload of some
pre-recorded show has been forgotten, or some live show is actually not taking place.
Usually in such cases the broadcast might end up with some timeslot filled with silence.
To avoid this, Engine provides multiple levels of fallback handling.
The available fallbacks are evaluated in following order:
1. **Schedule Fallback**: If the show fallback is not assigned, a configured fallback
playlist for the related timeslot is used. This playlist is aired in normal order.
It's important to note, in case that playlists containing anything different than
file entries, are ignored (i.e. live or stream content).
2. **Show Fallback**: If the schedule for some show has no playlist assigned, the
playlist assigned as a *show fallback* is used instead. In the dashboard this can
be done as seen in the screenshot below.
![Setting for the Show Fallback in AURA Dashboard](images/dashboard-fallback-setting.png "Show Fallback in Dashboard")
This playlist is aired in normal order. It's important to note, in case that playlists
containing anything different than file entries, are ignored (i.e. live or stream content).
3. **Station Fallback**: If everything goes wrong, meaning all the previous fallback
playlists are not assigned or are invalid, the *station fallback* will be triggered. This
fallback type is specified by either some **M3U Playlist** or an **Audio Folder**. Both are watched
and automatically updated upon content change. Media is played in an randomized way, meaning
they are shuffled and played until nothing is left for the given playlist/folder. Then it
starts all over again.
To configure the behavior of fallbacks, check out the `[fallback]` section in your `engine.ini` configuration.
### Silence Detector
The aforementioned fallback levels are triggered using a Silence Detector reacting to situations
where no or unwanted sound is on air. The Silence Detector allows detection of absoulte silence,
weak signals or even noise.
To configure the sensitivity of the Silence Detector adapt following properties in
`engine.ini`:
```ini
# max_blank => maximum time of blank from source (defaults to 20., seconds, float)
# min_noise => minimum duration of noise on source to switch back over (defaults to 0, seconds, float)
# threshold => power in dB under which the stream is considered silent (defaults to -40., float)
fallback_max_blank="10."
fallback_min_noise="0."
fallback_threshold="-50."
```
Let's assume you want to broadcast a live show. At the given example the Silence Detector will
react upon 10 seconds of silence, it will evaluate if there is a schedule or show fallback playlist
defined. If yes, it will immediately schedule and play such playlist. If there is no such playlist
the station fallback will kick in by playing any randomized music.
As soon some signal from the live source is sensed again, the audio routing switches back to the live
channel.
> Note, all these fallback source are fully integrated with the Engine's playlog and track-service
feature including indications from which fallback level some audio is broadcasted.
## Monitoring
You have following options to monitor the Engine:
- Send mails on errors and warnings
- Engine Status Information
- Engine Heartbeat
- Logging
### Send mails on errors and warnings
To activate you'll need to set some mail account within the `[monitoring]` section of your configuration.
```ini
[monitoring]
mail_server="mail.o94.at"
mail_server_port="587"
mail_user="aura@o94.at"
mail_pass="---SECRET--PASSWORD---"
# If you want to send multiple adminmails, make them space separated
admin_mail="admin-email@your.domain"
# Which from mailadress should be used
from_mail="monitoring@aura.engine"
# The beginning of the subject. With that you can easily apply filter rules using a mail client
mailsubject_prefix="[Aura Engine]"
```
### Engine Health Information via Engine API
Whenever the Engine's status turns into some unhealthy state this is logged to [Engine API](https://gitlab.servus.at/aura/engine-api).
Also, when it returns to some valid state this is logged to the Engine API.
### Engine Heartbeat
Instead of checking all status properties, the Heartbeat only validates the vital ones
required to run the engine. If all of those are valid, as network socket request is sent
to a defined server.
This heartbeat is sent continiously based on the configured `heartbeat_frequency`.
```ini
# Server where heartbeat info is sent to
heartbeat_server = "127.0.0.1"
# Some UDP port
heartbeat_port = 43334
# Seconds how often the vitality of the Engine should be checked (0 = disabled)
heartbeat_frequency = 1
```
The service receiving this heartbeat ticks can decide what to do with that information.
One scenario could be switching to another Engine instance or any other failover scenario.
Under `contrib/heartbeat-monitor` you'll find some sample application digesting these
heartbeat signals.
### Logging
In development all Engine logs can be found under `./log`, and for production they can
are located in `/var/log/aura`. Adapt the log-level within your configuration to get
more or less verbose log output:
```ini
logdir="/var/log/aura"
# Possible values: debug, info, warning, error, critical
loglevel="info"
```
The log directory holds individual logs from Engine Core, Liquidsoap and the API.
But also `stout` outputs from supervisor services are written there.
Additionally you'll finde Supervisor specific logs under`/var/log/supervisor`.
## Read more
- [Overview](/README.md)
- [Installation for Development](installation-development.md)
- [Installation for Production](installation-production.md)
- [Running with Docker](running-docker.md)
- [Setup the Audio Store](docs/setup-audio-store.md)
- [Developer Guide](developer-guide.md)
- [Engine Features](engine-features.md)
- [Frequently Asked Questions (FAQ)](docs/frequently-asked-questions.md)
\ No newline at end of file
# Frequently Asked Questions
<!-- TOC -->
- [Frequently Asked Questions](#frequently-asked-questions)
- [Which Audio Interface / Soundcard is compatible with Aura?](#which-audio-interface--soundcard-is-compatible-with-aura)
- [ALSA Settings](#alsa-settings)
- [In the Liquidsoap Logs I get 'Error when starting output output_lineout_0: Failure("Error while setting open_pcm: Device or resource busy")!'. What does it mean?](#in-the-liquidsoap-logs-i-get-error-when-starting-output-output_lineout_0-failureerror-while-setting-open_pcm-device-or-resource-busy-what-does-it-mean)
- [How can I find the audio device IDs, required for settings in engine.ini?](#how-can-i-find-the-audio-device-ids-required-for-settings-in-engineini)
- [I have issues with starting the Engine](#i-have-issues-with-starting-the-engine)
- [I have issues during some Engine play-out](#i-have-issues-during-some-engine-play-out)
- [Read More](#read-more)
<!-- /TOC -->
## Which Audio Interface / Soundcard is compatible with Aura?
Basically any audio device which is supported by Linux Debian/Ubuntu and has ALSA drivers.
Engine has been tested with following audio interfaces:
- ASUS Xonar DGX,
- Roland Duo-Capture Ex
- Onboard Soundcard (HDA Intel ALC262)
- Native Instruments Komplete Audio 6
## ALSA Settings
### In the Liquidsoap Logs I get 'Error when starting output output_lineout_0: Failure("Error while setting open_pcm: Device or resource busy")!'. What does it mean?
You probably have set a wrong or occupied device ID.
### How can I find the audio device IDs, required for settings in engine.ini?
* **ALSA**: You can get the device numbers or IDs by executing:
cat /proc/asound/cards
* **Pulse Audio**: You might not need this for Pulse Audio, but still, to see all available devices use:
pactl list
**If you cannot find correct ALSA settings**
Well, this is - at least for me - a hard one. I could not manage to find correct ALSA settings for the above mentioned soundcards. The best experience i had with the ASUS Xonar DGX, but still very problematic (especially the first couple of minutes after starting liquidsoap). Since i enabled JACK support i only use that. It is also a bit of trial and error, but works pretty much out of the box.
**If you experience 'hangs' or other artefacts on the output signal**
* Reduce the quality (especially, when hangs are on the stream) or
* Check the logs (especially the Liquidsoap logs) for any configuration issues
* Check ther performance of your computer or audio hardware
* Install the realtime kernel with
```bash
apt install linux-image-rt-amd64
reboot
```
## I have issues with starting the Engine
**Cannot connect to socketpath /opt/aura/engine/src/liquidsoap/engine.sock. Reason: [Errno 111] Connection refused**
- This indicates some issue with the proper startup of Liquidsoap or any related audio device. Check the Liquidsoap logs for details.
**[clock.wallclock_alsa:2] Error when starting output lineout: Failure("Error while setting open_pcm: No such file or directory")!**
- This might indicate some issue with your ALSA device. Check if `aplay -l` and `aplay -L` returns some valid device(s). You can also try `alsamixer`.
- It might be also helpful to set your default audio device in `/etc/asound.conf`.
- Also check if your user (è.g. `engineuser`) belongs to the group `audio`.
- Check the audio interface configuration section in `engine.ini`. Verify if the default settings `input_device_0="hw:0"` and `output_device_0="hw:0"` are valid device IDs.
## I have issues during some Engine play-out
**Some stuttering sound is hearable. The logs say "strange error flushing buffer ..."**
- Check your Icecast connection. Is it up and running?
- Maybe there is some authentication issue or an Icecast limitation of max clients. See https://github.com/savonet/liquidsoap/issues/524.
## Read More
- [Overview](/README.md)
- [Installation for Development](installation-development.md)
- [Installation for Production](installation-production.md)
- [Running with Docker](running-docker.md)
- [Setup the Audio Store](docs/setup-audio-store.md)
- [Developer Guide](developer-guide.md)
- [Engine Features](engine-features.md)
- [Frequently Asked Questions (FAQ)](docs/frequently-asked-questions.md)
\ No newline at end of file
docs/images/dashboard-fallback-setting.png

20.9 KiB

# Install for Development
<!-- TOC -->
- [Install for Development](#install-for-development)
- [Prerequisites](#prerequisites)
- [Setup Database](#setup-database)
- [Installation](#installation)
- [Configuration](#configuration)
- [Running Engine](#running-engine)
- [Logging](#logging)
- [Read more](#read-more)
<!-- /TOC -->
## Prerequisites
Aura Engine runs on any modern Debian-based OS. It requires at least
- `Python 3.7` or `Python 3.8`
- `git`
Additionally you'll need these system packages:
```bash
sudo apt-get install \
opam \
libsndfile1 \
ffmpeg \
quelcom \
python3-pip \
virtualenv \
libssl-dev
```
Depending on the database management system you gonna use, you'll also need to install those packages.
In case of MariaDB this is:
```shell
sudo apt-get install \
python-dev \
default-libmysqlclient-dev \
mariadb-server \
libmariadbclient-dev
```
**Liquidsoap Repository**
Engine requires at least `Liquidsoap 1.4.3` or newer, installed using [OPAM (OCaml Package Manager)](https://opam.ocaml.org/).
Add the current Liquidsoap repository from [this installation guide](https://www.liquidsoap.info/doc-1.4.3/install.html).
The other steps required for the Liquidsoap installation are handled by the `install.sh` script. If you experience any
errors, carefully review them and consult the official documentation for installing Liquidsoap.
## Setup Database
The following installation script sets up the database.
```bash
bash scripts/setup-db.sh
```
By default Aura Engine uses MariaDB for persistence. When starting this script, please
ensure you have root access to your database instance. The installation script automatically
creates a database plus an associated user with password. If you want to use your own database
system, select "Other / Manually" during the database installation step.
In case of MariaDB the script also installs these system dependencies:
apt-get install mariadb-server libmariadbclient-dev
When this is completed, carefully check if any error occured. In case your database has been setup
automatically, note the relevant credentials for later use in your `engine.ini` configuration.
**Cloning the project**
Then clone the project to some development folder:
```bash
git clone https://gitlab.servus.at/autoradio/engine
```
## Installation
```shell
./install.sh
```
This script does the following:
- Install Liquidsoap components using OPAM (`scripts/install-opam-packages`)
- NodeJS Modules (`scripts/install-web.sh`)
- Python Packages (`requirements.txt`)
- Creates a default configuration file in `config/engine.ini`
When this is completed, carefully check if any error occured.
## Configuration
In your development environment edit following file to configure the engine:
```shell
./config/engine.ini
```
Please note if some configuration exists under `/etc/aura/engine.ini` the configuration is
read from there by default.
Now, specify at least following settings to get started:
```ini
[database]
db_user="aura"
db_name="aura_engine"
db_pass="---SECRET--PASSWORD---"
```
Set the URLs to the *Steering* and *Tank* API:
```ini
[api]
# The URL to get the health status
api_steering_status = "http://aura.local:8000/api/v1/"
# The URL to get the Calendar via Steering
api_steering_calendar="http://aura.local:8000/api/v1/playout"
# The URL to get show details via Steering
api_steering_show="http://aura.local:8000/api/v1/shows/${ID}/"
## TANK ##
# The URL to get the health status
api_tank_status = "http://aura.local:8040/healthz/"
# The URL to get playlist details via Tank
api_tank_playlist="http://aura.local:8040/api/v1/playlists/${ID}"
## ENGINE-API ##
# Engine ID (1 or 2)
api_engine_number = 1
# Engine API endpoint to store playlogs
api_engine_store_playlog = "http://localhost:8008/api/v1/playlog/store"
# Engine API endpoint to store clock information
api_engine_store_clock = "http://localhost:8008/api/v1/clock"
# Engine API endpoint to store health information
api_engine_store_health = "http://localhost:8008/api/v1/source/health/${ENGINE_NUMBER}"
```
Ensure that the Liquidsoap installation path is valid:
```ini
[lqs]
liquidsoap_path="/home/david/.opam/4.08.0/bin/liquidsoap"
```
Finally Engine needs to be able to access the audio folder, where all the tracks of the playlists
are stored via *Tank*:
```ini
[audiosource]
audio_source_folder="/var/audio"
```
If the audio device desired for playback is set as `default`, the Engine now should be ready to play
sound. You can check the default audio hardware by executing `aplay -L` on the command line. If that's
not the case you can set the default device in `/etc/asound.conf`. More advanced audio device settings
can be looked up in the [Configuration Guide](docs/configuration-guide.md).
Read about all other available settings in the [Configuration Guide](docs/configuration-guide.md).
## Running Engine
Use the convencience script `run.sh` to get engine started in different ways:
**Run the Engine**
This includes the Liquidsoap audio engine, but does not start the API server.
```shell
./run.sh
```
**Run the Engine Core and Liquidsoap separately**
When developing and debugging engine it is helpful to start the core and the Liquidsoap
component separately. In such case it is important to start both in the correct order.
First start the core of the engine:
```shell
./run.sh core
```
When engine-core completed its boot phase, indicated by a log that it is waiting for
Liquidsoap, you can run following:
```shell
./run.sh lqs
```
## Logging
All Engine logs for development can be found under `./logs`.
## Read more
- [Overview](/README.md)
- [Installation for Development](installation-development.md)
- [Installation for Production](installation-production.md)
- [Running with Docker](running-docker.md)
- [Setup the Audio Store](docs/setup-audio-store.md)
- [Developer Guide](developer-guide.md)
- [Engine Features](engine-features.md)
- [Frequently Asked Questions (FAQ)](docs/frequently-asked-questions.md)
\ No newline at end of file
# Install for Production
<!-- TOC -->
- [Install for Production](#install-for-production)
- [Prerequisites](#prerequisites)
- [Installation](#installation)
- [Configuration](#configuration)
- [Running Engine](#running-engine)
- [Running with Systemd](#running-with-systemd)
- [Running with Supervisor](#running-with-supervisor)
- [Logging](#logging)
- [Read more](#read-more)
<!-- /TOC -->
## Prerequisites
Aura Engine runs on any modern Debian-based OS. It requires at least
- `Python 3.7` or `Python 3.8`
- `git`
Additionally you'll need these system packages below.
```shell
sudo apt-get update
sudo apt-get install \
supervisor \
opam \
libsndfile1 \
ffmpeg \
quelcom \
python3-pip \
virtualenv \
libssl-dev
```
Depending on the database management system you gonna use, you'll also need to install those packages.
In case of MariaDB this is:
```shell
sudo apt-get install \
python-dev \
default-libmysqlclient-dev \
mariadb-server \
libmariadbclient-dev
```
**Create an user**
While previous packages need superuser rights to be installed, the following ones are installed for the user which is
executing the engine. In your development environment you can skip this step. In production you first need to create
a user called `engineuser`.
```shell
sudo adduser engineuser
sudo adduser engineuser audio sudo
```
And switch to that user
```shell
su engineuser
```
**Liquidsoap Repository**
Engine requires at least `Liquidsoap 1.4.3` or newer, installed using [OPAM (OCaml Package Manager)](https://opam.ocaml.org/).
Add the current Liquidsoap repository from [this installation guide](https://www.liquidsoap.info/doc-1.4.3/install.html).
The other steps required for the Liquidsoap installation are handled by the `install.sh` script. If you experience any
errors, carefully review them and consult the official documentation for installing Liquidsoap.
**Cloning the project**
Create the folder `/opt/aura` and clone the engine project from there:
```shell
engineuser:/opt/aura/$ git clone https://gitlab.servus.at/autoradio/engine
```
Now you should have `/opt/aura/engine/`.
Let's move inside the home of engine:
```shell
engineuser:/opt/aura/$ cd engine
```
**Setup the database**
The following installation script sets up the database. You either need to be logged in as root
or have sudo rights.
```shell
root:/opt/aura/engine/$ bash scripts/setup-db.sh
```
By default Aura Engine uses MariaDB for persistence. When starting this script, please
ensure you have root access to your database instance. The installation script automatically
creates a database plus an associated user with password. If you want to use your own database
system, select "Other / Manually" during the database installation step.
If you have chosen to setup your database automatically, note the relevant credentials.
**Initialize folders and permissions**
Call this script to create the required log folders and update all permissions.
```bash
root:/opt/aura/engine$ bash scripts/initialize.sh
```
## Installation
The following installation script also sets up the database.
By default Aura Engine uses MariaDB for persistence. When starting the installation, please
ensure you have root access to your database instance. The installation script automatically
creates a database plus an associated user with password. If you want to use your own database
system, select "Other / Manually" during the database installation step.
```shell
engineuser:/opt/aura/engine$ ./install.sh prod
```
This script does the following:
- Install Liquidsoap components using OPAM (`scripts/install-opam-packages`)
- Python Packages (`requirements.txt`)
- Creates a default Engine configuration file in `/etc/aura/engine.ini`
When this is completed, carefully check if any error occured. In case your database has been setup
automatically, note the relevant credentials for later use in your `engine.ini` configuration.
## Configuration
In your production environment edit following file to configure the engine:
```shell
engineuser:/opt/aura/engine$ nano /etc/aura/engine.ini
```
Now, specify at least following settings to get started:
```ini
[database]
db_user="aura"
db_name="aura_engine"
db_pass="---SECRET--PASSWORD---"
```
Set the URLs to the *Steering*, *Tank* and *Engine* API:
```ini
[api]
# The URL to get the health status
api_steering_status = "http://aura.local:8000/api/v1/"
# The URL to get the Calendar via Steering
api_steering_calendar="http://aura.local:8000/api/v1/playout"
# The URL to get show details via Steering
api_steering_show="http://aura.local:8000/api/v1/shows/${ID}/"
## TANK ##
# The URL to get the health status
api_tank_status = "http://aura.local:8040/healthz/"
# The URL to get playlist details via Tank
api_tank_playlist="http://aura.local:8040/api/v1/playlists/${ID}"
## ENGINE-API ##
# Engine ID (1 or 2)
api_engine_number = 1
# Engine API endpoint to store playlogs
api_engine_store_playlog = "http://localhost:8008/api/v1/playlog/store"
# Engine API endpoint to store clock information
api_engine_store_clock = "http://localhost:8008/api/v1/clock"
# Engine API endpoint to store health information
api_engine_store_health = "http://localhost:8008/api/v1/source/health/${ENGINE_NUMBER}"
```
Ensure that the Liquidsoap installation path is valid:
```ini
[lqs]
liquidsoap_path="/home/engineuser/.opam/4.08.0/bin/liquidsoap"
```
**Configuring the Audio Store**
Finally Engine needs to be able to access the audio folder, where all the tracks of the playlists
are stored via *Tank*:
```ini
[audiosource]
audio_source_folder="/var/audio/source"
```
There is some document on how to [Setup the Audio Store](docs/setup-audio-store.md).
If the audio device desired for playback is set as `default`, the Engine now should be ready to play
sound. You can check the default audio hardware by executing `aplay -L` on the command line. If that's
not the case you can set the default device in `/etc/asound.conf`. More advanced audio device settings
can be looked up in the [Configuration Guide](docs/configuration-guide.md).
Read about all other available settings in the [Configuration Guide](docs/configuration-guide.md).
## Running Engine
In production the process of starting the engine is slightly different compared to some development environment.
This is due to the need of ensuring the engine's components are always running i.e. letting them to restart
automatically after some system restart or crash has happened.
For this you can utilize either [Systemd](https://systemd.io/) or [Supervisor](http://supervisord.org/).
### Running with Systemd
Copy the unit file `/opt/aura/engine/config/systemd/aura-engine.service` to `/etc/systemd/system/`.
This unit file starts engine-core and engine-liquidsoap within one command. Here Liquidsoap is started as
as subprocess.
> Please note not to use any other unit files in that directory yet. They are work in progress.
Now you'll need to reload the Systemd unit files
```shell
sudo systemctl daemon-reload
```
**Start**
```shell
sudo systemctl start aura-engine
```
**Stop**
```shell
sudo systemctl start aura-engine
```
**Restart**
```shell
sudo systemctl restart aura-engine
```
**Status**
```shell
sudo systemctl status aura-engine
```
### Running with Supervisor
Now, given you are in the engine's home directory `/opt/aura/engine/`, simply type following to start
the services:
```shell
supervisord
```
This picks up the supervisor configuration provided in the local `supervisord.conf` and the service configurations
located in `config/supervisor/*.conf`.
Experience has shown it might be helpful to reload the supervisor configuration using `sudo`:
```shell
sudo supervisorctl reload
```
Note that the supervisor daemon starts all (both) services at once. If you want more fine-grained control for
starting services individually, please check-out the next section.
**Listing available Services**
```shell
engineuser:/opt/aura/engine$ supervisorctl avail
```
You should get these all services with their actual state listed:
```c++
aura-engine in use auto 666:666
aura-engine-api in use auto 999:999
```
**Maintanence using Supervisor**
Please remember to call all `supervisorctl` commands from within your engine home directory (`/opt/aura/engine/`),
to pickup the correct `supervisord.conf`.
**Starting Services**
```shell
supervisorctl start <service-name>
```
**Stopping Services**
```shell
supervisorctl stop <service-name>
```
**Restarting Services**
```shell
supervisorctl restart <service-name>
```
**Refresh after changing configurations**
```shell
supervisorctl restart <service-name>
```
In case you want to reload whole supervisor service
```shell
sudo service supervisor restart
```
## Logging
All Engine logs for production can be found under:
```shell
`/var/log/aura`
```
and
```shell
`/opt/aura/engine/logs`
```
## Read more
- [Overview](/README.md)
- [Installation for Development](installation-development.md)
- [Installation for Production](installation-production.md)
- [Running with Docker](running-docker.md)
- [Setup the Audio Store](docs/setup-audio-store.md)
- [Developer Guide](developer-guide.md)
- [Engine Features](engine-features.md)
- [Frequently Asked Questions (FAQ)](docs/frequently-asked-questions.md)
\ No newline at end of file
# Running Engine with Docker
Docker provides a simple way to get your engine with all dependencies running.
Here you can find the official AURA Engine Docker images:
https://hub.docker.com/repository/docker/autoradio/engine
> Note: The Engine Docker image is in *POC* state and waiting to be fully implemented.
It's not yet ready to be offically used. If you want to try AURA Engine meanwhile
try the [Standard Installation](docs/installation-development).
<!-- TOC -->
- [Running Engine with Docker](#running-engine-with-docker)
- [Basic configuration](#basic-configuration)
- [Start an image](#start-an-image)
- [Configure an image](#configure-an-image)
- [Making Docker Releases](#making-docker-releases)
- [Build your own, local Docker image](#build-your-own-local-docker-image)
- [Releasing a new version to DockerHub](#releasing-a-new-version-to-dockerhub)
- [Read more](#read-more)
<!-- /TOC -->
## Basic configuration
Create a default configuration and edit according to your settings
```shell
cp config/sample-docker.engine.ini config/docker/engine.ini
```
Create a symlink in `./audio` to point to the audio source of tank
```shell
ln -s /path/to/tank/audio-store ./audio/source
```
## Start an image
```shell
./run.sh docker:engine
```
*To be extended ...*
## Configure an image
*To be extended ...*
## Making Docker Releases
This section is only relevant if you are an Engine Developer.
### Build your own, local Docker image
```shell
./run.sh docker:build
```
### Releasing a new version to DockerHub
```shell
./run.sh docker:push
```
## Read more
- [Overview](/README.md)
- [Installation for Development](installation-development.md)
- [Installation for Production](installation-production.md)
- [Running with Docker](running-docker.md)
- [Setup the Audio Store](docs/setup-audio-store.md)
- [Developer Guide](developer-guide.md)
- [Engine Features](engine-features.md)
- [Frequently Asked Questions (FAQ)](docs/frequently-asked-questions.md)
\ No newline at end of file
# Setting up the Audio Store
The *Audio Store* is a folder which is utilized by AURA Tank and Engine to exchange audio files.
Assuming AURA Engine and Tank are hosted on different machines, the `audio_source_folder` must by shared
using some network share.
In case you are hosting Engine and Tank on the same machine (e.g. in development), you can skip
this documentation. Just think about pointing them to the same directory.
<!-- TOC -->
- [Setting up the Audio Store](#setting-up-the-audio-store)
- [Share Location](#share-location)
- [Share Type](#share-type)
- [Setting up SSHFS](#setting-up-sshfs)
- [Configuring Engine](#configuring-engine)
- [Configuring Tank](#configuring-tank)
- [Read more](#read-more)
<!-- /TOC -->
By default Engine expects audio files shared by Tank in `/var/audio/source`.
This can be configurated in `engine.ini`:
```ini
[audiosource]
audio_source_folder="/var/audio/source"
```
Now, this folder must be somehow writable by Tank.
## Share Location
You have following options where your share can be located:
1. **Engine and all other AURA components (Tank, Dashboard, Steering) are running on the same instance.** This is the most simple solution,
as Engine and Tank can share the same directory locally. But this scenario requires some more sophisticated tuning of the system resources
to avoid e.g. some overload of multiple Uploads in Tank may affect the performance of engine. You can eliminate this risk by setting CPU and
memory limits for Steering, Dashboard and Tank using Docker or `systemd-cgroups`. A disadvantage here is the case of maintainence of system
reboot. This would mean that all components are offline at once.
2. **Physical directory where the Engine lives, mounted to Tank**. This may cause an issue with the mount, when no network connection to Engine
is unavailable or the instance is rebooting.
3. **Physical directory where the Tank lives, mounted to Engine.** This may cause an issue with the mount, when no network connection to Tank
is unavailable or the instance is rebooting.
4. **Central Data Store or *Storage Box*** which is mountet to Engine and Tank. In this case a downtime of the store make both, Engine and Tank
dysfunctional.
5. **Replicated storage solution using [Gluster](https://www.gluster.org/), both Engine and Tank have their virtual audio directory mounted.**
That's the ideal approach, because if any of the instances is down, the other has all the data available.
In any case, you should think about some backup solution involving this directory.
## Share Type
Then, there's the question how the share is managed. Beside other you have the options
to use [NFS](https://en.wikipedia.org/wiki/Network_File_System), [SSHFS](https://en.wikipedia.org/wiki/SSHFS) or even something like [Gluster](https://www.gluster.org/).
For our initial setup we have chosen to use *SSHFS*.
Please share your experience with other share types, and we will include it in further releases of
this documentation.
## Setting up SSHFS
SSHFS allows you to access the filesystem on a remote computer via SSH. Interaction with files and folders behaves similar to any local data.
This example is setting up the `audio_source_folder` on the Engine instance.
### Configuring Engine
First, you'll need to create an user which enables Tank to access the `audio_source_folder` on Engine:
```shell
adduser tankuser
chown tankuser:engineuser /var/audio/source
```
Ensure that `engineuser` has no permissions to write the directory:
```shell
chmod u=+rwx,go=+rx-w /var/audio/source
```
### Configuring Tank
On the Tank side you need to install `sshfs`:
```shell
sudo apt-get install sshfs
```
Then create an `audio-store` folder inside the AURA home:
```shell
:/opt/aura/$ mkdir audio-store
```
Try if you can connect to the engine over SSH using your `tankuser`:
```shell
ssh tankuser@192.168.0.111 -p22
```
Replace `-p22` with the actual port number your SSH service is running with. For security reasons it's recommended
to run SSH not over the default port 22.
Uncomment following setting in `/etc/fuse.conf` to allow the tank-user access the share with write permissions:
```conf
# Allow non-root users to specify the allow_other or allow_root mount options.
user_allow_other
```
Now create the mount:
```shell
sudo sshfs -o allow_other -o IdentityFile=~/.ssh/id_rsa tankuser@192.168.0.111:/var/audio /opt/aura/audio-store -p22
```
Replace `192.168.0.111` with the actual IP for your Engine and `-p22` with the actual port number your Engine's SSH service
is running with.
To make this mount persistent i.e. keep it alive even after a system reboot, you'll need to add a configuration
in the `/etc/fstab` file by adding this at the end:
```yaml
# Audio Store @ AURA Engine
sshfs#tankuser@192.168.0.111:/var/audio /opt/aura/audio-store fuse auto,port=22,identityfile=~/.ssh/id_rsa,allow_other,_netdev 0 0
```
Again, check for the correct port number in the line above.
To take this into effect you'll need to remount the filesystem with `sudo mount -a` or reboot the machine. When mounting
you'll need to authenticate with the `tankuser` password once.
Then review if your Tank's Docker configuration mounts the exact same volume (`/opt/aura/audio-store`).
If not edit the tank configuration `/etc/aura/tank.env` and set following property:
```shell
TANK_STORE_PATH=/opt/aura/audio-store
```
Finally, do some testing if the directory is writable from Tank's system (`touch some-file`) and if the Engine's side can read
this file. Then restart your Tank Docker container and you should be good to go.
## Read more
- [Overview](/README.md)
- [Installation for Development](installation-development.md)
- [Installation for Production](installation-production.md)
- [Running with Docker](running-docker.md)
- [Setup the Audio Store](docs/setup-audio-store.md)
- [Developer Guide](developer-guide.md)
- [Engine Features](engine-features.md)
- [Frequently Asked Questions (FAQ)](docs/frequently-asked-questions.md)
#!/bin/bash
mode="dev"
if [[ $* =~ ^(prod)$ ]]; then
mode="prod"
fi
if [ $mode == "dev" ]; then
echo "[Installing AURA ENGINE for Development]"
fi
if [ $mode == "prod" ]; then
echo "[Installing AURA ENGINE for Production]"
fi
# Find the correct Python version (3.7 or 3.8)
if hash python3.8 2>/dev/null; then
PYTHON_EXEC="python3.8"
echo "[ Using Python 3.8 ]"
else
PYTHON_EXEC="python3.7"
echo "[ Using Python 3.7 ]"
fi
# Development and Production
echo "Installing OPAM Packages ..."
bash scripts/install-opam-packages.sh
echo "Installing Python Requirements ..."
$PYTHON_EXEC $(which pip3) install -r requirements.txt
# Development
if [ $mode == "dev" ]; then
echo "Create local 'logs' Folder ..."
mkdir -p logs
echo "Copy configuration to './config/engine.ini'"
cp -n config/sample-development.engine.ini config/engine.ini
fi
# Production
if [ $mode == "prod" ]; then
echo "Create local 'tmp' Folder ..."
mkdir -p tmp
echo "Copy default Engine configuration to '/etc/aura/engine.ini'"
cp -n config/sample-production.engine.ini /etc/aura/engine.ini
fi
echo
echo "+++ Installation of AURA Engine finished! +++"
echo
\ No newline at end of file
# Meta
__author__ = "David Trattnig and Gottfried Gaisbauer"
__copyright__ = "Copyright 2017-2020, Aura Engine Team"
__credits__ = ["David Trattnig", "Gottfried Gaisbauer", "Michael Liebler"]
__license__ = "GNU Affero General Public License (AGPL) Version 3"
__version__ = "0.8.3"
__version_info__ = (0, 8, 3)
__maintainer__ = "David Trattnig"
__email__ = "david.trattnig@subsquare.at"
__status__ = "Development"
\ No newline at end of file
requests==2.24.0
sqlalchemy==1.3.17
Flask==1.1.2
Flask_SQLAlchemy==2.4.3
mysqlclient==1.3.12
validators==0.12.1
accessify==0.3.1
http-parser==0.9.0
\ No newline at end of file
#!/bin/sh
''''which python3.8 >/dev/null 2>&1 && exec python3.8 "$0" "$@" # '''
''''which python3.7 >/dev/null 2>&1 && exec python3.7 "$0" "$@" # '''
''''exec echo "Error: Snaaakey Python, where are you?" # '''
#
# 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
import time
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from src.base.logger import AuraLogger
from src.base.config import AuraConfig
from src.base.utils import SimpleUtil as SU
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 EngineRunner:
"""
EngineRunner is in charge of
1. Initializing the engine and all dependencies
3. Starting Liquidsoap in a separate thread, if requested
"""
logger = None
config = None
engine = None
lqs = None
def __init__(self):
"""
Constructor
"""
self.config = config
AuraLogger(self.config)
self.logger = logging.getLogger("AuraEngine")
def run(self, start_lqs, lqs_debug_flags):
"""
Starts Engine Core.
"""
from src.scheduling.scheduler import AuraScheduler
from src.core.engine import Engine
# Check if the database has to be re-created
if self.config.get("recreate_db") is not None:
AuraScheduler.init_database()
if start_lqs:
runner.run_lqs(lqs_debug_flags)
else:
self.logger.info(SU.yellow("Please note, Liquidsoap needs to be started manually."))
self.engine = Engine()
def run_lqs(self, lqs_debug_flags):
"""
Starts Liquidsoap.
"""
debug_output = lqs_debug_flags.get("debug_output")
verbose_output = lqs_debug_flags.get("verbose_output")
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 Liquidsoap 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.engine:
self.engine.terminate()
if self.lqs:
self.lqs.terminate()
self.logger.info("Terminated Liquidsoap")
self.logger.info("Gracefully terminated Aura Engine! (signum:%s, frame:%s)" % (signum, frame))
sys.exit(0)
#
# START THE ENGINE
#
if __name__ == "__main__":
runner = EngineRunner()
do_start_lqs = True
lqs_cmd = False
signal.signal(signal.SIGINT, runner.exit_gracefully)
signal.signal(signal.SIGTERM, runner.exit_gracefully)
if len(sys.argv) >= 2:
if "--without-lqs" in sys.argv:
do_start_lqs = False
if "--get-lqs-command" in sys.argv:
lqs_cmd = True
if "--use-test-data" in sys.argv:
runner.config.set("use_test_data", True)
if "--recreate-database" in sys.argv:
runner.config.set("recreate_db", True)
if lqs_cmd:
print(runner.get_lqs_cmd(True, True))
else:
runner.run(do_start_lqs, { "debug_output": False, "verbose_output": False })
#!/bin/bash
mode="engine"
docker="false"
#
# Run Script for AURA Engine
#
# Call with one of these parameters:
#
# - init
# - engine
# - core
# - lqs
# - recreate-database
# - docker:engine
# - docker:build
# - docker:push
#
if [[ $* =~ ^(init|env|engine|core|lqs)$ ]]; then
mode=$1
fi
if [[ "$1" == *"docker:"* ]]; then
docker="true"
mode=${1#*:}
fi
echo "[ Run mode=$mode ]"
echo "[ Docker=$docker ]"
# Find the correct Python version (3.7 or 3.8)
if hash python3.8 2>/dev/null; then
PYTHON_EXEC="python3.8"
echo "[ Using Python 3.8 ]"
else
PYTHON_EXEC="python3.7"
echo "[ Using Python 3.7 ]"
fi
# +++ DEFAULT COMMANDS +++ #
if [[ $docker == "false" ]]; then
### Initializes the environment & installs dependencies ###
if [[ $mode == "init" ]]; then
mkdir -p logs
pip3 install -r requirements.txt
fi
### Runs Engine Core & Liquidsoap ###
if [[ $mode == "engine" ]]; then
eval $(opam env)
/usr/bin/env $PYTHON_EXEC run.py
fi
### Runs Engine Core only ###
if [[ $mode == "core" ]]; then
/usr/bin/env $PYTHON_EXEC run.py --without-lqs
fi
### Runs Liquidsoap only ###
if [[ $mode == "lqs" ]]; then
lqs=$(/usr/bin/env $PYTHON_EXEC run.py --get-lqs-command)
eval "$lqs"
fi
### CAUTION: This deletes everything in your database ###
if [[ $mode == "recreate-database" ]]; then
/usr/bin/env $PYTHON_EXEC run.py --recreate-database
fi
fi
# +++ DOCKER COMMANDS +++ #
if [[ $docker == "true" ]]; then
BASE_D=$(realpath "${BASH_SOURCE%/*}/")
### Runs Engine Core & Liquidsoap ###
if [[ $mode == "engine" ]]; then
exec sudo docker run \
--network="host" \
--name aura-engine \
--rm -d \
-u $UID:$GID \
-v "$BASE_D":/srv \
-v "$BASE_D/audio/source":/var/audio/source:ro \
-v "$BASE_D/config/docker":/etc/aura \
-v "/dev/snd":/dev/snd \
--privileged \
--tmpfs /var/log/aura/ \
autoradio/engine
fi
### Create Docker Image from local project ###
if [[ $mode == "build" ]]; then
exec sudo docker build -t autoradio/engine .
fi
### Pushes the latest Docker Image to Docker Hub ###
if [[ $mode == "push" ]]; then
exec sudo docker push autoradio/engine
fi
fi
\ No newline at end of file
#!/bin/bash
if getent passwd 'engineuser' > /dev/null 2>&1; then
echo "User 'engineuser' exists already.";
else
echo "Creating Engine User ..."
adduser engineuser
adduser engineuser audio
fi
\ No newline at end of file
#!/bin/bash
#
# Prepare folders and permissions for installing engine on production.
#
# You'll need sudo/root privileges.
#
echo "Create Log Directory '/var/log/aura/'"
mkdir -p /var/log/aura
echo "Create Configuration Directory '/etc/aura/'"
mkdir -p /etc/aura
echo "Set Ownership of '/opt/aura/engine', '/var/log/aura/' and '/etc/aura/engine.ini' to Engine User"
chown -R engineuser:engineuser /opt/aura
chown -R engineuser:engineuser /etc/aura
chown -R engineuser:engineuser /var/log/aura
chown -R engineuser:engineuser /var/log/supervisor
echo "Copy Systemd unit files to '/etc/systemd/system/'"
cp -n /opt/aura/engine/config/systemd/* /etc/systemd/system/
\ No newline at end of file
#!/bin/bash
opam update -y
opam init -y
opam switch create 4.08.0
opam install depext -y
opam depext taglib mad lame vorbis flac opus cry samplerate pulseaudio bjack alsa ssl liquidsoap -y
opam install taglib mad lame vorbis flac opus cry samplerate pulseaudio bjack alsa ssl liquidsoap -y
eval $(opam env)