Merge pull request #6 from LibreTime/master

update
This commit is contained in:
Codenift 2020-03-16 00:53:12 -04:00 committed by GitHub
commit ad73c9bb4c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 2398 additions and 51 deletions

View File

@ -297,17 +297,23 @@ class ApiController extends Zend_Controller_Action
$stationUrl = Application_Common_HTTPHelper::getStationUrl(); $stationUrl = Application_Common_HTTPHelper::getStationUrl();
$previousID = $result["previous"]["metadata"]["id"]; if ($result["previous"]["type"] != "livestream") {
$get_prev_artwork_url = $stationUrl . 'api/track?id='. $previousID .'&return=artwork'; $previousID = $result["previous"]["metadata"]["id"];
$result["previous"]["metadata"]["artwork_url"] = $get_prev_artwork_url; $get_prev_artwork_url = $stationUrl . 'api/track?id='. $previousID .'&return=artwork';
$result["previous"]["metadata"]["artwork_url"] = $get_prev_artwork_url;
}
$currID = $result["current"]["metadata"]["id"]; if ($result["current"]["type"] != "livestream") {
$get_curr_artwork_url = $stationUrl . 'api/track?id='. $currID .'&return=artwork'; $currID = $result["current"]["metadata"]["id"];
$result["current"]["metadata"]["artwork_url"] = $get_curr_artwork_url; $get_curr_artwork_url = $stationUrl . 'api/track?id='. $currID .'&return=artwork';
$result["current"]["metadata"]["artwork_url"] = $get_curr_artwork_url;
}
$nextID = $result["previous"]["metadata"]["id"]; if ($result["next"]["type"] != "livestream") {
$get_next_artwork_url = $stationUrl . 'api/track?id='. $nextID .'&return=artwork'; $nextID = $result["next"]["metadata"]["id"];
$result["previous"]["metadata"]["artwork_url"] = $get_next_artwork_url; $get_next_artwork_url = $stationUrl . 'api/track?id='. $nextID .'&return=artwork';
$result["next"]["metadata"]["artwork_url"] = $get_next_artwork_url;
}
// apply user-defined timezone, or default to station // apply user-defined timezone, or default to station
Application_Common_DateHelper::convertTimestampsToTimezone( Application_Common_DateHelper::convertTimestampsToTimezone(

View File

@ -302,8 +302,10 @@ class ScheduleController extends Zend_Controller_Action
$range["previous"]["ends"] = Application_Common_DateHelper::UTCStringToUserTimezoneString($range["previous"]["ends"]); $range["previous"]["ends"] = Application_Common_DateHelper::UTCStringToUserTimezoneString($range["previous"]["ends"]);
} }
if (isset($range["current"])) { if (isset($range["current"])) {
$get_artwork = FileDataHelper::getArtworkData($range["current"]["metadata"]["artwork"], 256); if (isset($range["current"]["metadata"])) {
$range["current"]["metadata"]["artwork_data"] = $get_artwork; $get_artwork = FileDataHelper::getArtworkData($range["current"]["metadata"]["artwork"], 256);
$range["current"]["metadata"]["artwork_data"] = $get_artwork;
}
$range["current"]["starts"] = Application_Common_DateHelper::UTCStringToUserTimezoneString($range["current"]["starts"]); $range["current"]["starts"] = Application_Common_DateHelper::UTCStringToUserTimezoneString($range["current"]["starts"]);
$range["current"]["ends"] = Application_Common_DateHelper::UTCStringToUserTimezoneString($range["current"]["ends"]); $range["current"]["ends"] = Application_Common_DateHelper::UTCStringToUserTimezoneString($range["current"]["ends"]);
} }

View File

@ -32,7 +32,7 @@ INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s1_host', '1
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s1_port', '8000', 'integer'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s1_port', '8000', 'integer');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s1_user', '', 'string'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s1_user', '', 'string');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s1_pass', 'hackme', 'string'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s1_pass', 'hackme', 'string');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s1_admin_user', '', 'string'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s1_admin_user', 'admin', 'string');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s1_admin_pass', '', 'string'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s1_admin_pass', '', 'string');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s1_mount', 'airtime_128', 'string'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s1_mount', 'airtime_128', 'string');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s1_url', 'https://libretime.org', 'string'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s1_url', 'https://libretime.org', 'string');
@ -47,7 +47,7 @@ INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s2_host', ''
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s2_port', '', 'integer'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s2_port', '', 'integer');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s2_user', '', 'string'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s2_user', '', 'string');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s2_pass', '', 'string'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s2_pass', '', 'string');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s2_admin_user', '', 'string'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s2_admin_user', 'admin', 'string');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s2_admin_pass', '', 'string'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s2_admin_pass', '', 'string');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s2_mount', '', 'string'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s2_mount', '', 'string');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s2_url', '', 'string'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s2_url', '', 'string');
@ -62,7 +62,7 @@ INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s3_host', ''
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s3_port', '', 'integer'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s3_port', '', 'integer');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s3_user', '', 'string'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s3_user', '', 'string');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s3_pass', '', 'string'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s3_pass', '', 'string');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s3_admin_user', '', 'string'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s3_admin_user', 'admin', 'string');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s3_admin_pass', '', 'string'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s3_admin_pass', '', 'string');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s3_mount', '', 'string'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s3_mount', '', 'string');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s3_url', '', 'string'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s3_url', '', 'string');
@ -370,7 +370,7 @@ INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_host', ''
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_port', '', 'integer'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_port', '', 'integer');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_user', '', 'string'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_user', '', 'string');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_pass', '', 'string'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_pass', '', 'string');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_admin_user', '', 'string'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_admin_user', 'admin', 'string');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_admin_pass', '', 'string'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_admin_pass', '', 'string');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_mount', '', 'string'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_mount', '', 'string');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_url', '', 'string'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_url', '', 'string');

View File

