From 695535ae64fbdbb8e3b11ce04b24e012dc6524fa Mon Sep 17 00:00:00 2001 From: James Date: Fri, 24 Feb 2012 13:12:50 -0500 Subject: [PATCH] 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 --- .../application/controllers/ApiController.php | 1 - .../controllers/SystemstatusController.php | 36 -- .../controllers/plugins/RabbitMqPlugin.php | 2 +- airtime_mvc/application/models/RabbitMq.php | 4 +- .../application/models/Systemstatus.php | 15 - install_minimal/include/AirtimeIni.php | 8 - install_minimal/include/AirtimeInstall.php | 5 +- install_minimal/include/airtime-copy-files.sh | 6 +- install_minimal/include/airtime-initialize.sh | 12 +- .../include/airtime-remove-files.sh | 4 +- .../include/airtime-uninitialize.sh | 6 +- python_apps/api_clients/api_client.py | 63 ++-- python_apps/pypo/install/pypo-copy-files.py | 3 + python_apps/pypo/logging.cfg | 24 +- python_apps/pypo/pypo-cli.py | 9 +- python_apps/pypo/pypo.cfg | 14 + python_apps/pypo/pypofetch.py | 56 ++- python_apps/pypo/recorder.py | 270 ++++++++++++++ .../show-recorder/airtime-show-recorder | 26 -- .../airtime-show-recorder-init-d | 71 ---- .../install/recorder-copy-files.py | 74 ---- .../install/recorder-initialize.py | 24 -- .../install/recorder-remove-files.py | 48 --- .../install/recorder-uninitialize.py | 19 - .../install/recorder-uninstall.py | 54 --- python_apps/show-recorder/logging.cfg | 22 -- .../monit-airtime-show-recorder.cfg | 9 - python_apps/show-recorder/recorder.cfg | 31 -- python_apps/show-recorder/recorder.py | 348 ------------------ utils/airtime-check-system.php | 11 - 30 files changed, 419 insertions(+), 856 deletions(-) create mode 100644 python_apps/pypo/recorder.py delete mode 100755 python_apps/show-recorder/airtime-show-recorder delete mode 100755 python_apps/show-recorder/airtime-show-recorder-init-d delete mode 100644 python_apps/show-recorder/install/recorder-copy-files.py delete mode 100644 python_apps/show-recorder/install/recorder-initialize.py delete mode 100644 python_apps/show-recorder/install/recorder-remove-files.py delete mode 100644 python_apps/show-recorder/install/recorder-uninitialize.py delete mode 100644 python_apps/show-recorder/install/recorder-uninstall.py delete mode 100644 python_apps/show-recorder/logging.cfg delete mode 100644 python_apps/show-recorder/monit-airtime-show-recorder.cfg delete mode 100644 python_apps/show-recorder/recorder.cfg delete mode 100644 python_apps/show-recorder/recorder.py diff --git a/airtime_mvc/application/controllers/ApiController.php b/airtime_mvc/application/controllers/ApiController.php index d15e3ca96..8bf9226bb 100644 --- a/airtime_mvc/application/controllers/ApiController.php +++ b/airtime_mvc/application/controllers/ApiController.php @@ -763,7 +763,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() ) ); diff --git a/airtime_mvc/application/controllers/SystemstatusController.php b/airtime_mvc/application/controllers/SystemstatusController.php index 8a04f2f9b..b660cd4e1 100644 --- a/airtime_mvc/application/controllers/SystemstatusController.php +++ b/airtime_mvc/application/controllers/SystemstatusController.php @@ -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; - } - } } diff --git a/airtime_mvc/application/controllers/plugins/RabbitMqPlugin.php b/airtime_mvc/application/controllers/plugins/RabbitMqPlugin.php index 900f5a237..07f58c742 100644 --- a/airtime_mvc/application/controllers/plugins/RabbitMqPlugin.php +++ b/airtime_mvc/application/controllers/plugins/RabbitMqPlugin.php @@ -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"); } } } diff --git a/airtime_mvc/application/models/RabbitMq.php b/airtime_mvc/application/models/RabbitMq.php index f3b823eb5..4d0e501a5 100644 --- a/airtime_mvc/application/models/RabbitMq.php +++ b/airtime_mvc/application/models/RabbitMq.php @@ -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); diff --git a/airtime_mvc/application/models/Systemstatus.php b/airtime_mvc/application/models/Systemstatus.php index 601cbaa1f..8503a98d2 100644 --- a/airtime_mvc/application/models/Systemstatus.php +++ b/airtime_mvc/application/models/Systemstatus.php @@ -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"); diff --git a/install_minimal/include/AirtimeIni.php b/install_minimal/include/AirtimeIni.php index d8395efc4..f66a3caa7 100644 --- a/install_minimal/include/AirtimeIni.php +++ b/install_minimal/include/AirtimeIni.php @@ -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); diff --git a/install_minimal/include/AirtimeInstall.php b/install_minimal/include/AirtimeInstall.php index 606d74e55..dc5869165 100644 --- a/install_minimal/include/AirtimeInstall.php +++ b/install_minimal/include/AirtimeInstall.php @@ -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; diff --git a/install_minimal/include/airtime-copy-files.sh b/install_minimal/include/airtime-copy-files.sh index 5eab4a4ef..4f88cab72 100755 --- a/install_minimal/include/airtime-copy-files.sh +++ b/install_minimal/include/airtime-copy-files.sh @@ -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 diff --git a/install_minimal/include/airtime-initialize.sh b/install_minimal/include/airtime-initialize.sh index dc69073bd..56be18066 100755 --- a/install_minimal/include/airtime-initialize.sh +++ b/install_minimal/include/airtime-initialize.sh @@ -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 diff --git a/install_minimal/include/airtime-remove-files.sh b/install_minimal/include/airtime-remove-files.sh index b7c6c54b8..93e9efb60 100755 --- a/install_minimal/include/airtime-remove-files.sh +++ b/install_minimal/include/airtime-remove-files.sh @@ -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 diff --git a/install_minimal/include/airtime-uninitialize.sh b/install_minimal/include/airtime-uninitialize.sh index 2d0388b90..d26256367 100755 --- a/install_minimal/include/airtime-uninitialize.sh +++ b/install_minimal/include/airtime-uninitialize.sh @@ -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 diff --git a/python_apps/api_clients/api_client.py b/python_apps/api_clients/api_client.py index b9eb7d11f..cea8062d4 100755 --- a/python_apps/api_clients/api_client.py +++ b/python_apps/api_clients/api_client.py @@ -23,14 +23,17 @@ from configobj import ConfigObj AIRTIME_VERSION = "2.0.1" -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'): @@ -160,17 +163,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: @@ -190,7 +196,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"]) @@ -210,7 +216,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))) @@ -226,7 +232,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): @@ -245,7 +251,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"]) @@ -266,7 +272,7 @@ class AirTimeApiClient(ApiClientInterface): def get_media(self, uri, dst): - logger = logging.getLogger() + logger = self.logger try: src = uri + "/api_key/%%api_key%%" @@ -283,7 +289,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"]) @@ -310,7 +316,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 @@ -330,7 +336,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: @@ -340,13 +346,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) @@ -359,7 +364,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"]) @@ -393,7 +398,7 @@ class AirTimeApiClient(ApiClientInterface): return response def setup_media_monitor(self): - logger = logging.getLogger() + logger = self.logger response = None try: @@ -410,7 +415,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: @@ -455,7 +460,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"]) @@ -472,7 +477,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"]) @@ -488,7 +493,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"]) @@ -505,7 +510,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"]) @@ -522,7 +527,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"]) @@ -539,7 +544,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"]) @@ -560,7 +565,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"]) @@ -572,7 +577,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"]) diff --git a/python_apps/pypo/install/pypo-copy-files.py b/python_apps/pypo/install/pypo-copy-files.py index 90d4705fb..1bef4d1de 100644 --- a/python_apps/pypo/install/pypo-copy-files.py +++ b/python_apps/pypo/install/pypo-copy-files.py @@ -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") diff --git a/python_apps/pypo/logging.cfg b/python_apps/pypo/logging.cfg index 3699d3dbf..acff7007d 100644 --- a/python_apps/pypo/logging.cfg +++ b/python_apps/pypo/logging.cfg @@ -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= diff --git a/python_apps/pypo/pypo-cli.py b/python_apps/pypo/pypo-cli.py index 4617a30f2..d82d0b782 100644 --- a/python_apps/pypo/pypo-cli.py +++ b/python_apps/pypo/pypo-cli.py @@ -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() diff --git a/python_apps/pypo/pypo.cfg b/python_apps/pypo/pypo.cfg index 3280ba540..09ccdb197 100644 --- a/python_apps/pypo/pypo.cfg +++ b/python_apps/pypo/pypo.cfg @@ -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/' diff --git a/python_apps/pypo/pypofetch.py b/python_apps/pypo/pypofetch.py index 12e27ddbe..ab12e8eef 100644 --- a/python_apps/pypo/pypofetch.py +++ b/python_apps/pypo/pypofetch.py @@ -43,11 +43,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") @@ -95,6 +96,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) @@ -313,6 +320,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) """ @@ -488,6 +519,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") @@ -542,8 +584,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: diff --git a/python_apps/pypo/recorder.py b/python_apps/pypo/recorder.py new file mode 100644 index 000000000..6347c4f76 --- /dev/null +++ b/python_apps/pypo/recorder.py @@ -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) + diff --git a/python_apps/show-recorder/airtime-show-recorder b/python_apps/show-recorder/airtime-show-recorder deleted file mode 100755 index db0165649..000000000 --- a/python_apps/show-recorder/airtime-show-recorder +++ /dev/null @@ -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 diff --git a/python_apps/show-recorder/airtime-show-recorder-init-d b/python_apps/show-recorder/airtime-show-recorder-init-d deleted file mode 100755 index 2fb8f4666..000000000 --- a/python_apps/show-recorder/airtime-show-recorder-init-d +++ /dev/null @@ -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 diff --git a/python_apps/show-recorder/install/recorder-copy-files.py b/python_apps/show-recorder/install/recorder-copy-files.py deleted file mode 100644 index 44ae93cc9..000000000 --- a/python_apps/show-recorder/install/recorder-copy-files.py +++ /dev/null @@ -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 diff --git a/python_apps/show-recorder/install/recorder-initialize.py b/python_apps/show-recorder/install/recorder-initialize.py deleted file mode 100644 index dd0c8bdf4..000000000 --- a/python_apps/show-recorder/install/recorder-initialize.py +++ /dev/null @@ -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 diff --git a/python_apps/show-recorder/install/recorder-remove-files.py b/python_apps/show-recorder/install/recorder-remove-files.py deleted file mode 100644 index feaf574eb..000000000 --- a/python_apps/show-recorder/install/recorder-remove-files.py +++ /dev/null @@ -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 diff --git a/python_apps/show-recorder/install/recorder-uninitialize.py b/python_apps/show-recorder/install/recorder-uninitialize.py deleted file mode 100644 index 168ff14b8..000000000 --- a/python_apps/show-recorder/install/recorder-uninitialize.py +++ /dev/null @@ -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 - diff --git a/python_apps/show-recorder/install/recorder-uninstall.py b/python_apps/show-recorder/install/recorder-uninstall.py deleted file mode 100644 index d778f8d07..000000000 --- a/python_apps/show-recorder/install/recorder-uninstall.py +++ /dev/null @@ -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) diff --git a/python_apps/show-recorder/logging.cfg b/python_apps/show-recorder/logging.cfg deleted file mode 100644 index 766f56972..000000000 --- a/python_apps/show-recorder/logging.cfg +++ /dev/null @@ -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= diff --git a/python_apps/show-recorder/monit-airtime-show-recorder.cfg b/python_apps/show-recorder/monit-airtime-show-recorder.cfg deleted file mode 100644 index 137e56c03..000000000 --- a/python_apps/show-recorder/monit-airtime-show-recorder.cfg +++ /dev/null @@ -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" diff --git a/python_apps/show-recorder/recorder.cfg b/python_apps/show-recorder/recorder.cfg deleted file mode 100644 index d817a32b2..000000000 --- a/python_apps/show-recorder/recorder.cfg +++ /dev/null @@ -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' - diff --git a/python_apps/show-recorder/recorder.py b/python_apps/show-recorder/recorder.py deleted file mode 100644 index 3ec1caf39..000000000 --- a/python_apps/show-recorder/recorder.py +++ /dev/null @@ -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() - - diff --git a/utils/airtime-check-system.php b/utils/airtime-check-system.php index ae1b11641..4f7237fc0 100644 --- a/utils/airtime-check-system.php +++ b/utils/airtime-check-system.php @@ -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);