diff --git a/airtime_mvc/application/controllers/NowplayingController.php b/airtime_mvc/application/controllers/NowplayingController.php index 257b354a2..03f89d9bc 100644 --- a/airtime_mvc/application/controllers/NowplayingController.php +++ b/airtime_mvc/application/controllers/NowplayingController.php @@ -52,7 +52,6 @@ class NowplayingController extends Zend_Controller_Action // unset session Zend_Session::namespaceUnset('referrer'); }else{ - var_dump($form->getMessages()); $logo = Application_Model_Preference::GetStationLogo(); if($logo){ $this->view->logoImg = $logo; diff --git a/airtime_mvc/application/controllers/PreferenceController.php b/airtime_mvc/application/controllers/PreferenceController.php index f036edf15..d69b91323 100644 --- a/airtime_mvc/application/controllers/PreferenceController.php +++ b/airtime_mvc/application/controllers/PreferenceController.php @@ -7,9 +7,7 @@ class PreferenceController extends Zend_Controller_Action { /* Initialize action controller here */ $ajaxContext = $this->_helper->getHelper('AjaxContext'); - $ajaxContext/*->addActionContext('register', 'json') - ->addActionContext('remindme', 'json')*/ - ->addActionContext('server-browse', 'json') + $ajaxContext->addActionContext('server-browse', 'json') ->addActionContext('change-stor-directory', 'json') ->addActionContext('reload-watch-directory', 'json') ->addActionContext('remove-watch-directory', 'json') @@ -91,7 +89,66 @@ class PreferenceController extends Zend_Controller_Action $this->view->headScript()->appendFile($baseUrl.'/js/airtime/preferences/streamsetting.js','text/javascript'); - $this->view->form = new Application_Form_StreamSetting(); + // get current settings + $temp = Application_Model_StreamSetting::getStreamSetting(); + $setting = array(); + foreach ($temp as $t){ + $setting[$t['keyname']] = $t['value']; + } + + // get predefined type and bitrate from pref table + $temp_types = Application_Model_Preference::GetStreamType(); + $stream_types = array(); + foreach ($temp_types as $type){ + $stream_types[trim($type)] = strtoupper(trim($type)); + } + + $temp_bitrate = Application_Model_Preference::GetStreamBitrate(); + $stream_bitrates = array(); + foreach ($temp_bitrate as $type){ + $stream_bitrates[trim($type)] = strtoupper(trim($type))." Kbit/s"; + } + + $num_of_stream = 3; + $form = new Application_Form_StreamSetting(); + $form->setSetting($setting); + $form->startFrom(); + for($i=1; $i<=$num_of_stream; $i++){ + $subform = new Application_Form_StreamSettingSubForm(); + $subform->setPrefix($i); + $subform->setSetting($setting); + $subform->setStreamTypes($stream_types); + $subform->setStreamBitrates($stream_bitrates); + $subform->startForm(); + $form->addSubForm($subform, "s".$i."_subform"); + } + if ($request->isPost()) { + $post_data = $request->getPost(); + $error = false; + $values = array(); + for($i=1; $i<=$num_of_stream; $i++){ + if(!$form->getSubForm("s".$i."_subform")->isValid($post_data["s".$i."_data"])){ + $error = true; + }else{ + // getValues returne array of size 1, so reorganized it + foreach($form->getSubForm("s".$i."_subform")->getValues() as $key => $d){ + $values[$key] = $d; + } + } + } + if($form->isValid($post_data['output_sound_device'])){ + $values['output_sound_device'] = $form->getValue('output_sound_device'); + } + if(!$error){ + Application_Model_StreamSetting::setStreamSetting($values); + $data = array(); + $data['setting'] = Application_Model_StreamSetting::getStreamSetting(); + RabbitMq::SendMessageToPypo("update_stream_setting", $data); + $this->view->statusMsg = "
Stream Setting Updated.
"; + } + } + $this->view->num_stream = $num_of_stream; + $this->view->form = $form; } public function serverBrowseAction() @@ -145,16 +202,6 @@ 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 index 9b2cba999..dd7c01924 100644 --- a/airtime_mvc/application/forms/StreamSetting.php +++ b/airtime_mvc/application/forms/StreamSetting.php @@ -1,13 +1,30 @@ setDecorators(array( - array('ViewScript', array('viewScript' => 'form/stream_setting.phtml')) - )); + + } + + public function setSetting($setting){ + $this->setting = $setting; + } + + public function startFrom(){ + $setting = $this->setting; + $output_sound_device = new Zend_Form_Element_Checkbox('output_sound_device'); + $output_sound_device->setLabel('Enabled') + ->setRequired(false) + ->setValue($setting['output_sound_device']) + ->setDecorators(array('ViewHelper')); + $this->addElement($output_sound_device); + } + + public function isValid($data){ + $this->populate(array("output_sound_device"=>$data)); + return true; } } - diff --git a/airtime_mvc/application/forms/StreamSettingSubForm.php b/airtime_mvc/application/forms/StreamSettingSubForm.php new file mode 100644 index 000000000..2a5ff94a3 --- /dev/null +++ b/airtime_mvc/application/forms/StreamSettingSubForm.php @@ -0,0 +1,145 @@ +prefix = $prefix; + } + + public function setSetting($setting){ + $this->setting = $setting; + } + + public function setStreamTypes($stream_types){ + $this->stream_types = $stream_types; + } + + public function setStreamBitrates($stream_bitrates){ + $this->stream_bitrates = $stream_bitrates; + } + + public function startForm(){ + $prefix = "s".$this->prefix; + $stream_number = $this->prefix; + $setting = $this->setting; + $stream_types = $this->stream_types; + $stream_bitrates = $this->stream_bitrates; + + $this->setIsArray(true); + $this->setElementsBelongTo($prefix."_data"); + + $enable = new Zend_Form_Element_Checkbox('enable'); + $enable->setLabel('Enabled:') + ->setValue($setting['output_'.$prefix] != 'disabled'?1:0) + ->setDecorators(array('ViewHelper')); + $this->addElement($enable); + + $type = new Zend_Form_Element_Select('type'); + $type->setLabel("Type:") + ->setMultiOptions($stream_types) + ->setValue($setting[$prefix.'_type']) + ->setDecorators(array('ViewHelper')); + $this->addElement($type); + + $bitrate = new Zend_Form_Element_Select('bitrate'); + $bitrate->setLabel("Bitrate:") + ->setMultiOptions($stream_bitrates) + ->setValue($setting[$prefix.'_bitrate']) + ->setDecorators(array('ViewHelper')); + $this->addElement($bitrate); + + $output = new Zend_Form_Element_Select('output'); + $output->setLabel("Output to:") + ->setMultiOptions(array("icecast"=>"Icecast", "shoutcast"=>"Shoutcast")) + ->setValue($setting['output_'.$prefix]) + ->setDecorators(array('ViewHelper')); + $this->addElement($output); + + $host = new Zend_Form_Element_Text('host'); + $host->setLabel("Server") + ->setValue($setting[$prefix.'_host']) + ->setDecorators(array('ViewHelper')); + $this->addElement($host); + + $port = new Zend_Form_Element_Text('port'); + $port->setLabel("Port") + ->setValue($setting[$prefix.'_port']) + ->setDecorators(array('ViewHelper')); + $this->addElement($port); + + $pass = new Zend_Form_Element_Text('pass'); + $pass->setLabel("Password") + ->setValue($setting[$prefix.'_pass']) + ->setDecorators(array('ViewHelper')); + $this->addElement($pass); + + $genre = new Zend_Form_Element_Text('genre'); + $genre->setLabel("Genre:") + ->setValue($setting[$prefix.'_genre']) + ->setDecorators(array('ViewHelper')); + $this->addElement($genre); + + $url = new Zend_Form_Element_Text('url'); + $url->setLabel("URL") + ->setValue($setting[$prefix.'_url']) + ->setDecorators(array('ViewHelper')); + $this->addElement($url); + + $description = new Zend_Form_Element_Text('description'); + $description->setLabel("Description") + ->setValue($setting[$prefix.'_description']) + ->setDecorators(array('ViewHelper')); + $this->addElement($description); + + $mount_info = explode('.',$setting[$prefix.'_mount']); + $mount = new Zend_Form_Element_Text('mount'); + $mount->class = "with-info"; + $mount->setLabel("Mount Point") + ->setValue($mount_info[0]) + ->setDecorators(array('ViewHelper')); + $this->addElement($mount); + + $stream_url_value = "http://".$setting[$prefix.'_host'].":".$setting[$prefix.'_port']."/".$mount_info[0].".".$setting[$prefix.'_type']; + + $this->setDecorators(array( + array('ViewScript', array('viewScript' => 'form/stream-setting-form.phtml', "stream_number"=>$stream_number, "stream_url"=>$stream_url_value)) + )); + } + + public function isValid ($data){ + $isValid = parent::isValid($data); + if($data['enable'] == 1){ + if($data['host'] == ''){ + $element = $this->getElement("host"); + $element->addError("Server cannot be empty."); + $isValid = false; + } + if($data['port'] == ''){ + $element = $this->getElement("port"); + $element->addError("Port cannot be empty."); + $isValid = false; + } + if($data['pass'] == ''){ + $element = $this->getElement("pass"); + $element->addError("Password cannot be empty."); + $isValid = false; + } + if($data['output'] == 'icecast'){ + if($data['mount'] == ''){ + $element = $this->getElement("mount"); + $element->addError("Mount cannot be empty with Icecast server."); + $isValid = false; + } + } + } + return $isValid; + } +} \ No newline at end of file diff --git a/airtime_mvc/application/models/Preference.php b/airtime_mvc/application/models/Preference.php index 792abe9de..4be4882f5 100644 --- a/airtime_mvc/application/models/Preference.php +++ b/airtime_mvc/application/models/Preference.php @@ -376,5 +376,15 @@ class Application_Model_Preference public static function GetImportTimestamp(){ return Application_Model_Preference::GetValue("import_timestamp"); } + + public static function GetStreamType(){ + $st = Application_Model_Preference::GetValue("stream_type"); + return explode(',', $st); + } + + public static function GetStreamBitrate(){ + $sb = Application_Model_Preference::GetValue("stream_bitrate"); + return explode(',', $sb); + } } diff --git a/airtime_mvc/application/models/StreamSetting.php b/airtime_mvc/application/models/StreamSetting.php index a32d9774d..e6228931d 100644 --- a/airtime_mvc/application/models/StreamSetting.php +++ b/airtime_mvc/application/models/StreamSetting.php @@ -1,8 +1,9 @@ getAll($sql); return $rows; } + public static function setStreamSetting($data){ + global $CC_DBC; + foreach($data as $key=>$d){ + if($key == "output_sound_device"){ + $v = $d == 1?"true":"false"; + $sql = "UPDATE cc_stream_setting SET value='$v' WHERE keyname='$key'"; + $CC_DBC->query($sql); + } + else{ + $temp = explode('_', $key); + $prefix = $temp[0]; + foreach($d as $k=>$v){ + $keyname = $prefix."_".$k; + if( $k == 'output'){ + $keyname = $k."_".$prefix; + if( $d["enable"] == 0){ + $v = 'disabled'; + } + } + if( $k == 'mount'){ + $v = $d['mount'].".".$d['type']; + } + $sql = "UPDATE cc_stream_setting SET value='$v' WHERE keyname='$keyname'"; + $CC_DBC->query($sql); + } + } + } + } } \ No newline at end of file diff --git a/airtime_mvc/application/views/scripts/form/stream-setting-form.phtml b/airtime_mvc/application/views/scripts/form/stream-setting-form.phtml new file mode 100644 index 000000000..74c904a91 --- /dev/null +++ b/airtime_mvc/application/views/scripts/form/stream-setting-form.phtml @@ -0,0 +1,148 @@ + stream_number; + ?> +