@ -130,8 +130,7 @@ function updatePlaybar(){
$('#current').html("<span style='color:red; font-weight:bold'>"+$.i18n._("Recording:")+"</span>"+currentSong.name+","); $('#current').html("<span style='color:red; font-weight:bold'>"+$.i18n._("Recording:")+"</span>"+currentSong.name+",");
} else { } else {
$('#current').text(currentSong.name+","); $('#current').text(currentSong.name+",");
if (currentSong.metadata && currentSong.metadata.artwork_data) {
if (currentSong.metadata.artwork_data) {
var check_current_song = Cookies.get('current_track'); var check_current_song = Cookies.get('current_track');
var loaded = Cookies.get('loaded'); var loaded = Cookies.get('loaded');

View File

@ -79,6 +79,7 @@ class DatabaseSetup extends Setup {
$this->setNewDatabaseConnection(self::$_properties["dbname"]); $this->setNewDatabaseConnection(self::$_properties["dbname"]);
$this->checkSchemaExists(); $this->checkSchemaExists();
$this->createDatabaseTables(); $this->createDatabaseTables();
$this->updateIcecastPassword();
} }
/** /**
@ -175,5 +176,82 @@ class DatabaseSetup extends Setup {
array(self::DB_NAME,)); array(self::DB_NAME,));
} }
} }
/**
* Updates the icecast password in the database based upon the temp file created during install
* @throws AirtimeDatabaseException
*/
private function updateIcecastPassword() {
if (!file_exists(LIBRETIME_CONF_DIR . '/icecast_pass')) {
throw new AirtimeDatabaseException("The Icecast Password file was not accessible", array());
};
$icecast_pass_txt = file(LIBRETIME_CONF_DIR . '/icecast_pass');
$icecast_pass = $icecast_pass_txt[0];
$icecast_pass = str_replace(PHP_EOL, '', $icecast_pass);
$statement = self::$dbh->prepare("UPDATE cc_stream_setting SET value = :icecastpass WHERE keyname = 's1_pass'");
$statement->bindValue(':icecastpass', $icecast_pass, PDO::PARAM_STR);
try {
$statement->execute();
}
catch (PDOException $ex) {
print "Error!: " . $ex->getMessage() . "<br />";
}
$statement = self::$dbh->prepare("UPDATE cc_stream_setting SET value = :icecastpass WHERE keyname = 's1_admin_pass'");
$statement->bindValue(':icecastpass', $icecast_pass, PDO::PARAM_STR);
try {
$statement->execute();
}
catch (PDOException $ex) {
print "Error!: " . $ex->getMessage() . "<br />";
}
$statement = self::$dbh->prepare("UPDATE cc_stream_setting SET value = :icecastpass WHERE keyname = 's2_pass'");
$statement->bindValue(':icecastpass', $icecast_pass, PDO::PARAM_STR);
try {
$statement->execute();
}
catch (PDOException $ex) {
print "Error!: " . $ex->getMessage() . "<br />";
}
$statement = self::$dbh->prepare("UPDATE cc_stream_setting SET value = :icecastpass WHERE keyname = 's2_admin_pass'");
$statement->bindValue(':icecastpass', $icecast_pass, PDO::PARAM_STR);
try {
$statement->execute();
}
catch (PDOException $ex) {
print "Error!: " . $ex->getMessage() . "<br />";
}
$statement = self::$dbh->prepare("UPDATE cc_stream_setting SET value = :icecastpass WHERE keyname = 's3_pass'");
$statement->bindValue(':icecastpass', $icecast_pass, PDO::PARAM_STR);
try {
$statement->execute();
}
catch (PDOException $ex) {
print "Error!: " . $ex->getMessage() . "<br />";
}
$statement = self::$dbh->prepare("UPDATE cc_stream_setting SET value = :icecastpass WHERE keyname = 's3_admin_pass'");
$statement->bindValue(':icecastpass', $icecast_pass, PDO::PARAM_STR);
try {
$statement->execute();
}
catch (PDOException $ex) {
print "Error!: " . $ex->getMessage() . "<br />";
}
$statement = self::$dbh->prepare("UPDATE cc_stream_setting SET value = :icecastpass WHERE keyname = 's1_admin_pass'");
$statement->bindValue(':icecastpass', $icecast_pass, PDO::PARAM_STR);
try {
$statement->execute();
}
catch (PDOException $ex) {
print "Error!: " . $ex->getMessage() . "<br />";
}
$statement = self::$dbh->prepare("INSERT INTO cc_pref (keystr, valstr) VALUES ('default_icecast_password', :icecastpass )");
$statement->bindValue(':icecastpass', $icecast_pass, PDO::PARAM_STR);
try {
$statement->execute();
}
catch (PDOException $ex) {
print "Error!: " . $ex->getMessage() . "<br />";
}
}
} }

1343
docs/api/openapi.yaml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -45,5 +45,5 @@ Plans are in the works for `.rpm` packages, as well as Docker and AWS images.
Please note that the install script does not take care to ensure that any Please note that the install script does not take care to ensure that any
packages installed are set up in a secure manner. Please see the chapter on packages installed are set up in a secure manner. Please see the chapter on
[preparing the server](manual/preparing-the-server.md) for more details on [preparing the server](manual/preparing-the-server) for more details on
how to set up a secure installation. how to set up a secure installation.

View File

@ -50,7 +50,9 @@ wish. (There is more about this feature in the
*Advanced Configuration* section of this book). *Advanced Configuration* section of this book).
The **Allowed CORS URLs** is intended to deal with situations where you want a The **Allowed CORS URLs** is intended to deal with situations where you want a
remote site with a different domain to access the API. remote site with a different domain to access the API. This is relevant when
there is a reverse proxy server in front of LibreTime. If you are using a
reverse proxy, the URLs that will be used to access it should be added here.
The **Display login button on your Radio Page?** will determine whether visitors The **Display login button on your Radio Page?** will determine whether visitors
to your site see a link to login. If this is disabled DJs and admins will need to your site see a link to login. If this is disabled DJs and admins will need

View File

@ -94,3 +94,15 @@ your LibreTime server has made to this Icecast server. If you have only just
installed LibreTime, there may not be any media playing out yet. installed LibreTime, there may not be any media playing out yet.
![](static/Screenshot293-Icecast_status_page.png) ![](static/Screenshot293-Icecast_status_page.png)
Reverse Proxy Connections
-------------------------
In some deployments, the LibreTime server is deployed behind a reverse proxy,
for example in containerization use-cases such as Docker and LXC. LibreTime
makes extensive use of its API for some site functionality, which causes
[Cross-Origin Resource Sharing (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)
to occur. By default, CORS requests are blocked by your browser and the origins
need to be added to the **Allowed CORS URLs** block in
[**General Settings**](/manual/general/). These origins should include any
domains that will be used externally to connect to your reverse proxy that you
want handled by LibreTime.

25
install
View File

@ -633,6 +633,15 @@ case "${dist}-${code}" in
is_debian_stretch=true is_debian_stretch=true
;; ;;
#End of fix #End of fix
#Fix for Raspbian 10 (buster)
raspbian-10|10)
code="buster"
dist="debian"
is_debian_dist=true
is_debian_buster=true
;;
#End of fix
debian-8|debian-jessie) debian-8|debian-jessie)
echo -e "ERROR: Debian Jessie is archived and does not receive any security or other updates since 2018-05-17." >&2 echo -e "ERROR: Debian Jessie is archived and does not receive any security or other updates since 2018-05-17." >&2
echo -e "The LibreTime installer dropped support for installing LibreTime on Jessie in 3.0.0-alpha.8." >&2 echo -e "The LibreTime installer dropped support for installing LibreTime on Jessie in 3.0.0-alpha.8." >&2
@ -891,8 +900,19 @@ if [ "$icecast" = "t" ]; then
icecast_unit_name="icecast2" icecast_unit_name="icecast2"
if [ "$dist" != "centos" ]; then if [ "$dist" != "centos" ]; then
sed -i 's/ENABLE=false/ENABLE=true/g' /etc/default/icecast2 sed -i 's/ENABLE=false/ENABLE=true/g' /etc/default/icecast2
icecast_config="/etc/icecast2/icecast.xml"
else else
icecast_unit_name="icecast" icecast_unit_name="icecast"
icecast_config="/etc/icecast.xml"
fi
# only update icecast password if
if [ ! -f "/etc/airtime/airtime.conf" ] && [ !-f "/etc/airtime/airtime.conf.tmp" ]; then
icecast_pass=$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c${1:-12};)
echo $icecast_pass > /tmp/icecast_pass
loud "\n New install detected setting icecast password to random value."
xmlstarlet ed --inplace -u /icecast/authentication/source-password -v $icecast_pass $icecast_config
xmlstarlet ed --inplace -u /icecast/authentication/relay-password -v $icecast_pass $icecast_config
xmlstarlet ed --inplace -u /icecast/authentication/admin-password -v $icecast_pass $icecast_config
fi fi
# restart in case icecast was already started (like is the case on debian) # restart in case icecast was already started (like is the case on debian)
systemInitCommand restart ${icecast_unit_name} systemInitCommand restart ${icecast_unit_name}
@ -905,6 +925,7 @@ loud "-----------------------------------------------------"
verbose "\n * Installing necessary python services..." verbose "\n * Installing necessary python services..."
loudCmd "pip install setuptools --upgrade" loudCmd "pip install setuptools --upgrade"
loudCmd "pip install zipp==1.0.0"
verbose "...Done" verbose "...Done"
# Ubuntu Trusty and Debian Wheezy needs a workaround for python version SSL downloads # Ubuntu Trusty and Debian Wheezy needs a workaround for python version SSL downloads
@ -1100,6 +1121,10 @@ if [ ! -d "/etc/airtime" ]; then
mkdir /etc/airtime mkdir /etc/airtime
fi fi
if [ ! -f "/etc/airtime/airtime.conf" ] && [ !-f "/etc/airtime/airtime.conf.tmp" ]; then
# need to copy the icecast_pass from temp to /etc/airtime so web-based installer can read it
cp /tmp/icecast_pass /etc/airtime/icecast_pass
fi
chown -R ${web_user}:${web_user} /etc/airtime chown -R ${web_user}:${web_user} /etc/airtime

View File

@ -59,7 +59,7 @@ libfaad2
php-apcu php-apcu
lame lame
silan
coreutils coreutils
liquidsoap liquidsoap
@ -67,3 +67,5 @@ liquidsoap
libopus0 libopus0
systemd-sysv systemd-sysv
xmlstarlet

View File

@ -63,3 +63,5 @@ libopus0
sysvinit sysvinit
sysvinit-utils sysvinit-utils
xmlstarlet

View File

@ -67,3 +67,5 @@ liquidsoap
libopus0 libopus0
systemd-sysv systemd-sysv
xmlstarlet

View File

@ -81,3 +81,5 @@ build-essential
libssl-dev libssl-dev
libffi-dev libffi-dev
python-dev python-dev
xmlstarlet

View File

@ -70,3 +70,5 @@ liquidsoap-plugin-pulseaudio
liquidsoap-plugin-taglib liquidsoap-plugin-taglib
liquidsoap-plugin-voaacenc liquidsoap-plugin-voaacenc
liquidsoap-plugin-vorbis liquidsoap-plugin-vorbis
xmlstarlet

View File

@ -81,3 +81,5 @@ build-essential
libssl-dev libssl-dev
libffi-dev libffi-dev
python-dev python-dev
xmlstarlet

View File

@ -76,3 +76,5 @@ build-essential
libssl-dev libssl-dev
libffi-dev libffi-dev
python-dev python-dev
xmlstarlet

View File

@ -86,7 +86,8 @@ yum install -y \
policycoreutils-python \ policycoreutils-python \
python-celery \ python-celery \
python2-pika \ python2-pika \
lsof lsof \
xmlstarlet
# for pip ssl install # for pip ssl install
yum install -y \ yum install -y \

View File

