From 67ffd2d34a1318cedc1e0839939a6bd2c07a4338 Mon Sep 17 00:00:00 2001 From: Robb Ebright Date: Tue, 14 Mar 2017 01:15:14 +0000 Subject: [PATCH 1/2] Making show and master source ports and mount points editable. --- .../controllers/PreferenceController.php | 36 ++++++ .../forms/LiveStreamingPreferences.php | 109 ++++++++++++++---- .../application/models/StreamSetting.php | 2 +- .../scripts/form/preferences_livestream.phtml | 3 +- python_apps/pypo/pypo/pypofetch.py | 95 ++++++++++++++- 5 files changed, 216 insertions(+), 29 deletions(-) diff --git a/airtime_mvc/application/controllers/PreferenceController.php b/airtime_mvc/application/controllers/PreferenceController.php index 44c32d8fa..f4dcdd897 100644 --- a/airtime_mvc/application/controllers/PreferenceController.php +++ b/airtime_mvc/application/controllers/PreferenceController.php @@ -262,6 +262,41 @@ class PreferenceController extends Zend_Controller_Action //Application_Model_RabbitMq::PushSchedule(); } + // pulling this from the 2.5.x branch + if (!Application_Model_Preference::GetMasterDjConnectionUrlOverride()) { + $master_connection_url = "http://".$_SERVER['SERVER_NAME'].":".$values["master_source_port"].$values["master_source_mount"]; + if (empty($values["master_source_port"]) || empty($values["master_source_port"])) { + Application_Model_Preference::SetMasterDJSourceConnectionURL('N/A'); + } else { + Application_Model_Preference::SetMasterDJSourceConnectionURL($master_connection_url); + } + } else { + Application_Model_Preference::SetMasterDJSourceConnectionURL($values["master_source_host"]); + } + + if (!Application_Model_Preference::GetLiveDjConnectionUrlOverride()) { + $live_connection_url = "http://".$_SERVER['SERVER_NAME'].":".$values["show_source_port"].$values["show_source_mount"]; + if (empty($values["show_source_port"]) || empty($values["show_source_mount"])) { + Application_Model_Preference::SetLiveDJSourceConnectionURL('N/A'); + } else { + Application_Model_Preference::SetLiveDJSourceConnectionURL($live_connection_url); + } + } else { + Application_Model_Preference::SetLiveDJSourceConnectionURL($values["show_source_host"]); + } + + + Application_Model_StreamSetting::setMasterLiveStreamPort($values["master_source_port"]); + Application_Model_StreamSetting::setMasterLiveStreamMountPoint($values["master_source_mount"]); + Application_Model_StreamSetting::setDjLiveStreamPort($values["show_source_port"]); + Application_Model_StreamSetting::setDjLiveStreamMountPoint($values["show_source_mount"]); + + + + + + + Application_Model_StreamSetting::setOffAirMeta($values['offAirMeta']); // store stream update timestamp @@ -355,6 +390,7 @@ class PreferenceController extends Zend_Controller_Action Application_Model_Preference::SetDefaultTransitionFade($values["transition_fade"]); Application_Model_Preference::SetAutoTransition($values["auto_transition"]); Application_Model_Preference::SetAutoSwitch($values["auto_switch"]); + } public function serverBrowseAction() diff --git a/airtime_mvc/application/forms/LiveStreamingPreferences.php b/airtime_mvc/application/forms/LiveStreamingPreferences.php index 1637ed76f..1e44c7cc2 100644 --- a/airtime_mvc/application/forms/LiveStreamingPreferences.php +++ b/airtime_mvc/application/forms/LiveStreamingPreferences.php @@ -7,6 +7,7 @@ class Application_Form_LiveStreamingPreferences extends Zend_Form_SubForm { $CC_CONFIG = Config::getConfig(); $isDemo = isset($CC_CONFIG['demo']) && $CC_CONFIG['demo'] == 1; + $isStreamConfigable = Application_Model_Preference::GetEnableStreamConf() == "true"; $defaultFade = Application_Model_Preference::GetDefaultTransitionFade(); @@ -63,42 +64,54 @@ class Application_Form_LiveStreamingPreferences extends Zend_Form_SubForm // Master source connection url parameters $masterSourceHost = new Zend_Form_Element_Text('master_source_host'); - $masterSourceHost->setAttrib('readonly', true) - ->setLabel(_('Host:')) - ->setValue(isset($masterSourceParams["host"])?$masterSourceParams["host"]:""); + $masterSourceHost->setLabel(_('Host:')) + ->setAttrib('readonly', true) + ->setValue(Application_Model_Preference::GetMasterDJSourceConnectionURL()); $this->addElement($masterSourceHost); + //liquidsoap harbor.input port + $betweenValidator = Application_Form_Helper_ValidationTypes::overrideBetweenValidator(1024, 49151); + $m_port = Application_Model_StreamSetting::getMasterLiveStreamPort(); $masterSourcePort = new Zend_Form_Element_Text('master_source_port'); - $masterSourcePort->setAttrib('readonly', true) - ->setLabel(_('Port:')) - ->setValue(isset($masterSourceParams["port"])?$masterSourceParams["port"]:""); + $masterSourcePort->setLabel(_('Master Source Port:')) + ->setValue($m_port) + ->setValidators(array($betweenValidator)) + ->addValidator('regex', false, array('pattern'=>'/^[0-9]+$/', 'messages'=>array('regexNotMatch'=>_('Only numbers are allowed.')))); $this->addElement($masterSourcePort); + $m_mount = Application_Model_StreamSetting::getMasterLiveStreamMountPoint(); $masterSourceMount = new Zend_Form_Element_Text('master_source_mount'); - $masterSourceMount->setAttrib('readonly', true) - ->setLabel(_('Mount:')) - ->setValue(isset($masterSourceParams["path"])?$masterSourceParams["path"]:""); + $masterSourceMount->setLabel(_('Master Source Mount:')) + ->setValue($m_mount) + ->setValidators(array( + array('regex', false, array('/^[^ &<>]+$/', 'messages' => _('Invalid character entered'))))); $this->addElement($masterSourceMount); $showSourceParams = parse_url(Application_Model_Preference::GetLiveDJSourceConnectionURL()); // Show source connection url parameters $showSourceHost = new Zend_Form_Element_Text('show_source_host'); - $showSourceHost->setAttrib('readonly', true) - ->setLabel(_('Host:')) - ->setValue(isset($showSourceParams["host"])?$showSourceParams["host"]:""); + $showSourceHost->setLabel(_('Host:')) + ->setAttrib('readonly', true) + ->setValue(Application_Model_Preference::GetLiveDJSourceConnectionURL()); $this->addElement($showSourceHost); + //liquidsoap harbor.input port + $l_port = Application_Model_StreamSetting::getDjLiveStreamPort(); + $showSourcePort = new Zend_Form_Element_Text('show_source_port'); - $showSourcePort->setAttrib('readonly', true) - ->setLabel(_('Port:')) - ->setValue(isset($showSourceParams["port"])?$showSourceParams["port"]:""); + $showSourcePort->setLabel(_('Show Source Port:')) + ->setValue($l_port) + ->setValidators(array($betweenValidator)) + ->addValidator('regex', false, array('pattern'=>'/^[0-9]+$/', 'messages'=>array('regexNotMatch'=>_('Only numbers are allowed.')))); $this->addElement($showSourcePort); + $l_mount = Application_Model_StreamSetting::getDjLiveStreamMountPoint(); $showSourceMount = new Zend_Form_Element_Text('show_source_mount'); - $showSourceMount->setAttrib('readonly', true) - ->setLabel(_('Mount:')) - ->setValue(isset($showSourceParams["path"])?$showSourceParams["path"]:""); + $showSourceMount->setLabel(_('Show Source Mount:')) + ->setValue($l_mount) + ->setValidators(array( + array('regex', false, array('/^[^ &<>]+$/', 'messages' => _('Invalid character entered'))))); $this->addElement($showSourceMount); // demo only code @@ -125,12 +138,12 @@ class Application_Form_LiveStreamingPreferences extends Zend_Form_SubForm array ('ViewScript', array ( 'viewScript' => 'form/preferences_livestream.phtml', - 'master_source_host' => isset($masterSourceParams["host"])?$masterSourceParams["host"]:"", - 'master_source_port' => isset($masterSourceParams["port"])?$masterSourceParams["port"]:"", - 'master_source_mount' => isset($masterSourceParams["path"])?$masterSourceParams["path"]:"", - 'show_source_host' => isset($showSourceParams["host"])?$showSourceParams["host"]:"", - 'show_source_port' => isset($showSourceParams["port"])?$showSourceParams["port"]:"", - 'show_source_mount' => isset($showSourceParams["path"])?$showSourceParams["path"]:"", + 'master_source_host' => isset($masterSourceHost)?$masterSourceParams["host"]:"", + 'master_source_port' => isset($masterSourcePort)?$masterSourceParams["port"]:"", + 'master_source_mount' => isset($masterSourceMount)?$masterSourceParams["path"]:"", + 'show_source_host' => isset($showSourceHost)?$showSourceParams["host"]:"", + 'show_source_port' => isset($showSourcePort)?$showSourceParams["port"]:"", + 'show_source_mount' => isset($showSourceMount)?$showSourceParams["path"]:"", 'isDemo' => $isDemo, ) ) @@ -140,7 +153,53 @@ class Application_Form_LiveStreamingPreferences extends Zend_Form_SubForm public function isValid($data) { - return parent::isValid($data); + $isValid = parent::isValid($data); + $master_source_port = $data['master_source_port']; + $show_source_port = $data['show_source_port']; + + if ($master_source_port == $show_source_port && $master_source_port != "") { + $element = $this->getElement('show_source_port'); + $element->addError(_("You cannot use same port as Master DJ port.")); + $isValid = false; + } + if ($master_source_port != "") { + if (is_numeric($master_source_port)) { + if ($master_source_port != Application_Model_StreamSetting::getMasterLiveStreamPort()) { + $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); + try { + socket_bind($sock, 0, $master_source_port); + } catch (Exception $e) { + $element = $this->getElement("master_source_port"); + $element->addError(sprintf(_("Port %s is not available"), $master_source_port)); + $isValid = false; + } + + socket_close($sock); + } + } else { + $isValid = false; + } + } + if ($show_source_port != "") { + if (is_numeric($show_source_port)) { + if ($show_source_port != Application_Model_StreamSetting::getDjLiveStreamPort()) { + $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); + try { + socket_bind($sock, 0, $show_source_port); + } catch (Exception $e) { + $element = $this->getElement("show_source_port"); + $element->addError(sprintf(_("Port %s is not available"), $show_source_port)); + $isValid = false; + } + socket_close($sock); + } + } else { + $isValid = false; + } + } + + return $isValid; + } } diff --git a/airtime_mvc/application/models/StreamSetting.php b/airtime_mvc/application/models/StreamSetting.php index 34e037cd5..faf5e4f5f 100644 --- a/airtime_mvc/application/models/StreamSetting.php +++ b/airtime_mvc/application/models/StreamSetting.php @@ -464,7 +464,7 @@ class Application_Model_StreamSetting public static function getDjLiveStreamPort() { - return self::getValue("dj_live_stream_port", 8001); + return self::getValue("dj_live_stream_port", 8002); } public static function setDjLiveStreamMountPoint($value) diff --git a/airtime_mvc/application/views/scripts/form/preferences_livestream.phtml b/airtime_mvc/application/views/scripts/form/preferences_livestream.phtml index 983e9eedb..788153bd2 100644 --- a/airtime_mvc/application/views/scripts/form/preferences_livestream.phtml +++ b/airtime_mvc/application/views/scripts/form/preferences_livestream.phtml @@ -15,8 +15,8 @@ element->getElement('master_username')->render() ?> element->getElement('master_password')->render() ?> - element->getElement("master_source_host")->render() ?> + element->getElement("master_source_port")->render() ?> element->getElement("master_source_mount")->render() ?> @@ -26,6 +26,7 @@