Stream stream_number?>

+
+
+
+
+ +
+
+ element->getElement('enable')?> +
+ +
+ +
+
+ element->getElement('type')?> +
+ +
+ +
+
+ element->getElement('bitrate')?> +
+ +
+ +
+
+ element->getElement('output')?> +
+
+ + +
+
+
+ +
+
+ element->getElement('host')?> + element->getElement('host')->hasErrors()) : ?> +
    + element->getElement('host')->getMessages() as $error): ?> +
  • + +
+ +
+
+ +
+
+ element->getElement('port')?> + element->getElement('port')->hasErrors()) : ?> +
    + element->getElement('port')->getMessages() as $error): ?> +
  • + +
+ +
+
+ +
+
+ element->getElement('pass')?> + element->getElement('pass')->hasErrors()) : ?> +
    + element->getElement('pass')->getMessages() as $error): ?> +
  • + +
+ +
+ +
+ The following info will be displayed to listeners in their media player: +
+ +
+ +
+
+ element->getElement('genre')?> + element->getElement('genre')->hasErrors()) : ?> +
    + element->getElement('genre')->getMessages() as $error): ?> +
  • + +
+ +
+ +
+ +
+
+ element->getElement('url')?> + element->getElement('url')->hasErrors()) : ?> +
    + element->getElement('url')->getMessages() as $error): ?> +
  • + +
