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:
martin 2011-06-17 15:10:33 -04:00
commit 2e4519f156
32 changed files with 401 additions and 389 deletions

View file

@ -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;
}
} }

View file

@ -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;

View file

@ -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();

View file

@ -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

View file

@ -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");

View file

@ -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);
} }
} }

View file

@ -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\"");
} }
} }

View file

@ -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();

View file

@ -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']

View file

@ -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):

View file

@ -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
} }

View file

@ -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")

View file

@ -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__)

View 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

View file

@ -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
} }

View file

@ -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")

View file

@ -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__)

View 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

View file

@ -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

View file

View 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))

View file

@ -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

View file

@ -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"

View file

@ -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'

View file

@ -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'])

View file

@ -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()

View file

@ -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

View file

@ -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)

View file

@ -1,4 +1,5 @@
import os import os
import sys
import time import time
def remove_user(username): def remove_user(username):

View file

@ -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
} }

View file

@ -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")

View file

@ -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__)