+ element->getElement("show_source_host")->render() ?> element->getElement("show_source_port")->render() ?> element->getElement("show_source_mount")->render() ?> diff --git a/python_apps/pypo/pypo/pypofetch.py b/python_apps/pypo/pypo/pypofetch.py index d9fa34616..9c859ddbe 100644 --- a/python_apps/pypo/pypo/pypofetch.py +++ b/python_apps/pypo/pypo/pypofetch.py @@ -208,8 +208,99 @@ class PypoFetch(Thread): TODO: This function needs to be way shorter, and refactored :/ - MK """ def regenerate_liquidsoap_conf(self, setting): - self.restart_liquidsoap() - self.update_liquidsoap_connection_status() + existing = {} + + setting = sorted(setting.items()) + try: + fh = open('/etc/airtime/liquidsoap.cfg', 'r') + except IOError, e: + #file does not exist + self.restart_liquidsoap() + return + + self.logger.info("Reading existing config...") + # read existing conf file and build dict + while True: + line = fh.readline() + + # empty line means EOF + if not line: + break + + line = line.strip() + + if not len(line) or line[0] == "#": + continue + + try: + key, value = line.split('=', 1) + except ValueError: + continue + key = key.strip() + value = value.strip() + value = value.replace('"', '') + if value == '' or value == "0": + value = '' + existing[key] = value + fh.close() + + # dict flag for any change in config + change = {} + # this flag is to detect disable -> disable change + # in that case, we don't want to restart even if there are changes. + state_change_restart = {} + #restart flag + restart = False + + self.logger.info("Looking for changes...") + # look for changes + for k, s in setting: + if "output_sound_device" in s[u'keyname'] or "icecast_vorbis_metadata" in s[u'keyname']: + 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']] != str(s[u'value'])): + self.logger.info("'Need-to-restart' state detected for %s...", s[u'keyname']) + restart = True; + elif "master_live_stream_port" in s[u'keyname'] or "master_live_stream_mp" in s[u'keyname'] or "dj_live_stream_port" in s[u'keyname'] or "dj_live_stream_mp" in s[u'keyname'] or "off_air_meta" in s[u'keyname']: + if (existing[s[u'keyname']] != s[u'value']): + self.logger.info("'Need-to-restart' state detected for %s...", s[u'keyname']) + restart = True; + else: + stream, dump = s[u'keyname'].split('_', 1) + if "_output" in s[u'keyname']: + if (existing[s[u'keyname']] != s[u'value']): + self.logger.info("'Need-to-restart' state detected for %s...", s[u'keyname']) + restart = True; + state_change_restart[stream] = True + elif (s[u'value'] != 'disabled'): + state_change_restart[stream] = True + else: + state_change_restart[stream] = False + else: + # setting inital value + if stream not in change: + change[stream] = False + if not (s[u'value'] == existing[s[u'keyname']]): + self.logger.info("Keyname: %s, Current 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 + self.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]: + self.logger.info("'Need-to-restart' state detected for %s...", k) + restart = True + # rewrite + if restart: + self.restart_liquidsoap() + else: + self.logger.info("No change detected in setting...") + self.update_liquidsoap_connection_status() + @ls_timeout From 1b90dbd4ca2f46c04e18462f35f87aa813810c41 Mon Sep 17 00:00:00 2001 From: Robb Ebright Date: Tue, 14 Mar 2017 04:02:51 +0000 Subject: [PATCH 2/2] working python liquidsoap reconfigure script, might need further refinement --- python_apps/pypo/pypo/pypofetch.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/python_apps/pypo/pypo/pypofetch.py b/python_apps/pypo/pypo/pypofetch.py index 9c859ddbe..9cdaf7163 100644 --- a/python_apps/pypo/pypo/pypofetch.py +++ b/python_apps/pypo/pypo/pypofetch.py @@ -255,25 +255,25 @@ class PypoFetch(Thread): self.logger.info("Looking for changes...") # look for changes for k, s in setting: - if "output_sound_device" in s[u'keyname'] or "icecast_vorbis_metadata" in s[u'keyname']: - dump, stream = s[u'keyname'].split('_', 1) + if "output_sound_device" in k or "icecast_vorbis_metadata" in k: + dump, stream = k.split('_', 1) state_change_restart[stream] = False # This is the case where restart is required no matter what - if (existing[s[u'keyname']] != str(s[u'value'])): - self.logger.info("'Need-to-restart' state detected for %s...", s[u'keyname']) + if (existing[k] != str(s)): + self.logger.info("'Need-to-restart' state detected for %s...", s) restart = True; - elif "master_live_stream_port" in s[u'keyname'] or "master_live_stream_mp" in s[u'keyname'] or "dj_live_stream_port" in s[u'keyname'] or "dj_live_stream_mp" in s[u'keyname'] or "off_air_meta" in s[u'keyname']: - if (existing[s[u'keyname']] != s[u'value']): - self.logger.info("'Need-to-restart' state detected for %s...", s[u'keyname']) + elif "master_live_stream_port" in k or "master_live_stream_mp" in k or "dj_live_stream_port" in k or "dj_live_stream_mp" in k or "off_air_meta" in k: + if (existing[k] != s): + self.logger.info("'Need-to-restart' state detected for %s...", s) restart = True; else: - stream, dump = s[u'keyname'].split('_', 1) - if "_output" in s[u'keyname']: - if (existing[s[u'keyname']] != s[u'value']): - self.logger.info("'Need-to-restart' state detected for %s...", s[u'keyname']) + stream, dump = k.split('_', 1) + if "_output" in k: + if (existing[k] != s): + self.logger.info("'Need-to-restart' state detected for %s...", s) restart = True; state_change_restart[stream] = True - elif (s[u'value'] != 'disabled'): + elif (k != 'disabled'): state_change_restart[stream] = True else: state_change_restart[stream] = False @@ -281,8 +281,8 @@ class PypoFetch(Thread): # setting inital value if stream not in change: change[stream] = False - if not (s[u'value'] == existing[s[u'keyname']]): - self.logger.info("Keyname: %s, Current value: %s, New Value: %s", s[u'keyname'], existing[s[u'keyname']], s[u'value']) + if not (s == existing[k]): + self.logger.info("Keyname: %s, Current value: %s, New Value: %s", k, existing[k], s) change[stream] = True # set flag change for sound_device alway True