+ +
+ +
+ +
+
+ element->getElement('description')?> + element->getElement('description')->hasErrors()) : ?> +
    + element->getElement('description')->getMessages() as $error): ?> +
  • + +
+ +
+ +
+ +
+
+ element->getElement('mount')?>.element->getElement('type')->getValue()?> + element->getElement('mount')->hasErrors()) : ?> +
    + element->getElement('mount')->getMessages() as $error): ?> +
  • + +
+ +
+
+ +
+
+

stream_url?>

+
+
+
+
+
\ 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 deleted file mode 100644 index dfec45ef5..000000000 --- a/airtime_mvc/application/views/scripts/form/stream_setting.phtml +++ /dev/null @@ -1 +0,0 @@ - \ 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 index bdbb30267..f28215968 100644 --- a/airtime_mvc/application/views/scripts/preference/stream-setting.phtml +++ b/airtime_mvc/application/views/scripts/preference/stream-setting.phtml @@ -1,4 +1,25 @@ -
-

Stream Setting

-form; ?> -
+
+

Stream configuration

+statusMsg;?> +
+
+ Hardware Audio Out +
+ +
+ + form->getElement('output_sound_device') ?> +
+
+
+ num_stream;$i++){ + echo $this->form->getSubform("s".$i."_subform"); + } + ?> +
+ +
+
+
\ No newline at end of file diff --git a/airtime_mvc/build/sql/defaultdata.sql b/airtime_mvc/build/sql/defaultdata.sql index 49d1b395a..e29a60543 100644 --- a/airtime_mvc/build/sql/defaultdata.sql +++ b/airtime_mvc/build/sql/defaultdata.sql @@ -1,5 +1,8 @@ INSERT INTO cc_subjs ("login", "type", "pass") VALUES ('admin', 'A', md5('admin')); +INSERT INTO cc_pref("keystr", "valstr") VALUES('stream_type', 'mp3, ogg'); +INSERT INTO cc_pref("keystr", "valstr") VALUES('stream_bitrate', '24, 32, 48, 64, 96, 128, 160, 192, 224, 256, 320'); + INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('output_sound_device', 'false', 'boolean'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('output_icecast_vorbis_metadata', 'false', 'boolean'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('output_s1', 'icecast', 'string'); diff --git a/airtime_mvc/public/css/styles.css b/airtime_mvc/public/css/styles.css index 2c801123e..d12dfb8e8 100644 --- a/airtime_mvc/public/css/styles.css +++ b/airtime_mvc/public/css/styles.css @@ -1247,7 +1247,7 @@ div.success{ border:1px solid #488214; } -.collapsible-header { +.collapsible-header, .collapsible-header-disabled { border: 1px solid #8f8f8f; background-color: #cccccc; background: -moz-linear-gradient(top, #cccccc 0, #b9b9b9 100%); @@ -1264,7 +1264,7 @@ div.success{ margin-top:-1px; display:none; } -.collapsible-header .arrow-icon { +.collapsible-header .arrow-icon, .collapsible-header-disabled .arrow-icon { display:block; background:url(images/arrows_collapse.png) no-repeat 0 0; height:11px; @@ -1274,7 +1274,7 @@ div.success{ top:8px; } -.collapsible-header.close .arrow-icon { +.collapsible-header.close .arrow-icon, collapsible-header-disabled.close .arrow-icon { background-position: 0 -11px; } @@ -1604,7 +1604,7 @@ div.success{ .medium-icon.finishedplaying { background:url(images/icon_finishedplaying_m.png) no-repeat 0 0; } -.preferences, .manage-folders { +.preferences, .manage-folders, .stream-config { width: 500px; } @@ -1614,46 +1614,55 @@ dt.block-display, dd.block-display { margin-left: 0; padding-left: 0; } -.preferences dt.block-display, .preferences dd.block-display { +.preferences dt.block-display, .preferences dd.block-display, +.stream-config dt.block-display, .stream-config dd.block-display { padding: 0 0 5px 0; } -.preferences dd.block-display { +.preferences dd.block-display, .stream-config dd.block-display { margin-bottom:4px; } -.preferences dd.block-display:last-child { +.preferences dd.block-display:last-child, .stream-config dd.block-display:last-child { margin-bottom:0; + padding-bottom:0; } .simple-formblock dd.block-display { width: 100%; } -.preferences input[type="radio"] { +.preferences input[type="radio"], .stream-config input[type="radio"] { margin:0; } -.preferences label input[type="radio"] { +.preferences label input[type="radio"], .stream-config label input[type="radio"] { margin:0 1px 0 0; } -.preferences label input[type="checkbox"] { +.preferences label input[type="checkbox"], .stream-config label input[type="checkbox"] { margin:0 5px 0 0; } -dd.radio-inline-list, .preferences dd.radio-inline-list { +dd.radio-inline-list, .preferences dd.radio-inline-list, .stream-config dd.radio-inline-list { margin-bottom:6px; } .radio-inline-list label { margin-right:12px; } -.preferences.simple-formblock dd.block-display { +.preferences.simple-formblock dd.block-display, .stream-config.simple-formblock dd.block-display { width: 100%; } -.preferences.simple-formblock dd.block-display select { +.preferences.simple-formblock dd.block-display select, .stream-config.simple-formblock dd.block-display select { width: 100%; } -.preferences dd.block-display .input_select { +.preferences dd.block-display .input_select, .stream-config dd.block-display .input_select { width: 100%; } -.preferences dd.block-display .input_text_area, .preferences dd.block-display .input_text { +.preferences dd.block-display .input_text_area, .preferences dd.block-display .input_text, +.stream-config dd.block-display .input_text_area, .stream-config dd.block-display .input_text, +.stream-config dd.block-display input[type="text"], .stream-config dd.block-display input[type="password"] { width: 99.5%; } + + + + + .preferences dd#SoundCloudTags-element.block-display .input_text_area { height: 120px; } @@ -1872,7 +1881,7 @@ fieldset legend .ui-icon, .ui-widget-content fieldset legend .ui-icon { input[type="checkbox"][disabled] { opacity: 0.6; } - + .play_small { height:11px; width: 15px; @@ -1909,4 +1918,54 @@ input[type="checkbox"][disabled] { dd .info-text-small { padding: 1px 0 2px; display:inline-block; +} + +.stream-config dt { + width:120px; + padding: 4px 0; +} +.stream-config dt.block-display { + width:auto; +} +.stream-config dd { + margin-bottom:0px; +} +.stream-config dd select { + width:160px; + line-height:140%; +} + +dt.block-display.info-block { + width: auto; + font-size:12px; + padding:10px 0; +} +.top-margin { + margin-top:10px; +} +.left-margin { + margin-left:20px; +} +.stream-config dd.block-display textarea { + width: 99.5%; + height: 110px; +} + +.input-info { + font-size:12px; + padding:0 0 0 5px; +} + +.stream-config dd.block-display input[type="text"].with-info, .stream-config dd.block-display input[type="password"].with-info { + width: 85%; +} +.stream-config dd.block-display p { + font-size:13px; + margin:4px 0 4px 2px; +} + +.collapsible-header-disabled { + cursor:default; + opacity:0.6; + } \ No newline at end of file diff --git a/airtime_mvc/public/js/airtime/preferences/preferences.js b/airtime_mvc/public/js/airtime/preferences/preferences.js index 8a77bb68a..b9f8818b2 100644 --- a/airtime_mvc/public/js/airtime/preferences/preferences.js +++ b/airtime_mvc/public/js/airtime/preferences/preferences.js @@ -1,6 +1,6 @@ function showErrorSections() { - if($("soundcloud-settings .errors").length > 0) { + if($("#soundcloud-settings .errors").length > 0) { $("#soundcloud-settings").show(); $(window).scrollTop($("soundcloud-settings .errors").position().top); } diff --git a/airtime_mvc/public/js/airtime/preferences/streamsetting.js b/airtime_mvc/public/js/airtime/preferences/streamsetting.js index 69eb6f11b..21a0195a5 100644 --- a/airtime_mvc/public/js/airtime/preferences/streamsetting.js +++ b/airtime_mvc/public/js/airtime/preferences/streamsetting.js @@ -1,13 +1,38 @@ -$(document).ready(function() { +function showErrorSections() { - $('#change_setting').click(function(){ - var url; - - url = "/Preference/change-stream-setting"; - - $.post(url, - {format: "json"} - ); + $(".errors").each(function(i){ + if($(this).length > 0){ + $(this).closest("div").show(); + $(window).scrollTop($(this).position().top); + return false; + } }); +} +function buildStreamUrl(){ + + $("input:[id$=-host], input:[id$=-port], input:[id$=-mount], select:[id$=-type]").change(function(){ + div = $(this).closest("div") + host = div.find("input:[id$=-host]").val() + port = div.find("input:[id$=-port]").val() + mount = div.find("input:[id$=-mount]").val() + type = div.find("select:[id$=-type]").val() + div.find("#stream_url").html("http://"+host+":"+port+"/"+mount+"."+type) + if($(this).attr('id').indexOf('type') != -1){ + div.find("#mount_ext").html("."+type) + } + }) +} + +$(document).ready(function() { + + $('.collapsible-header').click(function() { + $(this).next().toggle('fast'); + $(this).toggleClass("close"); + return false; + }).next().hide(); + + showErrorSections() + + buildStreamUrl() }); \ No newline at end of file diff --git a/python_apps/pypo/airtime-playout-init-d b/python_apps/pypo/airtime-playout-init-d index dabc0dac5..f6a9f499c 100755 --- a/python_apps/pypo/airtime-playout-init-d +++ b/python_apps/pypo/airtime-playout-init-d @@ -19,13 +19,24 @@ PIDFILE0=/var/run/airtime-playout.pid DAEMON1=/usr/lib/airtime/pypo/bin/airtime-liquidsoap PIDFILE1=/var/run/airtime-liquidsoap.pid +liquidsoap_start () { + monit monitor airtime-liquidsoap >/dev/null 2>&1 + start-stop-daemon --start --background --quiet --chuid $USERID:$GROUPID \ + --nicelevel -15 --make-pidfile --pidfile $PIDFILE1 --startas $DAEMON1 +} + +liquidsoap_stop () { + # Send TERM after 5 seconds, wait at most 30 seconds. + monit unmonitor airtime-liquidsoap >/dev/null 2>&1 + start-stop-daemon --stop --oknodo --retry TERM/5/0/30 --quiet --pidfile $PIDFILE1 + rm -f $PIDFILE1 +} + start () { monit monitor airtime-playout >/dev/null 2>&1 start-stop-daemon --start --background --quiet --chuid $USERID:$GROUPID --make-pidfile --pidfile $PIDFILE0 --startas $DAEMON0 - monit monitor airtime-liquidsoap >/dev/null 2>&1 - start-stop-daemon --start --background --quiet --chuid $USERID:$GROUPID \ - --nicelevel -15 --make-pidfile --pidfile $PIDFILE1 --startas $DAEMON1 + liquidsoap_start } stop () { @@ -35,12 +46,9 @@ stop () { start-stop-daemon --stop --oknodo --retry TERM/5/0/30 --quiet --pidfile $PIDFILE0 rm -f $PIDFILE0 - monit unmonitor airtime-liquidsoap >/dev/null 2>&1 - start-stop-daemon --stop --oknodo --retry TERM/5/0/30 --quiet --pidfile $PIDFILE1 - rm -f $PIDFILE1 + liquidsoap_stop } - case "${1:-''}" in 'start') # start commands here @@ -65,6 +73,13 @@ case "${1:-''}" in # status commands here /usr/bin/airtime-check-system ;; + 'restart-liquidsoap') + # restart commands here + echo -n "Restarting Liquidsoap: " + liquidsoap_stop + liquidsoap_start + echo "Done." + ;; *) # no parameter specified echo "Usage: $SELF start|stop|restart|status" exit 1 diff --git a/python_apps/pypo/install/pypo-install.py b/python_apps/pypo/install/pypo-install.py index feb030624..fcb4e7b81 100755 --- a/python_apps/pypo/install/pypo-install.py +++ b/python_apps/pypo/install/pypo-install.py @@ -114,26 +114,28 @@ try: p = Popen("update-rc.d airtime-playout defaults >/dev/null 2>&1", shell=True) sts = os.waitpid(p.pid, 0)[1] - #we should access the DB and generate liquidsoap.cfg under etc/airtime/ + # we should access the DB and generate liquidsoap.cfg under etc/airtime/ api_client = api_client.api_client_factory(config) ss = api_client.get_stream_setting() - data = ss['msg'] - fh = open('/etc/airtime/liquidsoap.cfg', 'w') - for d in data: - 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() + # if api_client is somehow not working, just use original cfg file + if(ss is not None): + data = ss['msg'] + fh = open('/etc/airtime/liquidsoap.cfg', 'w') + for d in data: + buffer = d[u'keyname'] + " = " + if(d[u'type'] == 'string'): + temp = d[u'value'] + if(temp == ""): + temp = "" + buffer += "\"" + temp + "\"" + else: + temp = d[u'value'] + if(temp == ""): + temp = "0" + buffer += temp + buffer += "\n" + fh.write(buffer) + fh.close() print "Waiting for processes to start..." p = Popen("/etc/init.d/airtime-playout start", shell=True) diff --git a/python_apps/pypo/liquidsoap_scripts/ls_lib.liq b/python_apps/pypo/liquidsoap_scripts/ls_lib.liq index 71ed8dc9f..e5cffa293 100644 --- a/python_apps/pypo/liquidsoap_scripts/ls_lib.liq +++ b/python_apps/pypo/liquidsoap_scripts/ls_lib.liq @@ -50,8 +50,12 @@ def output_to(output_type, type, bitrate, host, port, pass, mount_point, url, de description = description, genre = genre) if type == "mp3" then - if bitrate == 32 then + if bitrate == 24 then + ignore(output.icecast(%mp3(bitrate = 24),s)) + elsif bitrate == 32 then ignore(output.icecast(%mp3(bitrate = 32),s)) + elsif bitrate == 48 then + ignore(output.icecast(%mp3(bitrate = 48),s)) elsif bitrate == 64 then ignore(output.icecast(%mp3(bitrate = 64),s)) elsif bitrate == 96 then @@ -60,14 +64,26 @@ def output_to(output_type, type, bitrate, host, port, pass, mount_point, url, de ignore(output.icecast(%mp3(bitrate = 128),s)) elsif bitrate == 160 then ignore(output.icecast(%mp3(bitrate = 160),s)) + elsif bitrate == 192 then + ignore(output.icecast(%mp3(bitrate = 192),s)) + elsif bitrate == 224 then + ignore(output.icecast(%mp3(bitrate = 224),s)) + elsif bitrate == 256 then + ignore(output.icecast(%mp3(bitrate = 256),s)) + elsif bitrate == 320 then + ignore(output.icecast(%mp3(bitrate = 320),s)) end else source = ref s if not output_icecast_vorbis_metadata then source := add(normalize=false, [amplify(0.00001, noise()),s]) end - if bitrate == 32 then + if bitrate == 24 then + ignore(output.icecast(%vorbis.cbr(bitrate = 24),!source)) + elsif bitrate == 32 then ignore(output.icecast(%vorbis.cbr(bitrate = 32),!source)) + elsif bitrate == 48 then + ignore(output.icecast(%vorbis.cbr(bitrate = 48),!source)) elsif bitrate == 64 then ignore(output.icecast(%vorbis.cbr(bitrate = 64),!source)) elsif bitrate == 96 then @@ -76,6 +92,14 @@ def output_to(output_type, type, bitrate, host, port, pass, mount_point, url, de ignore(output.icecast(%vorbis.cbr(bitrate = 128),!source)) elsif bitrate == 160 then ignore(output.icecast(%vorbis.cbr(bitrate = 160),!source)) + elsif bitrate == 192 then + ignore(output.icecast(%vorbis.cbr(bitrate = 192),!source)) + elsif bitrate == 224 then + ignore(output.icecast(%vorbis.cbr(bitrate = 224),!source)) + elsif bitrate == 256 then + ignore(output.icecast(%vorbis.cbr(bitrate = 256),!source)) + elsif bitrate == 320 then + ignore(output.icecast(%vorbis.cbr(bitrate = 320),!source)) end end else @@ -87,17 +111,29 @@ def output_to(output_type, type, bitrate, host, port, pass, mount_point, url, de restart_delay = 5, url = url, genre = genre) - if bitrate == 32 then - ignore(output.shoutcast(%mp3(bitrate = 32),s)) - elsif bitrate == 64 then - ignore(output.shoutcast(%mp3(bitrate = 64),s)) - elsif bitrate == 96 then - ignore(output.shoutcast(%mp3(bitrate = 96),s)) - elsif bitrate == 128 then - ignore(output.shoutcast(%mp3(bitrate = 128),s)) - elsif bitrate == 160 then - ignore(output.shoutcast(%mp3(bitrate = 160),s)) - end + if bitrate == 24 then + ignore(output.shoutcast(%mp3(bitrate = 24),s)) + elsif bitrate == 32 then + ignore(output.shoutcast(%mp3(bitrate = 32),s)) + elsif bitrate == 48 then + ignore(output.shoutcast(%mp3(bitrate = 48),s)) + elsif bitrate == 64 then + ignore(output.shoutcast(%mp3(bitrate = 64),s)) + elsif bitrate == 96 then + ignore(output.shoutcast(%mp3(bitrate = 96),s)) + elsif bitrate == 128 then + ignore(output.shoutcast(%mp3(bitrate = 128),s)) + elsif bitrate == 160 then + ignore(output.shoutcast(%mp3(bitrate = 160),s)) + elsif bitrate == 192 then + ignore(output.shoutcast(%mp3(bitrate = 192),s)) + elsif bitrate == 224 then + ignore(output.shoutcast(%mp3(bitrate = 224),s)) + elsif bitrate == 256 then + ignore(output.shoutcast(%mp3(bitrate = 256),s)) + elsif bitrate == 320 then + ignore(output.shoutcast(%mp3(bitrate = 320),s)) + end end end diff --git a/python_apps/pypo/pypofetch.py b/python_apps/pypo/pypofetch.py index 1cbbe340f..bc644b2dc 100755 --- a/python_apps/pypo/pypofetch.py +++ b/python_apps/pypo/pypofetch.py @@ -103,7 +103,7 @@ class PypoFetch(Thread): self.schedule_data = m['schedule'] self.process_schedule(self.schedule_data, "scheduler", False) elif (command == 'update_stream_setting'): - logger.info("Updating stream setting: %s", m['setting']) + logger.info("Updating stream setting...") self.regenerateLiquidsoapConf(m['setting']) # ACK the message to take it off the queue message.ack() @@ -122,20 +122,58 @@ class PypoFetch(Thread): key = key.strip() value = value.strip() value = value.replace('"', '') - if value == "dummy_string" or value == "0": + if value == "" or value == "0": value = '' existing[key] = value fh.close() - # flag for any change in cofig - change = False + + # dict flag for any change in cofig + change = {} + # this flag is to detect diable -> disable change + # in that case, we don't want to restart even if there are chnges. + state_change_restart = {} + #restart flag + restart = 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 + if "output_" in s[u'keyname'] and s[u'keyname'] != "output_icecast_vorbis_metadata" and s[u'keyname'] != "output_sound_device": + dump, stream = s[u'keyname'].split('_', 1) + state_change_restart[stream] = False + # This is the case where restart is required no matter what + if (existing[s[u'keyname']] != s[u'value']): + logger.info("'Need-to-restart' state detected for %s...", s[u'keyname']) + restart = True; + # This is the case where we need further checking + if s[u'value'] != 'disabled': + state_change_restart[stream] = True + else: + if s[u'keyname'] == "output_sound_device": + dump, stream = s[u'keyname'].split('_',1) + state_change_restart[stream] = False + if not (s[u'value'] == existing[s[u'keyname']]): + logger.info("'Need-to-restart' state detected for %s...", s[u'keyname']) + state_change_restart[stream] = True + elif s[u'keyname'] != "output_icecast_vorbis_metadata" and s[u'keyname'] != "log_file": + stream, dump = s[u'keyname'].split('_',1) + # setting inital value + if stream not in change: + change[stream] = False + if not (s[u'value'] == existing[s[u'keyname']]): + logger.info("Keyname: %s, Curent value: %s, New Value: %s", s[u'keyname'], existing[s[u'keyname']], s[u'value']) + change[stream] = True + + # set flag change for sound_device alway True + logger.info("Change:%s, State_Change:%s...", change, state_change_restart) + + for k, v in state_change_restart.items(): + if k == "sound_device" and v: + restart = True + elif v and change[k]: + logger.info("'Need-to-restart' state detected for %s...", k) + restart = True # rewrite - if change: + if restart: fh = open('/etc/airtime/liquidsoap.cfg', 'w') logger.info("Rewriting liquidsoap.cfg...") for d in setting: @@ -143,7 +181,7 @@ class PypoFetch(Thread): if(d[u'type'] == 'string'): temp = d[u'value'] if(temp == ""): - temp = "dummy_string" + temp = "" buffer += "\"" + temp + "\"" else: temp = d[u'value'] @@ -153,10 +191,12 @@ class PypoFetch(Thread): buffer += "\n" fh.write(buffer) fh.close() - # restart playout - logger.info("Restarting airtime-playout...") + # restarting pypo. + # we could just restart liquidsoap but it take more time somehow. + logger.info("Restarting pypo...") p = Popen("/etc/init.d/airtime-playout restart >/dev/null 2>&1", shell=True) sts = os.waitpid(p.pid, 0)[1] + self.process_schedule(self.schedule_data, "scheduler", False) else: logger.info("No change detected in setting...") @@ -218,6 +258,8 @@ class PypoFetch(Thread): # TODO: THIS LIQUIDSOAP STUFF NEEDS TO BE MOVED TO PYPO-PUSH!!! stream_metadata = schedule_data['stream_metadata'] try: + logger.info(LS_HOST) + logger.info(LS_PORT) tn = telnetlib.Telnet(LS_HOST, LS_PORT) #encode in latin-1 due to telnet protocol not supporting utf-8 tn.write(('vars.stream_metadata_type %s\n' % stream_metadata['format']).encode('latin-1')) @@ -430,7 +472,7 @@ class PypoFetch(Thread): # most recent schedule. After that we can just wait for updates. status, self.schedule_data = self.api_client.get_schedule() if status == 1: - self.process_schedule(self.schedule_data , "scheduler", True) + self.process_schedule(self.schedule_data , "scheduler", True) logger.info("Bootstrap complete: got initial copy of the schedule") loops = 1 diff --git a/python_apps/pypo/pypopush.py b/python_apps/pypo/pypopush.py index 29f29c088..a9dc79a14 100755 --- a/python_apps/pypo/pypopush.py +++ b/python_apps/pypo/pypopush.py @@ -137,9 +137,9 @@ class PypoPush(Thread): tn.read_all() except Exception, e: logger.debug(e) + logger.debug('Could not connect to liquidsoap') self.liquidsoap_state_play = False - logger.debug('Could not connect to liquidsoap') def push_liquidsoap(self, pkey, schedule, playlists):