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
Commits on Source (315)
Showing
with 1399 additions and 0 deletions
.idea/
*.pyc
*.log
.vscode/tags
configuration/engine.ini
web/css/aura-clock-bundle.css
web/css/aura-player-bundle.css
web/js/aura-clock-bundle.js
web/js/aura-player-bundle.js
web/clock.html
\ No newline at end of file
image: python:3.7
stages:
- test
before_script:
- apt-get -qq update
- apt-cache search libmariadb
- apt-get install -y python3-virtualenv virtualenv redis-server redis-tools libev4 libev-dev # mariadb-server libmariadbclient-dev
- /usr/bin/virtualenv venv
- . venv/bin/activate
- python3 -V
- pip3 install -r requirements.txt
- mkdir /etc/aura
- mkdir /var/log/aura
- pwd
- cp configuration/sample.engine.ini configuration/engine.ini
simple_guru_help:
stage: test
script:
- python3 guru.py -h
#print_connection_status:
# stage: test
# script:
# - python3 guru.py -pcs
\ No newline at end of file
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Aura Engine - Run",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/aura.py",
"console": "integratedTerminal"
},
{
"name": "Aura Engine API - Run",
"type": "python",
"request": "launch",
"stopOnEntry": false,
"program": "${workspaceFolder}/api.py",
"env": {
"FLASK_APP": "${workspaceRoot}/api.py",
"FLASK_ENV": "development"
},
"args": [
"run"
],
"debugOptions": [
"RedirectOutput"
]
},
{
"name": "Aura Engine - Recreate Database",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/aura.py",
"args": ["--recreate-database"],
"console": "integratedTerminal"
}
]
}
\ No newline at end of file
{
"python.pythonPath": "/usr/bin/python3.7"
}
\ No newline at end of file
Gottfried Gaisbauer <gottfried.gaisbauer@servus.at>
David Trattnig <david.trattnig@subsquare.at>
This diff is collapsed.
# Aura Engine
<img src="https://gitlab.servus.at/autoradio/meta/-/raw/master/images/aura-engine.png" width="250" align="right" />
Aura Engine is a play-out engine as part of [Aura Radio Software Suite](#About),
specifically build for the requirements of community radios.
<!-- TOC -->
- [Aura Engine](#aura-engine)
- [Features](#features)
- [Architecture](#architecture)
- [Requirements](#requirements)
- [Installation](#installation)
- [Start the Engine](#start-the-engine)
- [Logging](#logging)
- [About](#about)
- [Resources](#resources)
<!-- /TOC -->
## Features
- Play audio from multiple sources
- Dynamic switching of sources
- Record output to filesystem
- Stream output to an Icecast Server
- Multichannel Line-out
- Blank Detenction / Silence Detecter
- Auto Pilot a.k.a. Fallback Handling
- API to query Track-Service
- API to query monthly reports
- API to query data for a studio clock
- Web Application for displaying the Track-Service
- Web Application for displaying the studio clock
Read more on the [Engine Features](docs/engine-features.md) page.
## Architecture
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 learn more, checkout the [Engine Developer Guide](docs/developer-guide.md) or visit
the [Aura Meta](https://gitlab.servus.at/autoradio/meta) repository.
## Requirements
**Hardware Requirements:** This depends on how many audio sources and targets you are
going to use, but for the most common scenarios any current hardware should be sufficient.
For the audio devices it is required to use an interface which has supported ALSA drivers.
**Operating System:** Any linux system with ALSA, PulseAudio or Jack2 support should work.
It is tested and coded on Debian Stretch and Ubuntu 18.0 with Python 3.6+.
## Installation
```bash
git clone https://gitlab.servus.at/autoradio/engine
```
**Install System Libraries:**
Aura Engine requires at least Node 13 and Python 3.7.
```bash
sudo apt install \
git \
nodejs\
python3.7 python3.7-pip python3.7-dev \
redis-server \
libsndfile1 ffmpeg \
liquidsoap liquidsoap-plugin-icecast \
mariadb-server libmariadbclient-dev \
quelcom \
liquidsoap-plugin-alsa liquidsoap-plugin-pulseaudio \
liquidsoap-plugin-all
```
**Install Engine Packages:**
```bash
sudo ./install.sh
```
**Setup Database:**
```bash
mysql -u root -p
CREATE DATABASE aura_engine CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'aura'@'localhost' IDENTIFIED BY 'secure-password';
GRANT ALL PRIVILEGES ON aura_engine.* TO 'aura'@'localhost';
```
**Configuration:**
Run
```bash
sh init.sh
```
This creates the folder */var/audio* and copies some default configuration
to */etc/aura/engine.ini*
After that, you have to edit the settings in */etc/aura/engine.ini*. Ensure to take
your time to carefully review those settings!
Read more about detailed settings in the [Configuration Guide](docs/configuration-guide.md).
## Start the Engine
**Development:** While developing there is a simple convencience script `run.sh`
to get you started. Call the engine's components in following order:
```shell
./run.sh # Starts the engine-core component
./run.sh lqs # Starts the engine-liquidsoap component
./run.sh api # Starts the engine-api component
```
**Production:** In production the process is slightly different to ensure the
engine's components are always running i.e. restart themselves after some system
restart or crash. Therefore they are executed using a system service:
```bash
systemctl start aura-engine
systemctl start aura-lqs
systemctl start aura-api
```
and on system boot run following:
```bash
systemctl enable aura-engine
systemctl enable aura-lqs
```
## Logging
You can access the service logs using one of:
```
journalctl -u aura-lqs
journalctl -u aura-engine
```
## About
<img src="https://gitlab.servus.at/autoradio/meta/-/raw/master/images/aura-logo.png" width="150" />
Aura Engine is the play-out engine of the Aura Radio Software Suite. Aura stands for Automated Radio and is a swiss army knife for community radios. Beside the Engine it provides Steering (Admin Interface for the radio station), Dashboard (Collaborative scheduling and programme coordination), Tank (Audio uploading, pre-processing and delivery). Read more in the [Aura Meta](https://gitlab.servus.at/autoradio/meta) repository or on the specific project pages.
| [<img src="https://gitlab.servus.at/autoradio/meta/-/raw/master/images/aura-steering.png" width="150" align="left" />](https://gitlab.servus.at/autoradio/pv) | [<img src="https://gitlab.servus.at/autoradio/meta/-/raw/master/images/aura-dashboard.png" width="150" align="left" />](https://gitlab.servus.at/autoradio/dashboard) | [<img src="https://gitlab.servus.at/autoradio/meta/-/raw/master/images/aura-tank.png" width="150" align="left" />](https://gitlab.servus.at/autoradio/tank) | [<img src="https://gitlab.servus.at/autoradio/meta/-/raw/master/images/aura-engine.png" width="150" align="left" />](https://gitlab.servus.at/autoradio/engine) |
|---|---|---|---|
| [Steering](https://gitlab.servus.at/autoradio/pv) | [Dashboard](https://gitlab.servus.at/autoradio/dashboard) | [Tank](https://gitlab.servus.at/autoradio/tank) | [Engine](https://gitlab.servus.at/autoradio/engine) |
## Resources ##
* **Python**: https://docs.python.org/
* **Redis**: https://redis.io/
* **Liquidsoap**: https://www.liquidsoap.info/doc-1.4.0/
* **Jack Audio**: https://jackaudio.org/
#
# Aura Engine
#
# Copyright (C) 2020 David Trattnig <david.trattnig@subsquare.at>
# 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/>.
# Meta
__version__ = '0.0.1'
__license__ = "GNU Affero General Public License (AGPL) Version 3"
__version_info__ = (0, 0, 1)
__author__ = 'David Trattnig <david.trattnig@subsquare.at>'
import logging
import os, os.path
from datetime import datetime, date, timedelta
from flask import Flask, Response
from flask_caching import Cache
from flask_cors import CORS
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
from marshmallow import Schema, fields, post_dump
from flask_restful import Api, Resource, abort
from apispec import APISpec
from apispec.ext.marshmallow import MarshmallowPlugin
from apispec_webframeworks.flask import FlaskPlugin
# import werkzeug
from werkzeug.exceptions import HTTPException, default_exceptions, Aborter
from libraries.base.logger import AuraLogger
from libraries.base.config import AuraConfig
from libraries.database.broadcasts import AuraDatabaseModel, Schedule, Playlist, PlaylistEntry, PlaylistEntryMetaData, TrackService
#
# Initialize the Aura Web App and API.
#
config = AuraConfig()
app = Flask(__name__,
static_url_path='',
static_folder='web/')
# static_folder='contrib/aura-player/public/')
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
app.config["SQLALCHEMY_DATABASE_URI"] = config.get_database_uri()
app.config["CACHE_TYPE"] = "simple"
app.config["CACHE_DEFAULT_TIMEOUT"] = 0
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0
cache = Cache(app)
cors = CORS(app, resources={r"/*": {"origins": "*"}}) # FIXME Update CORS for production use
db = SQLAlchemy(app)
ma = Marshmallow(app)
api = Api(app)
#
# Werkzeug HTTP code mappings
#
class NoDataAvailable(HTTPException):
code = 204
description = "There is currently no content available."
default_exceptions[204] = NoDataAvailable
abort = Aborter()
class EngineApi:
"""
Provides the Aura Engine API services.
"""
config = None
api = None
logger = None
# trackservice_schema = None
def __init__(self, config, api):
"""
Initializes the API.
Args:
config (AuraConfig): The Engine configuration.
api (Api): The Flask restful API object.
"""
self.config = config
self.logger = AuraLogger(self.config, "engine-api")
self.logger = logging.getLogger("engine-api")
self.api = api
# API Spec
spec.components.schema("TrackService", schema=TrackServiceSchema)
spec.components.schema("Report", schema=ReportSchema)
spec.components.schema("Schedule", schema=ScheduleSchema)
spec.components.schema("Clock", schema=ClockDataSchema)
# TODO Generates HTML for specification
#self.logger.info(spec.to_yaml())
# Schema instances
EngineApi.trackservice_schema = TrackServiceSchema(many=True)
EngineApi.track_schema = TrackServiceSchema()
EngineApi.report_schema = ReportSchema(many=True)
EngineApi.schedule_schema = ScheduleSchema(many=True)
EngineApi.clockdata_schema = ClockDataSchema()
# Define API routes
self.api.add_resource(TrackServiceResource, config.api_prefix + "/trackservice/")
self.api.add_resource(TrackResource, config.api_prefix + "/trackservice/<int:track_id>")
self.api.add_resource(CurrentTrackResource, config.api_prefix + "/trackservice/current")
self.api.add_resource(TracksByDayResource, config.api_prefix + "/trackservice/date/<string:date_string>")
self.api.add_resource(ReportResource, config.api_prefix + "/report/<string:year_month>")
self.api.add_resource(UpcomingSchedulesResource, config.api_prefix + "/schedule/upcoming")
self.api.add_resource(ClockDataResource, config.api_prefix + "/clock")
self.logger.info("Engine API routes successfully set!")
# Static resources
@app.route('/trackservice', methods=['GET'])
def trackservice():
content = open(os.path.join("contrib/aura-player/public/", "index.html"))
return Response(content, mimetype="text/html")
# Static resources
@app.route('/clock', methods=['GET'])
def clock():
content = open(os.path.join("web/", "clock.html"))
return Response(content, mimetype="text/html")
def generate_html(self):
"""
Generates HTML based on the configuration options and templates.
"""
template = open("web/templates/clock.html", "r")
html = open("web/clock.html", "w")
config_options = {
"CONFIG-STATION-NAME": config.station_name,
"CONFIG-STATION-LOGO-URL": config.station_logo_url,
"CONFIG-STATION-LOGO-SIZE": config.station_logo_size,
"CONFIG-API-URL": config.exposed_api_url
}
content = template.read()
for key, value in config_options.items():
content = content.replace(":::"+key+":::", value)
html.write(content)
template.close()
html.close()
def run(self):
"""
Starts the API server.
"""
# TODO Extend to provide production-grade app server
# Set debug=False if you want to use your native IDE debugger
self.api.app.run(port=self.config.api_port, debug=False)
#
# API SPEC
#
spec = APISpec(
title="Swagger API Specification for Aura Engine",
version="1.0.0",
openapi_version="3.0.2",
plugins=[FlaskPlugin(), MarshmallowPlugin()],
)
#
# API SCHEMA
#
class TrackServiceSchema(ma.Schema):
class Meta:
fields = (
"id",
"schedule.schedule_id",
"schedule.schedule_start",
"schedule.schedule_end",
"schedule.languages",
"schedule.type",
"schedule.category",
"schedule.topic",
"schedule.musicfocus",
"schedule.is_repetition",
"track",
"track_start",
"show"
)
class ClockDataSchema(ma.Schema):
class Meta:
fields = (
"current",
"next",
"track_id",
"track_start",
"track"
)
class ScheduleSchema(ma.Schema):
class Meta:
fields = (
"id",
"schedule_id",
"schedule_start",
"schedule",
"show_id",
"show_name",
"show_hosts",
"show_type"
)
class ReportSchema(ma.Schema):
class Meta:
fields = (
"id",
"schedule.schedule_id",
"schedule.schedule_start",
"schedule.schedule_end",
"schedule.languages",
"schedule.type",
"schedule.category",
"schedule.topic",
"schedule.musicfocus",
"schedule.is_repetition",
"schedule.show_id",
"schedule.show_name",
"schedule.show_hosts",
"schedule.show_type",
"schedule.show_funding_category",
"track",
"track_start",
"playlist_id",
"fallback_type",
"schedule_fallback_id",
"show_fallback_id",
"station_fallback_id"
)
#
# API RESOURCES
#
class TrackServiceResource(Resource):
logger = None
def __init__(self):
self.logger = logging.getLogger("engine-api")
def get(self):
today = date.today()
today = datetime(today.year, today.month, today.day)
tracks = TrackService.select_by_day(today)
return EngineApi.trackservice_schema.dump(tracks)
class TrackResource(Resource):
logger = None
def __init__(self):
self.logger = logging.getLogger("engine-api")
def get(self, track_id):
track = TrackService.select_one(track_id)
return EngineApi.track_schema.dump(track)
class ClockDataResource(Resource):
logger = None
def __init__(self):
self.logger = logging.getLogger("engine-api")
def get(self):
item = TrackService.select_current()
next_schedule = Schedule.select_upcoming(1)
if next_schedule:
next_schedule = next_schedule[0].as_dict()
next_schedule["playlist"] = None
else:
next_schedule = {}
clockdata = {
"track_id": item.id,
"track_start": item.track_start,
"track": item.track,
"current": {},
"next": next_schedule
}
if item.schedule:
clockdata["current"] = item.schedule.as_dict()
if item.schedule.playlist:
clockdata["current"]["playlist"] = item.schedule.playlist[0].as_dict()
clockdata["current"]["show"] = item.show
return EngineApi.clockdata_schema.dump(clockdata)
class CurrentTrackResource(Resource):
logger = None
def __init__(self):
self.logger = logging.getLogger("engine-api")
def get(self):
track = TrackService.select_current()
if not track:
return abort(204) # No content available
return EngineApi.track_schema.dump(track)
class TracksByDayResource(Resource):
logger = None
def __init__(self):
self.logger = logging.getLogger("engine-api")
def get(self, date_string):
date = datetime.strptime(date_string, "%Y-%m-%d")
self.logger.debug("Query track-service by day: %s" % str(date))
tracks = TrackService.select_by_day(date)
if not tracks:
return abort(204) # No content available
return EngineApi.trackservice_schema.dump(tracks)
class UpcomingSchedulesResource(Resource):
logger = None
def __init__(self):
self.logger = logging.getLogger("engine-api")
def get(self):
now = datetime.now()
self.logger.debug("Query upcoming schedules after %s" % str(now))
schedules = Schedule.select_upcoming(3)
if not schedules:
return abort(204) # No content available
return EngineApi.schedule_schema.dump(schedules)
class ReportResource(Resource):
logger = None
def __init__(self):
self.logger = logging.getLogger("engine-api")
def get(self, year_month):
year = int(year_month.split("-")[0])
month = int(year_month.split("-")[1])
first_day = datetime(year, month, 1)
next_month = first_day.replace(day=28) + timedelta(days=4)
next_month - timedelta(days=next_month.day)
self.logger.debug("Query report for month: %s - %s" % (str(first_day), str(next_month)))
report = TrackService.select_by_range(first_day, next_month)
if not report:
return abort(204) # No content available
return EngineApi.report_schema.dump(report)
# Initialization calls
if __name__ == "__main__":
engine_api = EngineApi(config, api)
engine_api.generate_html()
engine_api.run()
\ No newline at end of file
#!/usr/bin/python3.7
#
# Aura Engine
#
# Copyright (C) 2017-2020
# David Trattnig <david.trattnig@subsquare.at>
# Gottfried Gaisbauer <gottfried.gaisbauer@servus.at>
# 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 unittest
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
# from sqlalchemy.ext.declarative import declarative_base
from libraries.base.logger import AuraLogger
from libraries.base.config import AuraConfig
# from modules.monitoring.diskspace_watcher import DiskSpaceWatcher
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)
# Base = declarative_base()
class Aura:
"""
Aura Class
The core of Aura Engine.
"""
logger = None
config = None
server = None
messenger = None
controller = None
scheduler = None
def __init__(self):
"""
Initializes the Engine Core.
"""
self.config = config
AuraLogger(self.config)
self.logger = logging.getLogger("AuraEngine")
def startup(self):
"""
Starts the Engine Core.
"""
from modules.scheduling.scheduler import AuraScheduler
from modules.communication.liquidsoap.communicator import LiquidSoapCommunicator
from modules.communication.redis.adapter import ServerRedisAdapter
# Check if the database has to be re-created
if self.config.get("recreate_db") is not None:
AuraScheduler(self.config)
# Create scheduler and Liquidsoap communicator
self.liquidsoapcommunicator = LiquidSoapCommunicator(self.config)
self.scheduler = AuraScheduler(self.config)
# Give both a reference of each other
self.liquidsoapcommunicator.scheduler = self.scheduler
self.scheduler.liquidsoapcommunicator = self.liquidsoapcommunicator
# Create the Redis adapter
self.messenger = ServerRedisAdapter(self.config)
self.messenger.scheduler = self.scheduler
self.messenger.liquidsoapcommunicator = self.liquidsoapcommunicator
# TODO Check if it's working / needed.
#self.diskspace_watcher = DiskSpaceWatcher(self.config, self.logger, self.liquidsoapcommunicator)
#self.diskspace_watcher.start()
# And finally wait for redis message / start listener thread
self.messenger.start()
#
# START THE ENGINE
#
if __name__ == "__main__":
aura = Aura()
if len(sys.argv) >= 2:
if "--use-test-data" in sys.argv:
aura.config.set("use_test_data", True)
if "--recreate-database" in sys.argv:
aura.config.set("recreate_db", True)
aura.startup()
File added
###################
# engine Settings #
###################
[database]
db_user="aura_engine"
db_name="aura_engine"
db_pass="***change-me***"
db_host="localhost"
[monitoring]
# how often should i check the diskspace. defaults to 600s = 10m
diskspace_check_interval=20
# under which value should i start sending admin mails. possible values k, M, G, T or no metric prefix. defaults to 2G
diskspace_warning_value=1G
# under which value should i stop recording. defaults to 200M
diskspace_critical_value=100M
[mail]
mail_server=""
mail_server_port=""
mail_user=""
mail_pass=""
# if you want to send multiple adminmails, make them space separated
admin_mail="david@subsquare.at gogo@servus.at"
# with from mailadress should be used
from_mail="monitor@aura.py"
# The beginning of the subject. With that you can easily apply filter rules with any mail client
mailsubject_prefix="[AURA Engine]"
[dataurls]
# The URL to get the Calendar via PV/Steering
calendarurl="http://localhost:8000/api/v1/playout"
# The URL to get show details via PV/Steering
api_show_url="http://localhost:8000/api/v1/shows/${ID}/"
# The URL to get playlist details via Tank
importerurl="http://localhost:8040/api/v1/shows/${SLUG}/playlists"
# how often should the calendar be fetched in seconds (This determines the time of the last change before a specific show)
fetching_frequency=3600
# sets the time how long we have to fade in and out, when we select another mixer input
# values are in seconds
# this is solved on engine level because it is kind of tough with liquidsoap
[fading]
fade_in_time="0.5"
fade_out_time="2.5"
#######################
# LiquidSoap Settings #
#######################
# all these settings from here to the bottom require a restart of the liquidsoap server
[user]
# the user and group under which this software will run
daemongroup="david"
daemonuser="david"
[socket]
socketdir="/home/david/code/aura/aura-engine/modules/liquidsoap"
[logging]
logdir="/var/log/aura"
# possible values: debug, info, warning, error, critical
loglevel="info"
# track_sensitive => fallback_folder track sensitivity
# max_blank => maximum time of blank from source (float)
# min_noise => minimum duration of noise on source to switch back over (float)
# threshold => power in dB under which the stream is considered silent (float)
fallback_max_blank="10."
fallback_min_noise="1."
fallback_threshold="-60."
[soundcard]
# choose your weapon
# if you are starving for pain in the ass choose alsa
# if you don't care about latency choose pulseaudio
# if you want low latency and a bit of experimenting, choose jack
soundsystem="alsa"
# you can define up to 5 inputs and outputs
# it is tested with
# - ALSA with ONE input and ONE output
# - pulseaudio with ONE input and ONE output (should work with multiple ins/outs)
# - jack with multiple inputs and outputs
#
# boundaries:
# - if you use jack, you have to kill liquidsoap. somehow liquidsoap cannot disconnect from jackd when shutting down
#
# with alsa you have to write the devicenames like hw:0
# with pulse and jack => an non empty value means it is used
# devices with empty string are ignored and not used
input_device_0="hw:0"
input_device_1=""
input_device_2=""
input_device_3=""
input_device_4=""
# same same, but different
output_device_0="default"
output_device_1=""
output_device_2=""
output_device_3=""
output_device_4=""
# if you are using alsa, you most probably have to tweak these values
# out of the box you will hear alot of cracklings and artifacts
# alsa_buffer => int
alsa_buffer="8192"
# alsa_buffer_length => int
alsa_buffer_length="25"
# alsa_periods => int
alsa_periods="0"
# frame_duration => double
frame_duration=""
# frame_size => int
frame_size=""
#####################
# Recorder Settings #
#####################
# you can define up to 5 recorder types.
# aac, flac, mp3, ogg, opus and wav is supported
[recording]
# flac example
# enable this recorder. everything else than 'y' is considered as disabled
rec_0="n"
# first set a folder
rec_0_folder="/var/audio/rec/flac"
# after how many minutes the recording will be cut
rec_0_duration="30"
# file (or encoding-) type
rec_0_encoding="flac"
# bitrate (with encoding types without bitrate like flac or wav it is substituted. 32 => very poor quality. 320 => very high quality)
rec_0_bitrate="128"
# channels: everything else than 2 is considered as mono
rec_0_channels="2"
# aac example
rec_1="n"
rec_1_folder="/var/audio/rec/aac"
rec_1_duration="30"
rec_1_encoding="aac"
rec_1_bitrate="64"
rec_1_channels="2"
# mp3 example
rec_2="n"
rec_2_folder="/var/audio/rec/mp3"
rec_2_duration="30"
rec_2_encoding="mp3"
rec_2_bitrate="32"
rec_2_channels="2"
# ogg example
rec_3="n"
rec_3_folder="/var/audio/rec/ogg"
rec_3_duration="30"
rec_3_encoding="ogg"
rec_3_bitrate="320"
rec_3_channels="2"
# opus example
rec_4="n"
rec_4_folder="/var/audio/rec/opus"
rec_4_duration="30"
rec_4_encoding="opus"
rec_4_bitrate="32"
rec_4_channels="2"
# wav example
#rec_4="n"
#rec_4_folder="/var/audio/rec/wav"
#rec_4_duration="30"
#rec_4_filetype="wav"
#rec_4_bitrate="320"
#rec_4_channels="2"
###################
# Stream Settings #
###################
# You can define up to outgoing 5 streams
# aac, flac, mp3, ogg and opus is supported
[stream]
# defines enabled or not
stream_0="y"
# possible values: aac, flac, mp3, ogg, opus (depending on what liquidsoap-plugins you installed)
stream_0_encoding="aac"
# bitrate (with encoding types without bitrate like flac or ogg it is substituted. 32 => very poor quality. 320 => very high quality)
stream_0_bitrate="128"
# how many channels? everything else than 2 is considered as mono
stream_0_channels="2"
# to where we are streaming..?
stream_0_host="localhost"
# and which port?
stream_0_port="8888"
# the name of the mountpoint
stream_0_mountpoint="aura-test-0.aac"
# username
stream_0_user="source"
# and the password
stream_0_password="hack-me"
# stream url
stream_0_url="http://www.fro.at"
# the name of the stream
stream_0_name="AURA Test Stream 0"
# the genre of the stream
stream_0_genre="mixed"
# description of the stream
stream_0_description="Test Stream 0"
stream_1="y"
stream_1_encoding="flac"
stream_1_bitrate="128"
stream_1_channels="2"
stream_1_host="localhost"
stream_1_port="8888"
stream_1_mountpoint="aura-test-1.flac"
stream_1_user="source"
stream_1_password="hack-me"
stream_1_url="http://www.fro.at"
stream_1_name="AURA Test Stream 1"
stream_1_genre="mixed"
stream_1_description="Test Stream 1"
stream_2="y"
stream_2_encoding="mp3"
stream_2_bitrate="64"
stream_2_channels="2"
stream_2_host="localhost"
stream_2_port="8888"
stream_2_mountpoint="aura-test-2.mp3"
stream_2_user="source"
stream_2_password="hack-me"
stream_2_url="http://www.fro.at"
stream_2_name="AURA Test Stream 2"
stream_2_genre="mixed"
stream_2_description="Test Stream 2"
stream_3="y"
stream_3_encoding="ogg"
stream_3_bitrate="64"
stream_3_channels="2"
stream_3_host="localhost"
stream_3_port="8888"
stream_3_mountpoint="aura-test-3.ogg"
stream_3_user="source"
stream_3_password="hack-me"
stream_3_url="http://www.fro.at"
stream_3_name="AURA Test Stream 3"
stream_3_genre="mixed"
stream_3_description="Test Stream 3"
stream_4="y"
stream_4_encoding="opus"
stream_4_bitrate="64"
stream_4_channels="2"
stream_4_host="localhost"
stream_4_port="8888"
stream_4_mountpoint="aura-test-4.opus"
stream_4_user="source"
stream_4_password="hack-me"
stream_4_url="http://www.fro.at"
stream_4_name="AURA Test Stream 4"
stream_4_genre="mixed"
stream_4_description="Test Stream 4"
<Config>
<Jobs multiple="true">
<job>
<time>00:00</time>
<until>23:00</until>
<job>play_playlist</job>
<params>no_stop</params>
</job>
<job>
<job>start_recording</job>
<until>00:00</until>
<day>all</day>
<time>00:00</time>
<params>no_stop</params>
</job>
<job>
<daysolder>4</daysolder>
<job>clean_cached</job>
<day>1</day>
<time>00:03</time>
<params></params>
</job>
<job>
<time>01:00</time>
<day>all</day>
<job>precache</job>
<params></params>
</job>
</Jobs>
</Config>
/node_modules/
/public/build/
.DS_Store
*Looking for a shareable component template? Go here --> [sveltejs/component-template](https://github.com/sveltejs/component-template)*
---
# svelte app
This is a project template for [Svelte](https://svelte.dev) apps. It lives at https://github.com/sveltejs/template.
To create a new project based on this template using [degit](https://github.com/Rich-Harris/degit):
```bash
npx degit sveltejs/template svelte-app
cd svelte-app
```
*Note that you will need to have [Node.js](https://nodejs.org) installed.*
## Get started
Install the dependencies...
```bash
cd svelte-app
npm install
```
...then start [Rollup](https://rollupjs.org):
```bash
npm run dev
```
Navigate to [localhost:5000](http://localhost:5000). You should see your app running. Edit a component file in `src`, save it, and reload the page to see your changes.
By default, the server will only respond to requests from localhost. To allow connections from other computers, edit the `sirv` commands in package.json to include the option `--host 0.0.0.0`.
## Building and running in production mode
To create an optimised version of the app:
```bash
npm run build
```
You can run the newly built app with `npm run start`. This uses [sirv](https://github.com/lukeed/sirv), which is included in your package.json's `dependencies` so that the app will work when you deploy to platforms like [Heroku](https://heroku.com).
## Single-page app mode
By default, sirv will only respond to requests that match files in `public`. This is to maximise compatibility with static fileservers, allowing you to deploy your app anywhere.
If you're building a single-page app (SPA) with multiple routes, sirv needs to be able to respond to requests for *any* path. You can make it so by editing the `"start"` command in package.json:
```js
"start": "sirv public --single"
```
## Deploying to the web
### With [now](https://zeit.co/now)
Install `now` if you haven't already:
```bash
npm install -g now
```
Then, from within your project folder:
```bash
cd public
now deploy --name my-project
```
As an alternative, use the [Now desktop client](https://zeit.co/download) and simply drag the unzipped project folder to the taskbar icon.
### With [surge](https://surge.sh/)
Install `surge` if you haven't already:
```bash
npm install -g surge
```
Then, from within your project folder:
```bash
npm run build
surge public my-project.surge.sh
```
This diff is collapsed.
{
"name": "aura-engine-clock",
"version": "1.0.0",
"author": {
"name": "David Trattnig",
"email": "david.trattnig@subsquare.at",
"url": "https://subsquare.at"
},
"license" : "AGPL-3.0-only",
"homepage": "https://gitlab.servus.at/aura/meta",
"repository": {
"type": "git",
"url": "git@gitlab.servus.at:aura/engine.git"
},
"scripts": {
"build": "rollup -c",
"dev": "rollup -c -w",
"start": "sirv public"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^11.0.0",
"@rollup/plugin-node-resolve": "^7.0.0",
"rollup": "^1.20.0",
"rollup-plugin-livereload": "^1.0.0",
"rollup-plugin-svelte": "^5.0.3",
"rollup-plugin-terser": "^5.1.2",
"svelte": "^3.0.0"
},
"dependencies": {
"sirv-cli": "^0.4.4"
}
}
contrib/aura-clock/public/favicon.png

18.8 KiB

html, body {
position: relative;
width: 100%;
height: 100%;
}
body {
color: #333;
margin: 0;
padding: 8px;
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
}
a {
color: rgb(0,100,200);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
a:visited {
color: rgb(0,80,160);
}
label {
display: block;
}
input, button, select, textarea {
font-family: inherit;
font-size: inherit;
padding: 0.4em;
margin: 0 0 0.5em 0;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 2px;
}
input:disabled {
color: #ccc;
}
input[type="range"] {
height: 0;
}
button {
color: #333;
background-color: #f4f4f4;
outline: none;
}
button:disabled {
color: #999;
}
button:not(:disabled):active {
background-color: #ddd;
}
button:focus {
border-color: #666;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<title>Aura Engine - Studio Clock</title>
<link rel='icon' type='image/png' href='/favicon.png'>
<link rel='stylesheet' href='/global.css'>
<link rel='stylesheet' href='/build/aura-clock-bundle.css'>
<script defer src='/build/aura-clock-bundle.js'></script>
</head>
<body style="background-color: black;">
<aura-clock
name="Studio Clock"
logo="https://o94.at/themes/custom/radio_orange/logo1.png"
logosize="180px"
api="http://localhost:3333/api/v1/" />
</body>
</html>
import svelte from 'rollup-plugin-svelte';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import livereload from 'rollup-plugin-livereload';
import { terser } from 'rollup-plugin-terser';
const production = !process.env.ROLLUP_WATCH;
export default {
input: 'src/main.js',
output: {
sourcemap: true,
format: 'iife',
name: 'app',
file: 'public/build/aura-clock-bundle.js'
},
plugins: [
svelte({
// enable run-time checks when not in production
dev: !production,
// we'll extract any component CSS out into
// a separate file - better for performance
css: css => {
css.write('public/build/aura-clock-bundle.css');
},
customElement: true
}),
// If you have external dependencies installed from
// npm, you'll most likely need these plugins. In
// some cases you'll need additional configuration -
// consult the documentation for details:
// https://github.com/rollup/plugins/tree/master/packages/commonjs
resolve({
browser: true,
dedupe: ['svelte']
}),
commonjs(),
// In dev mode, call `npm run start` once
// the bundle has been generated
!production && serve(),
// Watch the `public` directory and refresh the
// browser on changes when not in production
!production && livereload('public'),
// If we're building for production (npm run build
// instead of npm run dev), minify
production && terser()
],
watch: {
clearScreen: false
}
};
function serve() {
let started = false;
return {
writeBundle() {
if (!started) {
started = true;
require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
stdio: ['ignore', 'inherit', 'inherit'],
shell: true
});
}
}
};
}