CC-2607: Abilitiy to adjust stream bitrate, type, etc from the UI

interface

- dummy page "Stream Setting" page for the test
- StreamSetting model is added
- set owner and group as 'pypo' for liquidsoap.cfg
- pypofech handle 'update_stream_setting' command
This commit is contained in:
James 2011-08-15 16:10:46 -04:00
parent e18c0903cb
commit 4f2b2dba6d
12 changed files with 174 additions and 30 deletions

View File

@ -58,6 +58,12 @@ $pages = array(
'module' => 'default',
'controller' => 'Preference',
'action' => 'directory-config'
),
array(
'label' => 'Stream Setting',
'module' => 'default',
'controller' => 'Preference',
'action' => 'stream-setting'
)
)
),

View File

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

View File

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

View File

@ -0,0 +1,13 @@
<?php
class Application_Form_StreamSetting extends Zend_Form_SubForm
{
public function init()
{
$this->setDecorators(array(
array('ViewScript', array('viewScript' => 'form/stream_setting.phtml'))
));
}
}

View File

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

View File

@ -0,0 +1,14 @@
<?php
class Application_Model_StreamSetting {
public function __construct(){
}
public static function getStreamSetting(){
global $CC_DBC;
$sql = "SELECT *"
." FROM cc_stream_setting";
$rows = $CC_DBC->getAll($sql);
return $rows;
}
}

View File

@ -0,0 +1 @@
<input id="change_setting" type="button" value="change setting"></input>

View File

@ -0,0 +1,4 @@
<div id="stream-setting-section" class="ui-widget ui-widget-content block-shadow simple-formblock clearfix padded-strong manage-folders">
<h2>Stream Setting</h2>
<?php echo $this->form; ?>
</div>

View File

@ -0,0 +1,13 @@
$(document).ready(function() {
$('#change_setting').click(function(){
var url;
url = "/Preference/change-stream-setting";
$.post(url,
{format: "json"}
);
});
});

View File

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

View File

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

View File

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