@ -20,6 +20,10 @@ pages:
- 'Features': features.md - 'Features': features.md
- 'F.A.Q.': faq.md - 'F.A.Q.': faq.md
- 'Rights and Royalties': manual/rights-and-royalties/index.md - 'Rights and Royalties': manual/rights-and-royalties/index.md
- 'Installation':
- 'Install': install.md
- 'Preparing the Server': manual/preparing-the-server/index.md
- 'Setting the Server Time': manual/setting-the-server-time/index.md
- 'Using LibreTime': - 'Using LibreTime':
- 'On Air in 60 seconds!': 'manual/on-air-in-60-seconds/index.md' - 'On Air in 60 seconds!': 'manual/on-air-in-60-seconds/index.md'
- 'Getting Started': manual/getting-started/index.md - 'Getting Started': manual/getting-started/index.md
@ -55,10 +59,6 @@ pages:
- 'Smartphone Journalism': manual/smartphone-journalism/index.md - 'Smartphone Journalism': manual/smartphone-journalism/index.md
- 'Icecast and SHOUTcast': manual/icecast-and-shoutcast/index.md - 'Icecast and SHOUTcast': manual/icecast-and-shoutcast/index.md
- 'Recording Shows': manual/recording-shows/index.md - 'Recording Shows': manual/recording-shows/index.md
- 'Installation':
- 'Install': install.md
- 'Preparing the Server': manual/preparing-the-server/index.md
- 'Setting the Server Time': manual/setting-the-server-time/index.md
- 'Administration': - 'Administration':
- 'Backing Up the Server': manual/backing-up-the-server/index.md - 'Backing Up the Server': manual/backing-up-the-server/index.md
- 'Media Folders': manual/media-folders/index.md - 'Media Folders': manual/media-folders/index.md

View File

