CC-3346: Recorder: Merge recorder with pypo
- Pypo fech works as msg listner for recroder now. - recorder is part of pypo and all it does is waiting for msg from pypo fetch and spwan a show recorder thread. - added new parameter logger to api client. This way apiclient will log into specific log file instead of grabbing current log file. - show recoder is removed from all check system/status page
This commit is contained in:
parent
2ef6d230f9
commit
2f689ed583
|
@ -775,7 +775,6 @@ class ApiController extends Zend_Controller_Action
|
|||
"rabbitmq"=>Application_Model_Systemstatus::GetRabbitMqStatus(),
|
||||
"pypo"=>Application_Model_Systemstatus::GetPypoStatus(),
|
||||
"liquidsoap"=>Application_Model_Systemstatus::GetLiquidsoapStatus(),
|
||||
"show_recorder"=>Application_Model_Systemstatus::GetShowRecorderStatus(),
|
||||
"media_monitor"=>Application_Model_Systemstatus::GetMediaMonitorStatus()
|
||||
)
|
||||
);
|
||||
|
|
|
@ -21,10 +21,6 @@ class SystemstatusController extends Zend_Controller_Action
|
|||
"media-monitor"=>Application_Model_Systemstatus::GetMediaMonitorStatus(),
|
||||
"rabbitmq-server"=>Application_Model_Systemstatus::GetRabbitMqStatus()
|
||||
);
|
||||
|
||||
if (!isset($_SERVER["AIRTIME_SRV"])){
|
||||
$services["show-recorder"]=Application_Model_Systemstatus::GetShowRecorderStatus();
|
||||
}
|
||||
|
||||
$partitions = Application_Model_Systemstatus::GetDiskInfo();
|
||||
|
||||
|
@ -32,36 +28,4 @@ class SystemstatusController extends Zend_Controller_Action
|
|||
$this->view->status->services = $services;
|
||||
$this->view->status->partitions = $partitions;
|
||||
}
|
||||
|
||||
public function getLogFileAction()
|
||||
{
|
||||
$log_files = array("pypo"=>"/var/log/airtime/pypo/pypo.log",
|
||||
"liquidsoap"=>"/var/log/airtime/pypo-liquidsoap/ls_script.log",
|
||||
"media-monitor"=>"/var/log/airtime/media-monitor/media-monitor.log",
|
||||
"show-recorder"=>"/var/log/airtime/show-recorder/show-recorder.log",
|
||||
"icecast2"=>"/var/log/icecast2/error.log");
|
||||
|
||||
$id = $this->_getParam('id');
|
||||
Logging::log($id);
|
||||
|
||||
if (array_key_exists($id, $log_files)){
|
||||
$filepath = $log_files[$id];
|
||||
$filename = basename($filepath);
|
||||
header("Content-Disposition: attachment; filename=$filename");
|
||||
header("Content-Length: " . filesize($filepath));
|
||||
// !! binary mode !!
|
||||
$fp = fopen($filepath, 'rb');
|
||||
|
||||
//We can have multiple levels of output buffering. Need to
|
||||
//keep looping until all have been disabled!!!
|
||||
//http://www.php.net/manual/en/function.ob-end-flush.php
|
||||
while (@ob_end_flush());
|
||||
|
||||
fpassthru($fp);
|
||||
fclose($fp);
|
||||
|
||||
//make sure to exit here so that no other output is sent.
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ class RabbitMqPlugin extends Zend_Controller_Plugin_Abstract
|
|||
$md = array('schedule' => Application_Model_Schedule::GetScheduledPlaylists());
|
||||
Application_Model_RabbitMq::SendMessageToPypo("update_schedule", $md);
|
||||
if (!isset($_SERVER['AIRTIME_SRV'])){
|
||||
Application_Model_RabbitMq::SendMessageToShowRecorder("update_schedule");
|
||||
Application_Model_RabbitMq::SendMessageToShowRecorder("update_recorder_schedule");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ class Application_Model_RabbitMq
|
|||
$channel = $conn->channel();
|
||||
$channel->access_request($CC_CONFIG["rabbitmq"]["vhost"], false, false, true, true);
|
||||
|
||||
$EXCHANGE = 'airtime-show-recorder';
|
||||
$EXCHANGE = 'airtime-pypo';
|
||||
$channel->exchange_declare($EXCHANGE, 'direct', false, true);
|
||||
|
||||
$now = new DateTime("@".time()); //in UTC timezone
|
||||
|
@ -82,7 +82,7 @@ class Application_Model_RabbitMq
|
|||
|
||||
$temp['event_type'] = $event_type;
|
||||
$temp['server_timezone'] = Application_Model_Preference::GetTimezone();
|
||||
if($event_type = "update_schedule"){
|
||||
if($event_type = "update_recorder_schedule"){
|
||||
$temp['shows'] = Application_Model_Show::getShows($now, $end_timestamp, $excludeInstance=NULL, $onlyRecord=TRUE);
|
||||
}
|
||||
$data = json_encode($temp);
|
||||
|
|
|
@ -147,21 +147,6 @@ class Application_Model_Systemstatus
|
|||
}
|
||||
}
|
||||
|
||||
public static function GetShowRecorderStatus(){
|
||||
|
||||
$component = CcServiceRegisterQuery::create()->findOneByDbName("show-recorder");
|
||||
if (is_null($component)){
|
||||
return null;
|
||||
} else {
|
||||
$ip = $component->getDbIp();
|
||||
|
||||
$docRoot = self::GetMonitStatus($ip);
|
||||
$data = self::ExtractServiceInformation($docRoot, "airtime-show-recorder");
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
public static function GetMediaMonitorStatus(){
|
||||
|
||||
$component = CcServiceRegisterQuery::create()->findOneByDbName("media-monitor");
|
||||
|
|
|
@ -91,14 +91,6 @@ class AirtimeIni
|
|||
exit(1);
|
||||
}
|
||||
|
||||
if (!copy(__DIR__."/../../python_apps/show-recorder/recorder.cfg", AirtimeIni::CONF_FILE_RECORDER)){
|
||||
echo "Could not copy recorder.cfg to /etc/airtime/. Exiting.";
|
||||
exit(1);
|
||||
} else if (!self::ChangeFileOwnerGroupMod(AirtimeIni::CONF_FILE_RECORDER, self::CONF_PYPO_GRP)){
|
||||
echo "Could not set ownership of recorder.cfg to 'pypo'. Exiting.";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!copy(__DIR__."/../../python_apps/pypo/liquidsoap_scripts/liquidsoap.cfg", AirtimeIni::CONF_FILE_LIQUIDSOAP)){
|
||||
echo "Could not copy liquidsoap.cfg to /etc/airtime/. Exiting.";
|
||||
exit(1);
|
||||
|
|
|
@ -429,12 +429,9 @@ class AirtimeInstall
|
|||
AirtimeIni::CONF_FILE_PYPO,
|
||||
AirtimeIni::CONF_FILE_RECORDER,
|
||||
"/usr/lib/airtime/pypo",
|
||||
"/usr/lib/airtime/show-recorder",
|
||||
"/var/log/airtime",
|
||||
"/var/log/airtime/pypo",
|
||||
"/var/log/airtime/show-recorder",
|
||||
"/var/tmp/airtime/pypo",
|
||||
"/var/tmp/airtime/show-recorder");
|
||||
"/var/tmp/airtime/pypo");
|
||||
foreach ($dirs as $f) {
|
||||
if (file_exists($f)) {
|
||||
echo "+ $f".PHP_EOL;
|
||||
|
|
|
@ -63,9 +63,9 @@ if [ "$python_service" -eq "0" ]; then
|
|||
if [ "$pypo" = "t" ]; then
|
||||
python $AIRTIMEROOT/python_apps/pypo/install/pypo-copy-files.py
|
||||
fi
|
||||
if [ "$showrecorder" = "t" ]; then
|
||||
python $AIRTIMEROOT/python_apps/show-recorder/install/recorder-copy-files.py
|
||||
fi
|
||||
#if [ "$showrecorder" = "t" ]; then
|
||||
# python $AIRTIMEROOT/python_apps/show-recorder/install/recorder-copy-files.py
|
||||
#fi
|
||||
fi
|
||||
|
||||
mkdir -p /usr/lib/airtime
|
||||
|
|
|
@ -36,9 +36,9 @@ fi
|
|||
if [ "$pypo" = "t" ]; then
|
||||
python $AIRTIMEROOT/python_apps/pypo/install/pypo-initialize.py
|
||||
fi
|
||||
if [ "$showrecorder" = "t" ]; then
|
||||
python $AIRTIMEROOT/python_apps/show-recorder/install/recorder-initialize.py
|
||||
fi
|
||||
#if [ "$showrecorder" = "t" ]; then
|
||||
# python $AIRTIMEROOT/python_apps/show-recorder/install/recorder-initialize.py
|
||||
#fi
|
||||
|
||||
|
||||
# Start monit if it is not running, or restart if it is.
|
||||
|
@ -61,9 +61,9 @@ if [ "$disable_auto_start_services" = "f" ]; then
|
|||
monit monitor airtime-playout
|
||||
monit monitor airtime-liquidsoap
|
||||
fi
|
||||
if [ "$showrecorder" = "t" ]; then
|
||||
monit monitor airtime-show-recorder
|
||||
fi
|
||||
# if [ "$showrecorder" = "t" ]; then
|
||||
# monit monitor airtime-show-recorder
|
||||
# fi
|
||||
fi
|
||||
|
||||
monit monitor rabbitmq-server
|
||||
|
|
|
@ -38,8 +38,8 @@ echo "* Pypo"
|
|||
python $AIRTIMEROOT/python_apps/pypo/install/pypo-remove-files.py
|
||||
echo "* Media-Monitor"
|
||||
python $AIRTIMEROOT/python_apps/media-monitor/install/media-monitor-remove-files.py
|
||||
echo "* Show-Recorder"
|
||||
python $AIRTIMEROOT/python_apps/show-recorder/install/recorder-remove-files.py
|
||||
#echo "* Show-Recorder"
|
||||
#python $AIRTIMEROOT/python_apps/show-recorder/install/recorder-remove-files.py
|
||||
|
||||
#remove symlinks
|
||||
rm -f /usr/bin/airtime-import
|
||||
|
|
|
@ -19,8 +19,8 @@ set +e
|
|||
monit unmonitor airtime-media-monitor >/dev/null 2>&1
|
||||
monit unmonitor airtime-liquidsoap >/dev/null 2>&1
|
||||
monit unmonitor airtime-playout >/dev/null 2>&1
|
||||
monit unmonitor airtime-show-recorder >/dev/null 2>&1
|
||||
#monit unmonitor rabbitmq-server
|
||||
#monit unmonitor airtime-show-recorder >/dev/null 2>&1
|
||||
monit unmonitor rabbitmq-server
|
||||
set -e
|
||||
|
||||
#virtualenv_bin="/usr/lib/airtime/airtime_virtualenv/bin/"
|
||||
|
@ -29,7 +29,7 @@ set -e
|
|||
#uninitialize Airtime services
|
||||
python $AIRTIMEROOT/python_apps/pypo/install/pypo-uninitialize.py
|
||||
python $AIRTIMEROOT/python_apps/media-monitor/install/media-monitor-uninitialize.py
|
||||
python $AIRTIMEROOT/python_apps/show-recorder/install/recorder-uninitialize.py
|
||||
#python $AIRTIMEROOT/python_apps/show-recorder/install/recorder-uninitialize.py
|
||||
|
||||
#call Airtime uninstall script
|
||||
php --php-ini ${SCRIPTPATH}/../airtime-php.ini ${SCRIPTPATH}/airtime-uninstall.php
|
||||
|
|
|
@ -24,14 +24,17 @@ import string
|
|||
|
||||
AIRTIME_VERSION = "2.1.0"
|
||||
|
||||
def api_client_factory(config):
|
||||
logger = logging.getLogger()
|
||||
def api_client_factory(config, logger=None):
|
||||
if logger != None:
|
||||
temp_logger = logger
|
||||
else:
|
||||
temp_logger = logging.getLogger()
|
||||
if config["api_client"] == "airtime":
|
||||
return AirTimeApiClient()
|
||||
return AirTimeApiClient(temp_logger)
|
||||
elif config["api_client"] == "obp":
|
||||
return ObpApiClient()
|
||||
else:
|
||||
logger.info('API Client "'+config["api_client"]+'" not supported. Please check your config file.\n')
|
||||
temp_logger.info('API Client "'+config["api_client"]+'" not supported. Please check your config file.\n')
|
||||
sys.exit()
|
||||
|
||||
def to_unicode(obj, encoding='utf-8'):
|
||||
|
@ -161,17 +164,20 @@ class ApiClientInterface:
|
|||
|
||||
class AirTimeApiClient(ApiClientInterface):
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, logger=None):
|
||||
if logger != None:
|
||||
self.logger = logger
|
||||
else:
|
||||
self.logger = logging.getLogger()
|
||||
# loading config file
|
||||
try:
|
||||
self.config = ConfigObj('/etc/airtime/api_client.cfg')
|
||||
except Exception, e:
|
||||
logger = logging.getLogger()
|
||||
logger.error('Error loading config file: %s', e)
|
||||
self.logger.error('Error loading config file: %s', e)
|
||||
sys.exit(1)
|
||||
|
||||
def get_response_from_server(self, url):
|
||||
logger = logging.getLogger()
|
||||
logger = self.logger
|
||||
successful_response = False
|
||||
|
||||
while not successful_response:
|
||||
|
@ -191,7 +197,7 @@ class AirTimeApiClient(ApiClientInterface):
|
|||
|
||||
|
||||
def __get_airtime_version(self, verbose = True):
|
||||
logger = logging.getLogger()
|
||||
logger = self.logger
|
||||
url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["version_url"])
|
||||
logger.debug("Trying to contact %s", url)
|
||||
url = url.replace("%%api_key%%", self.config["api_key"])
|
||||
|
@ -211,7 +217,7 @@ class AirTimeApiClient(ApiClientInterface):
|
|||
return version
|
||||
|
||||
def test(self):
|
||||
logger = logging.getLogger()
|
||||
logger = self.logger
|
||||
status, items = self.get_schedule('2010-01-01-00-00-00', '2011-01-01-00-00-00')
|
||||
schedule = items["playlists"]
|
||||
logger.debug("Number of playlists found: %s", str(len(schedule)))
|
||||
|
@ -227,7 +233,7 @@ class AirTimeApiClient(ApiClientInterface):
|
|||
|
||||
|
||||
def is_server_compatible(self, verbose = True):
|
||||
logger = logging.getLogger()
|
||||
logger = self.logger
|
||||
version = self.__get_airtime_version(verbose)
|
||||
if (version == -1):
|
||||
if (verbose):
|
||||
|
@ -246,7 +252,7 @@ class AirTimeApiClient(ApiClientInterface):
|
|||
|
||||
|
||||
def get_schedule(self, start=None, end=None):
|
||||
logger = logging.getLogger()
|
||||
logger = self.logger
|
||||
|
||||
# Construct the URL
|
||||
export_url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["export_url"])
|
||||
|
@ -267,7 +273,7 @@ class AirTimeApiClient(ApiClientInterface):
|
|||
|
||||
|
||||
def get_media(self, uri, dst):
|
||||
logger = logging.getLogger()
|
||||
logger = self.logger
|
||||
|
||||
try:
|
||||
src = uri + "/api_key/%%api_key%%"
|
||||
|
@ -284,7 +290,7 @@ class AirTimeApiClient(ApiClientInterface):
|
|||
Tell server that the scheduled *playlist* has started.
|
||||
"""
|
||||
def notify_scheduled_item_start_playing(self, pkey, schedule):
|
||||
logger = logging.getLogger()
|
||||
logger = self.logger
|
||||
playlist = schedule[pkey]
|
||||
schedule_id = playlist["schedule_id"]
|
||||
url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["update_item_url"])
|
||||
|
@ -311,7 +317,7 @@ class AirTimeApiClient(ApiClientInterface):
|
|||
liquidsoap in get_liquidsoap_data().
|
||||
"""
|
||||
def notify_media_item_start_playing(self, data, media_id):
|
||||
logger = logging.getLogger()
|
||||
logger = self.logger
|
||||
response = ''
|
||||
try:
|
||||
schedule_id = data
|
||||
|
@ -331,7 +337,7 @@ class AirTimeApiClient(ApiClientInterface):
|
|||
return response
|
||||
|
||||
def get_liquidsoap_data(self, pkey, schedule):
|
||||
logger = logging.getLogger()
|
||||
logger = self.logger
|
||||
playlist = schedule[pkey]
|
||||
data = dict()
|
||||
try:
|
||||
|
@ -341,13 +347,12 @@ class AirTimeApiClient(ApiClientInterface):
|
|||
return data
|
||||
|
||||
def get_shows_to_record(self):
|
||||
logger = logging.getLogger()
|
||||
logger = self.logger
|
||||
response = None
|
||||
try:
|
||||
url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["show_schedule_url"])
|
||||
logger.debug(url)
|
||||
url = url.replace("%%api_key%%", self.config["api_key"])
|
||||
|
||||
response = self.get_response_from_server(url)
|
||||
|
||||
response = json.loads(response)
|
||||
|
@ -360,7 +365,7 @@ class AirTimeApiClient(ApiClientInterface):
|
|||
return response
|
||||
|
||||
def upload_recorded_show(self, data, headers):
|
||||
logger = logging.getLogger()
|
||||
logger = self.logger
|
||||
response = ''
|
||||
|
||||
retries = int(self.config["upload_retries"])
|
||||
|
@ -394,7 +399,7 @@ class AirTimeApiClient(ApiClientInterface):
|
|||
return response
|
||||
|
||||
def setup_media_monitor(self):
|
||||
logger = logging.getLogger()
|
||||
logger = self.logger
|
||||
|
||||
response = None
|
||||
try:
|
||||
|
@ -411,7 +416,7 @@ class AirTimeApiClient(ApiClientInterface):
|
|||
return response
|
||||
|
||||
def update_media_metadata(self, md, mode, is_record=False):
|
||||
logger = logging.getLogger()
|
||||
logger = self.logger
|
||||
response = None
|
||||
try:
|
||||
|
||||
|
@ -456,7 +461,7 @@ class AirTimeApiClient(ApiClientInterface):
|
|||
#Note that these are relative paths to the given directory. The full
|
||||
#path is not returned.
|
||||
def list_all_db_files(self, dir_id):
|
||||
logger = logging.getLogger()
|
||||
logger = self.logger
|
||||
try:
|
||||
url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["list_all_db_files"])
|
||||
|
||||
|
@ -473,7 +478,7 @@ class AirTimeApiClient(ApiClientInterface):
|
|||
return response
|
||||
|
||||
def list_all_watched_dirs(self):
|
||||
logger = logging.getLogger()
|
||||
logger = self.logger
|
||||
try:
|
||||
url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["list_all_watched_dirs"])
|
||||
|
||||
|
@ -489,7 +494,7 @@ class AirTimeApiClient(ApiClientInterface):
|
|||
return response
|
||||
|
||||
def add_watched_dir(self, path):
|
||||
logger = logging.getLogger()
|
||||
logger = self.logger
|
||||
try:
|
||||
url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["add_watched_dir"])
|
||||
|
||||
|
@ -506,7 +511,7 @@ class AirTimeApiClient(ApiClientInterface):
|
|||
return response
|
||||
|
||||
def remove_watched_dir(self, path):
|
||||
logger = logging.getLogger()
|
||||
logger = self.logger
|
||||
try:
|
||||
url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["remove_watched_dir"])
|
||||
|
||||
|
@ -523,7 +528,7 @@ class AirTimeApiClient(ApiClientInterface):
|
|||
return response
|
||||
|
||||
def set_storage_dir(self, path):
|
||||
logger = logging.getLogger()
|
||||
logger = self.logger
|
||||
try:
|
||||
url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["set_storage_dir"])
|
||||
|
||||
|
@ -540,7 +545,7 @@ class AirTimeApiClient(ApiClientInterface):
|
|||
return response
|
||||
|
||||
def get_stream_setting(self):
|
||||
logger = logging.getLogger()
|
||||
logger = self.logger
|
||||
try:
|
||||
url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["get_stream_setting"])
|
||||
|
||||
|
@ -561,7 +566,7 @@ class AirTimeApiClient(ApiClientInterface):
|
|||
via a http server.
|
||||
"""
|
||||
def register_component(self, component):
|
||||
logger = logging.getLogger()
|
||||
logger = self.logger
|
||||
try:
|
||||
url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["register_component"])
|
||||
|
||||
|
@ -573,7 +578,7 @@ class AirTimeApiClient(ApiClientInterface):
|
|||
logger.error("Exception: %s", e)
|
||||
|
||||
def notify_liquidsoap_status(self, msg, stream_id, time):
|
||||
logger = logging.getLogger()
|
||||
logger = self.logger
|
||||
try:
|
||||
url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["update_liquidsoap_status"])
|
||||
|
||||
|
|
|
@ -60,6 +60,8 @@ try:
|
|||
create_dir(config['cache_dir'])
|
||||
create_dir(config['file_dir'])
|
||||
create_dir(config['tmp_dir'])
|
||||
|
||||
create_dir(config["base_recorded_files"])
|
||||
|
||||
#copy files to bin dir
|
||||
copy_dir("%s/.."%current_script_dir, config["bin_dir"]+"/bin/")
|
||||
|
@ -72,6 +74,7 @@ try:
|
|||
os.system("chmod 755 "+os.path.join(config["bin_dir"], "bin/liquidsoap_scripts/notify.sh"))
|
||||
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["base_recorded_files"])
|
||||
|
||||
#copy init.d script
|
||||
shutil.copy(config["bin_dir"]+"/bin/airtime-playout-init-d", "/etc/init.d/airtime-playout")
|
||||
|
|
|
@ -1,34 +1,46 @@
|
|||
[loggers]
|
||||
keys=root,fetch,push
|
||||
keys=root,fetch,push,recorder
|
||||
|
||||
[handlers]
|
||||
keys=fileOutHandler
|
||||
keys=pypo,recorder
|
||||
|
||||
[formatters]
|
||||
keys=simpleFormatter
|
||||
|
||||
[logger_root]
|
||||
level=DEBUG
|
||||
handlers=fileOutHandler
|
||||
handlers=pypo
|
||||
|
||||
[logger_fetch]
|
||||
level=DEBUG
|
||||
handlers=fileOutHandler
|
||||
handlers=pypo
|
||||
qualname=fetch
|
||||
propagate=0
|
||||
|
||||
[logger_push]
|
||||
level=DEBUG
|
||||
handlers=fileOutHandler
|
||||
handlers=pypo
|
||||
qualname=push
|
||||
propagate=0
|
||||
|
||||
[handler_fileOutHandler]
|
||||
[logger_recorder]
|
||||
level=DEBUG
|
||||
handlers=recorder
|
||||
qualname=recorder
|
||||
propagate=0
|
||||
|
||||
[handler_pypo]
|
||||
class=logging.handlers.RotatingFileHandler
|
||||
level=DEBUG
|
||||
formatter=simpleFormatter
|
||||
args=("/var/log/airtime/pypo/pypo.log", 'a', 1000000, 5,)
|
||||
|
||||
[handler_recorder]
|
||||
class=logging.handlers.RotatingFileHandler
|
||||
level=DEBUG
|
||||
formatter=simpleFormatter
|
||||
args=("/var/log/airtime/pypo/show-recorder.log", 'a', 1000000, 5,)
|
||||
|
||||
[formatter_simpleFormatter]
|
||||
format=%(asctime)s %(levelname)s - [%(filename)s : %(funcName)s() : line %(lineno)d] - %(message)s
|
||||
datefmt=
|
||||
|
|
|
@ -15,6 +15,7 @@ from Queue import Queue
|
|||
|
||||
from pypopush import PypoPush
|
||||
from pypofetch import PypoFetch
|
||||
from recorder import Recorder
|
||||
|
||||
from configobj import ConfigObj
|
||||
|
||||
|
@ -127,12 +128,18 @@ if __name__ == '__main__':
|
|||
api_client.register_component("pypo")
|
||||
|
||||
q = Queue()
|
||||
|
||||
recorder_q = Queue()
|
||||
|
||||
pp = PypoPush(q)
|
||||
pp.daemon = True
|
||||
pp.start()
|
||||
|
||||
pf = PypoFetch(q)
|
||||
recorder = Recorder(recorder_q)
|
||||
recorder.daemon = True
|
||||
recorder.start()
|
||||
|
||||
pf = PypoFetch(q, recorder_q)
|
||||
pf.daemon = True
|
||||
pf.start()
|
||||
|
||||
|
|
|
@ -71,3 +71,17 @@ push_interval = 1 # in seconds
|
|||
# while 'otf' (on the fly) cues while loading into ls
|
||||
# (needs the post_processor patch)
|
||||
cue_style = 'pre'
|
||||
|
||||
############################################
|
||||
# Recorded Audio settings #
|
||||
############################################
|
||||
record_bitrate = 256
|
||||
record_samplerate = 44100
|
||||
record_channels = 2
|
||||
record_sample_size = 16
|
||||
|
||||
#can be either ogg|mp3, mp3 recording requires installation of the package "lame"
|
||||
record_file_type = 'ogg'
|
||||
|
||||
# base path to store recordered shows at
|
||||
base_recorded_files = '/var/tmp/airtime/show-recorder/'
|
||||
|
|
|
@ -42,11 +42,12 @@ except Exception, e:
|
|||
sys.exit()
|
||||
|
||||
class PypoFetch(Thread):
|
||||
def __init__(self, q):
|
||||
def __init__(self, q, recorder_q):
|
||||
Thread.__init__(self)
|
||||
self.api_client = api_client.api_client_factory(config)
|
||||
self.set_export_source('scheduler')
|
||||
self.queue = q
|
||||
self.recorder_queue = recorder_q
|
||||
self.schedule_data = []
|
||||
logger = logging.getLogger('fetch')
|
||||
logger.info("PypoFetch: init complete")
|
||||
|
@ -94,6 +95,12 @@ class PypoFetch(Thread):
|
|||
elif command == 'cancel_current_show':
|
||||
logger.info("Cancel current show command received...")
|
||||
self.stop_current_show()
|
||||
elif command == 'update_recorder_schedule':
|
||||
temp = m
|
||||
if temp is not None:
|
||||
self.parse_shows(temp)
|
||||
elif command == 'cancel_recording':
|
||||
self.recorder_queue.put('cancel_recording')
|
||||
except Exception, e:
|
||||
logger.error("Exception in handling RabbitMQ message: %s", e)
|
||||
|
||||
|
@ -312,6 +319,30 @@ class PypoFetch(Thread):
|
|||
# cleanup
|
||||
try: self.cleanup(self.export_source)
|
||||
except Exception, e: logger.error("%s", e)
|
||||
|
||||
def getDateTimeObj(self,time):
|
||||
timeinfo = time.split(" ")
|
||||
date = timeinfo[0].split("-")
|
||||
time = timeinfo[1].split(":")
|
||||
|
||||
date = map(int, date)
|
||||
time = map(int, time)
|
||||
|
||||
return datetime(date[0], date[1], date[2], time[0], time[1], time[2], 0, None)
|
||||
|
||||
def parse_shows(self, m):
|
||||
logger = logging.getLogger('fetch')
|
||||
logger.info("Parsing recording show schedules...")
|
||||
shows_to_record = {}
|
||||
shows = m['shows']
|
||||
for show in shows:
|
||||
show_starts = self.getDateTimeObj(show[u'starts'])
|
||||
show_end = self.getDateTimeObj(show[u'ends'])
|
||||
time_delta = show_end - show_starts
|
||||
|
||||
shows_to_record[show[u'starts']] = [time_delta, show[u'instance_id'], show[u'name'], m['server_timezone']]
|
||||
self.recorder_queue.put(shows_to_record)
|
||||
logger.info(shows_to_record)
|
||||
|
||||
|
||||
"""
|
||||
|
@ -487,6 +518,17 @@ class PypoFetch(Thread):
|
|||
if status == 1:
|
||||
logger.info("Bootstrap schedule received: %s", self.schedule_data)
|
||||
self.process_schedule(self.schedule_data, "scheduler", True)
|
||||
|
||||
# Bootstrap: since we are just starting up, we need to grab the
|
||||
# most recent schedule. After that we can just wait for updates.
|
||||
try:
|
||||
temp = self.api_client.get_shows_to_record()
|
||||
if temp is not None:
|
||||
self.parse_shows(temp)
|
||||
logger.info("Bootstrap recorder schedule received: %s", temp)
|
||||
except Exception, e:
|
||||
logger.error(e)
|
||||
|
||||
logger.info("Bootstrap complete: got initial copy of the schedule")
|
||||
|
||||
|
||||
|
@ -515,8 +557,18 @@ class PypoFetch(Thread):
|
|||
status, self.schedule_data = self.api_client.get_schedule()
|
||||
if status == 1:
|
||||
self.process_schedule(self.schedule_data, "scheduler", False)
|
||||
"""
|
||||
Fetch recorder schedule
|
||||
"""
|
||||
try:
|
||||
temp = self.api_client.get_shows_to_record()
|
||||
if temp is not None:
|
||||
self.parse_shows(temp)
|
||||
logger.info("updated recorder schedule received: %s", temp)
|
||||
except Exception, e:
|
||||
logger.error(e)
|
||||
|
||||
loops += 1
|
||||
loops += 1
|
||||
|
||||
"""
|
||||
Main loop of the thread:
|
||||
|
|
|
@ -0,0 +1,270 @@
|
|||
import urllib
|
||||
import logging
|
||||
import logging.config
|
||||
import json
|
||||
import time
|
||||
import datetime
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import socket
|
||||
import pytz
|
||||
import signal
|
||||
import math
|
||||
|
||||
from configobj import ConfigObj
|
||||
|
||||
from poster.encode import multipart_encode
|
||||
from poster.streaminghttp import register_openers
|
||||
import urllib2
|
||||
|
||||
from subprocess import Popen
|
||||
from threading import Thread
|
||||
|
||||
import mutagen
|
||||
|
||||
from api_clients import api_client
|
||||
|
||||
# For RabbitMQ
|
||||
from kombu.connection import BrokerConnection
|
||||
from kombu.messaging import Exchange, Queue, Consumer, Producer
|
||||
|
||||
# loading config file
|
||||
try:
|
||||
config = ConfigObj('/etc/airtime/pypo.cfg')
|
||||
except Exception, e:
|
||||
self.logger.error('Error loading config file: %s', e)
|
||||
sys.exit()
|
||||
|
||||
def getDateTimeObj(time):
|
||||
timeinfo = time.split(" ")
|
||||
date = timeinfo[0].split("-")
|
||||
time = timeinfo[1].split(":")
|
||||
|
||||
date = map(int, date)
|
||||
time = map(int, time)
|
||||
|
||||
return datetime.datetime(date[0], date[1], date[2], time[0], time[1], time[2], 0, None)
|
||||
|
||||
PUSH_INTERVAL = 2
|
||||
|
||||
class ShowRecorder(Thread):
|
||||
|
||||
def __init__ (self, show_instance, show_name, filelength, start_time):
|
||||
Thread.__init__(self)
|
||||
self.logger = logging.getLogger('recorder')
|
||||
self.api_client = api_client.api_client_factory(config, self.logger)
|
||||
self.filelength = filelength
|
||||
self.start_time = start_time
|
||||
self.show_instance = show_instance
|
||||
self.show_name = show_name
|
||||
self.p = None
|
||||
|
||||
def record_show(self):
|
||||
length = str(self.filelength)+".0"
|
||||
filename = self.start_time
|
||||
filename = filename.replace(" ", "-")
|
||||
|
||||
if config["record_file_type"] in ["mp3", "ogg"]:
|
||||
filetype = config["record_file_type"]
|
||||
else:
|
||||
filetype = "ogg";
|
||||
|
||||
filepath = "%s%s.%s" % (config["base_recorded_files"], filename, filetype)
|
||||
|
||||
br = config["record_bitrate"]
|
||||
sr = config["record_samplerate"]
|
||||
c = config["record_channels"]
|
||||
ss = config["record_sample_size"]
|
||||
|
||||
#-f:16,2,44100
|
||||
#-b:256
|
||||
command = "ecasound -f:%s,%s,%s -i alsa -o %s,%s000 -t:%s" % (ss, c, sr, filepath, br, length)
|
||||
args = command.split(" ")
|
||||
|
||||
self.logger.info("starting record")
|
||||
self.logger.info("command " + command)
|
||||
|
||||
self.p = Popen(args)
|
||||
|
||||
#blocks at the following line until the child process
|
||||
#quits
|
||||
code = self.p.wait()
|
||||
|
||||
self.logger.info("finishing record, return code %s", self.p.returncode)
|
||||
code = self.p.returncode
|
||||
|
||||
self.p = None
|
||||
|
||||
return code, filepath
|
||||
|
||||
def cancel_recording(self):
|
||||
#add 3 second delay before actually cancelling the show. The reason
|
||||
#for this is because it appears that ecasound starts 1 second later than
|
||||
#it should, and therefore this method is sometimes incorrectly called 1
|
||||
#second before the show ends.
|
||||
#time.sleep(3)
|
||||
|
||||
#send signal interrupt (2)
|
||||
self.logger.info("Show manually cancelled!")
|
||||
if (self.p is not None):
|
||||
self.p.send_signal(signal.SIGINT)
|
||||
|
||||
#if self.p is defined, then the child process ecasound is recording
|
||||
def is_recording(self):
|
||||
return (self.p is not None)
|
||||
|
||||
def upload_file(self, filepath):
|
||||
|
||||
filename = os.path.split(filepath)[1]
|
||||
|
||||
# Register the streaming http handlers with urllib2
|
||||
register_openers()
|
||||
|
||||
# headers contains the necessary Content-Type and Content-Length
|
||||
# datagen is a generator object that yields the encoded parameters
|
||||
datagen, headers = multipart_encode({"file": open(filepath, "rb"), 'name': filename, 'show_instance': self.show_instance})
|
||||
|
||||
self.api_client.upload_recorded_show(datagen, headers)
|
||||
|
||||
def set_metadata_and_save(self, filepath):
|
||||
try:
|
||||
date = self.start_time
|
||||
md = date.split(" ")
|
||||
time = md[1].replace(":", "-")
|
||||
self.logger.info("time: %s" % time)
|
||||
|
||||
name = time+"-"+self.show_name
|
||||
artist = api_client.encode_to("Airtime Show Recorder",'utf-8')
|
||||
|
||||
#set some metadata for our file daemon
|
||||
recorded_file = mutagen.File(filepath, easy=True)
|
||||
recorded_file['title'] = name
|
||||
recorded_file['artist'] = artist
|
||||
recorded_file['date'] = md[0]
|
||||
recorded_file['tracknumber'] = self.show_instance
|
||||
recorded_file.save()
|
||||
|
||||
except Exception, e:
|
||||
self.logger.error("Exception: %s", e)
|
||||
|
||||
def run(self):
|
||||
code, filepath = self.record_show()
|
||||
|
||||
if code == 0:
|
||||
try:
|
||||
self.logger.info("Preparing to upload %s" % filepath)
|
||||
|
||||
self.set_metadata_and_save(filepath)
|
||||
|
||||
self.upload_file(filepath)
|
||||
os.remove(filepath)
|
||||
except Exception, e:
|
||||
self.logger.error(e)
|
||||
else:
|
||||
self.logger.info("problem recording show")
|
||||
os.remove(filepath)
|
||||
|
||||
class Recorder(Thread):
|
||||
def __init__(self, q):
|
||||
Thread.__init__(self)
|
||||
self.logger = logging.getLogger('recorder')
|
||||
self.api_client = api_client.api_client_factory(config)
|
||||
self.api_client.register_component("show-recorder")
|
||||
self.sr = None
|
||||
self.shows_to_record = {}
|
||||
self.server_timezone = ''
|
||||
self.queue = q
|
||||
self.logger.info("RecorderFetch: init complete")
|
||||
|
||||
def handle_message(self):
|
||||
if not self.queue.empty():
|
||||
msg = self.queue.get()
|
||||
self.logger.info("Receivied msg from Pypo Fetch: %s", msg)
|
||||
if msg == 'cancel_recording':
|
||||
if self.sr is not None and self.sr.is_recording():
|
||||
self.sr.cancel_recording()
|
||||
else:
|
||||
self.shows_to_record = msg
|
||||
|
||||
if self.shows_to_record:
|
||||
self.start_record()
|
||||
|
||||
def get_time_till_next_show(self):
|
||||
if len(self.shows_to_record) != 0:
|
||||
tnow = datetime.datetime.utcnow()
|
||||
sorted_show_keys = sorted(self.shows_to_record.keys())
|
||||
|
||||
start_time = sorted_show_keys[0]
|
||||
next_show = getDateTimeObj(start_time)
|
||||
|
||||
delta = next_show - tnow
|
||||
out = delta.seconds
|
||||
|
||||
if out < 5:
|
||||
self.logger.debug("Shows %s", self.shows_to_record)
|
||||
self.logger.debug("Next show %s", next_show)
|
||||
self.logger.debug("Now %s", tnow)
|
||||
return out
|
||||
|
||||
def start_record(self):
|
||||
if len(self.shows_to_record) != 0:
|
||||
try:
|
||||
delta = self.get_time_till_next_show()
|
||||
if delta < 5:
|
||||
self.logger.debug("sleeping %s seconds until show", delta)
|
||||
time.sleep(delta)
|
||||
|
||||
sorted_show_keys = sorted(self.shows_to_record.keys())
|
||||
start_time = sorted_show_keys[0]
|
||||
show_length = self.shows_to_record[start_time][0]
|
||||
show_instance = self.shows_to_record[start_time][1]
|
||||
show_name = self.shows_to_record[start_time][2]
|
||||
server_timezone = self.shows_to_record[start_time][3]
|
||||
|
||||
T = pytz.timezone(server_timezone)
|
||||
start_time_on_UTC = getDateTimeObj(start_time)
|
||||
start_time_on_server = start_time_on_UTC.replace(tzinfo=pytz.utc).astimezone(T)
|
||||
start_time_formatted = '%(year)d-%(month)02d-%(day)02d %(hour)02d:%(min)02d:%(sec)02d' % \
|
||||
{'year': start_time_on_server.year, 'month': start_time_on_server.month, 'day': start_time_on_server.day,\
|
||||
'hour': start_time_on_server.hour, 'min': start_time_on_server.minute, 'sec': start_time_on_server.second}
|
||||
self.sr = ShowRecorder(show_instance, show_name, show_length.seconds, start_time_formatted)
|
||||
self.sr.start()
|
||||
#remove show from shows to record.
|
||||
del self.shows_to_record[start_time]
|
||||
#self.time_till_next_show = self.get_time_till_next_show()
|
||||
except Exception,e :
|
||||
import traceback
|
||||
top = traceback.format_exc()
|
||||
self.logger.error('Exception: %s', e)
|
||||
self.logger.error("traceback: %s", top)
|
||||
|
||||
"""
|
||||
Main loop of the thread:
|
||||
Wait for schedule updates from RabbitMQ, but in case there arent any,
|
||||
poll the server to get the upcoming schedule.
|
||||
"""
|
||||
def run(self):
|
||||
try:
|
||||
self.logger.info("Started...")
|
||||
|
||||
recording = False
|
||||
|
||||
loops = 0
|
||||
heartbeat_period = math.floor(30/PUSH_INTERVAL)
|
||||
|
||||
while True:
|
||||
if loops % heartbeat_period == 0:
|
||||
self.logger.info("heartbeat")
|
||||
loops = 0
|
||||
try: self.handle_message()
|
||||
except Exception, e:
|
||||
self.logger.error('Pypo Recorder Exception: %s', e)
|
||||
time.sleep(PUSH_INTERVAL)
|
||||
loops += 1
|
||||
except Exception,e :
|
||||
import traceback
|
||||
top = traceback.format_exc()
|
||||
self.logger.error('Exception: %s', e)
|
||||
self.logger.error("traceback: %s", top)
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
virtualenv_bin="/usr/lib/airtime/airtime_virtualenv/bin/"
|
||||
. ${virtualenv_bin}activate
|
||||
|
||||
recorder_user="pypo"
|
||||
|
||||
|
||||
# Location of pypo_cli.py Python script
|
||||
recorder_path="/usr/lib/airtime/show-recorder/"
|
||||
recorder_script="recorder.py"
|
||||
|
||||
api_client_path="/usr/lib/airtime/"
|
||||
cd ${recorder_path}
|
||||
|
||||
exec 2>&1
|
||||
|
||||
export HOME="/var/tmp/airtime/show-recorder/"
|
||||
export TERM=xterm
|
||||
export PYTHONPATH=${api_client_path}
|
||||
|
||||
#this line works: su ${recorder_user} -c "python -u ${recorder_path}${recorder_script}"
|
||||
# Note the -u when calling python! we need it to get unbuffered binary stdout and stderr
|
||||
exec python -u ${recorder_path}${recorder_script} > /var/log/airtime/show-recorder/py-interpreter.log 2>&1
|
||||
|
||||
# EOF
|
|
@ -1,71 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: airtime-show-recorder
|
||||
# Required-Start: $local_fs $remote_fs $network $syslog
|
||||
# Required-Stop: $local_fs $remote_fs $network $syslog
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: Manage airtime-show-recorder daemon
|
||||
### END INIT INFO
|
||||
|
||||
USERID=pypo
|
||||
GROUPID=pypo
|
||||
NAME=Airtime\ Show\ Recorder
|
||||
|
||||
DAEMON=/usr/lib/airtime/show-recorder/airtime-show-recorder
|
||||
PIDFILE=/var/run/airtime-show-recorder.pid
|
||||
|
||||
start () {
|
||||
start-stop-daemon --start --background --quiet --chuid $USERID:$GROUPID --make-pidfile --pidfile $PIDFILE --startas $DAEMON
|
||||
monit monitor airtime-show-recorder >/dev/null 2>&1
|
||||
}
|
||||
|
||||
stop () {
|
||||
# Send TERM after 5 seconds, wait at most 30 seconds.
|
||||
|
||||
monit unmonitor airtime-show-recorder >/dev/null 2>&1
|
||||
start-stop-daemon --stop --oknodo --retry TERM/5/0/30 --quiet --pidfile $PIDFILE
|
||||
rm -f $PIDFILE
|
||||
}
|
||||
|
||||
start_no_monit() {
|
||||
start-stop-daemon --start --background --quiet --chuid $USERID:$GROUPID --make-pidfile --pidfile $PIDFILE --startas $DAEMON
|
||||
}
|
||||
|
||||
|
||||
case "${1:-''}" in
|
||||
'start')
|
||||
# start commands here
|
||||
echo -n "Starting $NAME: "
|
||||
start
|
||||
echo "Done."
|
||||
;;
|
||||
'stop')
|
||||
# stop commands here
|
||||
echo -n "Stopping $NAME: "
|
||||
stop
|
||||
echo "Done."
|
||||
;;
|
||||
'restart')
|
||||
# restart commands here
|
||||
echo -n "Restarting $NAME: "
|
||||
stop
|
||||
start
|
||||
echo "Done."
|
||||
;;
|
||||
'start-no-monit')
|
||||
# restart commands here
|
||||
echo -n "Starting $NAME: "
|
||||
start_no_monit
|
||||
echo "Done."
|
||||
;;
|
||||
'status')
|
||||
# status commands here
|
||||
/usr/bin/airtime-check-system
|
||||
;;
|
||||
*) # no parameter specified
|
||||
echo "Usage: $SELF start|stop|restart|status"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
|
@ -1,74 +0,0 @@
|
|||
import os
|
||||
import shutil
|
||||
import sys
|
||||
from configobj import ConfigObj
|
||||
|
||||
if os.geteuid() != 0:
|
||||
print "Please run this as root."
|
||||
sys.exit(1)
|
||||
|
||||
def get_current_script_dir():
|
||||
current_script_dir = os.path.realpath(__file__)
|
||||
index = current_script_dir.rindex('/')
|
||||
return current_script_dir[0:index]
|
||||
|
||||
def copy_dir(src_dir, dest_dir):
|
||||
if (os.path.exists(dest_dir)) and (dest_dir != "/"):
|
||||
shutil.rmtree(dest_dir)
|
||||
if not (os.path.exists(dest_dir)):
|
||||
#print "Copying directory "+os.path.realpath(src_dir)+" to "+os.path.realpath(dest_dir)
|
||||
shutil.copytree(src_dir, dest_dir)
|
||||
|
||||
def create_dir(path):
|
||||
try:
|
||||
os.makedirs(path)
|
||||
except Exception, e:
|
||||
pass
|
||||
|
||||
PATH_INI_FILE = '/etc/airtime/recorder.cfg'
|
||||
|
||||
try:
|
||||
# Absolute path this script is in
|
||||
current_script_dir = get_current_script_dir()
|
||||
|
||||
if not os.path.exists(PATH_INI_FILE):
|
||||
shutil.copy('%s/../recorder.cfg'%current_script_dir, PATH_INI_FILE)
|
||||
|
||||
# load config file
|
||||
try:
|
||||
config = ConfigObj(PATH_INI_FILE)
|
||||
except Exception, e:
|
||||
print 'Error loading config file: ', e
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
#copy monit files
|
||||
shutil.copy('%s/../../monit/monit-airtime-generic.cfg'%current_script_dir, '/etc/monit/conf.d/')
|
||||
if os.environ["disable_auto_start_services"] == "f":
|
||||
shutil.copy('%s/../monit-airtime-show-recorder.cfg'%current_script_dir, '/etc/monit/conf.d/')
|
||||
|
||||
#create temporary media-storage directory
|
||||
#print "Creating temporary media storage directory"
|
||||
create_dir(config["base_recorded_files"])
|
||||
#os.system("chmod -R 755 "+config["base_recorded_files"])
|
||||
os.system("chown -R pypo:pypo "+config["base_recorded_files"])
|
||||
|
||||
#create log directories
|
||||
#print "Creating log directories"
|
||||
create_dir(config["log_dir"])
|
||||
os.system("chmod -R 755 " + config["log_dir"])
|
||||
os.system("chown -R pypo:pypo "+config["log_dir"])
|
||||
|
||||
#copy python files
|
||||
copy_dir("%s/.."%current_script_dir, config["bin_dir"])
|
||||
|
||||
#set python file permissions
|
||||
#print "Setting permissions"
|
||||
os.system("chmod -R 755 "+config["bin_dir"])
|
||||
os.system("chown -R pypo:pypo "+config["bin_dir"])
|
||||
|
||||
#copy init.d script
|
||||
shutil.copy(config["bin_dir"]+"/airtime-show-recorder-init-d", "/etc/init.d/airtime-show-recorder")
|
||||
|
||||
except Exception, e:
|
||||
print e
|
|
@ -1,24 +0,0 @@
|
|||
from subprocess import Popen
|
||||
import os
|
||||
import sys
|
||||
|
||||
if os.geteuid() != 0:
|
||||
print "Please run this as root."
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
if os.environ["disable_auto_start_services"] == "f":
|
||||
#register init.d script
|
||||
p = Popen("update-rc.d airtime-show-recorder defaults >/dev/null 2>&1", shell=True)
|
||||
sts = os.waitpid(p.pid, 0)[1]
|
||||
|
||||
#start daemon
|
||||
print "* Waiting for show-recorder processes to start..."
|
||||
"""
|
||||
p = Popen("/etc/init.d/airtime-show-recorder stop", shell=True)
|
||||
sts = os.waitpid(p.pid, 0)[1]
|
||||
"""
|
||||
p = Popen("/etc/init.d/airtime-show-recorder start-no-monit", shell=True)
|
||||
sts = os.waitpid(p.pid, 0)[1]
|
||||
except Exception, e:
|
||||
print e
|
|
@ -1,48 +0,0 @@
|
|||
import os
|
||||
import shutil
|
||||
import sys
|
||||
from configobj import ConfigObj
|
||||
|
||||
if os.geteuid() != 0:
|
||||
print "Please run this as root."
|
||||
sys.exit(1)
|
||||
|
||||
def remove_file(path):
|
||||
try:
|
||||
os.remove(path)
|
||||
except Exception, e:
|
||||
pass
|
||||
|
||||
PATH_INI_FILE = '/etc/airtime/recorder.cfg'
|
||||
|
||||
# load config file
|
||||
try:
|
||||
config = ConfigObj(PATH_INI_FILE)
|
||||
except Exception, e:
|
||||
print 'Error loading config file: ', e
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
#remove init.d script
|
||||
print " * Removing Show-Recorder init.d Script"
|
||||
remove_file('/etc/init.d/airtime-show-recorder')
|
||||
|
||||
#remove bin dir
|
||||
print " * Removing Show-Recorder Program Directory"
|
||||
shutil.rmtree(config["bin_dir"], ignore_errors=True)
|
||||
|
||||
#remove log dir
|
||||
print " * Removing Show-Recorder Log Directory"
|
||||
shutil.rmtree(config["log_dir"], ignore_errors=True)
|
||||
|
||||
#remove temporary media-storage dir
|
||||
print " * Removing Show-Recorder Temporary Directory"
|
||||
shutil.rmtree(config["base_recorded_files"], ignore_errors=True)
|
||||
|
||||
#remove monit files
|
||||
print " * Removing Show-Recorder Monit Files"
|
||||
remove_file("/etc/monit/conf.d/monit-airtime-show-recorder.cfg")
|
||||
remove_file("/etc/monit/conf.d/monit-airtime-generic.cfg")
|
||||
|
||||
except Exception, e:
|
||||
print "Error %s" % e
|
|
@ -1,19 +0,0 @@
|
|||
from subprocess import Popen
|
||||
import os
|
||||
import sys
|
||||
|
||||
if os.geteuid() != 0:
|
||||
print "Please run this as root."
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
print "Waiting for show-recorder processes to stop...",
|
||||
if (os.path.exists('/etc/init.d/airtime-show-recorder')):
|
||||
p = Popen("/etc/init.d/airtime-show-recorder stop", shell=True)
|
||||
sts = os.waitpid(p.pid, 0)[1]
|
||||
print "OK"
|
||||
else:
|
||||
print "Wasn't running"
|
||||
except Exception, e:
|
||||
print e
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import sys
|
||||
from configobj import ConfigObj
|
||||
|
||||
if os.geteuid() != 0:
|
||||
print "Please run this as root."
|
||||
sys.exit(1)
|
||||
|
||||
PATH_INI_FILE = '/etc/airtime/recorder.cfg'
|
||||
|
||||
def remove_path(path):
|
||||
os.system('rm -rf "%s"' % path)
|
||||
|
||||
def get_current_script_dir():
|
||||
current_script_dir = os.path.realpath(__file__)
|
||||
index = current_script_dir.rindex('/')
|
||||
return current_script_dir[0:index]
|
||||
|
||||
def remove_monit_file():
|
||||
os.system("rm -f /etc/monit/conf.d/monit-airtime-show-recorder.cfg")
|
||||
|
||||
|
||||
try:
|
||||
# load config file
|
||||
try:
|
||||
config = ConfigObj(PATH_INI_FILE)
|
||||
except Exception, e:
|
||||
print 'Error loading config file: ', e
|
||||
sys.exit(1)
|
||||
|
||||
os.system("/etc/init.d/airtime-show-recorder stop")
|
||||
os.system("rm -f /etc/init.d/airtime-show-recorder")
|
||||
os.system("update-rc.d -f airtime-show-recorder remove >/dev/null 2>&1")
|
||||
|
||||
print "Removing monit file"
|
||||
remove_monit_file()
|
||||
|
||||
print "Removing log directories"
|
||||
remove_path(config["log_dir"])
|
||||
|
||||
print "Removing symlinks"
|
||||
os.system("rm -f /usr/bin/airtime-show-recorder")
|
||||
|
||||
print "Removing application files"
|
||||
remove_path(config["bin_dir"])
|
||||
|
||||
print "Removing media files"
|
||||
remove_path(config["base_recorded_files"])
|
||||
|
||||
print "Uninstall complete."
|
||||
except Exception, e:
|
||||
print "exception:" + str(e)
|
|
@ -1,22 +0,0 @@
|
|||
[loggers]
|
||||
keys=root
|
||||
|
||||
[handlers]
|
||||
keys=fileOutHandler
|
||||
|
||||
[formatters]
|
||||
keys=simpleFormatter
|
||||
|
||||
[logger_root]
|
||||
level=DEBUG
|
||||
handlers=fileOutHandler
|
||||
|
||||
[handler_fileOutHandler]
|
||||
class=logging.handlers.RotatingFileHandler
|
||||
level=DEBUG
|
||||
formatter=simpleFormatter
|
||||
args=("/var/log/airtime/show-recorder/show-recorder.log", 'a', 1000000, 5,)
|
||||
|
||||
[formatter_simpleFormatter]
|
||||
format=%(asctime)s %(levelname)s - [%(filename)s : %(funcName)s() : line %(lineno)d] - %(message)s
|
||||
datefmt=
|
|
@ -1,9 +0,0 @@
|
|||
set daemon 10 # Poll at 5 second intervals
|
||||
set logfile /var/log/monit.log
|
||||
|
||||
set httpd port 2812
|
||||
|
||||
check process airtime-show-recorder
|
||||
with pidfile "/var/run/airtime-show-recorder.pid"
|
||||
start program = "/etc/init.d/airtime-show-recorder start" with timeout 10 seconds
|
||||
stop program = "/etc/init.d/airtime-show-recorder stop"
|
|
@ -1,31 +0,0 @@
|
|||
api_client = "airtime"
|
||||
|
||||
# where the binary files live
|
||||
bin_dir = '/usr/lib/airtime/show-recorder'
|
||||
|
||||
# base path to store recordered shows at
|
||||
base_recorded_files = '/var/tmp/airtime/show-recorder/'
|
||||
|
||||
# where the logging files live
|
||||
log_dir = '/var/log/airtime/show-recorder'
|
||||
|
||||
############################################
|
||||
# RabbitMQ settings #
|
||||
############################################
|
||||
rabbitmq_host = 'localhost'
|
||||
rabbitmq_user = 'guest'
|
||||
rabbitmq_password = 'guest'
|
||||
rabbitmq_vhost = '/'
|
||||
|
||||
############################################
|
||||
# Recorded Audio settings #
|
||||
############################################
|
||||
record_bitrate = 256
|
||||
record_samplerate = 44100
|
||||
record_channels = 2
|
||||
record_sample_size = 16
|
||||
record_timeout = 3600
|
||||
|
||||
#can be either ogg|mp3, mp3 recording requires installation of the package "lame"
|
||||
record_file_type = 'ogg'
|
||||
|
|
@ -1,348 +0,0 @@
|
|||
import urllib
|
||||
import logging
|
||||
import logging.config
|
||||
import json
|
||||
import time
|
||||
import datetime
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import socket
|
||||
import pytz
|
||||
import signal
|
||||
|
||||
from configobj import ConfigObj
|
||||
|
||||
from poster.encode import multipart_encode
|
||||
from poster.streaminghttp import register_openers
|
||||
import urllib2
|
||||
|
||||
from subprocess import Popen
|
||||
from threading import Thread
|
||||
|
||||
import mutagen
|
||||
|
||||
from api_clients import api_client
|
||||
|
||||
# For RabbitMQ
|
||||
from kombu.connection import BrokerConnection
|
||||
from kombu.messaging import Exchange, Queue, Consumer, Producer
|
||||
|
||||
# configure logging
|
||||
try:
|
||||
logging.config.fileConfig("logging.cfg")
|
||||
except Exception, e:
|
||||
print 'Error configuring logging: ', e
|
||||
sys.exit()
|
||||
|
||||
# loading config file
|
||||
try:
|
||||
config = ConfigObj('/etc/airtime/recorder.cfg')
|
||||
except Exception, e:
|
||||
logger = logging.getLogger()
|
||||
logger.error('Error loading config file: %s', e)
|
||||
sys.exit()
|
||||
|
||||
def getDateTimeObj(time):
|
||||
timeinfo = time.split(" ")
|
||||
date = timeinfo[0].split("-")
|
||||
time = timeinfo[1].split(":")
|
||||
|
||||
date = map(int, date)
|
||||
time = map(int, time)
|
||||
|
||||
return datetime.datetime(date[0], date[1], date[2], time[0], time[1], time[2], 0, None)
|
||||
|
||||
class ShowRecorder(Thread):
|
||||
|
||||
def __init__ (self, show_instance, show_name, filelength, start_time):
|
||||
Thread.__init__(self)
|
||||
self.api_client = api_client.api_client_factory(config)
|
||||
self.filelength = filelength
|
||||
self.start_time = start_time
|
||||
self.show_instance = show_instance
|
||||
self.show_name = show_name
|
||||
self.logger = logging.getLogger('root')
|
||||
self.p = None
|
||||
|
||||
def record_show(self):
|
||||
length = str(self.filelength)+".0"
|
||||
filename = self.start_time
|
||||
filename = filename.replace(" ", "-")
|
||||
|
||||
if config["record_file_type"] in ["mp3", "ogg"]:
|
||||
filetype = config["record_file_type"]
|
||||
else:
|
||||
filetype = "ogg";
|
||||
|
||||
filepath = "%s%s.%s" % (config["base_recorded_files"], filename, filetype)
|
||||
|
||||
br = config["record_bitrate"]
|
||||
sr = config["record_samplerate"]
|
||||
c = config["record_channels"]
|
||||
ss = config["record_sample_size"]
|
||||
|
||||
#-f:16,2,44100
|
||||
#-b:256
|
||||
command = "ecasound -f:%s,%s,%s -i alsa -o %s,%s000 -t:%s" % (ss, c, sr, filepath, br, length)
|
||||
args = command.split(" ")
|
||||
|
||||
self.logger.info("starting record")
|
||||
self.logger.info("command " + command)
|
||||
|
||||
self.p = Popen(args)
|
||||
|
||||
#blocks at the following line until the child process
|
||||
#quits
|
||||
code = self.p.wait()
|
||||
|
||||
self.logger.info("finishing record, return code %s", self.p.returncode)
|
||||
code = self.p.returncode
|
||||
|
||||
self.p = None
|
||||
|
||||
return code, filepath
|
||||
|
||||
def cancel_recording(self):
|
||||
#add 3 second delay before actually cancelling the show. The reason
|
||||
#for this is because it appears that ecasound starts 1 second later than
|
||||
#it should, and therefore this method is sometimes incorrectly called 1
|
||||
#second before the show ends.
|
||||
#time.sleep(3)
|
||||
|
||||
#send signal interrupt (2)
|
||||
self.logger.info("Show manually cancelled!")
|
||||
if (self.p is not None):
|
||||
self.p.send_signal(signal.SIGINT)
|
||||
|
||||
#if self.p is defined, then the child process ecasound is recording
|
||||
def is_recording(self):
|
||||
return (self.p is not None)
|
||||
|
||||
def upload_file(self, filepath):
|
||||
|
||||
filename = os.path.split(filepath)[1]
|
||||
|
||||
# Register the streaming http handlers with urllib2
|
||||
register_openers()
|
||||
|
||||
# headers contains the necessary Content-Type and Content-Length
|
||||
# datagen is a generator object that yields the encoded parameters
|
||||
datagen, headers = multipart_encode({"file": open(filepath, "rb"), 'name': filename, 'show_instance': self.show_instance})
|
||||
|
||||
self.api_client.upload_recorded_show(datagen, headers)
|
||||
|
||||
def set_metadata_and_save(self, filepath):
|
||||
try:
|
||||
date = self.start_time
|
||||
md = date.split(" ")
|
||||
time = md[1].replace(":", "-")
|
||||
self.logger.info("time: %s" % time)
|
||||
|
||||
name = time+"-"+self.show_name
|
||||
artist = api_client.encode_to("Airtime Show Recorder",'utf-8')
|
||||
|
||||
#set some metadata for our file daemon
|
||||
recorded_file = mutagen.File(filepath, easy=True)
|
||||
recorded_file['title'] = name
|
||||
recorded_file['artist'] = artist
|
||||
recorded_file['date'] = md[0]
|
||||
recorded_file['tracknumber'] = self.show_instance
|
||||
recorded_file.save()
|
||||
|
||||
except Exception, e:
|
||||
self.logger.error("Exception: %s", e)
|
||||
|
||||
def run(self):
|
||||
code, filepath = self.record_show()
|
||||
|
||||
if code == 0:
|
||||
try:
|
||||
self.logger.info("Preparing to upload %s" % filepath)
|
||||
|
||||
self.set_metadata_and_save(filepath)
|
||||
|
||||
self.upload_file(filepath)
|
||||
os.remove(filepath)
|
||||
except Exception, e:
|
||||
self.logger.error(e)
|
||||
else:
|
||||
self.logger.info("problem recording show")
|
||||
os.remove(filepath)
|
||||
|
||||
class CommandListener():
|
||||
def __init__(self):
|
||||
#Thread.__init__(self)
|
||||
self.api_client = api_client.api_client_factory(config)
|
||||
self.api_client.register_component("show-recorder")
|
||||
self.logger = logging.getLogger('root')
|
||||
self.sr = None
|
||||
self.current_schedule = {}
|
||||
self.shows_to_record = {}
|
||||
self.time_till_next_show = config["record_timeout"]
|
||||
self.logger.info("RecorderFetch: init complete")
|
||||
self.server_timezone = '';
|
||||
|
||||
def init_rabbit_mq(self):
|
||||
self.logger.info("Initializing RabbitMQ stuff")
|
||||
try:
|
||||
schedule_exchange = Exchange("airtime-show-recorder", "direct", durable=True, auto_delete=True)
|
||||
schedule_queue = Queue("recorder-fetch", exchange=schedule_exchange, key="foo")
|
||||
self.connection = BrokerConnection(config["rabbitmq_host"], config["rabbitmq_user"], config["rabbitmq_password"], config["rabbitmq_vhost"])
|
||||
channel = self.connection.channel()
|
||||
consumer = Consumer(channel, schedule_queue)
|
||||
consumer.register_callback(self.handle_message)
|
||||
consumer.consume()
|
||||
except Exception, e:
|
||||
self.logger.error(e)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def handle_message(self, body, message):
|
||||
# ACK the message to take it off the queue
|
||||
message.ack()
|
||||
self.logger.info("Received command from RabbitMQ: " + message.body)
|
||||
m = json.loads(message.body)
|
||||
command = m['event_type']
|
||||
self.logger.info("Handling command: " + command)
|
||||
|
||||
if(command == 'update_schedule'):
|
||||
temp = m['shows']
|
||||
if temp is not None:
|
||||
self.parse_shows(temp)
|
||||
self.server_timezone = m['server_timezone']
|
||||
elif(command == 'cancel_recording'):
|
||||
if self.sr is not None and self.sr.is_recording():
|
||||
self.sr.cancel_recording()
|
||||
|
||||
def parse_shows(self, shows):
|
||||
self.logger.info("Parsing show schedules...")
|
||||
self.shows_to_record = {}
|
||||
for show in shows:
|
||||
show_starts = getDateTimeObj(show[u'starts'])
|
||||
show_end = getDateTimeObj(show[u'ends'])
|
||||
time_delta = show_end - show_starts
|
||||
|
||||
self.shows_to_record[show[u'starts']] = [time_delta, show[u'instance_id'], show[u'name']]
|
||||
delta = self.get_time_till_next_show()
|
||||
# awake at least 5 seconds prior to the show start
|
||||
self.time_till_next_show = delta - 5
|
||||
|
||||
self.logger.info(self.shows_to_record)
|
||||
|
||||
def get_time_till_next_show(self):
|
||||
if len(self.shows_to_record) != 0:
|
||||
tnow = datetime.datetime.utcnow()
|
||||
sorted_show_keys = sorted(self.shows_to_record.keys())
|
||||
|
||||
start_time = sorted_show_keys[0]
|
||||
next_show = getDateTimeObj(start_time)
|
||||
|
||||
delta = next_show - tnow
|
||||
out = delta.seconds
|
||||
|
||||
self.logger.debug("Next show %s", next_show)
|
||||
self.logger.debug("Now %s", tnow)
|
||||
else:
|
||||
out = config["record_timeout"]
|
||||
return out
|
||||
|
||||
def start_record(self):
|
||||
if len(self.shows_to_record) != 0:
|
||||
try:
|
||||
delta = self.get_time_till_next_show()
|
||||
|
||||
self.logger.debug("sleeping %s seconds until show", delta)
|
||||
time.sleep(delta)
|
||||
|
||||
sorted_show_keys = sorted(self.shows_to_record.keys())
|
||||
start_time = sorted_show_keys[0]
|
||||
show_length = self.shows_to_record[start_time][0]
|
||||
show_instance = self.shows_to_record[start_time][1]
|
||||
show_name = self.shows_to_record[start_time][2]
|
||||
|
||||
T = pytz.timezone(self.server_timezone)
|
||||
start_time_on_UTC = getDateTimeObj(start_time)
|
||||
start_time_on_server = start_time_on_UTC.replace(tzinfo=pytz.utc).astimezone(T)
|
||||
start_time_formatted = '%(year)d-%(month)02d-%(day)02d %(hour)02d:%(min)02d:%(sec)02d' % \
|
||||
{'year': start_time_on_server.year, 'month': start_time_on_server.month, 'day': start_time_on_server.day,\
|
||||
'hour': start_time_on_server.hour, 'min': start_time_on_server.minute, 'sec': start_time_on_server.second}
|
||||
self.sr = ShowRecorder(show_instance, show_name, show_length.seconds, start_time_formatted)
|
||||
self.sr.start()
|
||||
#remove show from shows to record.
|
||||
del self.shows_to_record[start_time]
|
||||
self.time_till_next_show = self.get_time_till_next_show()
|
||||
except Exception,e :
|
||||
import traceback
|
||||
top = traceback.format_exc()
|
||||
self.logger.error('Exception: %s', e)
|
||||
self.logger.error("traceback: %s", top)
|
||||
else:
|
||||
self.logger.debug("No recording scheduled...")
|
||||
|
||||
"""
|
||||
Main loop of the thread:
|
||||
Wait for schedule updates from RabbitMQ, but in case there arent any,
|
||||
poll the server to get the upcoming schedule.
|
||||
"""
|
||||
def run(self):
|
||||
self.logger.info("Started...")
|
||||
while not self.init_rabbit_mq():
|
||||
self.logger.error("Error connecting to RabbitMQ Server. Trying again in few seconds")
|
||||
time.sleep(5)
|
||||
|
||||
# Bootstrap: since we are just starting up, we need to grab the
|
||||
# most recent schedule. After that we can just wait for updates.
|
||||
try:
|
||||
temp = self.api_client.get_shows_to_record()
|
||||
if temp is not None:
|
||||
shows = temp['shows']
|
||||
self.server_timezone = temp['server_timezone']
|
||||
self.parse_shows(shows)
|
||||
self.logger.info("Bootstrap complete: got initial copy of the schedule")
|
||||
except Exception, e:
|
||||
self.logger.error(e)
|
||||
|
||||
loops = 1
|
||||
recording = False
|
||||
|
||||
while True:
|
||||
self.logger.info("Loop #%s", loops)
|
||||
try:
|
||||
# block until 5 seconds before the next show start
|
||||
self.connection.drain_events(timeout=int(self.time_till_next_show))
|
||||
except socket.timeout, s:
|
||||
self.logger.info(s)
|
||||
|
||||
# start_record set time_till_next_show to config["record_timeout"] so we should check before
|
||||
# if timeout amount was 1 hr..
|
||||
update_schedule = False
|
||||
if int(self.time_till_next_show) == int(config["record_timeout"]) :
|
||||
update_schedule = True
|
||||
|
||||
# start recording
|
||||
self.start_record()
|
||||
|
||||
# if real timeout happended get show schedule from airtime
|
||||
if update_schedule :
|
||||
temp = self.api_client.get_shows_to_record()
|
||||
if temp is not None:
|
||||
shows = temp['shows']
|
||||
self.server_timezone = temp['server_timezone']
|
||||
self.parse_shows(shows)
|
||||
self.logger.info("Real Timeout: the schedule has updated")
|
||||
|
||||
except Exception, e:
|
||||
import traceback
|
||||
top = traceback.format_exc()
|
||||
self.logger.error('Exception: %s', e)
|
||||
self.logger.error("traceback: %s", top)
|
||||
time.sleep(3)
|
||||
loops += 1
|
||||
|
||||
if __name__ == '__main__':
|
||||
cl = CommandListener()
|
||||
cl.run()
|
||||
|
||||
|
|
@ -189,17 +189,6 @@ class AirtimeCheck {
|
|||
self::output_status("MEDIA_MONITOR_MEM_PERC", "0%");
|
||||
self::output_status("MEDIA_MONITOR_CPU_PERC", "0%");
|
||||
}
|
||||
if (isset($services->show_recorder)) {
|
||||
self::output_status("SHOW_RECORDER_PROCESS_ID", $data->services->show_recorder->process_id);
|
||||
self::output_status("SHOW_RECORDER_RUNNING_SECONDS", $data->services->show_recorder->uptime_seconds);
|
||||
self::output_status("SHOW_RECORDER_MEM_PERC", $data->services->show_recorder->memory_perc);
|
||||
self::output_status("SHOW_RECORDER_CPU_PERC", $data->services->show_recorder->cpu_perc);
|
||||
} else {
|
||||
self::output_status("SHOW_RECORDER_PROCESS_ID", "FAILED");
|
||||
self::output_status("SHOW_RECORDER_RUNNING_SECONDS", "0");
|
||||
self::output_status("SHOW_RECORDER_MEM_PERC", "0%");
|
||||
self::output_status("SHOW_RECORDER_CPU_PERC", "0%");
|
||||
}
|
||||
if (isset($services->rabbitmq)) {
|
||||
self::output_status("RABBITMQ_PROCESS_ID", $data->services->rabbitmq->process_id);
|
||||
self::output_status("RABBITMQ_RUNNING_SECONDS", $data->services->rabbitmq->uptime_seconds);
|
||||
|
|
Loading…
Reference in New Issue