Merge branch 'cc-2015-on-reboot-resume-show' into devel
Conflicts: python_apps/media-monitor/airtime-media-monitor-init-d
This commit is contained in:
commit
2e4519f156
32 changed files with 401 additions and 389 deletions
|
@ -127,5 +127,35 @@ class DateHelper
|
||||||
$explode = explode(" ", $p_timestamp);
|
$explode = explode(" ", $p_timestamp);
|
||||||
return $explode[1];
|
return $explode[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Given a track length in the format HH:MM:SS.mm, we want to
|
||||||
|
* convert this to seconds. This is useful for Liquidsoap which
|
||||||
|
* likes input parameters give in seconds.
|
||||||
|
* For example, 00:06:31.444, should be converted to 391.444 seconds
|
||||||
|
* @param int $p_time
|
||||||
|
* The time interval in format HH:MM:SS.mm we wish to
|
||||||
|
* convert to seconds.
|
||||||
|
* @return int
|
||||||
|
* The input parameter converted to seconds.
|
||||||
|
*/
|
||||||
|
public static function calculateLengthInSeconds($p_time){
|
||||||
|
|
||||||
|
if (2 !== substr_count($p_time, ":")){
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (1 === substr_count($p_time, ".")){
|
||||||
|
list($hhmmss, $ms) = explode(".", $p_time);
|
||||||
|
} else {
|
||||||
|
$hhmmss = $p_time;
|
||||||
|
$ms = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
list($hours, $minutes, $seconds) = explode(":", $hhmmss);
|
||||||
|
|
||||||
|
$totalSeconds = $hours*3600 + $minutes*60 + $seconds + $ms/1000;
|
||||||
|
|
||||||
|
return $totalSeconds;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -184,7 +184,9 @@ class ScheduleGroup {
|
||||||
." st.cue_out,"
|
." st.cue_out,"
|
||||||
." st.clip_length,"
|
." st.clip_length,"
|
||||||
." st.fade_in,"
|
." st.fade_in,"
|
||||||
." st.fade_out"
|
." st.fade_out,"
|
||||||
|
." st.starts,"
|
||||||
|
." st.ends"
|
||||||
." FROM $CC_CONFIG[scheduleTable] as st"
|
." FROM $CC_CONFIG[scheduleTable] as st"
|
||||||
." LEFT JOIN $CC_CONFIG[showInstances] as si"
|
." LEFT JOIN $CC_CONFIG[showInstances] as si"
|
||||||
." ON st.instance_id = si.id"
|
." ON st.instance_id = si.id"
|
||||||
|
@ -670,7 +672,7 @@ class Schedule {
|
||||||
$timestamp = strtotime($start);
|
$timestamp = strtotime($start);
|
||||||
$playlists[$pkey]['source'] = "PLAYLIST";
|
$playlists[$pkey]['source'] = "PLAYLIST";
|
||||||
$playlists[$pkey]['x_ident'] = $dx['group_id'];
|
$playlists[$pkey]['x_ident'] = $dx['group_id'];
|
||||||
$playlists[$pkey]['subtype'] = '1'; // Just needs to be between 1 and 4 inclusive
|
//$playlists[$pkey]['subtype'] = '1'; // Just needs to be between 1 and 4 inclusive
|
||||||
$playlists[$pkey]['timestamp'] = $timestamp;
|
$playlists[$pkey]['timestamp'] = $timestamp;
|
||||||
$playlists[$pkey]['duration'] = $dx['clip_length'];
|
$playlists[$pkey]['duration'] = $dx['clip_length'];
|
||||||
$playlists[$pkey]['played'] = '0';
|
$playlists[$pkey]['played'] = '0';
|
||||||
|
@ -690,27 +692,24 @@ class Schedule {
|
||||||
$scheduleGroup = new ScheduleGroup($playlist["schedule_id"]);
|
$scheduleGroup = new ScheduleGroup($playlist["schedule_id"]);
|
||||||
$items = $scheduleGroup->getItems();
|
$items = $scheduleGroup->getItems();
|
||||||
$medias = array();
|
$medias = array();
|
||||||
$playlist['subtype'] = '1';
|
|
||||||
foreach ($items as $item)
|
foreach ($items as $item)
|
||||||
{
|
{
|
||||||
$storedFile = StoredFile::Recall($item["file_id"]);
|
$storedFile = StoredFile::Recall($item["file_id"]);
|
||||||
$uri = $storedFile->getFileUrl();
|
$uri = $storedFile->getFileUrl();
|
||||||
|
|
||||||
// For pypo, a cueout of zero means no cueout
|
$starts = Schedule::AirtimeTimeToPypoTime($item["starts"]);
|
||||||
$cueOut = "0";
|
$medias[$starts] = array(
|
||||||
if (Schedule::TimeDiff($item["cue_out"], $item["clip_length"]) > 0.001) {
|
|
||||||
$cueOut = Schedule::WallTimeToMillisecs($item["cue_out"]);
|
|
||||||
}
|
|
||||||
$medias[] = array(
|
|
||||||
'row_id' => $item["id"],
|
'row_id' => $item["id"],
|
||||||
'id' => $storedFile->getGunid(),
|
'id' => $storedFile->getGunid(),
|
||||||
'uri' => $uri,
|
'uri' => $uri,
|
||||||
'fade_in' => Schedule::WallTimeToMillisecs($item["fade_in"]),
|
'fade_in' => Schedule::WallTimeToMillisecs($item["fade_in"]),
|
||||||
'fade_out' => Schedule::WallTimeToMillisecs($item["fade_out"]),
|
'fade_out' => Schedule::WallTimeToMillisecs($item["fade_out"]),
|
||||||
'fade_cross' => 0,
|
'fade_cross' => 0,
|
||||||
'cue_in' => Schedule::WallTimeToMillisecs($item["cue_in"]),
|
'cue_in' => DateHelper::CalculateLengthInSeconds($item["cue_in"]),
|
||||||
'cue_out' => $cueOut,
|
'cue_out' => DateHelper::CalculateLengthInSeconds($item["cue_out"]),
|
||||||
'export_source' => 'scheduler'
|
'export_source' => 'scheduler',
|
||||||
|
'start' => $starts,
|
||||||
|
'end' => Schedule::AirtimeTimeToPypoTime($item["ends"])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
$playlist['medias'] = $medias;
|
$playlist['medias'] = $medias;
|
||||||
|
|
|
@ -97,6 +97,8 @@ if ($overwrite) {
|
||||||
echo "* Creating INI files".PHP_EOL;
|
echo "* Creating INI files".PHP_EOL;
|
||||||
AirtimeIni::CreateIniFiles();
|
AirtimeIni::CreateIniFiles();
|
||||||
}
|
}
|
||||||
|
AirtimeIni::CreateMonitFile();
|
||||||
|
|
||||||
|
|
||||||
AirtimeInstall::InstallPhpCode();
|
AirtimeInstall::InstallPhpCode();
|
||||||
AirtimeInstall::InstallBinaries();
|
AirtimeInstall::InstallBinaries();
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
#Cause bash script to exit if any of the installers
|
||||||
|
#return with a non-zero return value.
|
||||||
|
set -e
|
||||||
|
|
||||||
# Absolute path to this script
|
# Absolute path to this script
|
||||||
SCRIPT=`readlink -f $0`
|
SCRIPT=`readlink -f $0`
|
||||||
# Absolute directory this script is in
|
# Absolute directory this script is in
|
||||||
|
|
|
@ -96,7 +96,7 @@ passthru("python ".__DIR__."/../python_apps/pypo/install/pypo-install.py");
|
||||||
echo PHP_EOL."*** Updating Recorder ***".PHP_EOL;
|
echo PHP_EOL."*** Updating Recorder ***".PHP_EOL;
|
||||||
passthru("python ".__DIR__."/../python_apps/show-recorder/install/recorder-install.py");
|
passthru("python ".__DIR__."/../python_apps/show-recorder/install/recorder-install.py");
|
||||||
|
|
||||||
echo PHP_EOL."*** Starting Media Monitor ***".PHP_EOL;
|
echo PHP_EOL."*** Updating Media Monitor ***".PHP_EOL;
|
||||||
passthru("python ".__DIR__."/../python_apps/media-monitor/install/media-monitor-install.py");
|
passthru("python ".__DIR__."/../python_apps/media-monitor/install/media-monitor-install.py");
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ class AirtimeIni
|
||||||
const CONF_FILE_RECORDER = "/etc/airtime/recorder.cfg";
|
const CONF_FILE_RECORDER = "/etc/airtime/recorder.cfg";
|
||||||
const CONF_FILE_LIQUIDSOAP = "/etc/airtime/liquidsoap.cfg";
|
const CONF_FILE_LIQUIDSOAP = "/etc/airtime/liquidsoap.cfg";
|
||||||
const CONF_FILE_MEDIAMONITOR = "/etc/airtime/media-monitor.cfg";
|
const CONF_FILE_MEDIAMONITOR = "/etc/airtime/media-monitor.cfg";
|
||||||
|
const CONF_FILE_MONIT = "/etc/monit/conf.d/airtime-monit.cfg";
|
||||||
|
|
||||||
public static function IniFilesExist()
|
public static function IniFilesExist()
|
||||||
{
|
{
|
||||||
|
@ -75,7 +76,14 @@ class AirtimeIni
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
if (!copy(__DIR__."/../../python_apps/media-monitor/media-monitor.cfg", AirtimeIni::CONF_FILE_MEDIAMONITOR)){
|
if (!copy(__DIR__."/../../python_apps/media-monitor/media-monitor.cfg", AirtimeIni::CONF_FILE_MEDIAMONITOR)){
|
||||||
echo "Could not copy MediaMonitor.cfg to /etc/airtime/. Exiting.";
|
echo "Could not copy media-monitor.cfg to /etc/airtime/. Exiting.";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function CreateMonitFile(){
|
||||||
|
if (!copy(__DIR__."/../../python_apps/monit/airtime-monit.cfg", AirtimeIni::CONF_FILE_MONIT)){
|
||||||
|
echo "Could not copy airtime-monit.cfg to /etc/monit/conf.d/. Exiting.";
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -277,7 +277,7 @@ class AirtimeInstall
|
||||||
|
|
||||||
public static function DeleteFilesRecursive($p_path)
|
public static function DeleteFilesRecursive($p_path)
|
||||||
{
|
{
|
||||||
$command = "rm -rf $p_path";
|
$command = "rm -rf \"$p_path\"";
|
||||||
exec($command);
|
exec($command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,7 +323,7 @@ class AirtimeInstall
|
||||||
public static function UninstallPhpCode()
|
public static function UninstallPhpCode()
|
||||||
{
|
{
|
||||||
echo "* Removing PHP code from ".AirtimeInstall::CONF_DIR_WWW.PHP_EOL;
|
echo "* Removing PHP code from ".AirtimeInstall::CONF_DIR_WWW.PHP_EOL;
|
||||||
exec("rm -rf ".AirtimeInstall::CONF_DIR_WWW);
|
exec('rm -rf "'.AirtimeInstall::CONF_DIR_WWW.'"');
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function InstallBinaries()
|
public static function InstallBinaries()
|
||||||
|
@ -336,7 +336,7 @@ class AirtimeInstall
|
||||||
public static function UninstallBinaries()
|
public static function UninstallBinaries()
|
||||||
{
|
{
|
||||||
echo "* Removing Airtime binaries from ".AirtimeInstall::CONF_DIR_BINARIES.PHP_EOL;
|
echo "* Removing Airtime binaries from ".AirtimeInstall::CONF_DIR_BINARIES.PHP_EOL;
|
||||||
exec("rm -rf ".AirtimeInstall::CONF_DIR_BINARIES);
|
exec('rm -rf "'.AirtimeInstall::CONF_DIR_BINARIES.'"');
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function DirCheck()
|
public static function DirCheck()
|
||||||
|
@ -386,6 +386,6 @@ class AirtimeInstall
|
||||||
$path = AirtimeInstall::CONF_DIR_LOG;
|
$path = AirtimeInstall::CONF_DIR_LOG;
|
||||||
echo "* Removing logs directory ".$path.PHP_EOL;
|
echo "* Removing logs directory ".$path.PHP_EOL;
|
||||||
|
|
||||||
exec("rm -rf $path");
|
exec("rm -rf \"$path\"");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ function InstallBinaries()
|
||||||
function UninstallBinaries()
|
function UninstallBinaries()
|
||||||
{
|
{
|
||||||
echo "* Removing Airtime binaries from ".CONF_DIR_BINARIES.PHP_EOL;
|
echo "* Removing Airtime binaries from ".CONF_DIR_BINARIES.PHP_EOL;
|
||||||
exec("rm -rf ".CONF_DIR_BINARIES);
|
exec('rm -rf "'.CONF_DIR_BINARIES.'"');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ $pathnames = array("/usr/bin/airtime-pypo-start",
|
||||||
|
|
||||||
foreach ($pathnames as $pn){
|
foreach ($pathnames as $pn){
|
||||||
echo "Removing $pn\n";
|
echo "Removing $pn\n";
|
||||||
exec("rm -rf ".$pn);
|
exec("rm -rf \"$pn\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -82,6 +82,7 @@ $values = parse_ini_file(CONF_FILE_AIRTIME, true);
|
||||||
$phpDir = $values['general']['airtime_dir'];
|
$phpDir = $values['general']['airtime_dir'];
|
||||||
|
|
||||||
InstallPhpCode($phpDir);
|
InstallPhpCode($phpDir);
|
||||||
|
AirtimeIni::CreateMonitFile();
|
||||||
|
|
||||||
//update utils (/usr/lib/airtime) folder
|
//update utils (/usr/lib/airtime) folder
|
||||||
UninstallBinaries();
|
UninstallBinaries();
|
||||||
|
|
|
@ -619,7 +619,7 @@ class ObpApiClient():
|
||||||
def get_liquidsoap_data(self, pkey, schedule):
|
def get_liquidsoap_data(self, pkey, schedule):
|
||||||
playlist = schedule[pkey]
|
playlist = schedule[pkey]
|
||||||
data = dict()
|
data = dict()
|
||||||
data["ptype"] = playlist['subtype']
|
#data["ptype"] = playlist['subtype']
|
||||||
try:
|
try:
|
||||||
data["user_id"] = playlist['user_id']
|
data["user_id"] = playlist['user_id']
|
||||||
data["playlist_id"] = playlist['id']
|
data["playlist_id"] = playlist['id']
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
from subprocess import Popen, PIPE, STDOUT
|
from subprocess import Popen, PIPE, STDOUT
|
||||||
|
|
||||||
def create_user(username):
|
def create_user(username):
|
||||||
|
|
|
@ -11,17 +11,19 @@
|
||||||
|
|
||||||
USERID=root
|
USERID=root
|
||||||
GROUPID=www-data
|
GROUPID=www-data
|
||||||
NAME=Airtime
|
NAME=Airtime\ Media\ Monitor
|
||||||
|
|
||||||
DAEMON=/usr/bin/airtime-media-monitor
|
DAEMON=/usr/lib/airtime/media-monitor/airtime-media-monitor
|
||||||
PIDFILE=/var/run/airtime-media-monitor.pid
|
PIDFILE=/var/run/airtime-media-monitor.pid
|
||||||
|
|
||||||
start () {
|
start () {
|
||||||
|
monit monitor airtime-media-monitor
|
||||||
start-stop-daemon --start --background --quiet --chuid $USERID:$GROUPID --make-pidfile --pidfile $PIDFILE --startas $DAEMON
|
start-stop-daemon --start --background --quiet --chuid $USERID:$GROUPID --make-pidfile --pidfile $PIDFILE --startas $DAEMON
|
||||||
}
|
}
|
||||||
|
|
||||||
stop () {
|
stop () {
|
||||||
# Send TERM after 5 seconds, wait at most 30 seconds.
|
# Send TERM after 5 seconds, wait at most 30 seconds.
|
||||||
|
monit unmonitor airtime-media-monitor
|
||||||
start-stop-daemon --stop --oknodo --retry TERM/5/0/30 --quiet --pidfile $PIDFILE
|
start-stop-daemon --stop --oknodo --retry TERM/5/0/30 --quiet --pidfile $PIDFILE
|
||||||
rm -f $PIDFILE
|
rm -f $PIDFILE
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,10 +66,6 @@ try:
|
||||||
#os.system("chmod -R 755 "+config["bin_dir"]+"/airtime-media-monitor)
|
#os.system("chmod -R 755 "+config["bin_dir"]+"/airtime-media-monitor)
|
||||||
os.system("chown -R pypo:pypo "+config["bin_dir"])
|
os.system("chown -R pypo:pypo "+config["bin_dir"])
|
||||||
|
|
||||||
print "Creating symbolic links"
|
|
||||||
os.system("rm -f /usr/bin/airtime-media-monitor")
|
|
||||||
os.system("ln -s "+config["bin_dir"]+"/airtime-media-monitor /usr/bin/")
|
|
||||||
|
|
||||||
print "Installing media-monitor daemon"
|
print "Installing media-monitor daemon"
|
||||||
shutil.copy(config["bin_dir"]+"/airtime-media-monitor-init-d", "/etc/init.d/airtime-media-monitor")
|
shutil.copy(config["bin_dir"]+"/airtime-media-monitor-init-d", "/etc/init.d/airtime-media-monitor")
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ if os.geteuid() != 0:
|
||||||
PATH_INI_FILE = '/etc/airtime/media-monitor.cfg'
|
PATH_INI_FILE = '/etc/airtime/media-monitor.cfg'
|
||||||
|
|
||||||
def remove_path(path):
|
def remove_path(path):
|
||||||
os.system("rm -rf " + path)
|
os.system('rm -rf "%s"' % path)
|
||||||
|
|
||||||
def get_current_script_dir():
|
def get_current_script_dir():
|
||||||
current_script_dir = os.path.realpath(__file__)
|
current_script_dir = os.path.realpath(__file__)
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
set daemon 10 # Poll at 10 second intervals
|
set daemon 10 # Poll at 10 second intervals
|
||||||
set logfile syslog facility log_daemon
|
set logfile syslog facility log_daemon
|
||||||
|
|
||||||
|
set httpd port 2812 and use address 127.0.0.1
|
||||||
|
allow localhost
|
||||||
|
allow admin:monit
|
||||||
|
|
||||||
check process airtime-playout
|
check process airtime-playout
|
||||||
with pidfile "/var/run/airtime-playout.pid"
|
with pidfile "/var/run/airtime-playout.pid"
|
||||||
start program = "/etc/init.d/airtime-playout start" with timeout 10 seconds
|
start program = "/etc/init.d/airtime-playout start" with timeout 10 seconds
|
||||||
|
|
|
@ -11,24 +11,31 @@
|
||||||
|
|
||||||
USERID=pypo
|
USERID=pypo
|
||||||
GROUPID=pypo
|
GROUPID=pypo
|
||||||
NAME=Airtime
|
NAME=Airtime\ Playout
|
||||||
|
|
||||||
DAEMON0=/usr/bin/airtime-playout
|
DAEMON0=/usr/lib/airtime/pypo/bin/airtime-playout
|
||||||
PIDFILE0=/var/run/airtime-playout.pid
|
PIDFILE0=/var/run/airtime-playout.pid
|
||||||
|
|
||||||
DAEMON1=/usr/bin/airtime-liquidsoap
|
DAEMON1=/usr/lib/airtime/pypo/bin/airtime-liquidsoap
|
||||||
PIDFILE1=/var/run/airtime-liquidsoap.pid
|
PIDFILE1=/var/run/airtime-liquidsoap.pid
|
||||||
|
|
||||||
start () {
|
start () {
|
||||||
|
monit monitor airtime-playout
|
||||||
start-stop-daemon --start --background --quiet --chuid $USERID:$GROUPID --make-pidfile --pidfile $PIDFILE0 --startas $DAEMON0
|
start-stop-daemon --start --background --quiet --chuid $USERID:$GROUPID --make-pidfile --pidfile $PIDFILE0 --startas $DAEMON0
|
||||||
|
|
||||||
|
monit monitor airtime-liquidsoap
|
||||||
start-stop-daemon --start --background --quiet --chuid $USERID:$GROUPID --make-pidfile --pidfile $PIDFILE1 --startas $DAEMON1
|
start-stop-daemon --start --background --quiet --chuid $USERID:$GROUPID --make-pidfile --pidfile $PIDFILE1 --startas $DAEMON1
|
||||||
}
|
}
|
||||||
|
|
||||||
stop () {
|
stop () {
|
||||||
# Send TERM after 5 seconds, wait at most 30 seconds.
|
# Send TERM after 5 seconds, wait at most 30 seconds.
|
||||||
|
|
||||||
|
monit unmonitor airtime-playout
|
||||||
start-stop-daemon --stop --oknodo --retry TERM/5/0/30 --quiet --pidfile $PIDFILE0
|
start-stop-daemon --stop --oknodo --retry TERM/5/0/30 --quiet --pidfile $PIDFILE0
|
||||||
start-stop-daemon --stop --oknodo --retry TERM/5/0/30 --quiet --pidfile $PIDFILE1
|
|
||||||
rm -f $PIDFILE0
|
rm -f $PIDFILE0
|
||||||
|
|
||||||
|
monit unmonitor airtime-liquidsoap
|
||||||
|
start-stop-daemon --stop --oknodo --retry TERM/5/0/30 --quiet --pidfile $PIDFILE1
|
||||||
rm -f $PIDFILE1
|
rm -f $PIDFILE1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -103,12 +103,6 @@ try:
|
||||||
os.system("chown -R pypo:pypo "+config["bin_dir"])
|
os.system("chown -R pypo:pypo "+config["bin_dir"])
|
||||||
os.system("chown -R pypo:pypo "+config["cache_base_dir"])
|
os.system("chown -R pypo:pypo "+config["cache_base_dir"])
|
||||||
|
|
||||||
print "Creating symbolic links"
|
|
||||||
os.system("rm -f /usr/bin/airtime-playout")
|
|
||||||
os.system("ln -s "+config["bin_dir"]+"/bin/airtime-playout /usr/bin/")
|
|
||||||
os.system("rm -f /usr/bin/airtime-liquidsoap")
|
|
||||||
os.system("ln -s "+config["bin_dir"]+"/bin/airtime-liquidsoap /usr/bin/")
|
|
||||||
|
|
||||||
print "Installing pypo daemon"
|
print "Installing pypo daemon"
|
||||||
shutil.copy(config["bin_dir"]+"/bin/airtime-playout-init-d", "/etc/init.d/airtime-playout")
|
shutil.copy(config["bin_dir"]+"/bin/airtime-playout-init-d", "/etc/init.d/airtime-playout")
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ if os.geteuid() != 0:
|
||||||
PATH_INI_FILE = '/etc/airtime/pypo.cfg'
|
PATH_INI_FILE = '/etc/airtime/pypo.cfg'
|
||||||
|
|
||||||
def remove_path(path):
|
def remove_path(path):
|
||||||
os.system("rm -rf " + path)
|
os.system('rm -rf "%s"' % path)
|
||||||
|
|
||||||
def get_current_script_dir():
|
def get_current_script_dir():
|
||||||
current_script_dir = os.path.realpath(__file__)
|
current_script_dir = os.path.realpath(__file__)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
|
|
||||||
|
SUBDIRS = tests
|
||||||
DISTFILES = $(wildcard *.in) Makefile ask-liquidsoap.rb ask-liquidsoap.pl \
|
DISTFILES = $(wildcard *.in) Makefile ask-liquidsoap.rb ask-liquidsoap.pl \
|
||||||
$(wildcard *.liq) extract-replaygain
|
$(wildcard *.liq) extract-replaygain
|
||||||
|
|
||||||
|
|
|
@ -9,37 +9,43 @@ my_get_mime = get_mime
|
||||||
get_mime = my_get_mime
|
get_mime = my_get_mime
|
||||||
|
|
||||||
%ifdef add_decoder
|
%ifdef add_decoder
|
||||||
if test_process("which flac") then
|
# Enable external FLAC decoders. Requires flac binary
|
||||||
log(level=3,"Found flac binary: enabling flac external decoder.")
|
# in the path for audio decoding and metaflac binary for
|
||||||
flac_p = "flac -d -c - 2>/dev/null"
|
# metadata. Does not work on Win32. Default: disabled.
|
||||||
def test_flac(file) =
|
# Please note that built-in support for FLAC is available
|
||||||
if test_process("which metaflac") then
|
# in liquidsoap if compiled and should be preferred over
|
||||||
channels = list.hd(get_process_lines("metaflac \
|
# the external decoder.
|
||||||
--show-channels #{quote(file)} \
|
# @category Liquidsoap
|
||||||
2>/dev/null"))
|
def enable_external_flac_decoder() =
|
||||||
# If the value is not an int, this returns 0 and we are ok :)
|
if test_process("which flac") then
|
||||||
int_of_string(channels)
|
log(level=3,"Found flac binary: enabling flac external decoder.")
|
||||||
else
|
flac_p = "flac -d -c - 2>/dev/null"
|
||||||
# Try to detect using mime test..
|
def test_flac(file) =
|
||||||
mime = get_mime(file)
|
if test_process("which metaflac") then
|
||||||
if string.match(pattern="flac",file) then
|
channels = list.hd(get_process_lines("metaflac \
|
||||||
# We do not know the number of audio channels
|
--show-channels #{quote(file)} \
|
||||||
# so setting to -1
|
2>/dev/null"))
|
||||||
(-1)
|
# If the value is not an int, this returns 0 and we are ok :)
|
||||||
|
int_of_string(channels)
|
||||||
else
|
else
|
||||||
# All tests failed: no audio decodable using flac..
|
# Try to detect using mime test..
|
||||||
0
|
mime = get_mime(file)
|
||||||
|
if string.match(pattern="flac",file) then
|
||||||
|
# We do not know the number of audio channels
|
||||||
|
# so setting to -1
|
||||||
|
(-1)
|
||||||
|
else
|
||||||
|
# All tests failed: no audio decodable using flac..
|
||||||
|
0
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
add_decoder(name="FLAC",description="Decode files using the flac \
|
||||||
|
decoder binary.", test=test_flac,flac_p)
|
||||||
|
else
|
||||||
|
log(level=3,"Did not find flac binary: flac decoder disabled.")
|
||||||
end
|
end
|
||||||
add_decoder(name="FLAC",description="Decode files using the flac \
|
|
||||||
decoder binary.", test=test_flac,flac_p)
|
|
||||||
else
|
|
||||||
log(level=3,"Did not find flac binary: flac decoder disabled.")
|
|
||||||
end
|
|
||||||
%endif
|
|
||||||
|
|
||||||
if os.type != "Win32" then
|
|
||||||
if test_process("which metaflac") then
|
if test_process("which metaflac") then
|
||||||
log(level=3,"Found metaflac binary: enabling flac external metadata \
|
log(level=3,"Found metaflac binary: enabling flac external metadata \
|
||||||
resolver.")
|
resolver.")
|
||||||
|
@ -55,49 +61,59 @@ if os.type != "Win32" then
|
||||||
if list.length(l) >= 1 then
|
if list.length(l) >= 1 then
|
||||||
list.append([(list.hd(l),"")],l')
|
list.append([(list.hd(l),"")],l')
|
||||||
else
|
else
|
||||||
l'
|
l'
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
list.fold(f,[],ret)
|
end
|
||||||
|
list.fold(f,[],ret)
|
||||||
end
|
end
|
||||||
add_metadata_resolver("FLAC",flac_meta)
|
add_metadata_resolver("FLAC",flac_meta)
|
||||||
else
|
else
|
||||||
log(level=3,"Did not find metaflac binary: flac metadata resolver disabled.")
|
log(level=3,"Did not find metaflac binary: flac metadata resolver disabled.")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
%endif
|
||||||
|
|
||||||
# A list of know extensions and content-type for AAC.
|
%ifdef add_oblivious_decoder
|
||||||
# Values from http://en.wikipedia.org/wiki/Advanced_Audio_Coding
|
# Enable or disable external FAAD (AAC/AAC+/M4A) decoders.
|
||||||
# TODO: can we register a setting for that ??
|
# Requires faad binary in the path for audio decoding and
|
||||||
aac_mimes =
|
# metaflac binary for metadata. Does not work on Win32.
|
||||||
["audio/aac", "audio/aacp", "audio/3gpp", "audio/3gpp2", "audio/mp4",
|
# Please note that built-in support for faad is available
|
||||||
"audio/MP4A-LATM", "audio/mpeg4-generic", "audio/x-hx-aac-adts"]
|
# in liquidsoap if compiled and should be preferred over
|
||||||
aac_filexts = ["m4a", "m4b", "m4p", "m4v",
|
# the external decoder.
|
||||||
"m4r", "3gp", "mp4", "aac"]
|
# @category Liquidsoap
|
||||||
|
def enable_external_faad_decoder() =
|
||||||
|
|
||||||
# Faad is not very selective so
|
# A list of know extensions and content-type for AAC.
|
||||||
# We are checking only file that
|
# Values from http://en.wikipedia.org/wiki/Advanced_Audio_Coding
|
||||||
# end with a known extension or mime type
|
# TODO: can we register a setting for that ??
|
||||||
def faad_test(file) =
|
aac_mimes =
|
||||||
# Get the file's mime
|
["audio/aac", "audio/aacp", "audio/3gpp", "audio/3gpp2", "audio/mp4",
|
||||||
mime = get_mime(file)
|
"audio/MP4A-LATM", "audio/mpeg4-generic", "audio/x-hx-aac-adts"]
|
||||||
# Test mime
|
aac_filexts = ["m4a", "m4b", "m4p", "m4v",
|
||||||
if list.mem(mime,aac_mimes) then
|
"m4r", "3gp", "mp4", "aac"]
|
||||||
true
|
|
||||||
else
|
# Faad is not very selective so
|
||||||
# Otherwise test file extension
|
# We are checking only file that
|
||||||
ret = string.extract(pattern='\.(.+)$',file)
|
# end with a known extension or mime type
|
||||||
|
def faad_test(file) =
|
||||||
|
# Get the file's mime
|
||||||
|
mime = get_mime(file)
|
||||||
|
# Test mime
|
||||||
|
if list.mem(mime,aac_mimes) then
|
||||||
|
true
|
||||||
|
else
|
||||||
|
# Otherwise test file extension
|
||||||
|
ret = string.extract(pattern='\.(.+)$',file)
|
||||||
if list.length(ret) != 0 then
|
if list.length(ret) != 0 then
|
||||||
ext = ret["1"]
|
ext = ret["1"]
|
||||||
list.mem(ext,aac_filexts)
|
list.mem(ext,aac_filexts)
|
||||||
else
|
else
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
if os.type != "Win32" then
|
|
||||||
if test_process("which faad") then
|
if test_process("which faad") then
|
||||||
log(level=3,"Found faad binary: enabling external faad decoder and \
|
log(level=3,"Found faad binary: enabling external faad decoder and \
|
||||||
metadata resolver.")
|
metadata resolver.")
|
||||||
|
@ -120,15 +136,13 @@ if os.type != "Win32" then
|
||||||
0
|
0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
%ifdef add_oblivious_decoder
|
|
||||||
add_oblivious_decoder(name="FAAD",description="Decode files using \
|
add_oblivious_decoder(name="FAAD",description="Decode files using \
|
||||||
the faad binary.", test=test_faad, faad_p)
|
the faad binary.", test=test_faad, faad_p)
|
||||||
%endif
|
|
||||||
def faad_meta(file) =
|
def faad_meta(file) =
|
||||||
if faad_test(file) then
|
if faad_test(file) then
|
||||||
ret = get_process_lines("faad -i \
|
ret = get_process_lines("faad -i \
|
||||||
#{quote(file)} 2>&1")
|
#{quote(file)} 2>&1")
|
||||||
# Yea, this is tuff programming (again) !
|
# Yea, this is ugly programming (again) !
|
||||||
def get_meta(l,s)=
|
def get_meta(l,s)=
|
||||||
ret = string.extract(pattern="^(\w+):\s(.+)$",s)
|
ret = string.extract(pattern="^(\w+):\s(.+)$",s)
|
||||||
if list.length(ret) > 0 then
|
if list.length(ret) > 0 then
|
||||||
|
@ -147,6 +161,7 @@ if os.type != "Win32" then
|
||||||
log(level=3,"Did not find faad binary: faad decoder disabled.")
|
log(level=3,"Did not find faad binary: faad decoder disabled.")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
%endif
|
||||||
|
|
||||||
# Standard function for displaying metadata.
|
# Standard function for displaying metadata.
|
||||||
# Shows artist and title, using "Unknown" when a field is empty.
|
# Shows artist and title, using "Unknown" when a field is empty.
|
||||||
|
@ -189,3 +204,22 @@ def notify_metadata(~urgency="low",~icon="stock_smiley-22",~time=3000,
|
||||||
^ ' -t #{time} #{quote(title)} '
|
^ ' -t #{time} #{quote(title)} '
|
||||||
on_metadata(fun (m) -> system(send^quote(display(m))),s)
|
on_metadata(fun (m) -> system(send^quote(display(m))),s)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
%ifdef input.external
|
||||||
|
# Stream data from mplayer
|
||||||
|
# @category Source / Input
|
||||||
|
# @param s data URI.
|
||||||
|
# @param ~restart restart on exit.
|
||||||
|
# @param ~restart_on_error restart on exit with error.
|
||||||
|
# @param ~buffer Duration of the pre-buffered data.
|
||||||
|
# @param ~max Maximum duration of the buffered data.
|
||||||
|
def input.mplayer(~id="input.mplayer",
|
||||||
|
~restart=true,~restart_on_error=false,
|
||||||
|
~buffer=0.2,~max=10.,s) =
|
||||||
|
input.external(id=id,restart=restart,
|
||||||
|
restart_on_error=restart_on_error,
|
||||||
|
buffer=buffer,max=max,
|
||||||
|
"mplayer -really-quiet -ao pcm:file=/dev/stdout \
|
||||||
|
-vc null -vo null #{quote(s)} 2>/dev/null")
|
||||||
|
end
|
||||||
|
%endif
|
||||||
|
|
0
python_apps/pypo/liquidsoap_scripts/library/liquidsoap.initd
Executable file → Normal file
0
python_apps/pypo/liquidsoap_scripts/library/liquidsoap.initd
Executable file → Normal file
|
@ -1,33 +0,0 @@
|
||||||
set("log.file",false)
|
|
||||||
|
|
||||||
echo = fun (x) -> system("echo "^quote(x))
|
|
||||||
|
|
||||||
def test(lbl,f)
|
|
||||||
if f() then echo(lbl) else system("echo fail "^lbl) end
|
|
||||||
end
|
|
||||||
|
|
||||||
test("1",{ 1==1 })
|
|
||||||
test("2",{ 1+1==2 })
|
|
||||||
test("3",{ (-1)+2==1 })
|
|
||||||
test("4",{ (-1)+2 <= 3*2 })
|
|
||||||
test("5",{ true })
|
|
||||||
test("6",{ true and true })
|
|
||||||
test("7",{ 1==1 and 1==1 })
|
|
||||||
test("8",{ (1==1) and (1==1) })
|
|
||||||
test("9",{ true and (-1)+2 <= 3*2 })
|
|
||||||
|
|
||||||
l = [ ("bla",""), ("bli","x"), ("blo","xx"), ("blu","xxx"), ("dix","10") ]
|
|
||||||
echo(l["dix"])
|
|
||||||
test("11",{ 2 == list.length(string.split(separator="",l["blo"])) })
|
|
||||||
|
|
||||||
%ifdef foobarbaz
|
|
||||||
if = if is not a well-formed expression, and we do not care...
|
|
||||||
%endif
|
|
||||||
|
|
||||||
echo("1#{1+1}")
|
|
||||||
echo(string_of(int_of_float(float_of_string(default=13.,"blah"))))
|
|
||||||
|
|
||||||
f=fun(x)->x
|
|
||||||
# Checking that the following is not recursive:
|
|
||||||
f=fun(x)->f(x)
|
|
||||||
print(f(14))
|
|
|
@ -1,112 +0,0 @@
|
||||||
# Check these examples with: liquidsoap --no-libs -i -c typing.liq
|
|
||||||
|
|
||||||
# TODO Throughout this file, parsing locations displayed in error messages
|
|
||||||
# are often much too inaccurate.
|
|
||||||
|
|
||||||
set("log.file",false)
|
|
||||||
|
|
||||||
# Check that some polymorphism is allowed.
|
|
||||||
# id :: (string,'a)->'a
|
|
||||||
def id(a,b)
|
|
||||||
log(a)
|
|
||||||
b
|
|
||||||
end
|
|
||||||
ignore("bla"==id("bla","bla"))
|
|
||||||
ignore(0==id("zero",0))
|
|
||||||
|
|
||||||
# Reporting locations for the next error is non-trivial, because it is about
|
|
||||||
# an instantiation of the type of id. The deep error doesn't have relevant
|
|
||||||
# information about why the int should be a string, the outer one has.
|
|
||||||
# id(0,0)
|
|
||||||
|
|
||||||
# Polymorphism is limited to outer generalizations, this is not system F.
|
|
||||||
# apply :: ((string)->'a)->'a
|
|
||||||
def apply(f)
|
|
||||||
f("bla")
|
|
||||||
# f is not polymorphic, the following is forbidden:
|
|
||||||
# f(0)
|
|
||||||
# f(f)
|
|
||||||
end
|
|
||||||
|
|
||||||
# The level checks forbid abusive generalization.
|
|
||||||
# id' :: ('a)->'a
|
|
||||||
def id'(x)
|
|
||||||
# If one isn't careful about levels/scoping, f2 gets the type ('a)->'b
|
|
||||||
# and so does twisted_id.
|
|
||||||
def f2(y)
|
|
||||||
x
|
|
||||||
end
|
|
||||||
f2(x)
|
|
||||||
end
|
|
||||||
|
|
||||||
# More errors...
|
|
||||||
# 0=="0"
|
|
||||||
# [3,""]
|
|
||||||
|
|
||||||
# Subtyping, functions and lists.
|
|
||||||
f1 = fun () -> 3
|
|
||||||
f2 = fun (a=1) -> a
|
|
||||||
|
|
||||||
# This is OK, l1 is a list of elements of type f1.
|
|
||||||
l1 = [f1,f2]
|
|
||||||
list.iter(fun (f) -> log(string_of(f())), l1)
|
|
||||||
# Forbidden. Indeed, f1 doesn't accept any argument -- although f2 does.
|
|
||||||
# Here the error message may even be too detailed since it goes back to the
|
|
||||||
# definition of l1 and requires that f1 has type (int)->int.
|
|
||||||
# list.iter(fun (f) -> log(string_of(f(42))), l1)
|
|
||||||
|
|
||||||
# Actually, this is forbidden too, but the reason is more complex...
|
|
||||||
# The infered type for the function is ((int)->int)->unit,
|
|
||||||
# and (int)->int is not a subtype of (?int)->int.
|
|
||||||
# There's no most general answer here since (?int)->int is not a
|
|
||||||
# subtype of (int)->int either.
|
|
||||||
# list.iter(fun (f) -> log(string_of(f(42))), [f2])
|
|
||||||
|
|
||||||
# Unlike l1, this is not OK, since we don't leave open subtyping constraints
|
|
||||||
# while infering types.
|
|
||||||
# I hope we can make the inference smarter in the future, without obfuscating
|
|
||||||
# the error messages too much.
|
|
||||||
# The type error here shows the use of all the displayed positions:
|
|
||||||
# f1 has type t1, f2 has type t2, t1 should be <: t2
|
|
||||||
# l2 = [ f2, f1 ]
|
|
||||||
|
|
||||||
# An error where contravariance flips the roles of both sides..
|
|
||||||
# [fun (x) -> x+1, fun (y) -> y^"."]
|
|
||||||
|
|
||||||
# An error without much locations..
|
|
||||||
# TODO An explaination about the missing label would help a lot here.
|
|
||||||
# def f(f)
|
|
||||||
# f(output.icecast.vorbis)
|
|
||||||
# f(output.icecast.mp3)
|
|
||||||
# end
|
|
||||||
|
|
||||||
# This causes an occur-check error.
|
|
||||||
# TODO The printing of the types breaks the sharing of one EVAR
|
|
||||||
# across two types. Here the sharing is actually the origin of the occur-check
|
|
||||||
# error. And it's not easy to understand..
|
|
||||||
# omega = fun (x) -> x(x)
|
|
||||||
|
|
||||||
# Now let's test ad-hoc polymorphism.
|
|
||||||
|
|
||||||
echo = fun(x) -> system("echo #{quote(string_of(x))}")
|
|
||||||
|
|
||||||
echo("bla")
|
|
||||||
echo((1,3.12))
|
|
||||||
echo(1 + 1)
|
|
||||||
echo(1. + 2.14)
|
|
||||||
|
|
||||||
# string is not a Num
|
|
||||||
# echo("bl"+"a")
|
|
||||||
|
|
||||||
echo(1 <= 2)
|
|
||||||
echo((1,2) == (1,3))
|
|
||||||
|
|
||||||
# float <> int
|
|
||||||
# echo(1 == 2.)
|
|
||||||
|
|
||||||
# source is not an Ord
|
|
||||||
# echo(blank()==blank())
|
|
||||||
|
|
||||||
def sum_eq(a,b)
|
|
||||||
a+b == a
|
|
||||||
end
|
|
|
@ -300,6 +300,25 @@ def server.insert_metadata(~id="",s) =
|
||||||
s
|
s
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Register a command that outputs the RMS of the returned source.
|
||||||
|
# @category Source / Visualization
|
||||||
|
# @param ~id Force the value of the source ID.
|
||||||
|
def server.rms(~id="",s) =
|
||||||
|
x = rms(id=id,s)
|
||||||
|
rms = fst(x)
|
||||||
|
s = snd(x)
|
||||||
|
id = source.id(s)
|
||||||
|
def rms(_) =
|
||||||
|
rms = rms()
|
||||||
|
"#{rms}"
|
||||||
|
end
|
||||||
|
server.register(namespace="#{id}",
|
||||||
|
description="Return the current RMS of the source.",
|
||||||
|
usage="rms",
|
||||||
|
"rms",rms)
|
||||||
|
s
|
||||||
|
end
|
||||||
|
|
||||||
# Get the base name of a path.
|
# Get the base name of a path.
|
||||||
# Implemented using the corresponding shell command.
|
# Implemented using the corresponding shell command.
|
||||||
# @category System
|
# @category System
|
||||||
|
@ -479,59 +498,95 @@ def smart_crossfade (~start_next=5.,~fade_in=3.,~fade_out=3.,
|
||||||
end
|
end
|
||||||
|
|
||||||
# Custom playlist source written using the script language.
|
# Custom playlist source written using the script language.
|
||||||
# Will read directory or playlist, play all files and stop
|
# Will read directory or playlist, play all files and stop.
|
||||||
|
# Returns a pair @(reload,source)@ where @reload@ is a function
|
||||||
|
# of type @(?uri:string)->unit@ used to reload the source and @source@
|
||||||
|
# is the actual source. The reload function can optionally be called
|
||||||
|
# with a new playlist URI. Otherwise, it reloads the previous URI.
|
||||||
# @category Source / Input
|
# @category Source / Input
|
||||||
|
# @param ~id Force the value of the source ID.
|
||||||
# @param ~random Randomize playlist content
|
# @param ~random Randomize playlist content
|
||||||
# @param ~on_done Function to execute when the playlist is finished
|
# @param ~on_done Function to execute when the playlist is finished
|
||||||
# @param uri Playlist URI
|
# @param uri Playlist URI
|
||||||
def playlist.once(~random=false,~on_done={()},uri)
|
def playlist.reloadable(~id="",~random=false,~on_done={()},uri)
|
||||||
x = ref 0
|
# A reference to the playlist
|
||||||
def playlist.custom(files)
|
playlist = ref []
|
||||||
length = list.length(files)
|
# A reference to the uri
|
||||||
if length == 0 then
|
playlist_uri = ref uri
|
||||||
log("Empty playlist..")
|
# A reference to know if the source
|
||||||
fail ()
|
# has been stopped
|
||||||
else
|
has_stopped = ref false
|
||||||
files =
|
# The next function
|
||||||
if random then
|
def next () =
|
||||||
list.sort(fun (x,y) -> int_of_float(random.float()), files)
|
file =
|
||||||
else
|
if list.length(!playlist) > 0 then
|
||||||
files
|
ret = list.hd(!playlist)
|
||||||
end
|
playlist := list.tl(!playlist)
|
||||||
def next () =
|
ret
|
||||||
state = !x
|
else
|
||||||
file =
|
# Playlist finished
|
||||||
if state < length then
|
if not !has_stopped then
|
||||||
x := state + 1
|
|
||||||
list.nth(files,state)
|
|
||||||
else
|
|
||||||
# Playlist finished
|
|
||||||
on_done ()
|
on_done ()
|
||||||
""
|
|
||||||
end
|
end
|
||||||
request.create(file)
|
has_stopped := true
|
||||||
|
""
|
||||||
end
|
end
|
||||||
request.dynamic(next)
|
request.create(file)
|
||||||
|
end
|
||||||
|
# Instanciate the source
|
||||||
|
source = request.dynamic(id=id,next)
|
||||||
|
# Get its id.
|
||||||
|
id = source.id(source)
|
||||||
|
# The load function
|
||||||
|
def load_playlist () =
|
||||||
|
files =
|
||||||
|
if test_process("test -d #{quote(!playlist_uri)}") then
|
||||||
|
log(label=id,"playlist is a directory.")
|
||||||
|
get_process_lines("find #{quote(!playlist_uri)} -type f | sort")
|
||||||
|
else
|
||||||
|
playlist = request.create.raw(!playlist_uri)
|
||||||
|
result =
|
||||||
|
if request.resolve(playlist) then
|
||||||
|
playlist = request.filename(playlist)
|
||||||
|
files = playlist.parse(playlist)
|
||||||
|
list.map(snd,files)
|
||||||
|
else
|
||||||
|
log(label=id,"Couldn't read playlist: request resolution failed.")
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
request.destroy(playlist)
|
||||||
|
result
|
||||||
|
end
|
||||||
|
if random then
|
||||||
|
playlist := list.sort(fun (x,y) -> int_of_float(random.float()), files)
|
||||||
|
else
|
||||||
|
playlist := files
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if test_process("test -d #{quote(uri)}") then
|
# The reload function
|
||||||
files = get_process_lines("find #{quote(uri)} -type f | sort")
|
def reload(~uri="") =
|
||||||
playlist.custom(files)
|
if uri != "" then
|
||||||
else
|
playlist_uri := uri
|
||||||
playlist = request.create.raw(uri)
|
end
|
||||||
result =
|
log(label=id,"Reloading playlist with URI #{!playlist_uri}")
|
||||||
if request.resolve(playlist) then
|
has_stopped := false
|
||||||
playlist = request.filename(playlist)
|
load_playlist()
|
||||||
files = playlist.parse(playlist)
|
|
||||||
files = list.map(snd,files)
|
|
||||||
playlist.custom(files)
|
|
||||||
else
|
|
||||||
log("Couldn't read playlist: request resolution failed.")
|
|
||||||
fail ()
|
|
||||||
end
|
|
||||||
request.destroy(playlist)
|
|
||||||
result
|
|
||||||
end
|
end
|
||||||
|
# Load the playlist
|
||||||
|
load_playlist()
|
||||||
|
# Return
|
||||||
|
(reload,source)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Custom playlist source written using the script language.
|
||||||
|
# Will read directory or playlist, play all files and stop
|
||||||
|
# @category Source / Input
|
||||||
|
# @param ~id Force the value of the source ID.
|
||||||
|
# @param ~random Randomize playlist content
|
||||||
|
# @param ~on_done Function to execute when the playlist is finished
|
||||||
|
# @param uri Playlist URI
|
||||||
|
def playlist.once(~id="",~random=false,~on_done={()},uri)
|
||||||
|
snd(playlist.reloadable(id=id,random=random,on_done=on_done,uri))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Mixes two streams, with faded transitions between the state when only the
|
# Mixes two streams, with faded transitions between the state when only the
|
||||||
|
@ -588,7 +643,8 @@ def exec_at(~freq=1.,~pred,f)
|
||||||
add_timeout(freq,check)
|
add_timeout(freq,check)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Register the replaygain protocol
|
# Register the replaygain protocol.
|
||||||
|
# @category Liquidsoap
|
||||||
def replaygain_protocol(arg,delay)
|
def replaygain_protocol(arg,delay)
|
||||||
# The extraction program
|
# The extraction program
|
||||||
extract_replaygain = "#{configure.libdir}/extract-replaygain"
|
extract_replaygain = "#{configure.libdir}/extract-replaygain"
|
||||||
|
|
|
@ -7,6 +7,7 @@ set("server.telnet", true)
|
||||||
set("server.telnet.port", 1234)
|
set("server.telnet.port", 1234)
|
||||||
|
|
||||||
queue = request.queue(id="queue", length=0.5)
|
queue = request.queue(id="queue", length=0.5)
|
||||||
|
queue = cue_cut(queue)
|
||||||
queue = audio_to_stereo(queue)
|
queue = audio_to_stereo(queue)
|
||||||
|
|
||||||
pypo_data = ref '0'
|
pypo_data = ref '0'
|
||||||
|
|
|
@ -90,7 +90,6 @@ class Global:
|
||||||
print '*****************************************'
|
print '*****************************************'
|
||||||
print '\033[0;32m%s %s\033[m' % ('scheduled at:', str(pkey))
|
print '\033[0;32m%s %s\033[m' % ('scheduled at:', str(pkey))
|
||||||
print 'cached at : ' + self.cache_dir + str(pkey)
|
print 'cached at : ' + self.cache_dir + str(pkey)
|
||||||
print 'subtype: ' + str(playlist['subtype'])
|
|
||||||
print 'played: ' + str(playlist['played'])
|
print 'played: ' + str(playlist['played'])
|
||||||
print 'schedule id: ' + str(playlist['schedule_id'])
|
print 'schedule id: ' + str(playlist['schedule_id'])
|
||||||
print 'duration: ' + str(playlist['duration'])
|
print 'duration: ' + str(playlist['duration'])
|
||||||
|
|
|
@ -37,7 +37,6 @@ from configobj import ConfigObj
|
||||||
# custom imports
|
# custom imports
|
||||||
from util import *
|
from util import *
|
||||||
from api_clients import *
|
from api_clients import *
|
||||||
from dls import *
|
|
||||||
|
|
||||||
# Set up command-line options
|
# Set up command-line options
|
||||||
parser = OptionParser()
|
parser = OptionParser()
|
||||||
|
|
|
@ -4,7 +4,6 @@ import time
|
||||||
import logging
|
import logging
|
||||||
import logging.config
|
import logging.config
|
||||||
import shutil
|
import shutil
|
||||||
import pickle
|
|
||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
import json
|
import json
|
||||||
|
@ -12,6 +11,8 @@ import telnetlib
|
||||||
import math
|
import math
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from subprocess import Popen, PIPE
|
from subprocess import Popen, PIPE
|
||||||
|
from datetime import datetime
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
# For RabbitMQ
|
# For RabbitMQ
|
||||||
from kombu.connection import BrokerConnection
|
from kombu.connection import BrokerConnection
|
||||||
|
@ -99,18 +100,58 @@ class PypoFetch(Thread):
|
||||||
logger.error(" * See this page for more info (v1.7): http://wiki.sourcefabric.org/x/BQBF")
|
logger.error(" * See this page for more info (v1.7): http://wiki.sourcefabric.org/x/BQBF")
|
||||||
logger.error(" * and also the 'FAQ and Support' page underneath it.")
|
logger.error(" * and also the 'FAQ and Support' page underneath it.")
|
||||||
|
|
||||||
|
"""
|
||||||
|
def get_currently_scheduled(self, playlistsOrMedias, str_tnow_s):
|
||||||
|
for key in playlistsOrMedias:
|
||||||
|
start = playlistsOrMedias[key]['start']
|
||||||
|
end = playlistsOrMedias[key]['end']
|
||||||
|
|
||||||
|
if start <= str_tnow_s and str_tnow_s < end:
|
||||||
|
return key
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def handle_shows_currently_scheduled(self, playlists):
|
||||||
|
logger = logging.getLogger('fetch')
|
||||||
|
|
||||||
|
dtnow = datetime.today()
|
||||||
|
tnow = dtnow.timetuple()
|
||||||
|
str_tnow_s = "%04d-%02d-%02d-%02d-%02d-%02d" % (tnow[0], tnow[1], tnow[2], tnow[3], tnow[4], tnow[5])
|
||||||
|
|
||||||
|
current_pkey = self.get_currently_scheduled(playlists, str_tnow_s)
|
||||||
|
if current_pkey is not None:
|
||||||
|
logger.debug("FOUND CURRENT PLAYLIST %s", current_pkey)
|
||||||
|
# So we have found that a playlist if currently scheduled
|
||||||
|
# even though we just started pypo. Perhaps there was a
|
||||||
|
# system crash. Lets calculate what position in the playlist
|
||||||
|
# we are supposed to be in.
|
||||||
|
medias = playlists[current_pkey]["medias"]
|
||||||
|
current_mkey = self.get_currently_scheduled(medias, str_tnow_s)
|
||||||
|
if current_mkey is not None:
|
||||||
|
mkey_split = map(int, current_mkey.split('-'))
|
||||||
|
media_start = datetime(mkey_split[0], mkey_split[1], mkey_split[2], mkey_split[3], mkey_split[4], mkey_split[5])
|
||||||
|
logger.debug("Found media item that started at %s.", media_start)
|
||||||
|
|
||||||
|
delta = dtnow - media_start #we get a TimeDelta object from this operation
|
||||||
|
logger.info("Starting media item at %d second point", delta.seconds)
|
||||||
|
"""
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Process the schedule
|
Process the schedule
|
||||||
- Reads the scheduled entries of a given range (actual time +/- "prepare_ahead" / "cache_for")
|
- Reads the scheduled entries of a given range (actual time +/- "prepare_ahead" / "cache_for")
|
||||||
- Saves a serialized file of the schedule
|
- Saves a serialized file of the schedule
|
||||||
- playlists are prepared. (brought to liquidsoap format) and, if not mounted via nsf, files are copied
|
- playlists are prepared. (brought to liquidsoap format) and, if not mounted via nsf, files are copied
|
||||||
to the cache dir (Folder-structure: cache/YYYY-MM-DD-hh-mm-ss)
|
to the cache dir (Folder-structure: cache/YYYY-MM-DD-hh-mm-ss)
|
||||||
- runs the cleanup routine, to get rid of unused cashed files
|
- runs the cleanup routine, to get rid of unused cached files
|
||||||
"""
|
"""
|
||||||
def process_schedule(self, schedule_data, export_source):
|
def process_schedule(self, schedule_data, export_source, bootstrapping):
|
||||||
logger = logging.getLogger('fetch')
|
logger = logging.getLogger('fetch')
|
||||||
playlists = schedule_data["playlists"]
|
playlists = schedule_data["playlists"]
|
||||||
|
|
||||||
|
#if bootstrapping:
|
||||||
|
#TODO: possible allow prepare_playlists to handle this.
|
||||||
|
#self.handle_shows_currently_scheduled(playlists)
|
||||||
|
|
||||||
self.check_matching_timezones(schedule_data["server_timezone"])
|
self.check_matching_timezones(schedule_data["server_timezone"])
|
||||||
|
|
||||||
# Push stream metadata to liquidsoap
|
# Push stream metadata to liquidsoap
|
||||||
|
@ -127,9 +168,9 @@ class PypoFetch(Thread):
|
||||||
logger.error("Exception %s", e)
|
logger.error("Exception %s", e)
|
||||||
status = 0
|
status = 0
|
||||||
|
|
||||||
# Download all the media and put playlists in liquidsoap format
|
# Download all the media and put playlists in liquidsoap "annotate" format
|
||||||
try:
|
try:
|
||||||
liquidsoap_playlists = self.prepare_playlists(playlists)
|
liquidsoap_playlists = self.prepare_playlists(playlists, bootstrapping)
|
||||||
except Exception, e: logger.error("%s", e)
|
except Exception, e: logger.error("%s", e)
|
||||||
|
|
||||||
# Send the data to pypo-push
|
# Send the data to pypo-push
|
||||||
|
@ -149,7 +190,7 @@ class PypoFetch(Thread):
|
||||||
and stored in a playlist folder.
|
and stored in a playlist folder.
|
||||||
file is e.g. 2010-06-23-15-00-00/17_cue_10.132-123.321.mp3
|
file is e.g. 2010-06-23-15-00-00/17_cue_10.132-123.321.mp3
|
||||||
"""
|
"""
|
||||||
def prepare_playlists(self, playlists):
|
def prepare_playlists(self, playlists, bootstrapping):
|
||||||
logger = logging.getLogger('fetch')
|
logger = logging.getLogger('fetch')
|
||||||
|
|
||||||
liquidsoap_playlists = dict()
|
liquidsoap_playlists = dict()
|
||||||
|
@ -170,27 +211,18 @@ class PypoFetch(Thread):
|
||||||
try:
|
try:
|
||||||
os.mkdir(self.cache_dir + str(pkey))
|
os.mkdir(self.cache_dir + str(pkey))
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
pass
|
logger.error(e)
|
||||||
|
|
||||||
#logger.debug('*****************************************')
|
#June 13, 2011: Commented this block out since we are not currently setting this to '1'
|
||||||
#logger.debug('pkey: ' + str(pkey))
|
#on the server side. Currently using a different method to detect if already played - Martin
|
||||||
#logger.debug('cached at : ' + self.cache_dir + str(pkey))
|
#if int(playlist['played']) == 1:
|
||||||
#logger.debug('subtype: ' + str(playlist['subtype']))
|
# logger.info("playlist %s already played / sent to liquidsoap, so will ignore it", pkey)
|
||||||
#logger.debug('played: ' + str(playlist['played']))
|
|
||||||
#logger.debug('schedule id: ' + str(playlist['schedule_id']))
|
|
||||||
#logger.debug('duration: ' + str(playlist['duration']))
|
|
||||||
#logger.debug('source id: ' + str(playlist['x_ident']))
|
|
||||||
#logger.debug('*****************************************')
|
|
||||||
|
|
||||||
if int(playlist['played']) == 1:
|
ls_playlist = self.handle_media_file(playlist, pkey, bootstrapping)
|
||||||
logger.info("playlist %s already played / sent to liquidsoap, so will ignore it", pkey)
|
|
||||||
|
|
||||||
elif int(playlist['subtype']) > 0 and int(playlist['subtype']) < 5:
|
|
||||||
ls_playlist = self.handle_media_file(playlist, pkey)
|
|
||||||
|
|
||||||
liquidsoap_playlists[pkey] = ls_playlist
|
liquidsoap_playlists[pkey] = ls_playlist
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
logger.info("%s", e)
|
logger.error("%s", e)
|
||||||
return liquidsoap_playlists
|
return liquidsoap_playlists
|
||||||
|
|
||||||
|
|
||||||
|
@ -199,27 +231,47 @@ class PypoFetch(Thread):
|
||||||
This handles both remote and local files.
|
This handles both remote and local files.
|
||||||
Returns an updated ls_playlist string.
|
Returns an updated ls_playlist string.
|
||||||
"""
|
"""
|
||||||
def handle_media_file(self, playlist, pkey):
|
def handle_media_file(self, playlist, pkey, bootstrapping):
|
||||||
|
logger = logging.getLogger('fetch')
|
||||||
|
|
||||||
ls_playlist = []
|
ls_playlist = []
|
||||||
|
|
||||||
logger = logging.getLogger('fetch')
|
dtnow = datetime.today()
|
||||||
for media in playlist['medias']:
|
str_tnow_s = dtnow.strftime('%Y-%m-%d-%H-%M-%S')
|
||||||
|
|
||||||
|
sortedKeys = sorted(playlist['medias'].iterkeys())
|
||||||
|
|
||||||
|
for key in sortedKeys:
|
||||||
|
media = playlist['medias'][key]
|
||||||
logger.debug("Processing track %s", media['uri'])
|
logger.debug("Processing track %s", media['uri'])
|
||||||
|
|
||||||
|
if bootstrapping:
|
||||||
|
start = media['start']
|
||||||
|
end = media['end']
|
||||||
|
|
||||||
|
if end <= str_tnow_s:
|
||||||
|
continue
|
||||||
|
elif start <= str_tnow_s and str_tnow_s < end:
|
||||||
|
#song is currently playing and we just started pypo. Maybe there
|
||||||
|
#was a power outage? Let's restart playback of this song.
|
||||||
|
start_split = map(int, start.split('-'))
|
||||||
|
media_start = datetime(start_split[0], start_split[1], start_split[2], start_split[3], start_split[4], start_split[5])
|
||||||
|
logger.debug("Found media item that started at %s.", media_start)
|
||||||
|
|
||||||
|
delta = dtnow - media_start #we get a TimeDelta object from this operation
|
||||||
|
logger.info("Starting media item at %d second point", delta.seconds)
|
||||||
|
media['cue_in'] = delta.seconds + 10
|
||||||
|
td = timedelta(seconds=10)
|
||||||
|
playlist['start'] = (dtnow + td).strftime('%Y-%m-%d-%H-%M-%S')
|
||||||
|
logger.info("Crash detected, setting playlist to restart at %s", (dtnow + td).strftime('%Y-%m-%d-%H-%M-%S'))
|
||||||
|
|
||||||
|
|
||||||
fileExt = os.path.splitext(media['uri'])[1]
|
fileExt = os.path.splitext(media['uri'])[1]
|
||||||
try:
|
try:
|
||||||
if str(media['cue_in']) == '0' and str(media['cue_out']) == '0':
|
dst = "%s%s/%s%s" % (self.cache_dir, str(pkey), str(media['id']), str(fileExt))
|
||||||
#logger.debug('No cue in/out detected for this file')
|
|
||||||
dst = "%s%s/%s%s" % (self.cache_dir, str(pkey), str(media['id']), str(fileExt))
|
|
||||||
do_cue = False
|
|
||||||
else:
|
|
||||||
#logger.debug('Cue in/out detected')
|
|
||||||
dst = "%s%s/%s_cue_%s-%s%s" % \
|
|
||||||
(self.cache_dir, str(pkey), str(media['id']), str(float(media['cue_in']) / 1000), str(float(media['cue_out']) / 1000), str(fileExt))
|
|
||||||
do_cue = True
|
|
||||||
|
|
||||||
# download media file
|
# download media file
|
||||||
self.handle_remote_file(media, dst, do_cue)
|
self.handle_remote_file(media, dst)
|
||||||
|
|
||||||
if True == os.access(dst, os.R_OK):
|
if True == os.access(dst, os.R_OK):
|
||||||
# check filesize (avoid zero-byte files)
|
# check filesize (avoid zero-byte files)
|
||||||
|
@ -230,11 +282,13 @@ class PypoFetch(Thread):
|
||||||
|
|
||||||
if fsize > 0:
|
if fsize > 0:
|
||||||
pl_entry = \
|
pl_entry = \
|
||||||
'annotate:export_source="%s",media_id="%s",liq_start_next="%s",liq_fade_in="%s",liq_fade_out="%s",schedule_table_id="%s":%s'\
|
'annotate:export_source="%s",media_id="%s",liq_start_next="%s",liq_fade_in="%s",liq_fade_out="%s",liq_cue_in="%s",liq_cue_out="%s",schedule_table_id="%s":%s' \
|
||||||
% (str(media['export_source']), media['id'], 0, str(float(media['fade_in']) / 1000), \
|
% (str(media['export_source']), media['id'], 0, \
|
||||||
str(float(media['fade_out']) / 1000), media['row_id'],dst)
|
str(float(media['fade_in']) / 1000), \
|
||||||
|
str(float(media['fade_out']) / 1000), \
|
||||||
#logger.debug(pl_entry)
|
str(float(media['cue_in'])), \
|
||||||
|
str(float(media['cue_out'])), \
|
||||||
|
media['row_id'], dst)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Tracks are only added to the playlist if they are accessible
|
Tracks are only added to the playlist if they are accessible
|
||||||
|
@ -248,7 +302,6 @@ class PypoFetch(Thread):
|
||||||
entry['show_name'] = playlist['show_name']
|
entry['show_name'] = playlist['show_name']
|
||||||
ls_playlist.append(entry)
|
ls_playlist.append(entry)
|
||||||
|
|
||||||
#logger.debug("everything ok, adding %s to playlist", pl_entry)
|
|
||||||
else:
|
else:
|
||||||
logger.warning("zero-size file - skipping %s. will not add it to playlist at %s", media['uri'], dst)
|
logger.warning("zero-size file - skipping %s. will not add it to playlist at %s", media['uri'], dst)
|
||||||
|
|
||||||
|
@ -262,51 +315,14 @@ class PypoFetch(Thread):
|
||||||
"""
|
"""
|
||||||
Download a file from a remote server and store it in the cache.
|
Download a file from a remote server and store it in the cache.
|
||||||
"""
|
"""
|
||||||
def handle_remote_file(self, media, dst, do_cue):
|
def handle_remote_file(self, media, dst):
|
||||||
logger = logging.getLogger('fetch')
|
logger = logging.getLogger('fetch')
|
||||||
if do_cue == False:
|
if os.path.isfile(dst):
|
||||||
if os.path.isfile(dst):
|
pass
|
||||||
pass
|
#logger.debug("file already in cache: %s", dst)
|
||||||
#logger.debug("file already in cache: %s", dst)
|
|
||||||
else:
|
|
||||||
logger.debug("try to download %s", media['uri'])
|
|
||||||
self.api_client.get_media(media['uri'], dst)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if os.path.isfile(dst):
|
logger.debug("try to download %s", media['uri'])
|
||||||
logger.debug("file already in cache: %s", dst)
|
self.api_client.get_media(media['uri'], dst)
|
||||||
|
|
||||||
else:
|
|
||||||
logger.debug("try to download and cue %s", media['uri'])
|
|
||||||
|
|
||||||
fileExt = os.path.splitext(media['uri'])[1]
|
|
||||||
dst_tmp = config["tmp_dir"] + "".join([random.choice(string.letters) for i in xrange(10)]) + fileExt
|
|
||||||
self.api_client.get_media(media['uri'], dst_tmp)
|
|
||||||
|
|
||||||
# cue
|
|
||||||
logger.debug("STARTING CUE")
|
|
||||||
debugDst = self.cue_file.cue(dst_tmp, dst, float(media['cue_in']) / 1000, float(media['cue_out']) / 1000)
|
|
||||||
logger.debug(debugDst)
|
|
||||||
logger.debug("END CUE")
|
|
||||||
|
|
||||||
if True == os.access(dst, os.R_OK):
|
|
||||||
try: fsize = os.path.getsize(dst)
|
|
||||||
except Exception, e:
|
|
||||||
logger.error("%s", e)
|
|
||||||
fsize = 0
|
|
||||||
|
|
||||||
if fsize > 0:
|
|
||||||
logger.debug('try to remove temporary file: %s' + dst_tmp)
|
|
||||||
try: os.remove(dst_tmp)
|
|
||||||
except Exception, e:
|
|
||||||
logger.error("%s", e)
|
|
||||||
|
|
||||||
else:
|
|
||||||
logger.warning('something went wrong cueing: %s - using uncued file' + dst)
|
|
||||||
try: os.rename(dst_tmp, dst)
|
|
||||||
except Exception, e:
|
|
||||||
logger.error("%s", e)
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Cleans up folders in cache_dir. Look for modification date older than "now - CACHE_FOR"
|
Cleans up folders in cache_dir. Look for modification date older than "now - CACHE_FOR"
|
||||||
|
@ -354,7 +370,7 @@ class PypoFetch(Thread):
|
||||||
# most recent schedule. After that we can just wait for updates.
|
# most recent schedule. After that we can just wait for updates.
|
||||||
status, schedule_data = self.api_client.get_schedule()
|
status, schedule_data = self.api_client.get_schedule()
|
||||||
if status == 1:
|
if status == 1:
|
||||||
self.process_schedule(schedule_data, "scheduler")
|
self.process_schedule(schedule_data, "scheduler", True)
|
||||||
logger.info("Bootstrap complete: got initial copy of the schedule")
|
logger.info("Bootstrap complete: got initial copy of the schedule")
|
||||||
|
|
||||||
loops = 1
|
loops = 1
|
||||||
|
@ -373,6 +389,6 @@ class PypoFetch(Thread):
|
||||||
status, schedule_data = self.api_client.get_schedule()
|
status, schedule_data = self.api_client.get_schedule()
|
||||||
|
|
||||||
if status == 1:
|
if status == 1:
|
||||||
self.process_schedule(schedule_data, "scheduler")
|
self.process_schedule(schedule_data, "scheduler", False)
|
||||||
loops += 1
|
loops += 1
|
||||||
|
|
||||||
|
|
|
@ -97,9 +97,11 @@ class PypoPush(Thread):
|
||||||
str_tnow_s = "%04d-%02d-%02d-%02d-%02d-%02d" % (tnow[0], tnow[1], tnow[2], tnow[3], tnow[4], tnow[5])
|
str_tnow_s = "%04d-%02d-%02d-%02d-%02d-%02d" % (tnow[0], tnow[1], tnow[2], tnow[3], tnow[4], tnow[5])
|
||||||
|
|
||||||
for pkey in schedule:
|
for pkey in schedule:
|
||||||
plstart = pkey[0:19]
|
plstart = schedule[pkey]['start'][0:19]
|
||||||
|
#plstart = pkey[0:19]
|
||||||
|
|
||||||
playedFlag = (pkey in playedItems) and playedItems[pkey].get("played", 0)
|
#playedFlag = (pkey in playedItems) and playedItems[pkey].get("played", 0)
|
||||||
|
playedFlag = False
|
||||||
|
|
||||||
if plstart == str_tcoming_s or (plstart < str_tcoming_s and plstart > str_tcoming2_s and not playedFlag):
|
if plstart == str_tcoming_s or (plstart < str_tcoming_s and plstart > str_tcoming2_s and not playedFlag):
|
||||||
logger.debug('Preparing to push playlist scheduled at: %s', pkey)
|
logger.debug('Preparing to push playlist scheduled at: %s', pkey)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
||||||
def remove_user(username):
|
def remove_user(username):
|
||||||
|
|
|
@ -11,17 +11,20 @@
|
||||||
|
|
||||||
USERID=pypo
|
USERID=pypo
|
||||||
GROUPID=pypo
|
GROUPID=pypo
|
||||||
NAME=Airtime
|
NAME=Airtime\ Show\ Recorder
|
||||||
|
|
||||||
DAEMON=/usr/bin/airtime-show-recorder
|
DAEMON=/usr/lib/airtime/show-recorder/airtime-show-recorder
|
||||||
PIDFILE=/var/run/airtime-show-recorder.pid
|
PIDFILE=/var/run/airtime-show-recorder.pid
|
||||||
|
|
||||||
start () {
|
start () {
|
||||||
|
monit monitor airtime-show-recorder
|
||||||
start-stop-daemon --start --background --quiet --chuid $USERID:$GROUPID --make-pidfile --pidfile $PIDFILE --startas $DAEMON
|
start-stop-daemon --start --background --quiet --chuid $USERID:$GROUPID --make-pidfile --pidfile $PIDFILE --startas $DAEMON
|
||||||
}
|
}
|
||||||
|
|
||||||
stop () {
|
stop () {
|
||||||
# Send TERM after 5 seconds, wait at most 30 seconds.
|
# Send TERM after 5 seconds, wait at most 30 seconds.
|
||||||
|
|
||||||
|
monit unmonitor airtime-show-recorder
|
||||||
start-stop-daemon --stop --oknodo --retry TERM/5/0/30 --quiet --pidfile $PIDFILE
|
start-stop-daemon --stop --oknodo --retry TERM/5/0/30 --quiet --pidfile $PIDFILE
|
||||||
rm -f $PIDFILE
|
rm -f $PIDFILE
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,10 +63,6 @@ try:
|
||||||
os.system("chmod -R 755 "+config["bin_dir"])
|
os.system("chmod -R 755 "+config["bin_dir"])
|
||||||
os.system("chown -R pypo:pypo "+config["bin_dir"])
|
os.system("chown -R pypo:pypo "+config["bin_dir"])
|
||||||
|
|
||||||
print "Creating symbolic links"
|
|
||||||
os.system("rm -f /usr/bin/airtime-show-recorder")
|
|
||||||
os.system("ln -s "+config["bin_dir"]+"/airtime-show-recorder /usr/bin/")
|
|
||||||
|
|
||||||
print "Installing show-recorder daemon"
|
print "Installing show-recorder daemon"
|
||||||
shutil.copy(config["bin_dir"]+"/airtime-show-recorder-init-d", "/etc/init.d/airtime-show-recorder")
|
shutil.copy(config["bin_dir"]+"/airtime-show-recorder-init-d", "/etc/init.d/airtime-show-recorder")
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ if os.geteuid() != 0:
|
||||||
PATH_INI_FILE = '/etc/airtime/recorder.cfg'
|
PATH_INI_FILE = '/etc/airtime/recorder.cfg'
|
||||||
|
|
||||||
def remove_path(path):
|
def remove_path(path):
|
||||||
os.system("rm -rf " + path)
|
os.system('rm -rf "%s"' % path)
|
||||||
|
|
||||||
def get_current_script_dir():
|
def get_current_script_dir():
|
||||||
current_script_dir = os.path.realpath(__file__)
|
current_script_dir = os.path.realpath(__file__)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue