diff --git a/airtime_mvc/application/configs/navigation.php b/airtime_mvc/application/configs/navigation.php index 4f2c3ea8b..42eee6f28 100644 --- a/airtime_mvc/application/configs/navigation.php +++ b/airtime_mvc/application/configs/navigation.php @@ -58,6 +58,12 @@ $pages = array( 'module' => 'default', 'controller' => 'Preference', 'action' => 'directory-config' + ), + array( + 'label' => 'Stream Setting', + 'module' => 'default', + 'controller' => 'Preference', + 'action' => 'stream-setting' ) ) ), diff --git a/airtime_mvc/application/controllers/ApiController.php b/airtime_mvc/application/controllers/ApiController.php index f011ddc2e..0a5a068af 100644 --- a/airtime_mvc/application/controllers/ApiController.php +++ b/airtime_mvc/application/controllers/ApiController.php @@ -609,7 +609,7 @@ class ApiController extends Zend_Controller_Action } public function getStreamSettingAction() { - global $CC_CONFIG, $CC_DBC; + global $CC_CONFIG; $request = $this->getRequest(); $api_key = $request->getParam('api_key'); @@ -619,12 +619,8 @@ class ApiController extends Zend_Controller_Action print 'You are not allowed to access this resource.'; exit; } - $sql = "SELECT *" - ." FROM cc_stream_setting"; - $rows = $CC_DBC->getAll($sql); - - $this->view->msg = $rows; + $this->view->msg = Application_Model_StreamSetting::getStreamSetting(); } } diff --git a/airtime_mvc/application/controllers/PreferenceController.php b/airtime_mvc/application/controllers/PreferenceController.php index 3d043cd59..f036edf15 100644 --- a/airtime_mvc/application/controllers/PreferenceController.php +++ b/airtime_mvc/application/controllers/PreferenceController.php @@ -14,6 +14,7 @@ class PreferenceController extends Zend_Controller_Action ->addActionContext('reload-watch-directory', 'json') ->addActionContext('remove-watch-directory', 'json') ->addActionContext('is-import-in-progress', 'json') + ->addActionContext('change-stream-setting', 'json') ->initContext(); } @@ -82,6 +83,16 @@ class PreferenceController extends Zend_Controller_Action $this->view->form = $watched_dirs_pref; } + + public function streamSettingAction() + { + $request = $this->getRequest(); + $baseUrl = $request->getBaseUrl(); + + $this->view->headScript()->appendFile($baseUrl.'/js/airtime/preferences/streamsetting.js','text/javascript'); + + $this->view->form = new Application_Form_StreamSetting(); + } public function serverBrowseAction() { @@ -134,6 +145,16 @@ class PreferenceController extends Zend_Controller_Action $this->view->subform = $watched_dirs_form->render(); } + + public function changeStreamSettingAction() + { + $data = array(); + $data['setting'] = Application_Model_StreamSetting::getStreamSetting(); + RabbitMq::SendMessageToPypo("update_stream_setting", $data); + + $form = new Application_Form_StreamSetting(); + $this->view->subform = $form->render(); + } public function reloadWatchDirectoryAction() { diff --git a/airtime_mvc/application/forms/StreamSetting.php b/airtime_mvc/application/forms/StreamSetting.php new file mode 100644 index 000000000..9b2cba999 --- /dev/null +++ b/airtime_mvc/application/forms/StreamSetting.php @@ -0,0 +1,13 @@ +setDecorators(array( + array('ViewScript', array('viewScript' => 'form/stream_setting.phtml')) + )); + } +} + diff --git a/airtime_mvc/application/models/RabbitMq.php b/airtime_mvc/application/models/RabbitMq.php index d93a9d1bc..2e7167f32 100644 --- a/airtime_mvc/application/models/RabbitMq.php +++ b/airtime_mvc/application/models/RabbitMq.php @@ -57,7 +57,7 @@ class RabbitMq $channel = $conn->channel(); $channel->access_request($CC_CONFIG["rabbitmq"]["vhost"], false, false, true, true); - $EXCHANGE = 'airtime-schedule'; + $EXCHANGE = 'airtime-pypo'; $channel->exchange_declare($EXCHANGE, 'direct', false, true); $data = json_encode($md); diff --git a/airtime_mvc/application/models/StreamSetting.php b/airtime_mvc/application/models/StreamSetting.php new file mode 100644 index 000000000..a32d9774d --- /dev/null +++ b/airtime_mvc/application/models/StreamSetting.php @@ -0,0 +1,14 @@ +getAll($sql); + return $rows; + } +} \ No newline at end of file diff --git a/airtime_mvc/application/views/scripts/form/stream_setting.phtml b/airtime_mvc/application/views/scripts/form/stream_setting.phtml new file mode 100644 index 000000000..dfec45ef5 --- /dev/null +++ b/airtime_mvc/application/views/scripts/form/stream_setting.phtml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/airtime_mvc/application/views/scripts/preference/stream-setting.phtml b/airtime_mvc/application/views/scripts/preference/stream-setting.phtml new file mode 100644 index 000000000..bdbb30267 --- /dev/null +++ b/airtime_mvc/application/views/scripts/preference/stream-setting.phtml @@ -0,0 +1,4 @@ +
+