@ -28,7 +28,7 @@ setup(name='airtime_analyzer',
packages=['airtime_analyzer'], packages=['airtime_analyzer'],
scripts=['bin/airtime_analyzer'], scripts=['bin/airtime_analyzer'],
install_requires=[ install_requires=[
'mutagen>=1.41.1', # got rid of specific version requirement 'mutagen~=1.43.0', # got rid of specific version requirement
'pika', 'pika',
'daemon', 'daemon',
'file-magic', 'file-magic',

View File

@ -5,6 +5,7 @@ import argparse
import os import os
import generate_liquidsoap_cfg import generate_liquidsoap_cfg
import logging import logging
import subprocess
PYPO_HOME = '/var/tmp/airtime/pypo/' PYPO_HOME = '/var/tmp/airtime/pypo/'
@ -21,11 +22,15 @@ def run():
logging.basicConfig(level=getattr(logging, 'DEBUG', None)) logging.basicConfig(level=getattr(logging, 'DEBUG', None))
generate_liquidsoap_cfg.run() generate_liquidsoap_cfg.run()
script_path = os.path.join(os.path.dirname(__file__), 'ls_script.liq') ''' check liquidsoap version if less than 1.3 use legacy liquidsoap script '''
liquidsoap_version=subprocess.check_output("liquidsoap --version", shell=True)
if "1.1.1" not in liquidsoap_version:
script_path = os.path.join(os.path.dirname(__file__), 'ls_script.liq')
else:
script_path = os.path.join(os.path.dirname(__file__), 'ls_script_legacy.liq')
if args.debug: if args.debug:
os.execl('/usr/bin/liquidsoap', 'airtime-liquidsoap', script_path, '--verbose', '-f', '--debug') os.execl('/usr/bin/liquidsoap', 'airtime-liquidsoap', script_path, '--verbose', '-f', '--debug')
else: else:
os.execl('/usr/bin/liquidsoap', 'airtime-liquidsoap', script_path, '--verbose', '-f') os.execl('/usr/bin/liquidsoap', 'airtime-liquidsoap', script_path, '--verbose', '-f')
run() run()

View File

@ -15,7 +15,9 @@ elif dj_type == '--dj':
response = api_clients.check_live_stream_auth(username, password, source_type) response = api_clients.check_live_stream_auth(username, password, source_type)
if 'msg' in response: if 'msg' in response and response['msg'] == True:
print response['msg'] print response['msg']
sys.exit(0)
else: else:
print False print False
sys.exit(1)

View File

@ -266,7 +266,7 @@ def input.http_restart(~id,~initial_url="http://dummy/url")
source = audio_to_stereo(input.http(buffer=5.,max=15.,id=id,autostart=false,initial_url)) source = audio_to_stereo(input.http(buffer=5.,max=15.,id=id,autostart=false,initial_url))
def stopped() def stopped()
"stopped" == list.hd(server.execute("#{id}.status")) "stopped" == list.hd(server.execute("#{id}.status"), default="")
end end
server.register(namespace=id, server.register(namespace=id,
@ -321,7 +321,7 @@ def cross_http(~debug=true,~http_input_id,source)
cross_d = 3. cross_d = 3.
def crosser(a,b) def crosser(a,b)
url = list.hd(server.execute('#{id}.url')) url = list.hd(server.execute('#{id}.url'), default="")
status = list.hd(server.execute('#{id}.status')) status = list.hd(server.execute('#{id}.status'))
on_m([("source_url",url)]) on_m([("source_url",url)])
if debug then if debug then
@ -374,7 +374,7 @@ def http_fallback(~http_input_id,~http,~default)
end end
def connected() def connected()
status = list.hd(server.execute("#{id}.status")) status = list.hd(server.execute("#{id}.status"), default="")
not(list.mem(status,["polling","stopped"])) not(list.mem(status,["polling","stopped"]))
end end
connected = gracetime(connected) connected = gracetime(connected)

View File

@ -0,0 +1,399 @@
def notify(m)
command = "timeout --signal=KILL 45 pyponotify --media-id=#{m['schedule_table_id']} &"
log(command)
system(command)
end
def notify_queue(m)
f = !dynamic_metadata_callback
ignore(f(m))
notify(m)
end
def notify_stream(m)
json_str = string.replace(pattern="\n",(fun (s) -> ""), json_of(m))
#if a string has a single apostrophe in it, let's comment it out by ending the string before right before it
#escaping the apostrophe, and then starting a new string right after it. This is why we use 3 apostrophes.
json_str = string.replace(pattern="'",(fun (s) -> "'\''"), json_str)
command = "timeout --signal=KILL 45 pyponotify --webstream='#{json_str}' --media-id=#{!current_dyn_id} &"
if !current_dyn_id != "-1" then
log(command)
system(command)
end
end
# A function applied to each metadata chunk
def append_title(m) =
log("Using stream_format #{!stream_metadata_type}")
if list.mem_assoc("mapped", m) then
#protection against applying this function twice. It shouldn't be happening
#and bug file with Liquidsoap.
m
else
if !stream_metadata_type == 1 then
[("title", "#{!show_name} - #{m['artist']} - #{m['title']}"), ("mapped", "true")]
elsif !stream_metadata_type == 2 then
[("title", "#{!station_name} - #{!show_name}"), ("mapped", "true")]
else
if "#{m['artist']}" == "" then
[("title", "#{m['title']}"), ("mapped", "true")]
else
[("title", "#{m['artist']} - #{m['title']}"), ("mapped", "true")]
end
end
end
end
def crossfade_airtime(s)
#duration is automatically overwritten by metadata fields passed in
#with audio
s = fade.in(type="log", duration=0., s)
s = fade.out(type="log", duration=0., s)
fader = fun (a,b) -> add(normalize=false,[b,a])
cross(fader,s)
end
def transition(a,b) =
log("transition called...")
add(normalize=false,
[ sequence([ blank(duration=0.01),
fade.initial(duration=!default_dj_fade, b) ]),
fade.final(duration=!default_dj_fade, a) ])
end
# we need this function for special transition case(from default to queue)
# we don't want the trasition fade to have effect on the first song that would
# be played siwtching out of the default(silent) source
def transition_default(a,b) =
log("transition called...")
if !just_switched then
just_switched := false
add(normalize=false,
[ sequence([ blank(duration=0.01),
fade.initial(duration=!default_dj_fade, b) ]),
fade.final(duration=!default_dj_fade, a) ])
else
just_switched := false
b
end
end
# Define a transition that fades out the
# old source, adds a single, and then
# plays the new source
def to_live(old,new) =
# Fade out old source
old = fade.final(old)
# Compose this in sequence with
# the new source
sequence([old,new])
end
def output_to(output_type, type, bitrate, host, port, pass, mount_point, url, description, genre, user, s, stream, connected, name, channels) =
source = ref s
def on_error(msg)
connected := "false"
command = "timeout --signal=KILL 45 pyponotify --error='#{msg}' --stream-id=#{stream} --time=#{!time} &"
system(command)
log(command)
5.
end
def on_connect()
connected := "true"
command = "timeout --signal=KILL 45 pyponotify --connect --stream-id=#{stream} --time=#{!time} &"
system(command)
log(command)
end
stereo = (channels == "stereo")
if output_type == "icecast" then
user_ref = ref user
if user == "" then
user_ref := "source"
end
output_mono = output.icecast(host = host,
port = port,
password = pass,
mount = mount_point,
fallible = true,
url = url,
description = description,
name = name,
genre = genre,
user = !user_ref,
on_error = on_error,
on_connect = on_connect)
output_stereo = output.icecast(host = host,
port = port,
password = pass,
mount = mount_point,
fallible = true,
url = url,
description = description,
name = name,
genre = genre,
user = !user_ref,
on_error = on_error,
on_connect = on_connect)
if type == "mp3" then
%include "mp3.liq"
end
if type == "ogg" then
%include "ogg.liq"
end
%ifencoder %opus
if type == "opus" then
%include "opus.liq"
end
%endif
%ifencoder %fdkaac
if type == "aac" then
%include "fdkaac.liq"
end
%endif
else
user_ref = ref user
if user == "" then
user_ref := "source"
end
output_mono = output.shoutcast(id = "shoutcast_stream_#{stream}",
host = host,
port = port,
password = pass,
fallible = true,
url = url,
genre = genre,
name = description,
user = !user_ref,
on_error = on_error,
on_connect = on_connect)
output_stereo = output.shoutcast(id = "shoutcast_stream_#{stream}",
host = host,
port = port,
password = pass,
fallible = true,
url = url,
genre = genre,
name = description,
user = !user_ref,
on_error = on_error,
on_connect = on_connect)
if type == "mp3" then
%include "mp3.liq"
end
%ifencoder %fdkaac
if type == "aac" then
%include "fdkaac.liq"
end
%endif
end
end
# Add a skip function to a source
# when it does not have one
# by default
#def add_skip_command(s)
# # A command to skip
# def skip(_)
# # get playing (active) queue and flush it
# l = list.hd(server.execute("queue.secondary_queue"))
# l = string.split(separator=" ",l)
# list.iter(fun (rid) -> ignore(server.execute("queue.remove #{rid}")), l)
#
# l = list.hd(server.execute("queue.primary_queue"))
# l = string.split(separator=" ", l)
# if list.length(l) > 0 then
# source.skip(s)
# "Skipped"
# else
# "Not skipped"
# end
# end
# # Register the command:
# server.register(namespace="source",
# usage="skip",
# description="Skip the current song.",
# "skip",fun(s) -> begin log("source.skip") skip(s) end)
#end
def clear_queue(s)
source.skip(s)
end
def set_dynamic_source_id(id) =
current_dyn_id := id
string_of(!current_dyn_id)
end
def get_dynamic_source_id() =
string_of(!current_dyn_id)
end
#cc-4633
# NOTE
# A few values are hardcoded and may be dependent:
# - the delay in gracetime is linked with the buffer duration of input.http
# (delay should be a bit less than buffer)
# - crossing duration should be less than buffer length
# (at best, a higher duration will be ineffective)
# HTTP input with "restart" command that waits for "stop" to be effected
# before "start" command is issued. Optionally it takes a new URL to play,
# which makes it a convenient replacement for "url".
# In the future, this may become a core feature of the HTTP input.
# TODO If we stop and restart quickly several times in a row,
# the data bursts accumulate and create buffer overflow.
# Flushing the buffer on restart could be a good idea, but
# it would also create an interruptions while the buffer is
# refilling... on the other hand, this would avoid having to
# fade using both cross() and switch().
def input.http_restart(~id,~initial_url="http://dummy/url")
source = audio_to_stereo(input.http(buffer=5.,max=15.,id=id,autostart=false,initial_url))
def stopped()
"stopped" == list.hd(server.execute("#{id}.status"))
end
server.register(namespace=id,
"restart",
usage="restart [url]",
fun (url) -> begin
if url != "" then
log(string_of(server.execute("#{id}.url #{url}")))
end
log(string_of(server.execute("#{id}.stop")))
add_timeout(0.5,
{ if stopped() then
log(string_of(server.execute("#{id}.start"))) ;
(-1.)
else 0.5 end})
"OK"
end)
# Dummy output should be useless if HTTP stream is meant
# to be listened to immediately. Otherwise, apply it.
#
# output.dummy(fallible=true,source)
source
end
# Transitions between URL changes in HTTP streams.
def cross_http(~debug=true,~http_input_id,source)
id = http_input_id
last_url = ref ""
change = ref false
def on_m(m)
notify_stream(m)
changed = m["source_url"] != !last_url
log("URL now #{m['source_url']} (change: #{changed})")
if changed then
if !last_url != "" then change := true end
last_url := m["source_url"]
end
end
# We use both metadata and status to know about the current URL.
# Using only metadata may be more precise is crazy corner cases,
# but it's also asking too much: the metadata may not pass through
# before the crosser is instantiated.
# Using only status in crosser misses some info, eg. on first URL.
source = on_metadata(on_m,source)
cross_d = 3.
def crosser(a,b)
url = list.hd(server.execute('#{id}.url'))
status = list.hd(server.execute('#{id}.status'))
on_m([("source_url",url)])
if debug then
log("New track inside HTTP stream")
log(" status: #{status}")
log(" need to cross: #{!change}")
log(" remaining #{source.remaining(a)} sec before, \
#{source.remaining(b)} sec after")
end
if !change then
change := false
# In principle one should avoid crossing on a live stream
# it'd be okay to do it here (eg. use add instead of sequence)
# because it's only once per URL, but be cautious.
sequence([fade.out(duration=cross_d,a),fade.in(b)])
else
# This is done on tracks inside a single stream.
# Do NOT cross here or you'll gradually empty the buffer!
sequence([a,b])
end
end
# Setting conservative=true would mess with the delayed switch below
cross(duration=cross_d,conservative=false,crosser,source)
end
# Custom fallback between http and default source with fading of
# beginning and end of HTTP stream.
# It does not take potential URL changes into account, as long as
# they do not interrupt streaming (thanks to the HTTP buffer).
def http_fallback(~http_input_id,~http,~default)
id = http_input_id
# We use a custom switching predicate to trigger switching (and thus,
# transitions) before the end of a track (rather, end of HTTP stream).
# It is complexified because we don't want to trigger switching when
# HTTP disconnects for just an instant, when changing URL: for that
# we use gracetime below.
def gracetime(~delay=3.,f)
last_true = ref 0.
{ if f() then
last_true := gettimeofday()
true
else
gettimeofday() < !last_true+delay
end }
end
def connected()
status = list.hd(server.execute("#{id}.status"))
not(list.mem(status,["polling","stopped"]))
end
connected = gracetime(connected)
def to_live(a,b) =
log("TRANSITION to live")
add(normalize=false,
[fade.initial(b),fade.final(a)])
end
def to_static(a,b) =
log("TRANSITION to static")
sequence([fade.out(a),fade.initial(b)])
end
switch(
track_sensitive=false,
transitions=[to_live,to_static],
[(# make sure it is connected, and not buffering
{connected() and source.is_ready(http) and !webstream_enabled}, http),
({true},default)])
end

View File

@ -41,7 +41,7 @@ source_id = ref 0
def check_version(~version=liquidsoap.version, major, minor) = def check_version(~version=liquidsoap.version, major, minor) =
v = list.map(int_of_string, string.split(separator="\.", version)) v = list.map(int_of_string, string.split(separator="\.", version))
list.nth(v,0) > major or list.nth(v,0) == major and list.nth(v,1) >= minor list.nth(v,0,default=0) > major or list.nth(v,0,default=0) == major and list.nth(v,1,default=0) >= minor
end end
# cue cut fix for liquidsoap <1.2.2 # cue cut fix for liquidsoap <1.2.2
@ -235,26 +235,40 @@ def master_dj_disconnect() =
update_source_status("master_dj", false) update_source_status("master_dj", false)
end end
#auth function for live stream # Auth function for live stream
def check_master_dj_client(user,password) = # @Category LiveStream
log("master connected") # @param user Username to check against LibreTime API
#get the output of the php script # @param password Password to check against LibreTime API
ret = get_process_lines("python #{auth_path} --master #{user} #{password}") # @param ~type Type of password to check, "dj" or "master, default: "master"
#ret has now the value of the live client (dj1,dj2, or djx), or "ERROR"/"unknown" ... def check_auth(user="", password="", ~type="master") =
ret = list.hd(ret) log("#{type} user #{user} connected",label="#{type}_source")
#return true to let the client transmit data, or false to tell harbor to decline # Check auth based on return value from auth script
ret == "True" ret = snd(snd(run_process("python #{auth_path} --#{type} #{user} #{password}"))) == "0"
if ret then
log("#{type} user #{user} authenticated",label="#{type}_source")
else
log("#{type} user #{user} auth failed",label="#{type}_source",level=2)
end
ret
end end
def check_dj_client(user,password) = # Check master source auth
log("live dj connected") # @Category LiveStream
#get the output of the php script # @param user Username to check against LibreTime API
ret = get_process_lines("python #{auth_path} --dj #{user} #{password}") # @param password Password to check against LibreTime API
#ret has now the value of the live client (dj1,dj2, or djx), or "ERROR"/"unknown" ... def check_master_dj_client(user, password) =
hd = list.hd(ret) check_auth(user, password)
log("Live DJ authenticated: #{hd}") end
hd == "True"
# Check dj/show source auth
# @Category LiveStream
# @param user Username to check against LibreTime API
# @param password Password to check against LibreTime API
def check_dj_client(user, password) =
check_auth(user, password, type="dj")
end end
s = switch(id="schedule_noise_switch", s = switch(id="schedule_noise_switch",

View File

@ -0,0 +1,445 @@
%include "/etc/airtime/liquidsoap.cfg"
set("log.file.path", log_file)
set("server.telnet", true)
set("server.telnet.port", 1234)
# set("init.daemon.pidfile.path", "/var/run/airtime/airtime-liquidsoap.pid")
#Dynamic source list
#dyn_sources = ref []
webstream_enabled = ref false
time = ref string_of(gettimeofday())
#live stream setup
set("harbor.bind_addr", "0.0.0.0")
current_dyn_id = ref '-1'
pypo_data = ref '0'
stream_metadata_type = ref 0
default_dj_fade = ref 0.
station_name = ref ''
show_name = ref ''
dynamic_metadata_callback = ref fun (s) -> begin () end
s1_connected = ref ''
s2_connected = ref ''
s3_connected = ref ''
s4_connected = ref ''
s1_namespace = ref ''
s2_namespace = ref ''
s3_namespace = ref ''
just_switched = ref false
%include "ls_lib_legacy.liq"
sources = ref []
source_id = ref 0
def check_version(~version=liquidsoap.version, major, minor) =
v = list.map(int_of_string, string.split(separator="\.", version))
list.nth(v,0) > major or list.nth(v,0) == major and list.nth(v,1) >= minor
end
# cue cut fix for liquidsoap <1.2.2
#
# This was most likely broken on 1.1.1 (debian) as well.
#
# adapted from https://github.com/savonet/liquidsoap/issues/390#issuecomment-277562081
#
def fix_cue_in(~cue_in_metadata='liq_cue_in', m) =
# 0.04 might need to be adjusted according to your frame size
if float_of_string(m[cue_in_metadata]) < 0.04 then
[(cue_in_metadata, "0")]
else
[]
end
end
def create_source()
l = request.equeue(id="s#{!source_id}", length=0.5)
l = audio_to_stereo(id="queue_src", l)
l = if not check_version(1, 3) then
map_metadata(fix_cue_in, l)
else
l
end
l = cue_cut(l)
l = amplify(1., override="replay_gain", l)
# the crossfade function controls fade in/out
l = crossfade_airtime(l)
l = on_metadata(notify_queue, l)
sources := list.append([l], !sources)
server.register(namespace="queues",
"s#{!source_id}_skip",
fun (s) -> begin log("queues.s#{!source_id}_skip")
clear_queue(l)
"Done"
end)
source_id := !source_id + 1
end
create_source()
create_source()
create_source()
create_source()
create_source()
create_source()
create_source()
create_source()
queue = add(!sources, normalize=false)
pair = insert_metadata(queue)
dynamic_metadata_callback := fst(pair)
queue = snd(pair)
output.dummy(fallible=true, queue)
http = input.http_restart(id="http")
http = cross_http(http_input_id="http",http)
output.dummy(fallible=true, http)
stream_queue = http_fallback(http_input_id="http", http=http, default=queue)
stream_queue = map_metadata(update=false, append_title, stream_queue)
ignore(output.dummy(stream_queue, fallible=true))
server.register(namespace="vars",
"pypo_data",
fun (s) -> begin log("vars.pypo_data") pypo_data := s "Done" end)
server.register(namespace="vars",
"stream_metadata_type",
fun (s) -> begin log("vars.stream_metadata_type") stream_metadata_type := int_of_string(s) s end)
server.register(namespace="vars",
"show_name",
fun (s) -> begin log("vars.show_name") show_name := s s end)
server.register(namespace="vars",
"station_name",
fun (s) -> begin log("vars.station_name") station_name := s s end)
server.register(namespace="vars",
"bootup_time",
fun (s) -> begin log("vars.bootup_time") time := s s end)
server.register(namespace="streams",
"connection_status",
fun (s) -> begin log("streams.connection_status") "1:#{!s1_connected},2:#{!s2_connected},3:#{!s3_connected},4:#{!s4_connected}" end)
server.register(namespace="vars",
"default_dj_fade",
fun (s) -> begin log("vars.default_dj_fade") default_dj_fade := float_of_string(s) s end)
server.register(namespace="dynamic_source",
description="Enable webstream output",
usage='start',
"output_start",
fun (s) -> begin log("dynamic_source.output_start")
notify([("schedule_table_id", !current_dyn_id)])
webstream_enabled := true "enabled" end)
server.register(namespace="dynamic_source",
description="Enable webstream output",
usage='stop',
"output_stop",
fun (s) -> begin log("dynamic_source.output_stop") webstream_enabled := false "disabled" end)
server.register(namespace="dynamic_source",
description="Set the streams cc_schedule row id",
usage="id <id>",
"id",
fun (s) -> begin log("dynamic_source.id") set_dynamic_source_id(s) end)
server.register(namespace="dynamic_source",
description="Get the streams cc_schedule row id",
usage="get_id",
"get_id",
fun (s) -> begin log("dynamic_source.get_id") get_dynamic_source_id() end)
#server.register(namespace="dynamic_source",
# description="Start a new dynamic source.",
# usage="start <uri>",
# "read_start",
# fun (uri) -> begin log("dynamic_source.read_start") begin_stream_read(uri) end)
#server.register(namespace="dynamic_source",
# description="Stop a dynamic source.",
# usage="stop <id>",
# "read_stop",
# fun (s) -> begin log("dynamic_source.read_stop") stop_stream_read(s) end)
#server.register(namespace="dynamic_source",
# description="Stop a dynamic source.",
# usage="stop <id>",
# "read_stop_all",
# fun (s) -> begin log("dynamic_source.read_stop") destroy_dynamic_source_all() end)
default = amplify(id="silence_src", 0.00001, noise())
ref_off_air_meta = ref off_air_meta
if !ref_off_air_meta == "" then
ref_off_air_meta := "LibreTime - offline"
end
default = rewrite_metadata([("title", !ref_off_air_meta)], default)
ignore(output.dummy(default, fallible=true))
master_dj_enabled = ref false
live_dj_enabled = ref false
scheduled_play_enabled = ref false
def make_master_dj_available()
master_dj_enabled := true
end
def make_master_dj_unavailable()
master_dj_enabled := false
end
def make_live_dj_available()
live_dj_enabled := true
end
def make_live_dj_unavailable()
live_dj_enabled := false
end
def make_scheduled_play_available()
scheduled_play_enabled := true
just_switched := true
end
def make_scheduled_play_unavailable()
scheduled_play_enabled := false
end
def update_source_status(sourcename, status) =
command = "timeout --signal=KILL 45 pyponotify --source-name=#{sourcename} --source-status=#{status} &"
system(command)
log(command)
end
def live_dj_connect(header) =
update_source_status("live_dj", true)
end
def live_dj_disconnect() =
update_source_status("live_dj", false)
end
def master_dj_connect(header) =
update_source_status("master_dj", true)
end
def master_dj_disconnect() =
update_source_status("master_dj", false)
end
#auth function for live stream
def check_master_dj_client(user,password) =
log("master connected")
#get the output of the php script
ret = get_process_lines("python #{auth_path} --master #{user} #{password}")
#ret has now the value of the live client (dj1,dj2, or djx), or "ERROR"/"unknown" ...
ret = list.hd(ret)
#return true to let the client transmit data, or false to tell harbor to decline
ret == "True"
end
def check_dj_client(user,password) =
log("live dj connected")
#get the output of the php script
ret = get_process_lines("python #{auth_path} --dj #{user} #{password}")
#ret has now the value of the live client (dj1,dj2, or djx), or "ERROR"/"unknown" ...
hd = list.hd(ret)
log("Live DJ authenticated: #{hd}")
hd == "True"
end
s = switch(id="schedule_noise_switch",
track_sensitive=false,
transitions=[transition_default, transition],
[({!scheduled_play_enabled}, stream_queue), ({true}, default)]
)
s = if dj_live_stream_port != 0 and dj_live_stream_mp != "" then
dj_live =
audio_to_stereo(
input.harbor(id="live_dj_harbor",
dj_live_stream_mp,
port=dj_live_stream_port,
auth=check_dj_client,
max=40.,
on_connect=live_dj_connect,
on_disconnect=live_dj_disconnect))
ignore(output.dummy(dj_live, fallible=true))
switch(id="show_schedule_noise_switch",
track_sensitive=false,
transitions=[transition, transition],
[({!live_dj_enabled}, dj_live), ({true}, s)]
)
else
s
end
s = if master_live_stream_port != 0 and master_live_stream_mp != "" then
master_dj =
audio_to_stereo(
input.harbor(id="master_harbor",
master_live_stream_mp,
port=master_live_stream_port,
auth=check_master_dj_client,
max=40.,
on_connect=master_dj_connect,
on_disconnect=master_dj_disconnect))
ignore(output.dummy(master_dj, fallible=true))
switch(id="master_show_schedule_noise_switch",
track_sensitive=false,
transitions=[transition, transition],
[({!master_dj_enabled}, master_dj), ({true}, s)]
)
else
s
end
# Attach a skip command to the source s:
#add_skip_command(s)
server.register(namespace="streams",
description="Stop Master DJ source.",
usage="master_dj_stop",
"master_dj_stop",
fun (s) -> begin log("streams.master_dj_stop") make_master_dj_unavailable() "Done." end)
server.register(namespace="streams",
description="Start Master DJ source.",
usage="master_dj_start",
"master_dj_start",
fun (s) -> begin log("streams.master_dj_start") make_master_dj_available() "Done." end)
server.register(namespace="streams",
description="Stop Live DJ source.",
usage="live_dj_stop",
"live_dj_stop",
fun (s) -> begin log("streams.live_dj_stop") make_live_dj_unavailable() "Done." end)
server.register(namespace="streams",
description="Start Live DJ source.",
usage="live_dj_start",
"live_dj_start",
fun (s) -> begin log("streams.live_dj_start") make_live_dj_available() "Done." end)
server.register(namespace="streams",
description="Stop Scheduled Play source.",
usage="scheduled_play_stop",
"scheduled_play_stop",
fun (s) -> begin log("streams.scheduled_play_stop") make_scheduled_play_unavailable() "Done." end)
server.register(namespace="streams",
description="Start Scheduled Play source.",
usage="scheduled_play_start",
"scheduled_play_start",
fun (s) -> begin log("streams.scheduled_play_start") make_scheduled_play_available() "Done." end)
if output_sound_device then
success = ref false
log(output_sound_device_type)
%ifdef output.alsa
if output_sound_device_type == "ALSA" then
ignore(output.alsa(s))
success := true
end
%endif
%ifdef output.ao
if output_sound_device_type == "AO" then
ignore(output.ao(s))
success := true
end
%endif
%ifdef output.oss
if output_sound_device_type == "OSS" then
ignore(output.oss(s))
success := true
end
%endif
%ifdef output.portaudio
if output_sound_device_type == "Portaudio" then
ignore(output.portaudio(s))
success := true
end
%endif
%ifdef output.pulseaudio
if output_sound_device_type == "Pulseaudio" then
ignore(output.pulseaudio(s))
success := true
end
%endif
if (!success == false) then
ignore(output.prefered(s))
end
end
if s1_enable == true then
if s1_output == 'shoutcast' then
s1_namespace := "shoutcast_stream_1"
else
s1_namespace := s1_mount
end
server.register(namespace=!s1_namespace, "connected", fun (s) -> begin log("#{!s1_namespace}.connected") !s1_connected end)
output_to(s1_output, s1_type, s1_bitrate, s1_host, s1_port, s1_pass,
s1_mount, s1_url, s1_description, s1_genre, s1_user, s, "1",
s1_connected, s1_name, s1_channels)
end
if s2_enable == true then
if s2_output == 'shoutcast' then
s2_namespace := "shoutcast_stream_2"
else
s2_namespace := s2_mount
end
server.register(namespace=!s2_namespace, "connected", fun (s) -> begin log("#{!s2_namespace}.connected") !s2_connected end)
output_to(s2_output, s2_type, s2_bitrate, s2_host, s2_port, s2_pass,
s2_mount, s2_url, s2_description, s2_genre, s2_user, s, "2",
s2_connected, s2_name, s2_channels)
end
if s3_enable == true then
if s3_output == 'shoutcast' then
s3_namespace := "shoutcast_stream_3"
else
s3_namespace := s3_mount
end
server.register(namespace=!s3_namespace, "connected", fun (s) -> begin log("#{!s3_namespace}.connected") !s3_connected end)
output_to(s3_output, s3_type, s3_bitrate, s3_host, s3_port, s3_pass,
s3_mount, s3_url, s3_description, s3_genre, s3_user, s, "3",
s3_connected, s3_name, s3_channels)
end
s4_namespace = ref ''
if s4_enable == true then
log("Stream 4 Enabled")
if s4_output == 'shoutcast' then
s4_namespace := "shoutcast_stream_4"
else
s4_namespace := s4_mount
end
server.register(namespace=!s4_namespace, "connected", fun (s) -> begin log("#{!s4_namespace}.connected") !s4_connected end)
output_to(s4_output, s4_type, s4_bitrate, s4_host, s4_port, s4_pass,
s4_mount, s4_url, s4_name, s4_genre, s4_user, s, "4",
s4_connected, s4_description, s4_channels)
end
command = "timeout --signal=KILL 45 pyponotify --liquidsoap-started &"
log(command)
system(command)