diff --git a/airtime_mvc/application/models/DateHelper.php b/airtime_mvc/application/models/DateHelper.php index d8f44a3c5..bc7b21a86 100644 --- a/airtime_mvc/application/models/DateHelper.php +++ b/airtime_mvc/application/models/DateHelper.php @@ -127,5 +127,35 @@ class DateHelper $explode = explode(" ", $p_timestamp); 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; + } } diff --git a/airtime_mvc/application/models/Schedule.php b/airtime_mvc/application/models/Schedule.php index 5676da420..d4750fb09 100644 --- a/airtime_mvc/application/models/Schedule.php +++ b/airtime_mvc/application/models/Schedule.php @@ -184,7 +184,9 @@ class ScheduleGroup { ." st.cue_out," ." st.clip_length," ." st.fade_in," - ." st.fade_out" + ." st.fade_out," + ." st.starts," + ." st.ends" ." FROM $CC_CONFIG[scheduleTable] as st" ." LEFT JOIN $CC_CONFIG[showInstances] as si" ." ON st.instance_id = si.id" @@ -670,7 +672,7 @@ class Schedule { $timestamp = strtotime($start); $playlists[$pkey]['source'] = "PLAYLIST"; $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]['duration'] = $dx['clip_length']; $playlists[$pkey]['played'] = '0'; @@ -690,27 +692,24 @@ class Schedule { $scheduleGroup = new ScheduleGroup($playlist["schedule_id"]); $items = $scheduleGroup->getItems(); $medias = array(); - $playlist['subtype'] = '1'; foreach ($items as $item) { $storedFile = StoredFile::Recall($item["file_id"]); $uri = $storedFile->getFileUrl(); - // For pypo, a cueout of zero means no cueout - $cueOut = "0"; - if (Schedule::TimeDiff($item["cue_out"], $item["clip_length"]) > 0.001) { - $cueOut = Schedule::WallTimeToMillisecs($item["cue_out"]); - } - $medias[] = array( + $starts = Schedule::AirtimeTimeToPypoTime($item["starts"]); + $medias[$starts] = array( 'row_id' => $item["id"], 'id' => $storedFile->getGunid(), 'uri' => $uri, 'fade_in' => Schedule::WallTimeToMillisecs($item["fade_in"]), 'fade_out' => Schedule::WallTimeToMillisecs($item["fade_out"]), 'fade_cross' => 0, - 'cue_in' => Schedule::WallTimeToMillisecs($item["cue_in"]), - 'cue_out' => $cueOut, - 'export_source' => 'scheduler' + 'cue_in' => DateHelper::CalculateLengthInSeconds($item["cue_in"]), + 'cue_out' => DateHelper::CalculateLengthInSeconds($item["cue_out"]), + 'export_source' => 'scheduler', + 'start' => $starts, + 'end' => Schedule::AirtimeTimeToPypoTime($item["ends"]) ); } $playlist['medias'] = $medias; diff --git a/install/airtime-install.php b/install/airtime-install.php index cb4a0c90f..ebec6de04 100644 --- a/install/airtime-install.php +++ b/install/airtime-install.php @@ -97,6 +97,8 @@ if ($overwrite) { echo "* Creating INI files".PHP_EOL; AirtimeIni::CreateIniFiles(); } +AirtimeIni::CreateMonitFile(); + AirtimeInstall::InstallPhpCode(); AirtimeInstall::InstallBinaries(); diff --git a/install/airtime-uninstall b/install/airtime-uninstall index b65700d00..6bfb48c21 100755 --- a/install/airtime-uninstall +++ b/install/airtime-uninstall @@ -1,5 +1,9 @@ #!/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 SCRIPT=`readlink -f $0` # Absolute directory this script is in diff --git a/install/airtime-upgrade.php b/install/airtime-upgrade.php index 109a978ec..1cf4b8a3e 100644 --- a/install/airtime-upgrade.php +++ b/install/airtime-upgrade.php @@ -96,7 +96,7 @@ passthru("python ".__DIR__."/../python_apps/pypo/install/pypo-install.py"); echo PHP_EOL."*** Updating Recorder ***".PHP_EOL; 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"); diff --git a/install/include/AirtimeIni.php b/install/include/AirtimeIni.php index dd228f997..1dc2b7d19 100644 --- a/install/include/AirtimeIni.php +++ b/install/include/AirtimeIni.php @@ -27,6 +27,7 @@ class AirtimeIni const CONF_FILE_RECORDER = "/etc/airtime/recorder.cfg"; const CONF_FILE_LIQUIDSOAP = "/etc/airtime/liquidsoap.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() { @@ -75,7 +76,14 @@ class AirtimeIni exit(1); } 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); } } diff --git a/install/include/AirtimeInstall.php b/install/include/AirtimeInstall.php index dbbff57b6..6b5ef787c 100644 --- a/install/include/AirtimeInstall.php +++ b/install/include/AirtimeInstall.php @@ -277,7 +277,7 @@ class AirtimeInstall public static function DeleteFilesRecursive($p_path) { - $command = "rm -rf $p_path"; + $command = "rm -rf \"$p_path\""; exec($command); } @@ -323,7 +323,7 @@ class AirtimeInstall public static function UninstallPhpCode() { 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() @@ -336,7 +336,7 @@ class AirtimeInstall public static function UninstallBinaries() { 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() @@ -386,6 +386,6 @@ class AirtimeInstall $path = AirtimeInstall::CONF_DIR_LOG; echo "* Removing logs directory ".$path.PHP_EOL; - exec("rm -rf $path"); + exec("rm -rf \"$path\""); } } diff --git a/install/upgrades/airtime-1.9/airtime-upgrade.php b/install/upgrades/airtime-1.9/airtime-upgrade.php index 0727c9c64..d988fb806 100644 --- a/install/upgrades/airtime-1.9/airtime-upgrade.php +++ b/install/upgrades/airtime-1.9/airtime-upgrade.php @@ -39,7 +39,7 @@ function InstallBinaries() function UninstallBinaries() { 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){ 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']; InstallPhpCode($phpDir); +AirtimeIni::CreateMonitFile(); //update utils (/usr/lib/airtime) folder UninstallBinaries(); diff --git a/python_apps/api_clients/api_client.py b/python_apps/api_clients/api_client.py index 3394c8fdb..abf58917c 100644 --- a/python_apps/api_clients/api_client.py +++ b/python_apps/api_clients/api_client.py @@ -619,7 +619,7 @@ class ObpApiClient(): def get_liquidsoap_data(self, pkey, schedule): playlist = schedule[pkey] data = dict() - data["ptype"] = playlist['subtype'] + #data["ptype"] = playlist['subtype'] try: data["user_id"] = playlist['user_id'] data["playlist_id"] = playlist['id'] diff --git a/python_apps/create-pypo-user.py b/python_apps/create-pypo-user.py index 38619a0f3..46a7bb1b9 100644 --- a/python_apps/create-pypo-user.py +++ b/python_apps/create-pypo-user.py @@ -1,4 +1,5 @@ import os +import sys from subprocess import Popen, PIPE, STDOUT def create_user(username): diff --git a/python_apps/media-monitor/airtime-media-monitor-init-d b/python_apps/media-monitor/airtime-media-monitor-init-d index 1fde178e1..2c088938c 100755 --- a/python_apps/media-monitor/airtime-media-monitor-init-d +++ b/python_apps/media-monitor/airtime-media-monitor-init-d @@ -11,17 +11,19 @@ USERID=root 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 start () { + monit monitor airtime-media-monitor start-stop-daemon --start --background --quiet --chuid $USERID:$GROUPID --make-pidfile --pidfile $PIDFILE --startas $DAEMON } stop () { # 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 rm -f $PIDFILE } diff --git a/python_apps/media-monitor/install/media-monitor-install.py b/python_apps/media-monitor/install/media-monitor-install.py index 730c8e395..ad0ef1b07 100755 --- a/python_apps/media-monitor/install/media-monitor-install.py +++ b/python_apps/media-monitor/install/media-monitor-install.py @@ -66,10 +66,6 @@ try: #os.system("chmod -R 755 "+config["bin_dir"]+"/airtime-media-monitor) 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" shutil.copy(config["bin_dir"]+"/airtime-media-monitor-init-d", "/etc/init.d/airtime-media-monitor") diff --git a/python_apps/media-monitor/install/media-monitor-uninstall.py b/python_apps/media-monitor/install/media-monitor-uninstall.py index d2732bffb..4f587d46b 100755 --- a/python_apps/media-monitor/install/media-monitor-uninstall.py +++ b/python_apps/media-monitor/install/media-monitor-uninstall.py @@ -12,7 +12,7 @@ if os.geteuid() != 0: PATH_INI_FILE = '/etc/airtime/media-monitor.cfg' def remove_path(path): - os.system("rm -rf " + path) + os.system('rm -rf "%s"' % path) def get_current_script_dir(): current_script_dir = os.path.realpath(__file__) diff --git a/python_apps/monit/airtime-monit.cfg b/python_apps/monit/airtime-monit.cfg index 10f328e3f..647fffd10 100644 --- a/python_apps/monit/airtime-monit.cfg +++ b/python_apps/monit/airtime-monit.cfg @@ -1,5 +1,10 @@ set daemon 10 # Poll at 10 second intervals 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 with pidfile "/var/run/airtime-playout.pid" start program = "/etc/init.d/airtime-playout start" with timeout 10 seconds diff --git a/python_apps/pypo/airtime-playout-init-d b/python_apps/pypo/airtime-playout-init-d index 5634baa13..038a83a42 100755 --- a/python_apps/pypo/airtime-playout-init-d +++ b/python_apps/pypo/airtime-playout-init-d @@ -11,24 +11,31 @@ USERID=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 -DAEMON1=/usr/bin/airtime-liquidsoap +DAEMON1=/usr/lib/airtime/pypo/bin/airtime-liquidsoap 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 + + monit monitor airtime-liquidsoap start-stop-daemon --start --background --quiet --chuid $USERID:$GROUPID --make-pidfile --pidfile $PIDFILE1 --startas $DAEMON1 } stop () { # 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 $PIDFILE1 rm -f $PIDFILE0 + + monit unmonitor airtime-liquidsoap + start-stop-daemon --stop --oknodo --retry TERM/5/0/30 --quiet --pidfile $PIDFILE1 rm -f $PIDFILE1 } diff --git a/python_apps/pypo/install/pypo-install.py b/python_apps/pypo/install/pypo-install.py index 32f3f873a..c24ec30bd 100755 --- a/python_apps/pypo/install/pypo-install.py +++ b/python_apps/pypo/install/pypo-install.py @@ -103,12 +103,6 @@ try: os.system("chown -R pypo:pypo "+config["bin_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" shutil.copy(config["bin_dir"]+"/bin/airtime-playout-init-d", "/etc/init.d/airtime-playout") diff --git a/python_apps/pypo/install/pypo-uninstall.py b/python_apps/pypo/install/pypo-uninstall.py index bfa77eb0e..7b7cf881f 100755 --- a/python_apps/pypo/install/pypo-uninstall.py +++ b/python_apps/pypo/install/pypo-uninstall.py @@ -12,7 +12,7 @@ if os.geteuid() != 0: PATH_INI_FILE = '/etc/airtime/pypo.cfg' def remove_path(path): - os.system("rm -rf " + path) + os.system('rm -rf "%s"' % path) def get_current_script_dir(): current_script_dir = os.path.realpath(__file__) diff --git a/python_apps/pypo/liquidsoap_scripts/library/Makefile b/python_apps/pypo/liquidsoap_scripts/library/Makefile index cc4c77cda..d10777180 100644 --- a/python_apps/pypo/liquidsoap_scripts/library/Makefile +++ b/python_apps/pypo/liquidsoap_scripts/library/Makefile @@ -1,4 +1,5 @@ +SUBDIRS = tests DISTFILES = $(wildcard *.in) Makefile ask-liquidsoap.rb ask-liquidsoap.pl \ $(wildcard *.liq) extract-replaygain diff --git a/python_apps/pypo/liquidsoap_scripts/library/externals.liq b/python_apps/pypo/liquidsoap_scripts/library/externals.liq index ede1d2e3d..1a50f4b95 100644 --- a/python_apps/pypo/liquidsoap_scripts/library/externals.liq +++ b/python_apps/pypo/liquidsoap_scripts/library/externals.liq @@ -9,37 +9,43 @@ my_get_mime = get_mime get_mime = my_get_mime %ifdef add_decoder -if test_process("which flac") then - log(level=3,"Found flac binary: enabling flac external decoder.") - flac_p = "flac -d -c - 2>/dev/null" - def test_flac(file) = - if test_process("which metaflac") then - channels = list.hd(get_process_lines("metaflac \ - --show-channels #{quote(file)} \ - 2>/dev/null")) - # If the value is not an int, this returns 0 and we are ok :) - int_of_string(channels) - else - # Try to detect using mime test.. - 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) +# Enable external FLAC decoders. Requires flac binary +# in the path for audio decoding and metaflac binary for +# metadata. Does not work on Win32. Default: disabled. +# Please note that built-in support for FLAC is available +# in liquidsoap if compiled and should be preferred over +# the external decoder. +# @category Liquidsoap +def enable_external_flac_decoder() = + if test_process("which flac") then + log(level=3,"Found flac binary: enabling flac external decoder.") + flac_p = "flac -d -c - 2>/dev/null" + def test_flac(file) = + if test_process("which metaflac") then + channels = list.hd(get_process_lines("metaflac \ + --show-channels #{quote(file)} \ + 2>/dev/null")) + # If the value is not an int, this returns 0 and we are ok :) + int_of_string(channels) else - # All tests failed: no audio decodable using flac.. - 0 + # Try to detect using mime test.. + 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 + 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 - 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 log(level=3,"Found metaflac binary: enabling flac external metadata \ resolver.") @@ -55,49 +61,59 @@ if os.type != "Win32" then if list.length(l) >= 1 then list.append([(list.hd(l),"")],l') else - l' - end + l' end end - list.fold(f,[],ret) + end + list.fold(f,[],ret) end add_metadata_resolver("FLAC",flac_meta) 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 +%endif -# A list of know extensions and content-type for AAC. -# Values from http://en.wikipedia.org/wiki/Advanced_Audio_Coding -# TODO: can we register a setting for that ?? -aac_mimes = - ["audio/aac", "audio/aacp", "audio/3gpp", "audio/3gpp2", "audio/mp4", - "audio/MP4A-LATM", "audio/mpeg4-generic", "audio/x-hx-aac-adts"] -aac_filexts = ["m4a", "m4b", "m4p", "m4v", - "m4r", "3gp", "mp4", "aac"] +%ifdef add_oblivious_decoder +# Enable or disable external FAAD (AAC/AAC+/M4A) decoders. +# Requires faad binary in the path for audio decoding and +# metaflac binary for metadata. Does not work on Win32. +# Please note that built-in support for faad is available +# in liquidsoap if compiled and should be preferred over +# the external decoder. +# @category Liquidsoap +def enable_external_faad_decoder() = -# Faad is not very selective so -# We are checking only file that -# 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) + # A list of know extensions and content-type for AAC. + # Values from http://en.wikipedia.org/wiki/Advanced_Audio_Coding + # TODO: can we register a setting for that ?? + aac_mimes = + ["audio/aac", "audio/aacp", "audio/3gpp", "audio/3gpp2", "audio/mp4", + "audio/MP4A-LATM", "audio/mpeg4-generic", "audio/x-hx-aac-adts"] + aac_filexts = ["m4a", "m4b", "m4p", "m4v", + "m4r", "3gp", "mp4", "aac"] + + # Faad is not very selective so + # We are checking only file that + # 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 - ext = ret["1"] - list.mem(ext,aac_filexts) - else - false - end + ext = ret["1"] + list.mem(ext,aac_filexts) + else + false + end + end end -end -if os.type != "Win32" then if test_process("which faad") then log(level=3,"Found faad binary: enabling external faad decoder and \ metadata resolver.") @@ -120,15 +136,13 @@ if os.type != "Win32" then 0 end end -%ifdef add_oblivious_decoder add_oblivious_decoder(name="FAAD",description="Decode files using \ the faad binary.", test=test_faad, faad_p) -%endif def faad_meta(file) = if faad_test(file) then ret = get_process_lines("faad -i \ #{quote(file)} 2>&1") - # Yea, this is tuff programming (again) ! + # Yea, this is ugly programming (again) ! def get_meta(l,s)= ret = string.extract(pattern="^(\w+):\s(.+)$",s) 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.") end end +%endif # Standard function for displaying metadata. # 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)} ' on_metadata(fun (m) -> system(send^quote(display(m))),s) 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 diff --git a/python_apps/pypo/liquidsoap_scripts/library/liquidsoap.initd b/python_apps/pypo/liquidsoap_scripts/library/liquidsoap.initd old mode 100755 new mode 100644 diff --git a/python_apps/pypo/liquidsoap_scripts/library/test.liq b/python_apps/pypo/liquidsoap_scripts/library/test.liq deleted file mode 100644 index ac78f2037..000000000 --- a/python_apps/pypo/liquidsoap_scripts/library/test.liq +++ /dev/null @@ -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)) diff --git a/python_apps/pypo/liquidsoap_scripts/library/typing.liq b/python_apps/pypo/liquidsoap_scripts/library/typing.liq deleted file mode 100644 index 2a4c7eaa7..000000000 --- a/python_apps/pypo/liquidsoap_scripts/library/typing.liq +++ /dev/null @@ -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 diff --git a/python_apps/pypo/liquidsoap_scripts/library/utils.liq b/python_apps/pypo/liquidsoap_scripts/library/utils.liq index da4224dc0..fe1963100 100644 --- a/python_apps/pypo/liquidsoap_scripts/library/utils.liq +++ b/python_apps/pypo/liquidsoap_scripts/library/utils.liq @@ -300,6 +300,25 @@ def server.insert_metadata(~id="",s) = s 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. # Implemented using the corresponding shell command. # @category System @@ -479,59 +498,95 @@ def smart_crossfade (~start_next=5.,~fade_in=3.,~fade_out=3., end # 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 +# @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(~random=false,~on_done={()},uri) - x = ref 0 - def playlist.custom(files) - length = list.length(files) - if length == 0 then - log("Empty playlist..") - fail () - else - files = - if random then - list.sort(fun (x,y) -> int_of_float(random.float()), files) - else - files - end - def next () = - state = !x - file = - if state < length then - x := state + 1 - list.nth(files,state) - else - # Playlist finished +def playlist.reloadable(~id="",~random=false,~on_done={()},uri) + # A reference to the playlist + playlist = ref [] + # A reference to the uri + playlist_uri = ref uri + # A reference to know if the source + # has been stopped + has_stopped = ref false + # The next function + def next () = + file = + if list.length(!playlist) > 0 then + ret = list.hd(!playlist) + playlist := list.tl(!playlist) + ret + else + # Playlist finished + if not !has_stopped then on_done () - "" end - request.create(file) + has_stopped := true + "" 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 - if test_process("test -d #{quote(uri)}") then - files = get_process_lines("find #{quote(uri)} -type f | sort") - playlist.custom(files) - else - playlist = request.create.raw(uri) - result = - if request.resolve(playlist) then - playlist = request.filename(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 + # The reload function + def reload(~uri="") = + if uri != "" then + playlist_uri := uri + end + log(label=id,"Reloading playlist with URI #{!playlist_uri}") + has_stopped := false + load_playlist() 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 # 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) end -# Register the replaygain protocol +# Register the replaygain protocol. +# @category Liquidsoap def replaygain_protocol(arg,delay) # The extraction program extract_replaygain = "#{configure.libdir}/extract-replaygain" diff --git a/python_apps/pypo/liquidsoap_scripts/ls_script.liq b/python_apps/pypo/liquidsoap_scripts/ls_script.liq index 0689a309f..ab856d903 100644 --- a/python_apps/pypo/liquidsoap_scripts/ls_script.liq +++ b/python_apps/pypo/liquidsoap_scripts/ls_script.liq @@ -7,6 +7,7 @@ set("server.telnet", true) set("server.telnet.port", 1234) queue = request.queue(id="queue", length=0.5) +queue = cue_cut(queue) queue = audio_to_stereo(queue) pypo_data = ref '0' diff --git a/python_apps/pypo/pypo-cli.py b/python_apps/pypo/pypo-cli.py index 3ac2e8f50..76f5482a5 100755 --- a/python_apps/pypo/pypo-cli.py +++ b/python_apps/pypo/pypo-cli.py @@ -90,7 +90,6 @@ class Global: print '*****************************************' print '\033[0;32m%s %s\033[m' % ('scheduled at:', str(pkey)) print 'cached at : ' + self.cache_dir + str(pkey) - print 'subtype: ' + str(playlist['subtype']) print 'played: ' + str(playlist['played']) print 'schedule id: ' + str(playlist['schedule_id']) print 'duration: ' + str(playlist['duration']) diff --git a/python_apps/pypo/pypo-notify.py b/python_apps/pypo/pypo-notify.py index d59932dcb..754305748 100755 --- a/python_apps/pypo/pypo-notify.py +++ b/python_apps/pypo/pypo-notify.py @@ -37,7 +37,6 @@ from configobj import ConfigObj # custom imports from util import * from api_clients import * -from dls import * # Set up command-line options parser = OptionParser() diff --git a/python_apps/pypo/pypofetch.py b/python_apps/pypo/pypofetch.py index 368ab269b..9c7fc9d84 100755 --- a/python_apps/pypo/pypofetch.py +++ b/python_apps/pypo/pypofetch.py @@ -4,7 +4,6 @@ import time import logging import logging.config import shutil -import pickle import random import string import json @@ -12,6 +11,8 @@ import telnetlib import math from threading import Thread from subprocess import Popen, PIPE +from datetime import datetime +from datetime import timedelta # For RabbitMQ from kombu.connection import BrokerConnection @@ -98,19 +99,59 @@ class PypoFetch(Thread): logger.error(" * To fix this, you need to set the 'date.timezone' value in your php.ini file and restart apache.") 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.") + + """ + 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 - Reads the scheduled entries of a given range (actual time +/- "prepare_ahead" / "cache_for") - Saves a serialized file of the schedule - 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) - - 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') 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"]) # Push stream metadata to liquidsoap @@ -127,9 +168,9 @@ class PypoFetch(Thread): logger.error("Exception %s", e) status = 0 - # Download all the media and put playlists in liquidsoap format + # Download all the media and put playlists in liquidsoap "annotate" format try: - liquidsoap_playlists = self.prepare_playlists(playlists) + liquidsoap_playlists = self.prepare_playlists(playlists, bootstrapping) except Exception, e: logger.error("%s", e) # Send the data to pypo-push @@ -149,7 +190,7 @@ class PypoFetch(Thread): and stored in a playlist folder. 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') liquidsoap_playlists = dict() @@ -170,27 +211,18 @@ class PypoFetch(Thread): try: os.mkdir(self.cache_dir + str(pkey)) except Exception, e: - pass + logger.error(e) - #logger.debug('*****************************************') - #logger.debug('pkey: ' + str(pkey)) - #logger.debug('cached at : ' + self.cache_dir + str(pkey)) - #logger.debug('subtype: ' + str(playlist['subtype'])) - #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: - 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) + #June 13, 2011: Commented this block out since we are not currently setting this to '1' + #on the server side. Currently using a different method to detect if already played - Martin + #if int(playlist['played']) == 1: + # logger.info("playlist %s already played / sent to liquidsoap, so will ignore it", pkey) + + ls_playlist = self.handle_media_file(playlist, pkey, bootstrapping) liquidsoap_playlists[pkey] = ls_playlist except Exception, e: - logger.info("%s", e) + logger.error("%s", e) return liquidsoap_playlists @@ -199,27 +231,47 @@ class PypoFetch(Thread): This handles both remote and local files. Returns an updated ls_playlist string. """ - def handle_media_file(self, playlist, pkey): - ls_playlist = [] - + def handle_media_file(self, playlist, pkey, bootstrapping): logger = logging.getLogger('fetch') - for media in playlist['medias']: + + ls_playlist = [] + + dtnow = datetime.today() + 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']) + + 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] try: - if str(media['cue_in']) == '0' and str(media['cue_out']) == '0': - #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 + dst = "%s%s/%s%s" % (self.cache_dir, str(pkey), str(media['id']), str(fileExt)) # 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): # check filesize (avoid zero-byte files) @@ -230,11 +282,13 @@ class PypoFetch(Thread): if fsize > 0: 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'\ - % (str(media['export_source']), media['id'], 0, str(float(media['fade_in']) / 1000), \ - str(float(media['fade_out']) / 1000), media['row_id'],dst) - - #logger.debug(pl_entry) + '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(float(media['fade_out']) / 1000), \ + 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 @@ -248,7 +302,6 @@ class PypoFetch(Thread): entry['show_name'] = playlist['show_name'] ls_playlist.append(entry) - #logger.debug("everything ok, adding %s to playlist", pl_entry) else: 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. """ - def handle_remote_file(self, media, dst, do_cue): + def handle_remote_file(self, media, dst): logger = logging.getLogger('fetch') - if do_cue == False: - if os.path.isfile(dst): - pass - #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) - + if os.path.isfile(dst): + pass + #logger.debug("file already in cache: %s", dst) else: - if os.path.isfile(dst): - logger.debug("file already in cache: %s", 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) - + logger.debug("try to download %s", media['uri']) + self.api_client.get_media(media['uri'], dst) """ 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. status, schedule_data = self.api_client.get_schedule() 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") loops = 1 @@ -373,6 +389,6 @@ class PypoFetch(Thread): status, schedule_data = self.api_client.get_schedule() if status == 1: - self.process_schedule(schedule_data, "scheduler") + self.process_schedule(schedule_data, "scheduler", False) loops += 1 diff --git a/python_apps/pypo/pypopush.py b/python_apps/pypo/pypopush.py index ea92e6605..78f005da6 100755 --- a/python_apps/pypo/pypopush.py +++ b/python_apps/pypo/pypopush.py @@ -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]) 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): logger.debug('Preparing to push playlist scheduled at: %s', pkey) diff --git a/python_apps/remove-pypo-user.py b/python_apps/remove-pypo-user.py index 278d6f783..4e29e78e5 100644 --- a/python_apps/remove-pypo-user.py +++ b/python_apps/remove-pypo-user.py @@ -1,4 +1,5 @@ import os +import sys import time def remove_user(username): diff --git a/python_apps/show-recorder/airtime-show-recorder-init-d b/python_apps/show-recorder/airtime-show-recorder-init-d index 9444a307e..7058a0c4b 100755 --- a/python_apps/show-recorder/airtime-show-recorder-init-d +++ b/python_apps/show-recorder/airtime-show-recorder-init-d @@ -11,17 +11,20 @@ USERID=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 -start () { +start () { + monit monitor airtime-show-recorder start-stop-daemon --start --background --quiet --chuid $USERID:$GROUPID --make-pidfile --pidfile $PIDFILE --startas $DAEMON } stop () { # 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 rm -f $PIDFILE } diff --git a/python_apps/show-recorder/install/recorder-install.py b/python_apps/show-recorder/install/recorder-install.py index cb84ee95d..ea8cb84e8 100755 --- a/python_apps/show-recorder/install/recorder-install.py +++ b/python_apps/show-recorder/install/recorder-install.py @@ -63,10 +63,6 @@ try: os.system("chmod -R 755 "+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" shutil.copy(config["bin_dir"]+"/airtime-show-recorder-init-d", "/etc/init.d/airtime-show-recorder") diff --git a/python_apps/show-recorder/install/recorder-uninstall.py b/python_apps/show-recorder/install/recorder-uninstall.py index 14cb63504..df4511428 100755 --- a/python_apps/show-recorder/install/recorder-uninstall.py +++ b/python_apps/show-recorder/install/recorder-uninstall.py @@ -12,7 +12,7 @@ if os.geteuid() != 0: PATH_INI_FILE = '/etc/airtime/recorder.cfg' def remove_path(path): - os.system("rm -rf " + path) + os.system('rm -rf "%s"' % path) def get_current_script_dir(): current_script_dir = os.path.realpath(__file__)