Stream Setting

+form; ?> +
diff --git a/airtime_mvc/public/js/airtime/preferences/streamsetting.js b/airtime_mvc/public/js/airtime/preferences/streamsetting.js new file mode 100644 index 000000000..69eb6f11b --- /dev/null +++ b/airtime_mvc/public/js/airtime/preferences/streamsetting.js @@ -0,0 +1,13 @@ +$(document).ready(function() { + + $('#change_setting').click(function(){ + var url; + + url = "/Preference/change-stream-setting"; + + $.post(url, + {format: "json"} + ); + }); + +}); \ No newline at end of file diff --git a/install_minimal/include/AirtimeIni.php b/install_minimal/include/AirtimeIni.php index be81a9226..893347920 100644 --- a/install_minimal/include/AirtimeIni.php +++ b/install_minimal/include/AirtimeIni.php @@ -81,6 +81,11 @@ class AirtimeIni 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); + }else{ + if (!chown(AirtimeIni::CONF_FILE_LIQUIDSOAP, "pypo") || !chgrp(AirtimeIni::CONF_FILE_LIQUIDSOAP, "pypo") ){ + echo "Could not set ownership of liquidsoap.cfg to 'pypo'. Exiting."; + exit(1); + } } if (!copy(__DIR__."/../../python_apps/media-monitor/media-monitor.cfg", AirtimeIni::CONF_FILE_MEDIAMONITOR)){ echo "Could not copy media-monitor.cfg to /etc/airtime/. Exiting."; diff --git a/python_apps/api_clients/api_client.py b/python_apps/api_clients/api_client.py index bfec4178a..7252b16a0 100644 --- a/python_apps/api_clients/api_client.py +++ b/python_apps/api_clients/api_client.py @@ -528,7 +528,7 @@ class AirTimeApiClient(ApiClientInterface): return response def get_stream_setting(self): - #logger = logging.getLogger() + logger = logging.getLogger() 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"]) @@ -538,7 +538,7 @@ class AirTimeApiClient(ApiClientInterface): response = json.loads(response) except Exception, e: response = None - #logger.error("Exception: %s", e) + logger.error("Exception: %s", e) return response diff --git a/python_apps/pypo/pypofetch.py b/python_apps/pypo/pypofetch.py index dcf0fc81c..f0cdecd7d 100755 --- a/python_apps/pypo/pypofetch.py +++ b/python_apps/pypo/pypofetch.py @@ -13,6 +13,7 @@ from threading import Thread from subprocess import Popen, PIPE from datetime import datetime from datetime import timedelta +import filecmp # For RabbitMQ from kombu.connection import BrokerConnection @@ -37,17 +38,14 @@ except Exception, e: logger.error('Error loading config file: %s', e) sys.exit() -# Yuk - using a global, i know! -SCHEDULE_PUSH_MSG = [] - """ Handle a message from RabbitMQ, put it into our yucky global var. Hopefully there is a better way to do this. """ -def handle_message(body, message): +"""def handle_message(body, message): logger = logging.getLogger('fetch') global SCHEDULE_PUSH_MSG - logger.info("Received schedule from RabbitMQ: " + message.body) + logger.info("Received event from RabbitMQ: " + message.body) m = json.loads(message.body) command = m['event_type'] @@ -59,10 +57,10 @@ def handle_message(body, message): logger.info("Setting timezone to %s", m['timezone']) os.environ['TZ'] = m['timezone'] time.tzset() - + elif (command == 'update_stream_setting'): + logger.info("Updating stream setting: %s", m['setting']) # ACK the message to take it off the queue - message.ack() - + message.ack()""" class PypoFetch(Thread): def __init__(self, q): @@ -71,26 +69,103 @@ class PypoFetch(Thread): self.api_client = api_client.api_client_factory(config) self.set_export_source('scheduler') self.queue = q + self.schedule_data = [] logger.info("PypoFetch: init complete") def init_rabbit_mq(self): logger = logging.getLogger('fetch') logger.info("Initializing RabbitMQ stuff") try: - schedule_exchange = Exchange("airtime-schedule", "direct", durable=True, auto_delete=True) + schedule_exchange = Exchange("airtime-pypo", "direct", durable=True, auto_delete=True) schedule_queue = Queue("pypo-fetch", exchange=schedule_exchange, key="foo") self.connection = BrokerConnection(config["rabbitmq_host"], config["rabbitmq_user"], config["rabbitmq_password"], "/") channel = self.connection.channel() consumer = Consumer(channel, schedule_queue) - consumer.register_callback(handle_message) + consumer.register_callback(self.handle_message) consumer.consume() except Exception, e: logger.error(e) return False return True + + """ + Handle a message from RabbitMQ, put it into our yucky global var. + Hopefully there is a better way to do this. + """ + def handle_message(self, body, message): + logger = logging.getLogger('fetch') + logger.info("Received event from RabbitMQ: " + message.body) + + m = json.loads(message.body) + command = m['event_type'] + logger.info("Handling command: " + command) + + if(command == 'update_schedule'): + self.schedule_data = m['schedule'] + self.process_schedule(self.schedule_data, "scheduler", False) + elif (command == 'update_timezone'): + logger.info("Setting timezone to %s", m['timezone']) + os.environ['TZ'] = m['timezone'] + time.tzset() + elif (command == 'update_stream_setting'): + logger.info("Updating stream setting: %s", m['setting']) + self.regenerateLiquidsoapConf(m['setting']) + # ACK the message to take it off the queue + message.ack() + + def regenerateLiquidsoapConf(self, setting): + logger = logging.getLogger('fetch') + existing = {} + # create a temp file + fh = open('/etc/airtime/liquidsoap.cfg', 'r') + # read existing conf file and build dict + while 1: + line = fh.readline() + if not line: + break; + key, value = line.split('=') + key = key.strip() + value = value.strip() + value = value.replace('"', '') + if value == "dummy_string" or value == "0": + value = '' + existing[key] = value + fh.close() + # flag for any change in cofig + change = False + + # look for changes + for s in setting: + if not s[u'value'] == existing[s[u'keyname']]: + logger.info("Keyname: %s, Curent value: %s, New Value: %s", s[u'keyname'], s[u'value'], existing[s[u'keyname']]) + change = True + # rewrite + if change: + fh = open('/etc/airtime/liquidsoap.cfg', 'w') + logger.info("Rewriting liquidsoap.cfg...") + for d in setting: + buffer = d[u'keyname'] + " = " + if(d[u'type'] == 'string'): + temp = d[u'value'] + if(temp == ""): + temp = "dummy_string" + buffer += "\"" + temp + "\"" + else: + temp = d[u'value'] + if(temp == ""): + temp = "0" + buffer += temp + buffer += "\n" + fh.write(buffer) + fh.close() + # restart playout + logger.info("Restarting airtime-playout...") + p = Popen("/etc/init.d/airtime-playout restart >/dev/null 2>&1", shell=True) + sts = os.waitpid(p.pid, 0)[1] + else: + logger.info("No change detected in setting...") - def set_export_source(self, export_source): logger = logging.getLogger('fetch') self.export_source = export_source @@ -379,9 +454,9 @@ class PypoFetch(Thread): # Bootstrap: since we are just starting up, we need to grab the # most recent schedule. After that we can just wait for updates. - status, schedule_data = self.api_client.get_schedule() + status, self.schedule_data = self.api_client.get_schedule() if status == 1: - self.process_schedule(schedule_data, "scheduler", True) + self.process_schedule(self.schedule_data , "scheduler", True) logger.info("Bootstrap complete: got initial copy of the schedule") loops = 1 @@ -391,15 +466,11 @@ class PypoFetch(Thread): # Wait for messages from RabbitMQ. Timeout if we # dont get any after POLL_INTERVAL. self.connection.drain_events(timeout=POLL_INTERVAL) - # Hooray for globals! - schedule_data = SCHEDULE_PUSH_MSG - status = 1 - except: + except Exception, e: # We didnt get a message for a while, so poll the server # to get an updated schedule. - status, schedule_data = self.api_client.get_schedule() - - if status == 1: - self.process_schedule(schedule_data, "scheduler", False) + logger.info("Exception %s", e) + status, self.schedule_data = self.api_client.get_schedule() + self.process_schedule(self.schedule_data, "scheduler", False) loops += 1