From c261182c8f2047b7d6d465cf207cd8afc739d1f2 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Thu, 12 Feb 2015 14:08:06 -0500 Subject: [PATCH 01/60] Skeleton player form/view/controller --- airtime_mvc/application/configs/ACL.php | 4 ++- .../application/configs/navigation.php | 7 +++++ .../EmbeddableplayerController.php | 31 +++++++++++++++++++ .../application/forms/EmbeddablePlayer.php | 20 ++++++++++++ .../scripts/embeddableplayer/index.phtml | 15 +++++++++ .../views/scripts/form/embeddableplayer.html | 6 ++++ 6 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 airtime_mvc/application/controllers/EmbeddableplayerController.php create mode 100644 airtime_mvc/application/forms/EmbeddablePlayer.php create mode 100644 airtime_mvc/application/views/scripts/embeddableplayer/index.phtml create mode 100644 airtime_mvc/application/views/scripts/form/embeddableplayer.html diff --git a/airtime_mvc/application/configs/ACL.php b/airtime_mvc/application/configs/ACL.php index c41f00a47..243e105d3 100644 --- a/airtime_mvc/application/configs/ACL.php +++ b/airtime_mvc/application/configs/ACL.php @@ -36,7 +36,8 @@ $ccAcl->add(new Zend_Acl_Resource('library')) ->add(new Zend_Acl_Resource('rest:media')) ->add(new Zend_Acl_Resource('rest:show-image')) ->add(new Zend_Acl_Resource('billing')) - ->add(new Zend_Acl_Resource('provisioning')); + ->add(new Zend_Acl_Resource('provisioning')) + ->add(new Zend_Acl_Resource('embeddableplayer')); /** Creating permissions */ $ccAcl->allow('G', 'index') @@ -68,6 +69,7 @@ $ccAcl->allow('G', 'index') ->allow('A', 'user') ->allow('A', 'systemstatus') ->allow('A', 'preference') + ->allow('A', 'embeddableplayer') ->allow('S', 'billing'); diff --git a/airtime_mvc/application/configs/navigation.php b/airtime_mvc/application/configs/navigation.php index 14438bdfe..01c8aa385 100644 --- a/airtime_mvc/application/configs/navigation.php +++ b/airtime_mvc/application/configs/navigation.php @@ -85,6 +85,13 @@ $pages = array( 'controller' => 'listenerstat', 'action' => 'index', 'resource' => 'listenerstat' + ), + array( + 'label' => _('Embeddable Player'), + 'module' => 'default', + 'controller' => 'embeddableplayer', + 'action' => 'index', + 'resource' => 'embeddableplayer' ) ) ), diff --git a/airtime_mvc/application/controllers/EmbeddableplayerController.php b/airtime_mvc/application/controllers/EmbeddableplayerController.php new file mode 100644 index 000000000..3cd0e3af4 --- /dev/null +++ b/airtime_mvc/application/controllers/EmbeddableplayerController.php @@ -0,0 +1,31 @@ +<?php + +class EmbeddablePlayerController extends Zend_Controller_Action +{ + public function init() + { + + } + + public function indexAction() + { + $form = new Application_Form_EmbeddablePlayer(); + + $request = $this->getRequest(); + + if ($request->isPost()) { + $formValues = $request->getPost(); + if ($form->isValid($formValues)) { + + $this->view->statusMsg = "<div class='success'>". _("Preferences updated.")."</div>"; + + } else { + + } + + $this->view->form = $form; + } + + $this->view->form = $form; + } +} \ No newline at end of file diff --git a/airtime_mvc/application/forms/EmbeddablePlayer.php b/airtime_mvc/application/forms/EmbeddablePlayer.php new file mode 100644 index 000000000..e9dd6b151 --- /dev/null +++ b/airtime_mvc/application/forms/EmbeddablePlayer.php @@ -0,0 +1,20 @@ +<?php + +class Application_Form_EmbeddablePlayer extends Zend_Form_SubForm +{ + public function init() + { + $this->setDecorators(array( + array('ViewScript', array('viewScript' => 'form/embeddableplayer.html')) + )); + + + $displayTrackMetadata = new Zend_Form_Element_Checkbox('display_track_metadata'); + $displayTrackMetadata->setValue(true); + $displayTrackMetadata->setLabel(_('Display track metadata?')); + $this->addElement($displayTrackMetadata); + + $submit = new Zend_Form_Element_Submit('submit'); + $this->addElement($submit); + } +} \ No newline at end of file diff --git a/airtime_mvc/application/views/scripts/embeddableplayer/index.phtml b/airtime_mvc/application/views/scripts/embeddableplayer/index.phtml new file mode 100644 index 000000000..df666df29 --- /dev/null +++ b/airtime_mvc/application/views/scripts/embeddableplayer/index.phtml @@ -0,0 +1,15 @@ +<div class="ui-widget ui-widget-content block-shadow simple-formblock clearfix padded-strong preferences"> + <h2 style="float:left"><?php echo _("Embeddable Player") ?></h2> + <?php $baseUrl = Application_Common_OsPath::getBaseDir(); ?> + <form method="post" id="pref_form" enctype="multipart/form-data"> + + <?php echo $this->form->getElement('submit')->render() ?> + <div style="clear:both"></div> + <?php + + echo $this->statusMsg; + echo $this->form; + ?> + <br /> + </form> +</div> diff --git a/airtime_mvc/application/views/scripts/form/embeddableplayer.html b/airtime_mvc/application/views/scripts/form/embeddableplayer.html new file mode 100644 index 000000000..4a8844f58 --- /dev/null +++ b/airtime_mvc/application/views/scripts/form/embeddableplayer.html @@ -0,0 +1,6 @@ +<fieldset class="padded"> + <dl class="zend_form"> + <?php echo $this->element->getElement('display_track_metadata'); ?> + + </dl> +</fieldset> \ No newline at end of file From 56cae342594c311a126148c74bec86ffc8eb29f0 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Thu, 12 Feb 2015 14:20:54 -0500 Subject: [PATCH 02/60] Fix typo --- .../application/views/scripts/embeddableplayer/index.phtml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/airtime_mvc/application/views/scripts/embeddableplayer/index.phtml b/airtime_mvc/application/views/scripts/embeddableplayer/index.phtml index df666df29..51e00dc03 100644 --- a/airtime_mvc/application/views/scripts/embeddableplayer/index.phtml +++ b/airtime_mvc/application/views/scripts/embeddableplayer/index.phtml @@ -1,15 +1,16 @@ <div class="ui-widget ui-widget-content block-shadow simple-formblock clearfix padded-strong preferences"> <h2 style="float:left"><?php echo _("Embeddable Player") ?></h2> <?php $baseUrl = Application_Common_OsPath::getBaseDir(); ?> - <form method="post" id="pref_form" enctype="multipart/form-data"> + <form method="post" id="player_form" enctype="multipart/form-data"> + - <?php echo $this->form->getElement('submit')->render() ?> <div style="clear:both"></div> <?php echo $this->statusMsg; echo $this->form; ?> + <?php echo $this->form->getElement('submit')->render() ?> <br /> </form> </div> From 4fd7b4c622315ef94e3c16b03eaae271c0acddb3 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Tue, 17 Feb 2015 11:48:43 -0500 Subject: [PATCH 03/60] SAAS-585: Create Embeddable Player form Added stream urls - hardcoded for now --- airtime_mvc/application/forms/EmbeddablePlayer.php | 9 ++++++++- .../application/views/scripts/form/embeddableplayer.html | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/airtime_mvc/application/forms/EmbeddablePlayer.php b/airtime_mvc/application/forms/EmbeddablePlayer.php index e9dd6b151..57c46bed9 100644 --- a/airtime_mvc/application/forms/EmbeddablePlayer.php +++ b/airtime_mvc/application/forms/EmbeddablePlayer.php @@ -8,12 +8,19 @@ class Application_Form_EmbeddablePlayer extends Zend_Form_SubForm array('ViewScript', array('viewScript' => 'form/embeddableplayer.html')) )); - $displayTrackMetadata = new Zend_Form_Element_Checkbox('display_track_metadata'); $displayTrackMetadata->setValue(true); $displayTrackMetadata->setLabel(_('Display track metadata?')); $this->addElement($displayTrackMetadata); + $streamURL = new Zend_Form_Element_Radio('stream_url'); + $streamURL->setMultiOptions(array( + 'AAC' => 'http://127.0.0.1:8000/airtime_a', + 'MP3' => 'http://127.0.0.1:8000/airtime_b' + )); + $streamURL->setLabel(_('Select stream:')); + $this->addElement($streamURL); + $submit = new Zend_Form_Element_Submit('submit'); $this->addElement($submit); } diff --git a/airtime_mvc/application/views/scripts/form/embeddableplayer.html b/airtime_mvc/application/views/scripts/form/embeddableplayer.html index 4a8844f58..8a1ad551e 100644 --- a/airtime_mvc/application/views/scripts/form/embeddableplayer.html +++ b/airtime_mvc/application/views/scripts/form/embeddableplayer.html @@ -2,5 +2,7 @@ <dl class="zend_form"> <?php echo $this->element->getElement('display_track_metadata'); ?> + <?php echo $this->element->getElement('stream_url'); ?> + </dl> </fieldset> \ No newline at end of file From a931e282e13f4a0870188d1dbce5c640d8c254f9 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Fri, 20 Feb 2015 15:44:25 -0500 Subject: [PATCH 04/60] SAAS-585: Create Embeddable Player form Fixed up the stream urls options --- airtime_mvc/application/forms/EmbeddablePlayer.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/airtime_mvc/application/forms/EmbeddablePlayer.php b/airtime_mvc/application/forms/EmbeddablePlayer.php index 57c46bed9..a8eb137ce 100644 --- a/airtime_mvc/application/forms/EmbeddablePlayer.php +++ b/airtime_mvc/application/forms/EmbeddablePlayer.php @@ -14,10 +14,13 @@ class Application_Form_EmbeddablePlayer extends Zend_Form_SubForm $this->addElement($displayTrackMetadata); $streamURL = new Zend_Form_Element_Radio('stream_url'); - $streamURL->setMultiOptions(array( - 'AAC' => 'http://127.0.0.1:8000/airtime_a', - 'MP3' => 'http://127.0.0.1:8000/airtime_b' - )); + $urlOptions = Array(); + foreach(Application_Model_StreamSetting::getStreamUrls() as $type => $url) { + $urlOptions[$url] = $type; + } + $streamURL->setMultiOptions( + $urlOptions + ); $streamURL->setLabel(_('Select stream:')); $this->addElement($streamURL); From 6dcc7ee2fce0d4432c058fbec613b0e749c92d72 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Fri, 20 Feb 2015 15:50:40 -0500 Subject: [PATCH 05/60] SAAS-587: Add stream urls to station-metadata API --- .../application/controllers/ApiController.php | 1 + .../application/forms/EmbeddablePlayer.php | 2 +- airtime_mvc/application/models/StreamSetting.php | 16 ++++++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/airtime_mvc/application/controllers/ApiController.php b/airtime_mvc/application/controllers/ApiController.php index 124b4b4d0..5f1847f77 100644 --- a/airtime_mvc/application/controllers/ApiController.php +++ b/airtime_mvc/application/controllers/ApiController.php @@ -637,6 +637,7 @@ class ApiController extends Zend_Controller_Action $result["description"] = Application_Model_Preference::GetStationDescription(); $result["timezone"] = Application_Model_Preference::GetDefaultTimezone(); $result["locale"] = Application_Model_Preference::GetDefaultLocale(); + $result["enabled_stream_urls"] = Application_Model_StreamSetting::getStreamUrls(); // used by caller to determine if the airtime they are running or widgets in use is out of date. $result['AIRTIME_API_VERSION'] = AIRTIME_API_VERSION; diff --git a/airtime_mvc/application/forms/EmbeddablePlayer.php b/airtime_mvc/application/forms/EmbeddablePlayer.php index a8eb137ce..2accf23a5 100644 --- a/airtime_mvc/application/forms/EmbeddablePlayer.php +++ b/airtime_mvc/application/forms/EmbeddablePlayer.php @@ -15,7 +15,7 @@ class Application_Form_EmbeddablePlayer extends Zend_Form_SubForm $streamURL = new Zend_Form_Element_Radio('stream_url'); $urlOptions = Array(); - foreach(Application_Model_StreamSetting::getStreamUrls() as $type => $url) { + foreach(Application_Model_StreamSetting::getEnabledStreamUrls() as $type => $url) { $urlOptions[$url] = $type; } $streamURL->setMultiOptions( diff --git a/airtime_mvc/application/models/StreamSetting.php b/airtime_mvc/application/models/StreamSetting.php index c34c09a2e..3bdac8a31 100644 --- a/airtime_mvc/application/models/StreamSetting.php +++ b/airtime_mvc/application/models/StreamSetting.php @@ -62,6 +62,22 @@ class Application_Model_StreamSetting return $result ? $result : ""; } + public static function getEnabledStreamUrls() + { + $urls = Array(); + $streamIds = Application_Model_StreamSetting::getEnabledStreamIds(); + foreach ($streamIds as $id) { + $prefix = $id."_"; + $streamData = Application_Model_StreamSetting::getStreamData($id); + $host = $streamData[$prefix."host"]; + $port = $streamData[$prefix."port"]; + $mount = $streamData[$prefix."mount"]; + $type = $streamData[$prefix."type"]; + $urls[$type] = "http://$host:$port/$mount"; + } + return $urls; + } + /* Returns the id's of all streams that are enabled in an array. An * example of the array returned in JSON notation is ["s1", "s2", "s3"] */ public static function getEnabledStreamIds() From 5e9252a562c1bf7a799d2c00df467a2a2d58991c Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Fri, 20 Feb 2015 15:52:13 -0500 Subject: [PATCH 06/60] SAAS-587: Add stream urls to station-metadata API Fix typo --- airtime_mvc/application/controllers/ApiController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airtime_mvc/application/controllers/ApiController.php b/airtime_mvc/application/controllers/ApiController.php index 5f1847f77..2e8b2a55b 100644 --- a/airtime_mvc/application/controllers/ApiController.php +++ b/airtime_mvc/application/controllers/ApiController.php @@ -637,7 +637,7 @@ class ApiController extends Zend_Controller_Action $result["description"] = Application_Model_Preference::GetStationDescription(); $result["timezone"] = Application_Model_Preference::GetDefaultTimezone(); $result["locale"] = Application_Model_Preference::GetDefaultLocale(); - $result["enabled_stream_urls"] = Application_Model_StreamSetting::getStreamUrls(); + $result["enabled_stream_urls"] = Application_Model_StreamSetting::getEnabledStreamUrls(); // used by caller to determine if the airtime they are running or widgets in use is out of date. $result['AIRTIME_API_VERSION'] = AIRTIME_API_VERSION; From 2272451e08a575676dffe590e3aaba2fa9b478de Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Wed, 25 Feb 2015 17:10:41 -0500 Subject: [PATCH 07/60] Added placeholders for player embed code and preview --- airtime_mvc/application/configs/conf.php | 2 +- .../EmbeddableplayerController.php | 19 ++++--------------- .../application/forms/EmbeddablePlayer.php | 16 +++++++++++----- .../scripts/embeddableplayer/index.phtml | 7 ++----- .../views/scripts/form/embeddableplayer.html | 8 -------- .../views/scripts/form/embeddableplayer.phtml | 13 +++++++++++++ airtime_mvc/public/css/embed-player.css | 4 ++++ 7 files changed, 35 insertions(+), 34 deletions(-) delete mode 100644 airtime_mvc/application/views/scripts/form/embeddableplayer.html create mode 100644 airtime_mvc/application/views/scripts/form/embeddableplayer.phtml create mode 100644 airtime_mvc/public/css/embed-player.css diff --git a/airtime_mvc/application/configs/conf.php b/airtime_mvc/application/configs/conf.php index d38a03797..6cf5f9d6c 100644 --- a/airtime_mvc/application/configs/conf.php +++ b/airtime_mvc/application/configs/conf.php @@ -92,7 +92,7 @@ class Config { public static function setAirtimeVersion() { $airtime_version = Application_Model_Preference::GetAirtimeVersion(); $uniqueid = Application_Model_Preference::GetUniqueId(); - $buildVersion = file_get_contents(self::$rootDir."/../VERSION"); + $buildVersion = @file_get_contents(self::$rootDir."/../VERSION"); self::$CC_CONFIG['airtime_version'] = md5($airtime_version.$buildVersion); } diff --git a/airtime_mvc/application/controllers/EmbeddableplayerController.php b/airtime_mvc/application/controllers/EmbeddableplayerController.php index 3cd0e3af4..f30683b79 100644 --- a/airtime_mvc/application/controllers/EmbeddableplayerController.php +++ b/airtime_mvc/application/controllers/EmbeddableplayerController.php @@ -9,23 +9,12 @@ class EmbeddablePlayerController extends Zend_Controller_Action public function indexAction() { + $CC_CONFIG = Config::getConfig(); + $baseUrl = Application_Common_OsPath::getBaseDir(); + $this->view->headLink()->appendStylesheet($baseUrl.'css/embed-player.css?'.$CC_CONFIG['airtime_version']); + $form = new Application_Form_EmbeddablePlayer(); - $request = $this->getRequest(); - - if ($request->isPost()) { - $formValues = $request->getPost(); - if ($form->isValid($formValues)) { - - $this->view->statusMsg = "<div class='success'>". _("Preferences updated.")."</div>"; - - } else { - - } - - $this->view->form = $form; - } - $this->view->form = $form; } } \ No newline at end of file diff --git a/airtime_mvc/application/forms/EmbeddablePlayer.php b/airtime_mvc/application/forms/EmbeddablePlayer.php index 2accf23a5..00f49384a 100644 --- a/airtime_mvc/application/forms/EmbeddablePlayer.php +++ b/airtime_mvc/application/forms/EmbeddablePlayer.php @@ -5,15 +5,22 @@ class Application_Form_EmbeddablePlayer extends Zend_Form_SubForm public function init() { $this->setDecorators(array( - array('ViewScript', array('viewScript' => 'form/embeddableplayer.html')) + array('ViewScript', array('viewScript' => 'form/embeddableplayer.phtml')) )); - $displayTrackMetadata = new Zend_Form_Element_Checkbox('display_track_metadata'); + $embedSrc = new Zend_Form_Element_Text('player_embed_src'); + $embedSrc->setAttrib("readonly", "readonly"); + $embedSrc->setAttrib("class", "player_embed_src"); + $embedSrc->setValue('<iframe></iframe>'); + $embedSrc->removeDecorator('label'); + $this->addElement($embedSrc); + + $displayTrackMetadata = new Zend_Form_Element_Checkbox('player_display_track_metadata'); $displayTrackMetadata->setValue(true); $displayTrackMetadata->setLabel(_('Display track metadata?')); $this->addElement($displayTrackMetadata); - $streamURL = new Zend_Form_Element_Radio('stream_url'); + $streamURL = new Zend_Form_Element_Radio('player_stream_url'); $urlOptions = Array(); foreach(Application_Model_StreamSetting::getEnabledStreamUrls() as $type => $url) { $urlOptions[$url] = $type; @@ -21,10 +28,9 @@ class Application_Form_EmbeddablePlayer extends Zend_Form_SubForm $streamURL->setMultiOptions( $urlOptions ); + $streamURL->setValue(0); $streamURL->setLabel(_('Select stream:')); $this->addElement($streamURL); - $submit = new Zend_Form_Element_Submit('submit'); - $this->addElement($submit); } } \ No newline at end of file diff --git a/airtime_mvc/application/views/scripts/embeddableplayer/index.phtml b/airtime_mvc/application/views/scripts/embeddableplayer/index.phtml index 51e00dc03..0a9e66daa 100644 --- a/airtime_mvc/application/views/scripts/embeddableplayer/index.phtml +++ b/airtime_mvc/application/views/scripts/embeddableplayer/index.phtml @@ -1,16 +1,13 @@ <div class="ui-widget ui-widget-content block-shadow simple-formblock clearfix padded-strong preferences"> <h2 style="float:left"><?php echo _("Embeddable Player") ?></h2> <?php $baseUrl = Application_Common_OsPath::getBaseDir(); ?> + <form method="post" id="player_form" enctype="multipart/form-data"> <div style="clear:both"></div> - <?php + <?php echo $this->form; ?> - echo $this->statusMsg; - echo $this->form; - ?> - <?php echo $this->form->getElement('submit')->render() ?> <br /> </form> </div> diff --git a/airtime_mvc/application/views/scripts/form/embeddableplayer.html b/airtime_mvc/application/views/scripts/form/embeddableplayer.html deleted file mode 100644 index 8a1ad551e..000000000 --- a/airtime_mvc/application/views/scripts/form/embeddableplayer.html +++ /dev/null @@ -1,8 +0,0 @@ -<fieldset class="padded"> - <dl class="zend_form"> - <?php echo $this->element->getElement('display_track_metadata'); ?> - - <?php echo $this->element->getElement('stream_url'); ?> - - </dl> -</fieldset> \ No newline at end of file diff --git a/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml b/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml new file mode 100644 index 000000000..f1ca8d478 --- /dev/null +++ b/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml @@ -0,0 +1,13 @@ +<fieldset class="padded"> + <dl class="zend_form"> + + <?php echo $this->element->getElement('player_embed_src'); ?> + + <?php echo $this->element->getElement('player_display_track_metadata'); ?> + + <?php echo $this->element->getElement('player_stream_url'); ?> + + <div style="clear:both"></div> + <div id="embed_player_preview">player preview holder</div> + </dl> +</fieldset> \ No newline at end of file diff --git a/airtime_mvc/public/css/embed-player.css b/airtime_mvc/public/css/embed-player.css new file mode 100644 index 000000000..1f19319e8 --- /dev/null +++ b/airtime_mvc/public/css/embed-player.css @@ -0,0 +1,4 @@ +#embed_player_preview { + margin-top:20px; + border: 1px solid; +} From d61b75f105e234927c607f2bcf8886430e6c40dd Mon Sep 17 00:00:00 2001 From: Albert Santoni <albert.santoni@sourcefabric.org> Date: Tue, 3 Mar 2015 16:08:53 -0500 Subject: [PATCH 08/60] Added a working 4th stream --- .../controllers/PreferenceController.php | 8 +++++++- airtime_mvc/build/sql/defaultdata.sql | 18 ++++++++++++++++++ .../js/airtime/preferences/streamsetting.js | 9 ++++++--- .../pypo/liquidsoap_scripts/liquidsoap.cfg | 18 +++++++++++++++--- .../pypo/liquidsoap_scripts/ls_script.liq | 16 +++++++++++++++- 5 files changed, 61 insertions(+), 8 deletions(-) diff --git a/airtime_mvc/application/controllers/PreferenceController.php b/airtime_mvc/application/controllers/PreferenceController.php index 219e35e01..6a0ba3541 100644 --- a/airtime_mvc/application/controllers/PreferenceController.php +++ b/airtime_mvc/application/controllers/PreferenceController.php @@ -223,6 +223,9 @@ class PreferenceController extends Zend_Controller_Action } elseif (strpos($v[0], "s3_data") !== false) { preg_match('/\[(.*)\]/', $v[0], $matches); $s3_data[$matches[1]] = $v[1]; + } elseif (strpos($v[0], "s4_data") !== false) { + preg_match('/\[(.*)\]/', $v[0], $matches); + $s4_data[$matches[1]] = $v[1]; } else { $values[$v[0]] = $v[1]; } @@ -230,6 +233,7 @@ class PreferenceController extends Zend_Controller_Action $values["s1_data"] = $s1_data; $values["s2_data"] = $s2_data; $values["s3_data"] = $s3_data; + $values["s4_data"] = $s4_data; $error = false; if ($form->isValid($values)) { @@ -245,6 +249,7 @@ class PreferenceController extends Zend_Controller_Action $s1_set_admin_pass = !empty($values["s1_data"]["admin_pass"]); $s2_set_admin_pass = !empty($values["s2_data"]["admin_pass"]); $s3_set_admin_pass = !empty($values["s3_data"]["admin_pass"]); + $s4_set_admin_pass = !empty($values["s4_data"]["admin_pass"]); // this goes into cc_pref table Application_Model_Preference::SetStreamLabelFormat($values['streamFormat']); @@ -290,6 +295,7 @@ class PreferenceController extends Zend_Controller_Action "s1_set_admin_pass"=>$s1_set_admin_pass, "s2_set_admin_pass"=>$s2_set_admin_pass, "s3_set_admin_pass"=>$s3_set_admin_pass, + "s4_set_admin_pass"=>$s4_set_admin_pass, )); } else { $live_stream_subform->updateVariables(); @@ -441,7 +447,7 @@ class PreferenceController extends Zend_Controller_Action public function getAdminPasswordStatusAction() { $out = array(); - for ($i=1; $i<=3; $i++) { + for ($i=1; $i<=4; $i++) { if (Application_Model_StreamSetting::getAdminPass('s'.$i)=='') { $out["s".$i] = false; } else { diff --git a/airtime_mvc/build/sql/defaultdata.sql b/airtime_mvc/build/sql/defaultdata.sql index 3c1a60752..438dfc979 100644 --- a/airtime_mvc/build/sql/defaultdata.sql +++ b/airtime_mvc/build/sql/defaultdata.sql @@ -358,3 +358,21 @@ INSERT INTO cc_pref (subjid, keystr, valstr) VALUES (1, 'user_timezone', 'UTC'); INSERT INTO cc_pref (keystr, valstr) VALUES ('import_timestamp', '0'); --end added in 2.5.2 + +INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_enable', 'false', 'boolean'); +INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_output', 'icecast', 'string'); +INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_name', '', 'string'); +INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_type', '', 'string'); +INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_bitrate', '', 'integer'); +INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_host', '', 'string'); +INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_port', '', 'integer'); +INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_user', '', 'string'); +INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_pass', '', 'string'); +INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_admin_user', '', 'string'); +INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_admin_pass', '', 'string'); +INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_mount', '', 'string'); +INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_url', '', 'string'); +INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_description', '', 'string'); +INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_genre', '', 'string'); +INSERT INTO cc_stream_setting (keyname, value, type) VALUES ('s4_channels', 'stereo', 'string'); + diff --git a/airtime_mvc/public/js/airtime/preferences/streamsetting.js b/airtime_mvc/public/js/airtime/preferences/streamsetting.js index 7529783a0..0fd0cd90a 100644 --- a/airtime_mvc/public/js/airtime/preferences/streamsetting.js +++ b/airtime_mvc/public/js/airtime/preferences/streamsetting.js @@ -443,7 +443,7 @@ function setSliderForReplayGain(){ $( "#replayGainModifier" ).val( $( "#slider-range-max" ).slider( "value" ) ); } -function setPseudoAdminPassword(s1, s2, s3) { +function setPseudoAdminPassword(s1, s2, s3, s4) { if (s1) { $('#s1_data-admin_pass').val('xxxxxx'); } @@ -453,11 +453,14 @@ function setPseudoAdminPassword(s1, s2, s3) { if (s3) { $('#s3_data-admin_pass').val('xxxxxx'); } + if (s4) { + $('#s4_data-admin_pass').val('xxxxxx'); + } } function getAdminPasswordStatus() { $.ajax({ url: baseUrl+'Preference/get-admin-password-status/format/json', dataType:"json", success:function(data){ - setPseudoAdminPassword(data.s1, data.s2, data.s3); + setPseudoAdminPassword(data.s1, data.s2, data.s3, data.s4); }}); } @@ -476,7 +479,7 @@ $(document).ready(function() { $('#content').empty().append(json.html); setupEventListeners(); setSliderForReplayGain(); - setPseudoAdminPassword(json.s1_set_admin_pass, json.s2_set_admin_pass, json.s3_set_admin_pass); + setPseudoAdminPassword(json.s1_set_admin_pass, json.s2_set_admin_pass, json.s3_set_admin_pass, json.s4_set_admin_pass); }); } }); diff --git a/python_apps/pypo/liquidsoap_scripts/liquidsoap.cfg b/python_apps/pypo/liquidsoap_scripts/liquidsoap.cfg index d76e3af3d..bb6c6d88f 100644 --- a/python_apps/pypo/liquidsoap_scripts/liquidsoap.cfg +++ b/python_apps/pypo/liquidsoap_scripts/liquidsoap.cfg @@ -10,18 +10,22 @@ output_sound_device_type = "ALSA" s1_output = "icecast" s2_output = "icecast" s3_output = "icecast" +s4_output = "icecast" s1_enable = true s2_enable = false s3_enable = false +s4_enable = false s1_type = "ogg" s2_type = "ogg" s3_type = "mp3" +s4_type = "mp3" s1_bitrate = 128 s2_bitrate = 128 s3_bitrate = 160 +s4_bitrate = 160 ########################################### # Logging settings # @@ -35,31 +39,39 @@ log_file = "/var/log/airtime/pypo-liquidsoap/<script>.log" s1_host = "127.0.0.1" s2_host = "127.0.0.1" s3_host = "127.0.0.1" +s4_host = "127.0.0.1" s1_port = 8000 s2_port = 8000 s3_port = 8000 +s4_port = 8000 s1_user = "" s2_user = "" s3_user = "" +s4_user = "" s1_pass = "hackme" s2_pass = "hackme" s3_pass = "hackme" +s4_pass = "hackme" # Icecast mountpoint names s1_mount = "airtime_128.ogg" s2_mount = "airtime_128.ogg" s3_mount = "airtime_160.mp3" +s4_mount = "airtime_160.mp3" # Webstream metadata settings s1_url = "http://airtime.sourcefabric.org" s2_url = "http://airtime.sourcefabric.org" s3_url = "http://airtime.sourcefabric.org" -s1_description = "Airtime Radio! stream1" -s2_description = "Airtime Radio! stream2" -s3_description = "Airtime Radio! stream3" +s4_url = "http://airtime.sourcefabric.org" +s1_description = "Airtime Radio! Stream 1" +s2_description = "Airtime Radio! Stream 2" +s3_description = "Airtime Radio! Stream 3" +s4_description = "Airtime Radio! Stream 4" s1_genre = "genre" s2_genre = "genre" s3_genre = "genre" +s4_genre = "genre" # Audio stream metadata for vorbis/ogg is disabled by default # due to a number of client media players that disconnect diff --git a/python_apps/pypo/liquidsoap_scripts/ls_script.liq b/python_apps/pypo/liquidsoap_scripts/ls_script.liq index ba4f8568d..398def91a 100644 --- a/python_apps/pypo/liquidsoap_scripts/ls_script.liq +++ b/python_apps/pypo/liquidsoap_scripts/ls_script.liq @@ -29,9 +29,11 @@ dynamic_metadata_callback = ref fun (s) -> begin () end s1_connected = ref '' s2_connected = ref '' s3_connected = ref '' +s4_connected = ref '' s1_namespace = ref '' s2_namespace = ref '' s3_namespace = ref '' +s4_namespace = ref '' just_switched = ref false %include "ls_lib.liq" @@ -103,7 +105,7 @@ server.register(namespace="vars", fun (s) -> begin log("vars.bootup_time") time := s s end) server.register(namespace="streams", "connection_status", - fun (s) -> begin log("streams.connection_status") "1:#{!s1_connected},2:#{!s2_connected},3:#{!s3_connected}" end) + fun (s) -> begin log("streams.connection_status") "1:#{!s1_connected},2:#{!s2_connected},3:#{!s3_connected}:#{!s4_connected}" end) server.register(namespace="vars", "default_dj_fade", fun (s) -> begin log("vars.default_dj_fade") default_dj_fade := float_of_string(s) s end) @@ -399,6 +401,18 @@ if s3_enable == true then s3_connected, s3_description, s3_channels) end +if s4_enable == true then + if s4_output == 'shoutcast' then + s4_namespace := "shoutcast_stream_4" + else + s4_namespace := s4_mount + end + server.register(namespace=!s4_namespace, "connected", fun (s) -> begin log("#{!s4_namespace}.connected") !s4_connected end) + output_to(s4_output, s4_type, s4_bitrate, s4_host, s4_port, s4_pass, + s4_mount, s4_url, s4_name, s4_genre, s4_user, s, "4", + s4_connected, s4_description, s4_channels) +end + command = "/usr/lib/airtime/pypo/bin/liquidsoap_scripts/notify.sh --liquidsoap-started &" log(command) system(command) From 968b739bf41b77574b65887d6be1e6f7999f12b9 Mon Sep 17 00:00:00 2001 From: Albert Santoni <albert.santoni@sourcefabric.org> Date: Tue, 10 Mar 2015 12:41:45 -0400 Subject: [PATCH 09/60] Make 4th stream backwards compatible with the autogenerated liquidsoap.cfg --- python_apps/pypo/liquidsoap_scripts/ls_script.liq | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python_apps/pypo/liquidsoap_scripts/ls_script.liq b/python_apps/pypo/liquidsoap_scripts/ls_script.liq index 398def91a..46ced98dc 100644 --- a/python_apps/pypo/liquidsoap_scripts/ls_script.liq +++ b/python_apps/pypo/liquidsoap_scripts/ls_script.liq @@ -401,6 +401,7 @@ if s3_enable == true then s3_connected, s3_description, s3_channels) end +%ifdef s4_enable if s4_enable == true then if s4_output == 'shoutcast' then s4_namespace := "shoutcast_stream_4" @@ -412,6 +413,8 @@ if s4_enable == true then s4_mount, s4_url, s4_name, s4_genre, s4_user, s, "4", s4_connected, s4_description, s4_channels) end +%endif + command = "/usr/lib/airtime/pypo/bin/liquidsoap_scripts/notify.sh --liquidsoap-started &" log(command) From 6232e3f4f00611e089bc7cb3ccb16ef7fde91fae Mon Sep 17 00:00:00 2001 From: Albert Santoni <albert.santoni@sourcefabric.org> Date: Tue, 10 Mar 2015 12:52:00 -0400 Subject: [PATCH 10/60] Better backwards compatibility fix for 4 streams --- python_apps/pypo/liquidsoap_scripts/ls_script.liq | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python_apps/pypo/liquidsoap_scripts/ls_script.liq b/python_apps/pypo/liquidsoap_scripts/ls_script.liq index 46ced98dc..38f647f7f 100644 --- a/python_apps/pypo/liquidsoap_scripts/ls_script.liq +++ b/python_apps/pypo/liquidsoap_scripts/ls_script.liq @@ -33,7 +33,6 @@ s4_connected = ref '' s1_namespace = ref '' s2_namespace = ref '' s3_namespace = ref '' -s4_namespace = ref '' just_switched = ref false %include "ls_lib.liq" @@ -402,6 +401,7 @@ if s3_enable == true then end %ifdef s4_enable +s4_namespace = ref '' if s4_enable == true then if s4_output == 'shoutcast' then s4_namespace := "shoutcast_stream_4" From 6c46f0a15681d753f52f42eaf3a95f62b2ced1a8 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Tue, 10 Mar 2015 16:32:35 -0400 Subject: [PATCH 11/60] Added player preview to view Everything is hardcoded right now --- .../EmbeddableplayerController.php | 9 +- .../application/forms/EmbeddablePlayer.php | 2 +- .../scripts/embeddableplayer/embed-code.phtml | 18 + .../views/scripts/form/embeddableplayer.phtml | 2 +- .../embeddableplayer/ffmp3-mcclean.xml | 10 + .../embeddableplayer/ffmp3-mcclean/bg.png | Bin 0 -> 3649 bytes .../embeddableplayer/ffmp3-mcclean/holder.png | Bin 0 -> 830 bytes .../embeddableplayer/ffmp3-mcclean/play.gif | Bin 0 -> 1493 bytes .../ffmp3-mcclean/playclick.jpg | Bin 0 -> 1422 bytes .../ffmp3-mcclean/statusplay.png | Bin 0 -> 138 bytes .../ffmp3-mcclean/statusstop.png | Bin 0 -> 136 bytes .../embeddableplayer/ffmp3-mcclean/stop.jpg | Bin 0 -> 1459 bytes .../ffmp3-mcclean/stopclick.jpg | Bin 0 -> 1459 bytes .../public/js/airtime/embeddableplayer/mrp.js | 3012 +++++++++++++++++ .../js/airtime/embeddableplayer/muses.swf | Bin 0 -> 74589 bytes 15 files changed, 3047 insertions(+), 6 deletions(-) create mode 100644 airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml create mode 100644 airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean.xml create mode 100644 airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/bg.png create mode 100644 airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/holder.png create mode 100644 airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/play.gif create mode 100644 airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/playclick.jpg create mode 100644 airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/statusplay.png create mode 100644 airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/statusstop.png create mode 100644 airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/stop.jpg create mode 100644 airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/stopclick.jpg create mode 100644 airtime_mvc/public/js/airtime/embeddableplayer/mrp.js create mode 100644 airtime_mvc/public/js/airtime/embeddableplayer/muses.swf diff --git a/airtime_mvc/application/controllers/EmbeddableplayerController.php b/airtime_mvc/application/controllers/EmbeddableplayerController.php index f30683b79..4536d7e18 100644 --- a/airtime_mvc/application/controllers/EmbeddableplayerController.php +++ b/airtime_mvc/application/controllers/EmbeddableplayerController.php @@ -9,12 +9,13 @@ class EmbeddablePlayerController extends Zend_Controller_Action public function indexAction() { - $CC_CONFIG = Config::getConfig(); - $baseUrl = Application_Common_OsPath::getBaseDir(); - $this->view->headLink()->appendStylesheet($baseUrl.'css/embed-player.css?'.$CC_CONFIG['airtime_version']); - $form = new Application_Form_EmbeddablePlayer(); $this->view->form = $form; } + + public function embedCodeAction() + { + $this->view->layout()->disableLayout(); + } } \ No newline at end of file diff --git a/airtime_mvc/application/forms/EmbeddablePlayer.php b/airtime_mvc/application/forms/EmbeddablePlayer.php index 00f49384a..4b9353aad 100644 --- a/airtime_mvc/application/forms/EmbeddablePlayer.php +++ b/airtime_mvc/application/forms/EmbeddablePlayer.php @@ -11,7 +11,7 @@ class Application_Form_EmbeddablePlayer extends Zend_Form_SubForm $embedSrc = new Zend_Form_Element_Text('player_embed_src'); $embedSrc->setAttrib("readonly", "readonly"); $embedSrc->setAttrib("class", "player_embed_src"); - $embedSrc->setValue('<iframe></iframe>'); + $embedSrc->setValue('<iframe frameborder="0" src="http://localhost/embeddableplayer/embed-code"></iframe>'); $embedSrc->removeDecorator('label'); $this->addElement($embedSrc); diff --git a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml new file mode 100644 index 000000000..74da31357 --- /dev/null +++ b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml @@ -0,0 +1,18 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + +<head> + <script src="http://localhost/widgets/muses/self_hosted/mrp.js" type="text/javascript"></script> +</head> + +<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="180" height="60" bgcolor="#FFFFFF"> + <param name="movie" value="muses.swf" /> + <param name="flashvars" value="url=http://albertprov1.out.airtime.pro:8000/albertprov1_a&lang=auto&codec=mp3&volume=100&introurl=&tracking=true&jsevents=true&buffering=5&skin=http://localhost/js/airtime/embeddableplayer/ffmp3-mcclean.xml&title=Albert's%20Test Stream" /> + <param name="wmode" value="window" /> + <param name="allowscriptaccess" value="always" /> + <param name="bgcolor" value="#FFFFFF" /> + <param name="scale" value="noscale" /> + <embed src="http://localhost/js/airtime/embeddableplayer/muses.swf" flashvars="url=http://albertprov1.out.airtime.pro:8000/albertprov1_a&lang=auto&codec=mp3&volume=100&introurl=&tracking=true&jsevents=true&buffering=5&skin=http://localhost/js/airtime/embeddableplayer/ffmp3-mcclean.xml&title=Albert's%20Test Stream" width="180" scale="noscale" height="60" wmode="window" bgcolor="#FFFFFF" allowscriptaccess="always" type="application/x-shockwave-flash" /> +</object> + +</html> \ No newline at end of file diff --git a/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml b/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml index f1ca8d478..568540d7f 100644 --- a/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml +++ b/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml @@ -8,6 +8,6 @@ <?php echo $this->element->getElement('player_stream_url'); ?> <div style="clear:both"></div> - <div id="embed_player_preview">player preview holder</div> + <iframe frameborder="0" src="http://localhost/embeddableplayer/embed-code"></iframe> </dl> </fieldset> \ No newline at end of file diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean.xml b/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean.xml new file mode 100644 index 000000000..57d7c3eab --- /dev/null +++ b/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean.xml @@ -0,0 +1,10 @@ +<ffmp3-skin folder="ffmp3-mcclean"> + <bg image="bg.png" x="0" y="0" /> + <play image="play.gif" x="8" y="29" clickimage="playclick.jpg" /> + <stop image="stop.jpg" x="40" y="29" clickimage="stopclick.jpg" /> + <text x="13" y="9" width="154" height="17" color="#ffffff" font="Arial" size="11" /> + <songtitle x="12" y="83" width="210" height="22" color="#000000" font="Arial" size="12" /> + <artist x="12" y="103" width="210" height="22" color="#000000" font="Arial" size="12" /> + <volume mode="holder" x="71" y="33" width="100" height="17" holderImage="holder.png" /> + <status imagePlay="statusplay.png" imageStop="statusstop.png" x="13" y="34" /> +</ffmp3-skin> \ No newline at end of file diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/bg.png b/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/bg.png new file mode 100644 index 0000000000000000000000000000000000000000..a399e16be7eeeb3a6a26d2488b98293dbf0eb29e GIT binary patch literal 3649 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 T`Az`^00000NkvXXu0mjf8;ca6 literal 0 HcmV?d00001 diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/holder.png b/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/holder.png new file mode 100644 index 0000000000000000000000000000000000000000..8b61d23458835ad36e762af595eb72011e809860 GIT binary patch literal 830 zcmV-E1Ht@>P)<h;3K|Lk000e1NJLTq000pH000mO1^@s7hc=|i00004XF*Lt006JZ zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!&`Cr=RCwBA zU;u*e-@pF{(M&+B+||_uVk-j#5aY39$F#trH*em&s;#Yk6(E2>9M-2#pZ<@DiTUs5 z<_6Nk3KC@e_U+rB|3CnuL41G!0tqsF{P^(>2!mC;fBzmN3zp#G;$mQ8W@cvCwQCmx z4-XFmhz$b%fByXW55gceKmZn~90x!U2(C25R!V&a576G0g3plfGI<IiRs<Sa3*12` znN0RRh9`t@=A3&<smB<H!iJyzNo%e3w$_r?nxvHEoTE|-2_Z-+wIOlg%7!}u@BL6? z3_3Q%C;EVccILsH@f+raO_<Ls01ae}!XOMr;g>eLiByUXLZD}H3<Ns4c5{+mz$-Ww zyhX3jn{+8ILOQfdruKhg0xvxN_Xzp^tphdB^G$F9MIWJg?Y;MJ2{IvB+gfWfe~O|w z?1cFmLdcX-==&Z?lAx|@aK=m<W4g9&QB@VJwMf$x&N<|Hj$s%&y)4VOX&OjzeV&D~ zELYErd$<slnx^R&Jr6S$kK;H#;<_mw&X_6jkPmWmXVGcHev0mrz5W6;O~tAa0#R^7 z4k9E)QifAfr!*g6Z6lZ_wT-Q$wi5h-Lu~woyCPzfUND_l<Qqcl(kKW<&CJF%c`)eP zoww|pH><>AoHtGLh`Ssh_kJ8lF-_CoCML@c=Xw5s($A)8zF>J$ZaedR{}z^d*LD3g z48y+bI!Ym^o$I>t%iOkY!F}?`XPpxLx~}V2m|$t!c7x+MOBjX{%q`23Uk1HPKI>S& zqX96XdpahLV=1SmX;ObW3Rjmh=XoxIAP`BC2*WU>!&t|99x|3?`G(hVm!|2aL6&9P zKM$c*i=q&H-*0QIQwF;L9-=6s9y5Ah6c}tr<!q5b9{H?W>wkR*&>~Ns=N+r6`h>XO zn8c#Moa@{tZ{_dA_J;qB8Mp*)fUA{64&N$m-T_}gug1&x0ct_DHu;mWmjD0&07*qo IM6N<$g2~ExeE<Le literal 0 HcmV?d00001 diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/play.gif b/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/play.gif new file mode 100644 index 0000000000000000000000000000000000000000..150591ffc95dfe9d45c6815eea1dce1f50c08941 GIT binary patch literal 1493 zcmWlYc|6p47{`Z@tS(zANoYE05u3!8#EdI7n@T6bW=F3!t=H;M;U$K(vayV#gACF* z29;?j#x>)Z`OSSFznS0Mv_z!5+WmQdpXc*=KF|02?-w10I^Y-Yh;Ts6BM=841p4P5 z2`D^zsPM>7kB;n8c>KWK?;-c~6CZK|ecjYQdaL}y3!=h`A_9xif#s->l2CL>#8Est z=m|QQfC_933vLNV6QhD(9Yei}LU%=lzdnI^c`~~592R#qrSfW0SMnXw{aci~fugV= zad?CXb4+#yt;R${m(D8_VxcQo$#0jT<P2>N4u{Ln&o3w_C@wB8DJdx{E5qaQ6%`fL z)zyz5KhC6NJ!*PXMl3IHEw5~?u5PWux8ZAQY6t{EeSLjnV`Fo3b8BlWi9~8|Z|~^n zc=P5>S63IAOr}sM-QC?iJw5N<y?g)ueKsGLsm490J|_&ls2^^uqc;$k4fSIUy}iAC zF#Xj2J}OLqe?OHv05do^I5adgJUmRJ(dcyg$jHd(=;+wk*!cK3lR3^{FqliQ#sDS* zfE{20Y=FUJF#!NzumA?YX0ka9IIvhO0ARCN95%q=u-LG%*=!Dn!)9~Y93F=U3zx&? z^8hZF2k?0uF6=xWmnY!!`2rZ8kS`Dk1R}n0NdzLHkRuQZcp`yNBoK>40*O>06beO4 zNJJ8$NFo*sMQ}^RLa|sZk-%9hkx3O2u}lK1R3;V5;7Kl#N~JQHOe&R2<uaK<s!+(} za=AhQKT*o0pj-he6rfzCQA$Nhg-ofGgP=mGQYe*55Cl~!m0GP<f*KX5RjV~h4XA?P zf=5UVX(1&Dsnig>hEy7j8iF8=Mgu`wjZUl8LU5}gy;iH&>I^!aL95rmqBX&zUZ*$e zbsG3$5)KAPZ`2tLI{gwx_?QevBkV?_K|g6QPMA!Srpbv3BWx4@nkFVD^%F~cfSH<_ znwgpTH2-mScJ|XeTyy^+5OZ_0b8~a^^9!FpfByRQtIL9g83OSI;f{b=`V$E2Z-|5y z7Y2=j&Q!^oUp5b~bR?&$ySG=*rTi0kmJ~x`bN`edzPC6Bn(~g<^<+3NEKqbzMZoD= zpKFPlhnKo<O+)0A;UXEOYC8L)?d^xLjVE3PZGYR)`r_mh@aAjvuSFMmO#wO=yTRmB z{tZ#QnvSNuQopohR#8gcA9B|)Y{p;9B-{&KX&T_JB==esIcIY5OH$6xLLxb>C!V6s zLi?u3Yr`CU_ud#deB~{%5_Kl_6_)MbsrPE`G|=h=#dJHH7+n%jkZQfQFV8!9w9)VU zkZpzc73@IV3Jr0F6wlZn*k=|$?2#~5YPN01mW`F3$9)!znwJ!h_@0~JwvVn!dSGpS zFk_OvexhvNbbJGydLWkX7DJ9Zo7`7xVQuCo{P{cgHg3Ar#wh2Y;z(jwP{i)0=whn* z@{NEQqN|fSvZp0EddNNs**=0pwq%dE*G0w!ncFlzGh1I*cX?zt@}7QRD|!ooj=^WL z>C2GoGR>_{$Mxs$d|KE}$0VkK7ETq3)X-AQx!M)TRnFI`mS>_*(zlhz*ySTJMXZqT z(>-gorG+O{*2qFzPjkyt0nh78Q=0s&>?4V!P}h9-Y?^C<&kY*VEY#C-U#5-CF4xQ* zt|<G+XIV4?epL|8B|F18#Jl{PnSqcW=U`<YaUIOtdXxAT>(Y7K!hE%7<}9me>;1L@ zbjv-TzK{*U%GBjf)GyPyTW-@xc}^Kz)*e#xDmVAF@i}WPD$^71<T<7GW(_;04@_G* zrP$s<lSK|*!JDwVFh`$aORG?uuho%IPHAnJ4-V5!sd)}4;Z$&U!aA4!m7$5O2lnTE zh?e%Ne|Pb+zZjQlvC`WaGvu&Xlr^y0t6NE>A<Hl9v)zXvSx^?6vT`?FbpPAJ&PQ;y SV?4}fdT<kV)^QC2;r~A$;C_q% literal 0 HcmV?d00001 diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/playclick.jpg b/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/playclick.jpg new file mode 100644 index 0000000000000000000000000000000000000000..48bbd4954a037eefc63f038727b538a96ec2b0da GIT binary patch literal 1422 zcma)5X;4#F6u$Spmjwiym#}7<vQt_?2^gxOWf6?ZqJkJ|*+~=;I}kNu+!}V2HAn!h zr9>f4#u;(JDJTqtB4O1k%Ya}(1}#&S8bMnaHGKg^e|pZ`nLFov-&ya~4e7=KEr7R) z2Ot7~kWip|3UGh1AV~rsz<}Nebko4mSC|~X69`)f3q*k_hLR190YJh5Kx+ZOssJG6 z>qbB(fS)eN2SJzQ9UXy?@DgXq@u?SGE5NCM2|h<54iFroIMlU)E}~FEKN1p|f?@`X z={_<8j6@;R0FfI-NlSJVLr8!I$<_>0x|^N58BR<AK`|-?zJnkTN9h=_VX)&&NWQji z@#*gNoHWHgzw<&59$k;dkY!8ZAdaBGn#PW!-^FZvLqur`wbu43i2;ecfDEzsg+km0 zuxZxMeM90zX)?vbE9$$t`@je#a5#z+tC*TH+?~5EQ7e_b_>-x9#`S&kPuRSJS{~TE z-J)d;bFznkQy~P9gm7Ey5)SV<)IFopc)s%Dns$45MfN8}O*~^KMvTlo(Xa*^C#Yjh zq^dRZc9R#6{TEZ~y+B6v<W7r}%Er>G%lW-3m4c677+7$j3$Dw!dGszx?REASwoDdh zcvbi!4Iz6Y%JvsU2Y;w3Zb*xIVYS($uQ#*2FG?gVAvt&Hz+#96n4g`rWc_{5ZJujW z+f6lxHqTfY3YvZF-wMTy+GZZR+jVrRJh|W<1wtZnLv+GV4o!_;3lewJOG9X_>n#Ia zD9jFF{LSdnFku_}Oa~>Uh$`=&QuZ{6hi7hun2ypN<DA3Gk=Q81JXL{lOhj8s<WAe! zgY{>a1wVe4C7EuKx8c1(GZlPJ=nc&oubv(C(5Uh1T>H9nt1T|giN3-gDP>AA*Xh~~ z_lGOLEDR7+U6iF^%O5|$ohq`nhI0-a=2dT~jykpF-xzIdgx8h7ngVKAT_@`wm8aga z%~?4(+YxNs8j#W%-10_k;L;}Z;KmB_xB7?5@E{pO!7vV$416O|o&P(z@_X<7LzN>V z+G)*vqt)yi`OMK~t5nvSg@Hj7dfS}U0f+HvMXY;#bDmt9Q{6wdw=C4vt$pbD>9=Ey zrxSMV`R*Z|5gmLr!fi*D1NT&5c3C<<U)tYm7H#IS+LD><IW;)Ja<tmO92mdeeYqe` z^t^=%66Bu0KNde3yU9)b?d5ZE&X%Vy)r;HIXKj75a@9G(^gfc4&ylbrbF`lslGmNt z#ecju7-dil)^f^f@?P(MzgIn>yUO+Dwz`t=<D>GRJ0%%>hfM#y{%5rZzhn4c@1<i5 z3S6G$Vq{RniCkcPc-OS-jpO;$#TTM)n6@dE9DKTDNO0QuUJ_>yZmmRaQ_Yvvtd``v z)t&g|ROY_yR6)%nzm3t4Ee}u&SBy@k^;nd*Y)_iW<#Ak!Oq%;x!>Dz^j?hx4>gu`& z*_xsHyegk>&5T%b68yE$_|@&KKCZ>ou?rc>=!=u%OD)s7tAp32uLv~>y5@JZK%`Wy zLPG3wr9POvL|!qU!e;~KB^!aQ8ZmKD1WxzdS^P1mzG6+0Z}GW|ljgYGLXejr>Ix8L hW-=&JW`n^lDRiV&I%`i@Zl%i<#m3Syo`mRs_<xxh-|hea literal 0 HcmV?d00001 diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/statusplay.png b/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/statusplay.png new file mode 100644 index 0000000000000000000000000000000000000000..d751aac25ba5107d963191a806f72dbaf13e8bdf GIT binary patch literal 138 zcmeAS@N?(olHy`uVBq!ia0vp^Od!m`#=yYHy3uwMki(Mh=<CS9u>OQOYu8I4U$VqC zq9iy!t)x7$D3!r6B|j-u!8128JvAsbF{QHbWU38Nk&LH{V@SoVq=X;(zt|KSBpg^A i8Vrr@h*uqEU}11fX1%|yRKOOfj=|H_&t;ucLK6VcjwMR~ literal 0 HcmV?d00001 diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/statusstop.png b/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/statusstop.png new file mode 100644 index 0000000000000000000000000000000000000000..e0cce0d1a8407d07b2b8470cfedcbb3b7a0c96b8 GIT binary patch literal 136 zcmeAS@N?(olHy`uVBq!ia0vp^Od!m`#=yYHy3uwMki(Mh=<CS9u>OQOYu8I4U$VqC zq9iy!t)x7$D3!r6B|j-u!8128JvAsbF{QHbWU38Nk(8&4V@SoVq#x%Gd|*>(kZ@pe gXfS-ozOjLU!C@|IGjG!oQ=lpaPgg&ebxsLQ0I(4y>;M1& literal 0 HcmV?d00001 diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/stop.jpg b/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/stop.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c7fda38d84c849610b89f211a34fd222b515e527 GIT binary patch literal 1459 zcmex=<NpH&0WUXCHwH!~28I+MWcdGvLC~c%IlGd9k%5H)B*^gp9fO)<N`6u*L&^c5 z2qQCtC<`+i7cT<?QzZif^CJcZmO~5-tQ-H|W@uqxBpxs!05cpwwKFj?0%d_1E<r5g z{~-oJ4hAU(8D>U71|~s9W<kdPM;NXE9m~WFbUhrfaj>v5vvV>sGBC0-!bO1$W+oO^ zHg*mH21XV}CKh(M6blnGD;pz&fP#=PyP%=5vxumwATZomnV49(xS^^Tfp!QAv9KBn zD>^C}iLeDGHZI)g6x3uanzTvTIl1|;X{(u=5Can<&^N4blfk-J1QZMv3xxs~UR2s7 z40IF|D>Dl-%wa&UC<=TqbPP<KxKUx@!Hb2B4?q6D#lQnpz$C~l2#l0BGYn;~UJX37 zN^0|zn*Ddz^c_EXTX*|;X9YR)GQ}N-({~+Yn^iK;_&HD)2iVU{oVj1*cU*0);JlI9 z^4awBq51>fy4{RtzLf>K?Y#OoP(|vZ$t%wmSC{MBoxfR%Blm34U;m~atepuMWK7qM zN_!q#&kDM&q<Yk<cbddG&1F&PGWK8v%pe8VBx)mqv)#VVQ1t6~$Kk^ecR1oXOUg^* zC)W={OkrZPXIkf4a&=;D=FF|1N?2E2<mSB((GQYZy~=dA)bA$i*K8q&XI#JOutK3B zvL@nRooP%>&P*ZE2V6qwr!MX4=6(m(!pI2J;@qgIRT3UFN%X+iAT1U(=kt8GW4`zA z-u9hG*VpFoPoHRgqenbbZm+fY6n5)UDT|-1#hyNGjcJ+u-?(RMU$|Jbih0>hzuc)U zGyS66)4!_68e8RFclkOsF4A?!AC)R)<Giz5H_egU^Rn=IwAVAge8tm^YV$e!KJMCZ zDNOQ^Vzlql4RzPeTn<&9I2!N#Wx*}o%)CeUGJ?GWyt)`)ST*?jZe2d(`62mTyAIyk zTU_*cyExyU^Jdpxzt`FEdFeKzvqnviyHqYsP)m9mQLP;ll~;E5R_oK(zjsT|4qdNU zT@oO5H>`zkO3f*+H?wzFZ?b3WvUb^e>(28xAKUh)|MQP8vp?Y+f92QVmDj)56n>BA z|5Wm8A|z;-8SNE!B>A~dXlP4%vtEGr^zsk`eP;HkM4Ly3c^w+3Ox7j8;reL$>g=nB zV*0rn)%SE18l3(!ELy9)VP>I|X~nv-z(t&&9xiBX%ww5)YSVm2e)+6<y3VS-2U3j! zr!P61#ywr{<+@kT-ZpE7FE+Nnv>y_`AZwS*NcnSZflM&3_^wO$b=v3e2{OvsvSRI; zDu^0pkeWq2dym-!7yLeG^Qa_X8s|>0bB}{x@4iv9QS7+D0_%`d9d6DM8^9V6>0Ln1 z!Dmu~0^ENBn&(z5t!!^93T5)Ty3pT2U<tDW+Z)5oJuM>FeE&17@t;}MmCo2V&r0gT zJ+YH}VoGPZwro5wEnpFwr#Hi`9d9@5lqqfPnBm5B@REYld<)sP$ytvRQde>bJ(zhV zZ7G-V4VM25N0;OTYnq+B&K9oo&$gfw?n!|qhj=I0Wt1vBl$Tg^F!q{W2ggk<vHD#5 zHjRpBF6*m2>Xia^`Ch*Y)&TOcz(UPQ3NjrnuL5(WxHhhFnDMeB`G}Vu>#7U(B{sLt j-kf*rV_i&&<6WDi@4L2qSJ@S<aKrcE?3e3T|Gx<UPJI59 literal 0 HcmV?d00001 diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/stopclick.jpg b/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/stopclick.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a25e025be1b2ecf0ffd3906fcc2c3cf9f6382873 GIT binary patch literal 1459 zcmex=<NpH&0WUXCHwH!~28I+MWcdGvLC~c%IlGd9k%5H)B*^gp9fO)<N`6u*L&^c5 z2qQCtC<`+i7cT<?QwakD^ECzrmhB7-tQ-H|W@uqxBpxs!05cpwwKFj?0%d_1E<r5g z{~-oJ4hAU(8D>U71|~s9W<kdPM;Oil9m~WFbUhrfaj<hTv#>HUGB7bS!bO1$7FK39 zb`AlcdPZgzgcLIi6Du2o0HcDCFuRems4>uRMn*PfRyKC1W=1Av76w5@Az@a-L=ne8 zqa@?Tg-T6}O^cLOQbf(%gn;Uqm{~dDRsneof&zvt3JW(L6jDqqoTwBetjsFR2n=9m zCN{WRfxb{Q6mU%Z5U5btIC0^@jTayOzs0}<bQqH$vmh`!HeRqZz3;4Ith0f~Ys*u+ zzlmmk8aLj><=To%oqgm!(Z^e_nIR#2;!L0}4xn!tS(#FmqULlZGtIhMayI*X^3PN4 zJHjKor=G8xb#?W1oBG;{$EU8z%{r<Rd(=QH#y)o9U#<J|y8nCy>t_P#zplnFbF5(A z+l`9z78R*Wdzvm+WN>^1mS+aZU(wF-WL|aZ_TwuD%9h{gVpV#x&$DVp{FV({ru#!o zVPdmqS}hj+<NAW;ZiVl1CU5TE`S_}YwKqyJ`)xhMAdr%UDpi#;`COC*4>epl_VK>n zQsq9YTA!T~$#<o?E*<39@SA`BG0UV~wRX!~T3s8&-rV*2&#+M2>-5xb`@x0)Bgmd< z=4-Y2%lg9G=1r2EC$X+$O6JnbTB(!V+4~qG7f&x-+UKGgXK7yQ?j{pDbrrLwSo-bN z-h0C9O8K6Bve7AY@(DHzZqJv_$ojW^#fx2MR@eE92Tyu4>z3S^AD=en{Y<xyd>ilV z<&aVC`!e><B>jKcg$Fd0H{HIo)qkamiFH(hp19Q78$G(qf8YK$b=sRc_2V;hj#diz zu3Ej!y2(2-&@^PVXl+d4t1BLtPKBAyQF=b{<bgu7HP!(u&c-fM&+2@(B26GmVv6LF zNe`BVJ-na2nLpa}UiPLZf5NLjZ2o(`;dg%j^QZY=%j)0!6Mprp-hSHMZ4jR`GukT( zZ=R{Xc!Pc$TWDSFq=#D~g}mn2<!oJIGe=tV_dfN_9euYnGwgm%aN^iH^{#U1mxV&t zj(UBMlX|mq<AM`6=e&2@-cc}B_=I@=-5^F)vl&;t^Y|ROcfDKo;q#or=d*TY?*8+o zv_1V-;bKUff^1wN%scV-+Ktu9Kd$R9KQ(LD<^1^Acq6?F8GeC}Akhj^weV<BkBpL| zrDjRqhhs@B$EEI_P_W!tb8MN%T;+L-I<6&{Eod+)oRBw-UmU0jk<<km9CkE!Fu*-1 zFxxlNKv2n3X3Djq0|D`}Y)(SWT^0(bXH8^S(;ThV@s#JiZQi7N<#%0AGR(<au6=at z^xWnJ8y0dV6^rWhUD@wdp2E{)n(7r1=;@(hGc9>;^#+zJ+V+17`Zj5}uJ7Z0I+uIX zE{PYFetk2m)*HLSy(zGq_l2{|iJtbh=AeaUH;<g`yj8kom)D02m8#Yg?9S|)we6Co z(Yl|@$`7e>Mda<%hx=S$@xh>GuPpA4&bH?18aW5ttXwvAs;ezpW#^c}VUzIn)?DEk kn{NKJ^9{XofU6-j@721uzV2CCj2i+HXA}zk*#G|~0Q5EfJpcdz literal 0 HcmV?d00001 diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/mrp.js b/airtime_mvc/public/js/airtime/embeddableplayer/mrp.js new file mode 100644 index 000000000..74bc30836 --- /dev/null +++ b/airtime_mvc/public/js/airtime/embeddableplayer/mrp.js @@ -0,0 +1,3012 @@ +(function(n) { + function w(a, c) { + function r() {} + r.prototype = a; + var b = new r, + f; + for (f in c) b[f] = c[f]; + c.toString !== Object.prototype.toString && (b.toString = c.toString); + return b + } + + function B(a) { + return a instanceof Array ? function() { + return m.iter(a) + } : "function" == typeof a.iterator ? p(a, a.iterator) : a.iterator + } + + function p(a, c) { + if (null == c) return null; + null == c.__id__ && (c.__id__ = G++); + var b; + null == a.hx__closures__ ? a.hx__closures__ = {} : b = a.hx__closures__[c.__id__]; + null == b && (b = function() { + return b.method.apply(b.scope, arguments) + }, + b.scope = a, b.method = c, a.hx__closures__[c.__id__] = b); + return b + } + n.muses = n.muses || {}; + var s = function(a, c) { + c = c.split("u").join(""); + this.r = new RegExp(a, c) + }; + s.__name__ = !0; + s.prototype = { + r: null, + match: function(a) { + this.r.global && (this.r.lastIndex = 0); + this.r.m = this.r.exec(a); + this.r.s = a; + return null != this.r.m + }, + matched: function(a) { + if (null != this.r.m && 0 <= a && a < this.r.m.length) return this.r.m[a]; + throw "EReg::matched"; + }, + __class__: s + }; + var m = function() {}; + m.__name__ = !0; + m.cca = function(a, c) { + var b = a.charCodeAt(c); + return b != b ? void 0 : + b + }; + m.substr = function(a, c, b) { + if (null != c && 0 != c && null != b && 0 > b) return ""; + null == b && (b = a.length); + 0 > c ? (c = a.length + c, 0 > c && (c = 0)) : 0 > b && (b = a.length + b - c); + return a.substr(c, b) + }; + m.indexOf = function(a, c, b) { + var e = a.length; + 0 > b && (b += e, 0 > b && (b = 0)); + for (; b < e;) { + if (a[b] === c) return b; + b++ + } + return -1 + }; + m.remove = function(a, c) { + var b = m.indexOf(a, c, 0); + if (-1 == b) return !1; + a.splice(b, 1); + return !0 + }; + m.iter = function(a) { + return { + cur: 0, + arr: a, + hasNext: function() { + return this.cur < this.arr.length + }, + next: function() { + return this.arr[this.cur++] + } + } + }; + var x = + function() {}; + x.__name__ = !0; + x.exists = function(a, c) { + for (var b = B(a)(); b.hasNext();) { + var e = b.next(); + if (c(e)) return !0 + } + return !1 + }; + var A = function() { + this.length = 0 + }; + A.__name__ = !0; + A.prototype = { + h: null, + length: null, + iterator: function() { + return { + h: this.h, + hasNext: function() { + return null != this.h + }, + next: function() { + if (null == this.h) return null; + var a = this.h[0]; + this.h = this.h[1]; + return a + } + } + }, + __class__: A + }; + var g = n.MRP = function() {}; + g.__name__ = !0; + g.setObject = function() { + eval("MRP.instance = document." + g.objectId + ";"); + null == g.instance && + (g.instance = document.getElementById(g.objectId)) + }; + g.setElementId = function(a) { + g.elementId = a + }; + g.setObjectId = function(a) { + g.objectId = a; + g.setObject() + }; + g.play = function() { + g.instance.playSound() + }; + g.stop = function() { + g.instance.stopSound() + }; + g.setVolume = function(a) { + g.instance.setVolume(a / 100) + }; + g.showInfo = function(a) { + g.instance.showInfo(a) + }; + g.setTitle = function(a) { + g.instance.setTitle(a) + }; + g.setUrl = function(a) { + g.instance.setUrl(a) + }; + g.setFallbackUrl = function(a) { + g.instance.setFallbackUrl(a) + }; + g.setCallbackFunction = + function(a) { + musesCallback = a + }; + g.callbackExists = function() { + var a = "error", + a = typeof musesCallback; + return "undefined" != a && "error" != a + }; + g.getScriptBaseHREF = function() { + return ("https:" == window.document.location.protocol ? "https://" : "http://") + "hosted.muses.org" + }; + g.getSkin = function(a, c) { + return -1 != a.indexOf("/") || c && ("original" == a || "tiny" == a) ? a : g.getScriptBaseHREF() + "/muses-" + a + ".xml" + }; + g.insert = function(a) { + null == a.elementId && null != g.elementId && (a.elementId = g.elementId); + FlashDetect.versionAtLeast(10, 1) ? g.flashInsert(a) : + g.jsInsert(a) + }; + g.jsInsert = function(a) { + a.autoplay = !1; + g.playerCounter++; + var c = "MusesRadioPlayer-HTML5-player-" + g.playerCounter, + b = '<div id="' + c + '" style="width:' + a.width + "px;height:" + a.height + 'px"></div>'; + null == a.elementId ? window.document.write(b) : window.document.getElementById(a.elementId).innerHTML = b; + a.elementId = c; + a.skin = g.getSkin(a.skin, !1); + new d.Muses(a) + }; + g.flashInsert = function(a) { + null == a.wmode && (a.wmode = "window"); + null == a.id && (a.id = g.objectId); + var c = "url=" + a.url, + c = c + ("&lang=" + (null != a.lang ? a.lang : + "auto")), + c = c + ("&codec=" + a.codec), + c = c + "&tracking=true" + ("&volume=" + (null != a.volume ? a.volume : 100)); + null != a.introurl && (c += "&introurl=" + a.introurl); + null != a.autoplay && (c += "&autoplay=" + (a.autoplay ? "true" : "false")); + null != a.jsevents && (c += "&jsevents=" + (a.jsevents ? "true" : "false")); + null != a.buffering && (c += "&buffering=" + a.buffering); + null != a.metadataProxy && (c += "&metadataproxy=" + a.metadataProxy); + null != a.reconnectTime && (c += "&reconnecttime=" + a.reconnectTime); + null != a.fallbackUrl && (c += "&fallback=" + a.fallbackUrl); + var c = + c + ("&skin=" + g.getSkin(a.skin, !0)), + c = c + ("&title=" + a.title), + c = c + ("&welcome=" + a.welcome), + b = g.getScriptBaseHREF() + "/muses-hosted.swf", + e = 'width="' + a.width + '" height="' + a.height + '" '; + null != a.bgcolor && (e += 'bgcolor="' + a.bgcolor + '" '); + var f = '<object id="' + a.id + '" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" ' + e + ">", + f = f + ('<param name="movie" value="' + b + '" />') + ('<param name="flashvars" value="' + c + '" />'), + f = f + ('<param name="wmode" value="' + a.wmode + '" />'), + f = f + '<param name="allowScriptAccess" value="always" />', + f = f + '<param name="scale" value="noscale" />'; + null != a.bgcolor && (f += '<param name="bgcolor" value="' + a.bgcolor + '" />'); + f += '<embed name="' + a.id + '" src="' + b + '" flashvars="' + c + '" scale="noscale" wmode="' + a.wmode + '" ' + e + ' allowScriptAccess="always" type="application/x-shockwave-flash" />'; + f += "</object>"; + null != a.callbackFunction ? g.setCallbackFunction(a.callbackFunction) : 1 != a.jsevents || g.callbackExists() || g.setCallbackFunction(function(a, c) {}); + null == a.elementId ? window.document.write(f) : window.document.getElementById(a.elementId).innerHTML = + f; + g.setObject() + }; + g.main = function() { + g.getScriptBaseHREF() + }; + var z = function() {}; + z.__name__ = !0; + z.prototype = { + exists: null, + remove: null, + iterator: null, + __class__: z + }; + Math.__name__ = !0; + var v = function() {}; + v.__name__ = !0; + v.field = function(a, c) { + try { + return a[c] + } catch (b) { + return null + } + }; + v.setField = function(a, c, b) { + a[c] = b + }; + v.isFunction = function(a) { + return "function" == typeof a && !(a.__name__ || a.__ename__) + }; + var y = function() {}; + y.__name__ = !0; + y.string = function(a) { + return u.Boot.__string_rec(a, "") + }; + y.parseInt = function(a) { + var c = parseInt(a, + 10); + 0 != c || 120 != m.cca(a, 1) && 88 != m.cca(a, 1) || (c = parseInt(a)); + return isNaN(c) ? null : c + }; + var C = function() { + this.b = "" + }; + C.__name__ = !0; + C.prototype = { + b: null, + add: function(a) { + this.b += y.string(a) + }, + addSub: function(a, c, b) { + this.b = null == b ? this.b + m.substr(a, c, null) : this.b + m.substr(a, c, b) + }, + __class__: C + }; + var t = function() {}; + t.__name__ = !0; + t.urlEncode = function(a) { + return encodeURIComponent(a) + }; + t.isSpace = function(a, c) { + var b = m.cca(a, c); + return 8 < b && 14 > b || 32 == b + }; + t.ltrim = function(a) { + for (var c = a.length, b = 0; b < c && t.isSpace(a, b);) b++; + return 0 < b ? m.substr(a, b, c - b) : a + }; + t.rtrim = function(a) { + for (var c = a.length, b = 0; b < c && t.isSpace(a, c - b - 1);) b++; + return 0 < b ? m.substr(a, 0, c - b) : a + }; + t.trim = function(a) { + return t.ltrim(t.rtrim(a)) + }; + t.replace = function(a, c, b) { + return a.split(c).join(b) + }; + t.fastCodeAt = function(a, c) { + return a.charCodeAt(c) + }; + var D = function() {}; + D.__name__ = !0; + D.getInstanceFields = function(a) { + var c = [], + b; + for (b in a.prototype) c.push(b); + m.remove(c, "__class__"); + m.remove(c, "__properties__"); + return c + }; + var h = function() {}; + h.__name__ = !0; + h.parse = function(a) { + return q.xml.Parser.parse(a) + }; + h.createElement = function(a) { + var c = new h; + c.nodeType = h.Element; + c._children = []; + c._attributes = new q.ds.StringMap; + c.set_nodeName(a); + return c + }; + h.createPCData = function(a) { + var c = new h; + c.nodeType = h.PCData; + c.set_nodeValue(a); + return c + }; + h.createCData = function(a) { + var c = new h; + c.nodeType = h.CData; + c.set_nodeValue(a); + return c + }; + h.createComment = function(a) { + var c = new h; + c.nodeType = h.Comment; + c.set_nodeValue(a); + return c + }; + h.createDocType = function(a) { + var c = new h; + c.nodeType = h.DocType; + c.set_nodeValue(a); + return c + }; + h.createProcessingInstruction = + function(a) { + var c = new h; + c.nodeType = h.ProcessingInstruction; + c.set_nodeValue(a); + return c + }; + h.createDocument = function() { + var a = new h; + a.nodeType = h.Document; + a._children = []; + return a + }; + h.prototype = { + nodeType: null, + _nodeName: null, + _nodeValue: null, + _attributes: null, + _children: null, + _parent: null, + get_nodeName: function() { + if (this.nodeType != h.Element) throw "bad nodeType"; + return this._nodeName + }, + set_nodeName: function(a) { + if (this.nodeType != h.Element) throw "bad nodeType"; + return this._nodeName = a + }, + set_nodeValue: function(a) { + if (this.nodeType == + h.Element || this.nodeType == h.Document) throw "bad nodeType"; + return this._nodeValue = a + }, + get: function(a) { + if (this.nodeType != h.Element) throw "bad nodeType"; + return this._attributes.get(a) + }, + set: function(a, c) { + if (this.nodeType != h.Element) throw "bad nodeType"; + this._attributes.set(a, c) + }, + exists: function(a) { + if (this.nodeType != h.Element) throw "bad nodeType"; + return this._attributes.exists(a) + }, + attributes: function() { + if (this.nodeType != h.Element) throw "bad nodeType"; + return this._attributes.keys() + }, + elements: function() { + if (null == + this._children) throw "bad nodetype"; + return { + cur: 0, + x: this._children, + hasNext: function() { + for (var a = this.cur, c = this.x.length; a < c && this.x[a].nodeType != h.Element;) a += 1; + this.cur = a; + return a < c + }, + next: function() { + for (var a = this.cur, c = this.x.length; a < c;) { + var b = this.x[a], + a = a + 1; + if (b.nodeType == h.Element) return this.cur = a, b + } + return null + } + } + }, + addChild: function(a) { + if (null == this._children) throw "bad nodetype"; + null != a._parent && m.remove(a._parent._children, a); + a._parent = this; + this._children.push(a) + }, + __class__: h + }; + var b = { + Campaign: function(a) { + this.responseCount = + 0; + "direct" != a && "organic" != a && "referral" != a && b.Tracker._raiseError("Campaign type has to be one of the Campaign::TYPE_* constant values.", "Campaign.new"); + this.type = a; + switch (a) { + case "direct": + this.source = this.name = "(direct)"; + this.medium = "(none)"; + break; + case "referral": + this.name = "(referral)"; + this.medium = "referral"; + break; + case "organic": + this.name = "(organic)", this.medium = "organic" + } + this.creationTime = new b.DateTime + } + }; + b.Campaign.__name__ = !0; + b.Campaign.createFromReferrer = function(a) { + var c = new b.Campaign("referral"); + a = new b.URLParser(a); + c.source = a.host; + c.content = a.path; + return c + }; + b.Campaign.prototype = { + type: null, + creationTime: null, + responseCount: null, + id: null, + source: null, + gClickId: null, + dClickId: null, + name: null, + medium: null, + term: null, + content: null, + validate: function() { + null == this.source && b.Tracker._raiseError('Campaigns need to have at least the "source" attribute defined.', "Campaign.validate") + }, + setType: function(a) { + this.type = a + }, + getType: function() { + return this.type + }, + setCreationTime: function(a) { + this.creationTime = a + }, + getCreationTime: function() { + return this.creationTime + }, + setResponseCount: function(a) { + this.responseCount = a + }, + getResponseCount: function() { + return this.responseCount + }, + increaseResponseCount: function(a) { + null == a && (a = 1); + this.responseCount += a + }, + setId: function(a) { + this.id = a + }, + getId: function() { + return this.id + }, + setSource: function(a) { + this.source = a + }, + getSource: function() { + return this.source + }, + setGClickId: function(a) { + this.gClickId = a + }, + getGClickId: function() { + return this.gClickId + }, + setDClickId: function(a) { + this.dClickId = a + }, + getDClickId: function() { + return this.dClickId + }, + setName: function(a) { + this.name = + a + }, + getName: function() { + return this.name + }, + setMedium: function(a) { + this.medium = a + }, + getMedium: function() { + return this.medium + }, + setTerm: function(a) { + this.term = a + }, + getTerm: function() { + return this.term + }, + setContent: function(a) { + this.content = a + }, + getContent: function() { + return this.content + }, + __class__: b.Campaign + }; + b.Config = function(a) { + null == a && (a = !1); + this.sitespeedSampleRate = 1; + this.endPointPath = "/__utm.gif"; + this.endPointHost = "www.google-analytics.com"; + this.urlScheme = "http"; + this.requestTimeout = 1; + this.sendOnShutdown = this.fireAndForget = !1; + this.errorSeverity = 2; + this.setUrlScheme("http" + (a ? "s" : "")) + }; + b.Config.__name__ = !0; + b.Config.prototype = { + errorSeverity: null, + sendOnShutdown: null, + fireAndForget: null, + loggingCallback: null, + requestTimeout: null, + urlScheme: null, + endPointHost: null, + endPointPath: null, + sitespeedSampleRate: null, + getErrorSeverity: function() { + return this.errorSeverity + }, + setErrorSeverity: function(a) { + this.errorSeverity = a + }, + getSendOnShutdown: function() { + return this.sendOnShutdown + }, + setSendOnShutdown: function(a) { + this.sendOnShutdown = a + }, + getFireAndForget: function() { + return this.fireAndForget + }, + setFireAndForget: function(a) { + this.fireAndForget = a + }, + getLoggingCallback: function() { + return this.loggingCallback + }, + setLoggingCallback: function(a) { + this.loggingCallback = a + }, + getRequestTimeout: function() { + return this.requestTimeout + }, + setRequestTimeout: function(a) { + this.requestTimeout = a + }, + getUrlScheme: function() { + return this.urlScheme + }, + setUrlScheme: function(a) { + return this.urlScheme = a + }, + getEndPointHost: function() { + return this.endPointHost + }, + setEndPointHost: function(a) { + this.endPointHost = a + }, + getEndPointPath: function() { + return this.endPointPath + }, + setEndPointPath: function(a) { + this.endPointPath = a + }, + getSitespeedSampleRate: function() { + return this.sitespeedSampleRate + }, + setSitespeedSampleRate: function(a) { + 0 > a || 100 < a ? b.Tracker._raiseError("For consistency with ga.js, sample rates must be specified as a number between 0 and 100.", "config.setSitespeedSampleRate") : this.sitespeedSampleRate = a + }, + __class__: b.Config + }; + b.CustomVariable = function(a, c, b, e) { + null == e && (e = 0); + null == a && (a = 0); + this.scope = 3; + 0 != a && this.setIndex(a); + null != c && this.setName(c); + null != b && this.setValue(b); + 0 != e && this.setScope(e) + }; + b.CustomVariable.__name__ = !0; + b.CustomVariable.prototype = { + index: null, + name: null, + value: null, + scope: null, + validate: function() { + 128 < (this.name + y.string(this.value)).length && b.Tracker._raiseError("Custom Variable combined name and value length must not be larger than 128 bytes.", "CustomVariable.validate") + }, + getIndex: function() { + return this.index + }, + setIndex: function(a) { + (1 > a || 5 < a) && b.Tracker._raiseError("Custom Variable index has to be between 1 and 5.", "CustomVariable.setIndex"); + this.index = + a + }, + getName: function() { + return this.name + }, + setName: function(a) { + this.name = a + }, + getValue: function() { + return this.value + }, + setValue: function(a) { + this.value = a + }, + getScope: function() { + return this.scope + }, + setScope: function(a) { + 3 != a && 2 != a && 1 != a && b.Tracker._raiseError("Custom Variable scope has to be one of the CustomVariable::SCOPE_* constant values.", "CustomVariable.setScope"); + this.scope = a + }, + __class__: b.CustomVariable + }; + b.DateTime = function(a) { + this.date = null == a ? Math.round((new Date).getTime()) + "" : a + }; + b.DateTime.__name__ = !0; + b.DateTime.prototype = { + date: null, + toString: function() { + return this.date + }, + __class__: b.DateTime + }; + b.Event = function(a, c, b, e, f) { + null == f && (f = !1); + null == e && (e = 0); + this.noninteraction = !1; + null != a && this.setCategory(a); + null != c && this.setAction(c); + null != b && this.setLabel(b); + this.setValue(e); + this.setNoninteraction(f) + }; + b.Event.__name__ = !0; + b.Event.prototype = { + category: null, + action: null, + label: null, + value: null, + noninteraction: null, + validate: function() { + null != this.category && null != this.action || b.Tracker._raiseError("Events need at least to have a category and action defined.", + "Event.validate") + }, + getCategory: function() { + return this.category + }, + setCategory: function(a) { + this.category = a + }, + getAction: function() { + return this.action + }, + setAction: function(a) { + this.action = a + }, + getLabel: function() { + return this.label + }, + setLabel: function(a) { + this.label = a + }, + getValue: function() { + return this.value + }, + setValue: function(a) { + this.value = a + }, + getNoninteraction: function() { + return this.noninteraction + }, + setNoninteraction: function(a) { + this.noninteraction = a + }, + __class__: b.Event + }; + b.Item = function() { + this.quantity = 1 + }; + b.Item.__name__ = !0; + b.Item.prototype = { + orderId: null, + sku: null, + name: null, + variation: null, + price: null, + quantity: null, + validate: function() { + null == this.sku && b.Tracker._raiseError("Items need to have a sku/product code defined.", "Item.validate") + }, + getOrderId: function() { + return this.orderId + }, + setOrderId: function(a) { + this.orderId = a + }, + getSku: function() { + return this.sku + }, + setSku: function(a) { + this.sku = a + }, + getName: function() { + return this.name + }, + setName: function(a) { + this.name = a + }, + getVariation: function() { + return this.variation + }, + setVariation: function(a) { + this.variation = + a + }, + getPrice: function() { + return this.price + }, + setPrice: function(a) { + this.price = a + }, + getQuantity: function() { + return this.quantity + }, + setQuantity: function(a) { + this.quantity = a + }, + __class__: b.Item + }; + b.Page = function(a) { + this.setPath(a) + }; + b.Page.__name__ = !0; + b.Page.prototype = { + path: null, + title: null, + charset: null, + referrer: null, + loadTime: null, + setPath: function(a) { + null != a && "/" != a.charAt(0) && b.Tracker._raiseError('The page path should always start with a slash ("/").', "Page.setPath"); + this.path = a + }, + getPath: function() { + return this.path + }, + setTitle: function(a) { + this.title = a + }, + getTitle: function() { + return this.title + }, + setCharset: function(a) { + this.charset = a + }, + getCharset: function() { + return this.charset + }, + setReferrer: function(a) { + this.referrer = a + }, + getReferrer: function() { + return this.referrer + }, + setLoadTime: function(a) { + this.loadTime = a + }, + getLoadTime: function() { + return this.loadTime + }, + __class__: b.Page + }; + b.Session = function() { + this.setSessionId(this.generateSessionId()); + this.setTrackCount(0); + this.setStartTime(new b.DateTime) + }; + b.Session.__name__ = !0; + b.Session.prototype = { + sessionId: null, + trackCount: null, + startTime: null, + fromUtmb: function(a) { + a = a.split("."); + if (4 != a.length) return b.Tracker._raiseError('The given "__utmb" cookie value is invalid.', "Session.fromUtmb"), this; + this.setTrackCount(b.internals.Util.parseInt(a[1], 0)); + this.setStartTime(new b.DateTime(a[3])); + return this + }, + generateSessionId: function() { + return b.internals.Util.generate32bitRandom() + }, + getSessionId: function() { + return this.sessionId + }, + setSessionId: function(a) { + this.sessionId = a + }, + getTrackCount: function() { + return this.trackCount + }, + setTrackCount: function(a) { + this.trackCount = a + }, + increaseTrackCount: function(a) { + null == a && (a = 1); + this.trackCount += a + }, + getStartTime: function() { + return this.startTime + }, + setStartTime: function(a) { + this.startTime = a + }, + __class__: b.Session + }; + b.SocialInteraction = function(a, c, b) { + null != a && this.setNetwork(a); + null != c && this.setAction(c); + null != b && this.setTarget(b) + }; + b.SocialInteraction.__name__ = !0; + b.SocialInteraction.prototype = { + network: null, + action: null, + target: null, + validate: function() { + null != this.network && null != this.action || b.Tracker._raiseError('Social interactions need to have at least the "network" and "action" attributes defined.', + "SocialInteraction.validate") + }, + setNetwork: function(a) { + this.network = a + }, + getNetwork: function() { + return this.network + }, + setAction: function(a) { + this.action = a + }, + getAction: function() { + return this.action + }, + setTarget: function(a) { + this.target = a + }, + getTarget: function() { + return this.target + }, + __class__: b.SocialInteraction + }; + b.Stats = function() {}; + b.Stats.__name__ = !0; + b.Stats.init = function(a, c) { + null == b.Stats.accountId && (b.Stats.accountId = a, b.Stats.domainName = c, b.Stats.tracker = new b.Tracker(a, c), b.Stats.cache = new q.ds.StringMap, + b.Stats.session = new b.Session, b.Stats.loadVisitor()) + }; + b.Stats.trackPageview = function(a, c) { + var r = "page:" + a; + if (!b.Stats.cache.exists(r)) { + var e = new b.Page(a); + null != c && e.setTitle(c); + e = new b._Stats.GATrackObject(e, null); + b.Stats.cache.set(r, e) + } + b.Stats.track(r) + }; + b.Stats.trackEvent = function(a, c, r, e) { + null == e && (e = 0); + var f = "event:" + a + "/" + c + "/" + r + ":" + e; + b.Stats.cache.exists(f) || (a = new b._Stats.GATrackObject(null, new b.Event(a, c, r, e)), b.Stats.cache.set(f, a)); + b.Stats.track(f) + }; + b.Stats.track = function(a) { + b.Stats.cache.get(a).track(b.Stats.tracker, + b.Stats.visitor, b.Stats.session); + b.Stats.persistVisitor() + }; + b.Stats.loadVisitor = function() { + b.Stats.visitor = new b.Visitor; + b.Stats.visitor.setUserAgent("-not-set- [haxe]"); + b.Stats.visitor.setScreenResolution("1024x768"); + b.Stats.visitor.setLocale("en_US"); + b.Stats.visitor.getUniqueId(); + b.Stats.visitor.addSession(b.Stats.session); + b.Stats.persistVisitor() + }; + b.Stats.persistVisitor = function() {}; + b._Stats = {}; + b._Stats.GATrackObject = function(a, c) { + this.page = a; + this.event = c + }; + b._Stats.GATrackObject.__name__ = !0; + b._Stats.GATrackObject.prototype = { + event: null, + page: null, + track: function(a, c, b) { + null != this.page && a.trackPageview(this.page, b, c); + null != this.event && a.trackEvent(this.event, b, c) + }, + __class__: b._Stats.GATrackObject + }; + b.Tracker = function(a, c, r) { + this.allowHash = !0; + this.customVariables = []; + b.Tracker.setConfig(null != r ? r : new b.Config); + this.setAccountId(a); + this.setDomainName(c) + }; + b.Tracker.__name__ = !0; + b.Tracker.getConfig = function() { + return b.Tracker.config + }; + b.Tracker.setConfig = function(a) { + b.Tracker.config = a + }; + b.Tracker._raiseError = function(a, c) { + a = c + "(): " + + a; + switch (null != b.Tracker.config ? b.Tracker.config.getErrorSeverity() : 0) { + case 1: + console.log(a); + break; + case 2: + throw a; + } + }; + b.Tracker.prototype = { + accountId: null, + domainName: null, + allowHash: null, + customVariables: null, + campaign: null, + setAccountId: function(a) { + (new s("^(UA|MO)-[0-9]*-[0-9]*$", "")).match(a) || b.Tracker._raiseError('"' + a + '" is not a valid Google Analytics account ID.', "Tracker.setAccountId"); + this.accountId = a + }, + getAccountId: function() { + return this.accountId + }, + setDomainName: function(a) { + this.domainName = a + }, + getDomainName: function() { + return this.domainName + }, + setAllowHash: function(a) { + this.allowHash = a + }, + getAllowHash: function() { + return this.allowHash + }, + addCustomVariable: function(a) { + a.validate(); + this.customVariables[a.getIndex()] = a + }, + getCustomVariables: function() { + return this.customVariables + }, + removeCustomVariable: function(a) { + m.remove(this.customVariables, this.customVariables[a]) + }, + setCampaign: function(a) { + null != a && a.validate(); + this.campaign = a + }, + getCampaign: function() { + return this.campaign + }, + trackPageview: function(a, + c, r) { + var e = new b.internals.request.PageviewRequest(b.Tracker.config); + e.setPage(a); + e.setSession(c); + e.setVisitor(r); + e.setTracker(this); + e.send() + }, + trackEvent: function(a, c, r) { + a.validate(); + var e = new b.internals.request.EventRequest(b.Tracker.config); + e.setEvent(a); + e.setSession(c); + e.setVisitor(r); + e.setTracker(this); + e.send() + }, + trackTransaction: function(a, c, r) { + a.validate(); + var e = new b.internals.request.TransactionRequest(b.Tracker.config); + e.setTransaction(a); + e.setSession(c); + e.setVisitor(r); + e.setTracker(this); + e.send(); + for (a = a.getItems().iterator(); a.hasNext();) { + e = a.next(); + e.validate(); + var f = new b.internals.request.ItemRequest(b.Tracker.config); + f.setItem(e); + f.setSession(c); + f.setVisitor(r); + f.setTracker(this); + f.send() + } + }, + trackSocial: function(a, c, r, e) { + var f = new b.internals.request.SocialInteractionRequest(b.Tracker.config); + f.setSocialInteraction(a); + f.setPage(c); + f.setSession(r); + f.setVisitor(e); + f.setTracker(this); + f.send() + }, + __class__: b.Tracker + }; + b.Transaction = function() { + this.items = new q.ds.StringMap + }; + b.Transaction.__name__ = !0; + b.Transaction.prototype = { + orderId: null, + affiliation: null, + total: null, + tax: null, + shipping: null, + city: null, + region: null, + country: null, + items: null, + validate: function() { + null == this.items && b.Tracker._raiseError("Transactions need to consist of at least one item.", "Transaction.validate") + }, + addItem: function(a) { + a.setOrderId(this.orderId); + var c = a.getSku(); + this.items.set(c, a) + }, + getItems: function() { + return this.items + }, + getOrderId: function() { + return this.orderId + }, + setOrderId: function(a) { + this.orderId = a; + for (var c = this.items.iterator(); c.hasNext();) c.next().setOrderId(a) + }, + getAffiliation: function() { + return this.affiliation + }, + setAffiliation: function(a) { + this.affiliation = a + }, + getTotal: function() { + return this.total + }, + setTotal: function(a) { + this.total = a + }, + getTax: function() { + return this.tax + }, + setTax: function(a) { + this.tax = a + }, + getShipping: function() { + return this.shipping + }, + setShipping: function(a) { + this.shipping = a + }, + getCity: function() { + return this.city + }, + setCity: function(a) { + this.city = a + }, + getRegion: function() { + return this.region + }, + setRegion: function(a) { + this.region = a + }, + getCountry: function() { + return this.country + }, + setCountry: function(a) { + this.country = a + }, + __class__: b.Transaction + }; + b.URLParser = function(a) { + this.url = a; + var c = new s("^(?:(?![^:@]+:[^:@/]*@)([^:/?#.]+):)?(?://)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:/?#]*)(?::(\\d*))?)(((/(?:[^?#](?![^?#/]*\\.[^?#/.]+(?:[?#]|$)))*/?)?([^?#/]*))(?:\\?([^#]*))?(?:#(.*))?)", ""); + c.match(a); + a = 0; + for (var r = b.URLParser.parts.length; a < r;) { + var e = a++; + v.setField(this, b.URLParser.parts[e], c.matched(e)) + } + }; + b.URLParser.__name__ = !0; + b.URLParser.parse = function(a) { + return new b.URLParser(a) + }; + b.URLParser.prototype = { + url: null, + source: null, + protocol: null, + authority: null, + userInfo: null, + user: null, + password: null, + host: null, + port: null, + relative: null, + path: null, + directory: null, + file: null, + query: null, + anchor: null, + toString: function() { + for (var a = "For Url -> " + this.url + "\n", c = 0, r = b.URLParser.parts.length; c < r;) var e = c++, + a = a + (b.URLParser.parts[e] + ": " + y.string(v.field(this, b.URLParser.parts[e])) + (e == b.URLParser.parts.length - 1 ? "" : "\n")); + return a + }, + __class__: b.URLParser + }; + b.Visitor = function() { + var a = new b.DateTime; + this.uniqueId = + 0; + this.setFirstVisitTime(a); + this.setPreviousVisitTime(a); + this.setCurrentVisitTime(a); + this.setVisitCount(1) + }; + b.Visitor.__name__ = !0; + b.Visitor.prototype = { + uniqueId: null, + firstVisitTime: null, + previousVisitTime: null, + currentVisitTime: null, + visitCount: null, + ipAddress: null, + userAgent: null, + locale: null, + flashVersion: null, + javaEnabled: null, + screenColorDepth: null, + screenResolution: null, + fromUtma: function(a) { + a = a.split("."); + if (6 != a.length) return b.Tracker._raiseError('The given "__utma" cookie value is invalid.', "Visitor.fromUtma"), + this; + this.setUniqueId(b.internals.Util.parseInt(a[1], 0)); + this.setFirstVisitTime(new b.DateTime(a[2])); + this.setPreviousVisitTime(new b.DateTime(a[3])); + this.setCurrentVisitTime(new b.DateTime(a[4])); + this.setVisitCount(b.internals.Util.parseInt(a[5], 0)); + return this + }, + generateHash: function() { + return b.internals.Util.generateHash(this.userAgent + this.screenResolution + this.screenColorDepth) + }, + generateUniqueId: function() { + return (b.internals.Util.generate32bitRandom() ^ this.generateHash()) & 2147483647 + }, + setUniqueId: function(a) { + (0 > + a || 2147483647 < a) && b.Tracker._raiseError("Visitor unique ID has to be a 32-bit integer between 0 and 2147483647.", "Visitor.setUniqueId"); + this.uniqueId = a + }, + getUniqueId: function() { + 0 == this.uniqueId && (this.uniqueId = this.generateUniqueId()); + return this.uniqueId + }, + addSession: function(a) { + a = a.getStartTime(); + a != this.currentVisitTime && (this.previousVisitTime = this.currentVisitTime, this.currentVisitTime = a, ++this.visitCount) + }, + setFirstVisitTime: function(a) { + this.firstVisitTime = a + }, + getFirstVisitTime: function() { + return this.firstVisitTime + }, + setPreviousVisitTime: function(a) { + this.previousVisitTime = a + }, + getPreviousVisitTime: function() { + return this.previousVisitTime + }, + setCurrentVisitTime: function(a) { + this.currentVisitTime = a + }, + getCurrentVisitTime: function() { + return this.currentVisitTime + }, + setVisitCount: function(a) { + this.visitCount = a + }, + getVisitCount: function() { + return this.visitCount + }, + setIpAddress: function(a) { + this.ipAddress = a + }, + getIpAddress: function() { + return this.ipAddress + }, + setUserAgent: function(a) { + this.userAgent = a + }, + getUserAgent: function() { + return this.userAgent + }, + setLocale: function(a) { + this.locale = a + }, + getLocale: function() { + return this.locale + }, + setFlashVersion: function(a) { + this.flashVersion = a + }, + getFlashVersion: function() { + return this.flashVersion + }, + setJavaEnabled: function(a) { + this.javaEnabled = a + }, + getJavaEnabled: function() { + return this.javaEnabled + }, + setScreenColorDepth: function(a) { + this.screenColorDepth = a + }, + getScreenColorDepth: function() { + return this.screenColorDepth + }, + setScreenResolution: function(a) { + this.screenResolution = a + }, + getScreenResolution: function() { + return this.screenResolution + }, + __class__: b.Visitor + }; + b.internals = {}; + b.internals.ParameterHolder = function() { + this.utmwv = "5.2.5"; + this.utmr = this.utmcs = this.utmfl = this.utmje = "0" + }; + b.internals.ParameterHolder.__name__ = !0; + b.internals.ParameterHolder.prototype = { + utmwv: null, + utmac: null, + utmhn: null, + utmvid: null, + utmt: null, + utms: null, + utmn: null, + utmcc: null, + utme: null, + utmni: null, + utmu: null, + utmp: null, + utmdt: null, + utmcs: null, + utmr: null, + utmip: null, + utmul: null, + utmfl: null, + utmje: null, + utmsc: null, + utmsr: null, + __utma: null, + utmhid: null, + __utmb: null, + __utmc: null, + utmipc: null, + utmipn: null, + utmipr: null, + utmiqt: null, + utmiva: null, + utmtid: null, + utmtst: null, + utmtto: null, + utmttx: null, + utmtsp: null, + utmtci: null, + utmtrg: null, + utmtco: null, + utmcn: null, + utmcr: null, + utmcid: null, + utmcsr: null, + utmgclid: null, + utmdclid: null, + utmccn: null, + utmcmd: null, + utmctr: null, + utmcct: null, + utmcvr: null, + __utmz: null, + utmsn: null, + utmsa: null, + utmsid: null, + __utmx: null, + __utmv: null, + toHashTable: function() { + for (var a = new q.ds.StringMap, c = 0, r = D.getInstanceFields(b.internals.ParameterHolder); c < r.length;) { + var e = r[c]; + ++c; + if ("_" != e.charAt(0) && + !v.isFunction(v.field(this, e))) { + var f = v.field(this, e); + a.set(e, f) + } + } + return a + }, + toQueryString: function() { + for (var a = "", c = 0, r = D.getInstanceFields(b.internals.ParameterHolder); c < r.length;) { + var e = r[c]; + ++c; + "_" == e.charAt(0) || v.isFunction(v.field(this, e)) || null == v.field(this, e) || "null" == v.field(this, e) || (a += e + "=" + t.replace(y.string(v.field(this, e)) + "", "&", "%26") + "&") + } + return a + }, + __class__: b.internals.ParameterHolder + }; + b.internals.Util = function() {}; + b.internals.Util.__name__ = !0; + b.internals.Util.encodeUriComponent = + function(a) { + return b.internals.Util.convertToUriComponentEncoding(t.urlEncode(a)) + }; + b.internals.Util.stringReplaceArray = function(a, c, b) { + for (var e = 0, f = c.length; e < f;) { + var d = e++; + null != c[d] && (a = t.replace(a + " ", c[d], b[d])) + } + return t.trim(a) + }; + b.internals.Util.parseInt = function(a, c) { + return null == a ? c : y.parseInt(a) + }; + b.internals.Util.convertToUriComponentEncoding = function(a) { + return b.internals.Util.stringReplaceArray(a, "!*'() +".split(""), "%21 %2A %27 %28 %29 %20 %20".split(" ")) + }; + b.internals.Util.generate32bitRandom = + function() { + return Math.round(2147483647 * Math.random()) + }; + b.internals.Util.generateHash = function(a) { + var c = 1, + b; + if (null != a && "" != a) + for (var c = 0, e = a.length - 1; 0 <= e;) b = m.cca(a, e), c = (c << 6 & 268435455) + b + (b << 14), b = c & 266338304, 0 != b && (c ^= b >> 21), e--; + return c + }; + b.internals.X10 = function() { + this.projectData = new q.ds.StringMap; + this.KEY = "k"; + this.VALUE = "v"; + this.SET = ["k", "v"]; + this.DELIM_BEGIN = "("; + this.DELIM_END = ")"; + this.DELIM_SET = "*"; + this.DELIM_NUM_VALUE = "!"; + this.MINIMUM = 1; + this.ESCAPE_CHAR_MAP = new q.ds.StringMap; + this.ESCAPE_CHAR_MAP.set("'", + "'0"); + this.ESCAPE_CHAR_MAP.set(")", "'1"); + this.ESCAPE_CHAR_MAP.set("*", "'2"); + this.ESCAPE_CHAR_MAP.set("!", "'3") + }; + b.internals.X10.__name__ = !0; + b.internals.X10.prototype = { + projectData: null, + KEY: null, + VALUE: null, + SET: null, + DELIM_BEGIN: null, + DELIM_END: null, + DELIM_SET: null, + DELIM_NUM_VALUE: null, + ESCAPE_CHAR_MAP: null, + MINIMUM: null, + hasProject: function(a) { + return this.projectData.exists(a) + }, + setKey: function(a, c, b) { + this.setInternal(a, this.KEY, c, b) + }, + getKey: function(a, c) { + return this.getInternal(a, this.KEY, c) + }, + clearKey: function(a) { + this.clearInternal(a, + this.KEY) + }, + setValue: function(a, c, b) { + this.setInternal(a, this.VALUE, c, b) + }, + getValue: function(a, c) { + return this.getInternal(a, this.VALUE, c) + }, + clearValue: function(a) { + this.clearInternal(a, this.VALUE) + }, + setInternal: function(a, c, b, e) { + if (!this.projectData.exists(a)) { + var f = new q.ds.StringMap; + this.projectData.set(a, f) + } + a = this.projectData.get(a); + a.exists(c) || a.set(c, []); + a.get(c)[b] = e + }, + getInternal: function(a, c, b) { + if (!this.projectData.exists(a)) return null; + a = this.projectData.get(a); + if (!a.exists(c)) return null; + c = a.get(c); + return null == c[b] ? null : c[b] + }, + clearInternal: function(a, c) { + var b; + if (b = this.projectData.exists(a)) b = this.projectData.get(a).exists(c); + b && this.projectData.get(a).remove(c) + }, + escapeExtensibleValue: function(a) { + for (var c = "", b = 0, e = a.length; b < e;) var f = b++, + f = a.charAt(f), + c = this.ESCAPE_CHAR_MAP.exists(f) ? c + this.ESCAPE_CHAR_MAP.get(f) : c + f; + return c + }, + SORT_NUMERIC: function(a, c) { + return a == c ? 0 : a > c ? 1 : -1 + }, + renderDataType: function(a) { + for (var c = [], b = 0, e = 0, f = a.length; e < f;) { + var d = e++, + g = a[d]; + if (null != g) { + var k = ""; + d != this.MINIMUM && + d - 1 != b && (k += d, k += this.DELIM_NUM_VALUE); + k += this.escapeExtensibleValue(g); + c.push(k) + } + b = d + } + return this.DELIM_BEGIN + c.join(this.DELIM_SET) + this.DELIM_END + }, + renderProject: function(a) { + for (var c = "", b = !1, e = 0, f = this.SET.length; e < f;) { + var d = e++; + a.exists(this.SET[d]) ? (b && (c += this.SET[d]), c += this.renderDataType(a.get(this.SET[d])), b = !1) : b = !0 + } + return c + }, + renderUrlString: function() { + for (var a = "", c = this.projectData.keys(); c.hasNext();) var b = c.next(), + a = a + (b + this.renderProject(this.projectData.get(b))); + return a + }, + __class__: b.internals.X10 + }; + b.internals.request = {}; + b.internals.request.Request = function(a) { + this.setConfig(null != a ? a : new b.Config) + }; + b.internals.request.Request.__name__ = !0; + b.internals.request.Request.onError = function(a) {}; + b.internals.request.Request.prototype = { + type: null, + config: null, + userAgent: null, + tracker: null, + visitor: null, + session: null, + getConfig: function() { + return this.config + }, + setConfig: function(a) { + this.config = a + }, + setUserAgent: function(a) { + this.userAgent = a + }, + getTracker: function() { + return this.tracker + }, + setTracker: function(a) { + this.tracker = + a + }, + getVisitor: function() { + return this.visitor + }, + setVisitor: function(a) { + this.visitor = a + }, + getSession: function() { + return this.session + }, + setSession: function(a) { + this.session = a + }, + increaseTrackCount: function() { + this.session.increaseTrackCount(); + 500 < this.session.getTrackCount() && b.Tracker._raiseError("Google Analytics does not guarantee to process more than 500 requests per session.", "Request.buildHttpRequest"); + null != this.tracker.getCampaign() && this.tracker.getCampaign().increaseResponseCount() + }, + send: function() { + if (null != + this.config.getEndPointHost()) { + var a = this.buildParameters(); + null != this.visitor && (this.setUserAgent(this.visitor.getUserAgent()), a.utmvid = this.visitor.getUniqueId()); + a = b.internals.Util.convertToUriComponentEncoding(a.toQueryString()); + a = this.config.getUrlScheme() + "://" + this.config.getEndPointHost() + this.config.getEndPointPath() + "?" + a; + this.increaseTrackCount(); + (new Image).src = a + } + }, + getType: function() { + return null + }, + buildParameters: function() { + var a = new b.internals.ParameterHolder; + a.utmac = this.tracker.getAccountId(); + a.utmhn = this.tracker.getDomainName(); + a.utmt = "" + this.getType(); + a.utmn = b.internals.Util.generate32bitRandom(); + a.utmip = this.visitor.getIpAddress(); + a.utmhid = this.session.getSessionId(); + a.utms = this.session.getTrackCount(); + a = this.buildVisitorParameters(a); + a = this.buildCustomVariablesParameter(a); + a = this.buildCampaignParameters(a); + return a = this.buildCookieParameters(a) + }, + buildVisitorParameters: function(a) { + null != this.visitor.getLocale() && (a.utmul = t.replace(this.visitor.getLocale(), "_", "-").toLowerCase()); + null != + this.visitor.getFlashVersion() && (a.utmfl = this.visitor.getFlashVersion()); + this.visitor.getJavaEnabled() ? a.utmje = "1" : a.utmje = "0"; + null != this.visitor.getScreenColorDepth() && (a.utmsc = this.visitor.getScreenColorDepth() + "-bit"); + a.utmsr = this.visitor.getScreenResolution(); + return a + }, + buildCustomVariablesParameter: function(a) { + var c = this.tracker.getCustomVariables(); + if (null == c) return a; + 5 < c.length && b.Tracker._raiseError("The sum of all custom variables cannot exceed 5 in any given request.", "Request.buildCustomVariablesParameter"); + var r = new b.internals.X10, + e, f; + r.clearKey("8"); + r.clearKey("9"); + r.clearKey("11"); + for (var d = 0; d < c.length;) { + var g = c[d]; + ++d; + e = b.internals.Util.encodeUriComponent(g.getName()); + f = b.internals.Util.encodeUriComponent(g.getValue()); + r.setKey("8", g.getIndex(), e); + r.setKey("9", g.getIndex(), f); + 3 != g.getScope() && r.setKey("11", g.getIndex(), g.getScope()) + } + c = r.renderUrlString(); + null != c && (a.utme = null == a.utme ? c : a.utme + c); + return a + }, + buildCookieParameters: function(a) { + var c = this.generateDomainHash(); + a.__utma = c + "."; + a.__utma += + this.visitor.getUniqueId() + "."; + a.__utma += this.visitor.getFirstVisitTime().toString() + "."; + a.__utma += this.visitor.getPreviousVisitTime().toString() + "."; + a.__utma += this.visitor.getCurrentVisitTime().toString() + "."; + a.__utma += this.visitor.getVisitCount(); + a.__utmb = c + "."; + a.__utmb += this.session.getTrackCount() + "."; + a.__utmb += "10."; + a.__utmb += this.session.getStartTime().toString(); + a.__utmc = c; + c = "__utma=" + a.__utma + ";"; + null != a.__utmz && (c += "+__utmz=" + a.__utmz + ";"); + null != a.__utmv && (c += "+__utmv=" + a.__utmv + ";"); + a.utmcc = + c; + return a + }, + buildCampaignParameters: function(a) { + var c = this.tracker.getCampaign(); + if (null == c) return a; + a.__utmz = this.generateDomainHash() + "."; + a.__utmz += c.getCreationTime().toString() + "."; + a.__utmz += this.visitor.getVisitCount() + "."; + a.__utmz += c.getResponseCount() + "."; + c = "utmcid=" + c.getId() + "|utmcsr=" + c.getSource() + "|utmgclid=" + c.getGClickId() + "|utmdclid=" + c.getDClickId() + "|utmccn=" + c.getName() + "|utmcmd=" + c.getMedium() + "|utmctr=" + c.getTerm() + "|utmcct=" + c.getContent(); + a.__utmz += t.replace(t.replace(c, "+", + "%20"), " ", "%20"); + return a + }, + generateDomainHash: function() { + var a = 1; + this.tracker.getAllowHash() && (a = b.internals.Util.generateHash(this.tracker.getDomainName())); + return a + }, + __class__: b.internals.request.Request + }; + b.internals.request.EventRequest = function(a) { + b.internals.request.Request.call(this, a) + }; + b.internals.request.EventRequest.__name__ = !0; + b.internals.request.EventRequest.__super__ = b.internals.request.Request; + b.internals.request.EventRequest.prototype = w(b.internals.request.Request.prototype, { + event: null, + getType: function() { + return "event" + }, + buildParameters: function() { + var a = b.internals.request.Request.prototype.buildParameters.call(this), + c = new b.internals.X10; + c.clearKey("5"); + c.clearValue("5"); + c.setKey("5", 1, this.event.getCategory()); + c.setKey("5", 2, this.event.getAction()); + null != this.event.getLabel() && c.setKey("5", 3, this.event.getLabel()); + 0 != this.event.getValue() && c.setValue("5", 1, this.event.getValue()); + c = c.renderUrlString(); + null != c && (a.utme = null == a.utme ? c : a.utme + c); + this.event.getNoninteraction() && (a.utmni = + 1); + return a + }, + getEvent: function() { + return this.event + }, + setEvent: function(a) { + this.event = a + }, + __class__: b.internals.request.EventRequest + }); + b.internals.request.ItemRequest = function(a) { + b.internals.request.Request.call(this, a) + }; + b.internals.request.ItemRequest.__name__ = !0; + b.internals.request.ItemRequest.__super__ = b.internals.request.Request; + b.internals.request.ItemRequest.prototype = w(b.internals.request.Request.prototype, { + item: null, + getType: function() { + return "item" + }, + buildParameters: function() { + var a = b.internals.request.Request.prototype.buildParameters.call(this); + a.utmtid = this.item.getOrderId(); + a.utmipc = this.item.getSku(); + a.utmipn = this.item.getName(); + a.utmiva = this.item.getVariation(); + a.utmipr = this.item.getPrice(); + a.utmiqt = this.item.getQuantity(); + return a + }, + buildVisitorParameters: function(a) { + return a + }, + buildCustomVariablesParameter: function(a) { + return a + }, + getItem: function() { + return this.item + }, + setItem: function(a) { + this.item = a + }, + __class__: b.internals.request.ItemRequest + }); + b.internals.request.PageviewRequest = function(a) { + b.internals.request.Request.call(this, a) + }; + b.internals.request.PageviewRequest.__name__ = !0; + b.internals.request.PageviewRequest.__super__ = b.internals.request.Request; + b.internals.request.PageviewRequest.prototype = w(b.internals.request.Request.prototype, { + page: null, + getType: function() { + return null + }, + buildParameters: function() { + var a = b.internals.request.Request.prototype.buildParameters.call(this); + a.utmp = this.page.getPath(); + a.utmdt = this.page.getTitle(); + null != this.page.getCharset() && (a.utmcs = this.page.getCharset()); + null != this.page.getReferrer() && (a.utmr = this.page.getReferrer()); + 0 != this.page.getLoadTime() && + a.utmn % 100 < this.config.getSitespeedSampleRate() && (a.utme = null == a.utme ? "0" : a.utme + 0); + return a + }, + getPage: function() { + return this.page + }, + setPage: function(a) { + this.page = a + }, + __class__: b.internals.request.PageviewRequest + }); + b.internals.request.SocialInteractionRequest = function(a) { + b.internals.request.PageviewRequest.call(this, a) + }; + b.internals.request.SocialInteractionRequest.__name__ = !0; + b.internals.request.SocialInteractionRequest.__super__ = b.internals.request.PageviewRequest; + b.internals.request.SocialInteractionRequest.prototype = + w(b.internals.request.PageviewRequest.prototype, { + socialInteraction: null, + getType: function() { + return "social" + }, + buildParameters: function() { + var a = b.internals.request.PageviewRequest.prototype.buildParameters.call(this); + a.utmsn = this.socialInteraction.getNetwork(); + a.utmsa = this.socialInteraction.getAction(); + a.utmsid = this.socialInteraction.getTarget(); + null == a.utmsid && (a.utmsid = this.page.getPath()); + return a + }, + getSocialInteraction: function() { + return this.socialInteraction + }, + setSocialInteraction: function(a) { + this.socialInteraction = + a + }, + __class__: b.internals.request.SocialInteractionRequest + }); + b.internals.request.TransactionRequest = function(a) { + b.internals.request.Request.call(this, a) + }; + b.internals.request.TransactionRequest.__name__ = !0; + b.internals.request.TransactionRequest.__super__ = b.internals.request.Request; + b.internals.request.TransactionRequest.prototype = w(b.internals.request.Request.prototype, { + transaction: null, + getType: function() { + return "tran" + }, + buildParameters: function() { + var a = b.internals.request.Request.prototype.buildParameters.call(this); + a.utmtid = this.transaction.getOrderId(); + a.utmtst = this.transaction.getAffiliation(); + a.utmtto = this.transaction.getTotal(); + a.utmttx = this.transaction.getTax(); + a.utmtsp = this.transaction.getShipping(); + a.utmtci = this.transaction.getCity(); + a.utmtrg = this.transaction.getRegion(); + a.utmtco = this.transaction.getCountry(); + return a + }, + buildVisitorParameters: function(a) { + return a + }, + buildCustomVariablesParameter: function(a) { + return a + }, + getTransaction: function() { + return this.transaction + }, + setTransaction: function(a) { + this.transaction = + a + }, + __class__: b.internals.request.TransactionRequest + }); + var q = { + Http: function(a) { + this.url = a; + this.headers = new A; + this.params = new A; + this.async = !0 + } + }; + q.Http.__name__ = !0; + q.Http.requestUrl = function(a) { + a = new q.Http(a); + a.async = !1; + var c = null; + a.onData = function(a) { + c = a + }; + a.onError = function(a) { + throw a; + }; + a.request(!1); + return c + }; + q.Http.prototype = { + url: null, + responseData: null, + async: null, + postData: null, + headers: null, + params: null, + req: null, + request: function(a) { + var c = this; + c.responseData = null; + var b = this.req = u.Browser.createXMLHttpRequest(), + e = function(a) { + if (4 == b.readyState) { + var e; + try { + e = b.status + } catch (f) { + e = null + } + void 0 == e && (e = null); + if (null != e) c.onStatus(e); + if (null != e && 200 <= e && 400 > e) c.req = null, c.onData(c.responseData = b.responseText); + else if (null == e) c.req = null, c.onError("Failed to connect or resolve host"); + else switch (e) { + case 12029: + c.req = null; + c.onError("Failed to connect to host"); + break; + case 12007: + c.req = null; + c.onError("Unknown host"); + break; + default: + c.req = null, c.responseData = b.responseText, c.onError("Http Error #" + b.status) + } + } + }; + this.async && + (b.onreadystatechange = e); + var f = this.postData; + if (null != f) a = !0; + else + for (var d = this.params.iterator(); d.hasNext();) var g = d.next(), + f = null == f ? "" : f + "&", + f = f + (encodeURIComponent(g.param) + "=" + encodeURIComponent(g.value)); + try { + if (a) b.open("POST", this.url, this.async); + else if (null != f) { + var k = 1 >= this.url.split("?").length; + b.open("GET", this.url + (k ? "?" : "&") + f, this.async); + f = null + } else b.open("GET", this.url, this.async) + } catch (h) { + c.req = null; + this.onError(h.toString()); + return + }!x.exists(this.headers, function(a) { + return "Content-Type" == + a.header + }) && a && null == this.postData && b.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + for (a = this.headers.iterator(); a.hasNext();) d = a.next(), b.setRequestHeader(d.header, d.value); + b.send(f); + this.async || e(null) + }, + onData: function(a) {}, + onError: function(a) {}, + onStatus: function(a) {}, + __class__: q.Http + }; + q.Timer = function(a) { + var c = this; + this.id = setInterval(function() { + c.run() + }, a) + }; + q.Timer.__name__ = !0; + q.Timer.delay = function(a, c) { + var b = new q.Timer(c); + b.run = function() { + b.stop(); + a() + }; + return b + }; + q.Timer.prototype = { + id: null, + stop: function() { + null != this.id && (clearInterval(this.id), this.id = null) + }, + run: function() {}, + __class__: q.Timer + }; + q.ds = {}; + q.ds.StringMap = function() { + this.h = {} + }; + q.ds.StringMap.__name__ = !0; + q.ds.StringMap.__interfaces__ = [z]; + q.ds.StringMap.prototype = { + h: null, + set: function(a, c) { + this.h["$" + a] = c + }, + get: function(a) { + return this.h["$" + a] + }, + exists: function(a) { + return this.h.hasOwnProperty("$" + a) + }, + remove: function(a) { + a = "$" + a; + if (!this.h.hasOwnProperty(a)) return !1; + delete this.h[a]; + return !0 + }, + keys: function() { + var a = [], + c; + for (c in this.h) this.h.hasOwnProperty(c) && a.push(c.substr(1)); + return m.iter(a) + }, + iterator: function() { + return { + ref: this.h, + it: this.keys(), + hasNext: function() { + return this.it.hasNext() + }, + next: function() { + var a = this.it.next(); + return this.ref["$" + a] + } + } + }, + __class__: q.ds.StringMap + }; + q.io = {}; + q.io.Eof = function() {}; + q.io.Eof.__name__ = !0; + q.io.Eof.prototype = { + toString: function() { + return "Eof" + }, + __class__: q.io.Eof + }; + q.xml = {}; + q.xml.Parser = function() {}; + q.xml.Parser.__name__ = !0; + q.xml.Parser.parse = function(a) { + var c = h.createDocument(); + q.xml.Parser.doParse(a, 0, c); + return c + }; + q.xml.Parser.doParse = function(a, c, b) { + null == c && (c = 0); + for (var e = null, f = 1, d = 1, g = null, k = 0, p = 0, n = 0, l = a.charCodeAt(c), s = new C; l == l;) { + switch (f) { + case 0: + switch (l) { + case 10: + case 13: + case 9: + case 32: + break; + default: + f = d; + continue + } + break; + case 1: + switch (l) { + case 60: + f = 0; + d = 2; + break; + default: + k = c; + f = 13; + continue + } + break; + case 13: + 60 == l ? (d = h.createPCData(s.b + m.substr(a, k, c - k)), s = new C, b.addChild(d), p++, f = 0, d = 2) : 38 == l && (s.addSub(a, k, c - k), f = 18, d = 13, k = c + 1); + break; + case 17: + 93 == l && 93 == a.charCodeAt(c + 1) && + 62 == a.charCodeAt(c + 2) && (f = h.createCData(m.substr(a, k, c - k)), b.addChild(f), p++, c += 2, f = 1); + break; + case 2: + switch (l) { + case 33: + if (91 == a.charCodeAt(c + 1)) { + c += 2; + if ("CDATA[" != m.substr(a, c, 6).toUpperCase()) throw "Expected <![CDATA["; + c += 5; + f = 17 + } else if (68 == a.charCodeAt(c + 1) || 100 == a.charCodeAt(c + 1)) { + if ("OCTYPE" != m.substr(a, c + 2, 6).toUpperCase()) throw "Expected <!DOCTYPE"; + c += 8; + f = 16 + } else { + if (45 != a.charCodeAt(c + 1) || 45 != a.charCodeAt(c + 2)) throw "Expected \x3c!--"; + c += 2; + f = 15 + } + k = c + 1; + break; + case 63: + f = 14; + k = c; + break; + case 47: + if (null == + b) throw "Expected node name"; + k = c + 1; + f = 0; + d = 10; + break; + default: + f = 3; + k = c; + continue + } + break; + case 3: + if (!(97 <= l && 122 >= l || 65 <= l && 90 >= l || 48 <= l && 57 >= l || 58 == l || 46 == l || 95 == l || 45 == l)) { + if (c == k) throw "Expected node name"; + e = h.createElement(m.substr(a, k, c - k)); + b.addChild(e); + f = 0; + d = 4; + continue + } + break; + case 4: + switch (l) { + case 47: + f = 11; + p++; + break; + case 62: + f = 9; + p++; + break; + default: + f = 5; + k = c; + continue + } + break; + case 5: + if (!(97 <= l && 122 >= l || 65 <= l && 90 >= l || 48 <= l && 57 >= l || 58 == l || 46 == l || 95 == l || 45 == l)) { + if (k == c) throw "Expected attribute name"; + g = m.substr(a, + k, c - k); + if (e.exists(g)) throw "Duplicate attribute"; + f = 0; + d = 6; + continue + } + break; + case 6: + switch (l) { + case 61: + f = 0; + d = 7; + break; + default: + throw "Expected ="; + } + break; + case 7: + switch (l) { + case 34: + case 39: + f = 8; + k = c; + break; + default: + throw 'Expected "'; + } + break; + case 8: + l == a.charCodeAt(k) && (d = m.substr(a, k + 1, c - k - 1), e.set(g, d), f = 0, d = 4); + break; + case 9: + k = c = q.xml.Parser.doParse(a, c, e); + f = 1; + break; + case 11: + switch (l) { + case 62: + f = 1; + break; + default: + throw "Expected >"; + } + break; + case 12: + switch (l) { + case 62: + return 0 == p && b.addChild(h.createPCData("")), c; + default: + throw "Expected >"; + } + case 10: + if (!(97 <= l && 122 >= l || 65 <= l && 90 >= l || 48 <= l && 57 >= l || 58 == l || 46 == l || 95 == l || 45 == l)) { + if (k == c) throw "Expected node name"; + if (m.substr(a, k, c - k) != b.get_nodeName()) throw "Expected </" + b.get_nodeName() + ">"; + f = 0; + d = 12; + continue + } + break; + case 15: + 45 == l && 45 == a.charCodeAt(c + 1) && 62 == a.charCodeAt(c + 2) && (b.addChild(h.createComment(m.substr(a, k, c - k))), c += 2, f = 1); + break; + case 16: + 91 == l ? n++ : 93 == l ? n-- : 62 == l && 0 == n && (b.addChild(h.createDocType(m.substr(a, k, c - k))), f = 1); + break; + case 14: + 63 == l && 62 == a.charCodeAt(c + 1) && (c++, f = m.substr(a, + k + 1, c - k - 2), b.addChild(h.createProcessingInstruction(f)), f = 1); + break; + case 18: + 59 == l && (k = m.substr(a, k, c - k), 35 == k.charCodeAt(0) ? (k = 120 == k.charCodeAt(1) ? y.parseInt("0" + m.substr(k, 1, k.length - 1)) : y.parseInt(m.substr(k, 1, k.length - 1)), s.add(String.fromCharCode(k))) : q.xml.Parser.escapes.exists(k) ? s.add(q.xml.Parser.escapes.get(k)) : s.b += y.string("&" + k + ";"), k = c + 1, f = d) + } + l = t.fastCodeAt(a, ++c) + } + 1 == f && (k = c, f = 13); + if (13 == f) return c == k && 0 != p || b.addChild(h.createPCData(s.b + m.substr(a, k, c - k))), c; + throw "Unexpected end"; + }; + var u = { + Boot: function() {} + }; + u.Boot.__name__ = !0; + u.Boot.getClass = function(a) { + return a instanceof Array && null == a.__enum__ ? Array : a.__class__ + }; + u.Boot.__string_rec = function(a, c) { + if (null == a) return "null"; + if (5 <= c.length) return "<...>"; + var b = typeof a; + "function" == b && (a.__name__ || a.__ename__) && (b = "object"); + switch (b) { + case "object": + if (a instanceof Array) { + if (a.__enum__) { + if (2 == a.length) return a[0]; + b = a[0] + "("; + c += "\t"; + for (var e = 2, d = a.length; e < d;) var g = e++, + b = 2 != g ? b + ("," + u.Boot.__string_rec(a[g], c)) : b + u.Boot.__string_rec(a[g], + c); + return b + ")" + } + b = a.length; + e = "["; + c += "\t"; + for (d = 0; d < b;) g = d++, e += (0 < g ? "," : "") + u.Boot.__string_rec(a[g], c); + return e + "]" + } + try { + e = a.toString + } catch (h) { + return "???" + } + if (null != e && e != Object.toString && (b = a.toString(), "[object Object]" != b)) return b; + b = null; + e = "{\n"; + c += "\t"; + d = null != a.hasOwnProperty; + for (b in a) d && !a.hasOwnProperty(b) || "prototype" == b || "__class__" == b || "__super__" == b || "__interfaces__" == b || "__properties__" == b || (2 != e.length && (e += ", \n"), e += c + b + " : " + u.Boot.__string_rec(a[b], c)); + c = c.substring(1); + return e + + ("\n" + c + "}"); + case "function": + return "<function>"; + case "string": + return a; + default: + return String(a) + } + }; + u.Boot.__interfLoop = function(a, c) { + if (null == a) return !1; + if (a == c) return !0; + var b = a.__interfaces__; + if (null != b) + for (var e = 0, d = b.length; e < d;) { + var g = e++, + g = b[g]; + if (g == c || u.Boot.__interfLoop(g, c)) return !0 + } + return u.Boot.__interfLoop(a.__super__, c) + }; + u.Boot.__instanceof = function(a, c) { + if (null == c) return !1; + switch (c) { + case H: + return (a | 0) === a; + case E: + return "number" == typeof a; + case F: + return "boolean" == typeof a; + case String: + return "string" == + typeof a; + case Array: + return a instanceof Array && null == a.__enum__; + case I: + return !0; + default: + if (null != a) { + if ("function" == typeof c && (a instanceof c || u.Boot.__interfLoop(u.Boot.getClass(a), c))) return !0 + } else return !1; + return c == J && null != a.__name__ || c == K && null != a.__ename__ ? !0 : a.__enum__ == c + } + }; + u.Browser = function() {}; + u.Browser.__name__ = !0; + u.Browser.createXMLHttpRequest = function() { + if ("undefined" != typeof XMLHttpRequest) return new XMLHttpRequest; + if ("undefined" != typeof ActiveXObject) return new ActiveXObject("Microsoft.XMLHTTP"); + throw "Unable to create XMLHttpRequest object."; + }; + var d = {}; + d.Muses = n.muses.Muses = function(a) { + this.src = this.name = this.lastMessage = null; + this.progress = 0; + this.lastAudioName = null; + this.playURL = ""; + this.playTimeout = this.bufferingTimeout = 0; + this.desiredStatus = "stop"; + this.audio = this.lastAudioStatus = this.lastAudioSrc = null; + this.src = a.url; + this.name = a.title; + this.audio = new Audio; + this.ui = new d.UI(this, a); + a.autoplay && (a = window.navigator.userAgent.toLowerCase(), -1 == a.indexOf("iphone") && -1 == a.indexOf("ipad") && -1 == a.indexOf("ipod") && + this.playAudio()) + }; + d.Muses.__name__ = !0; + d.Muses.initTimer = function(a) { + -1 == m.indexOf(d.Muses.instances, a, 0) && d.Muses.instances.push(a); + null == d.Muses.statusTimer && (d.Muses.statusTimer = new q.Timer(500), d.Muses.statusTimer.run = function() { + for (var a = 0, b = d.Muses.instances; a < b.length;) { + var e = b[a]; + ++a; + try { + e.checkAudioStatus() + } catch (f) { + if (u.Boot.__instanceof(f, String)) console.log("Error: " + f); + else throw f; + } + } + }) + }; + d.Muses.prototype = { + audio: null, + lastAudioStatus: null, + lastAudioSrc: null, + desiredStatus: null, + playTimeout: null, + bufferingTimeout: null, + playURL: null, + lastAudioName: null, + progress: null, + src: null, + name: null, + lastMessage: null, + ui: null, + playAudio: function() { + d.Muses.initTimer(this); + this.stopAudio(!1); + this.playURL = this.src; + this.desiredStatus = "play"; + this.playTimeout = 3600; + this.bufferingTimeout = 40; + this.lastAudioSrc = this.audio.src = this.src; + this.lastAudioName = this.name; + this.lastAudioStatus = null; + this.audio.autoplay = !0; + this.audio.play(); + this.ui.setPlaying(); + d.Tracker.track(this.src, this.name, this.ui, !0) + }, + stopAudio: function(a) { + this.desiredStatus = + "stop"; + null != this.audio && (this.audio.pause(), this.audio.src = ""); + a && (this.lastAudioStatus = 4) + }, + retryAudio: function() { + var a = this; + this.lastAudioStatus = -1; + q.Timer.delay(function() { + -1 == a.lastAudioStatus && a.playAudio() + }, 2E3) + }, + setVolume: function(a) { + this.audio.volume = a; + null != this.ui && this.ui.setVolume(a) + }, + checkAudioStatus: function() { + var a = "", + a = null; + if (null != this.audio) { + a = this.audio.networkState; + y.string(this.audio.error); + if (2 == a || 1 == a) a = 0 == this.audio.played.length ? 1 : 2; + if (null != this.audio.error || 4 == this.lastAudioStatus) a = + 3 + } + 0 == a ? (a = "Error al conectar", this.lastMessage != a && this.ui.setError()) : -1 == a ? a = "retry..." : null == a ? a = "init" : 1 == a ? (this.bufferingTimeout--, 0 == this.bufferingTimeout && this.retryAudio(), a = "Buffering... " + Math.round(this.bufferingTimeout / 2), this.lastMessage != a && this.ui.setBuffering()) : 2 == a ? (this.playTimeout--, 0 == this.playTimeout && this.retryAudio(), a = "Playing... ", this.lastMessage != a && this.ui.setPlaying()) : 4 == a || 3 == a ? "play" == this.desiredStatus ? (a = "Error de red", this.retryAudio(), this.lastMessage != a && this.ui.setError()) : + (a = "Stopped.", this.lastMessage != a && this.ui.setStopped()) : (a = "ERROR: " + a, console.log(a)); + this.lastMessage = a + }, + __class__: d.Muses + }; + d.Tracker = function() {}; + d.Tracker.__name__ = !0; + d.Tracker.track = function(a, c, g, e) { + d.Tracker.enabled && (null == d.Tracker.tracked && (d.Tracker.tracked = new q.ds.StringMap, b.Stats.init("UA-12297597-1", "hosted.musesradioplayer.com")), e && d.Tracker.tracked.get(a) || (b.Stats.trackPageview("/tracker/track.php?version=0.2 beta&url=" + a + "&player=HTML5&skin=" + g.skin, "Muses - HTML5 Tracking [Radio: " + + c + "]"), d.Tracker.tracked.set(a, !0))) + }; + d.UI = n.muses.UI = function(a, c) { + this.skinFolder = this.baseURL = this.skinDomain = ""; + this.togglePlayStopEnabled = this.lastToggleValue = !1; + this.mainDiv = this.playButton = this.stopButton = this.volumeControl = this.bg = this.statusText = this.artistText = this.songTitleText = this.statusLed = null; + this.skin = ""; + var b = this; + this.title = c.title; + this.skin = c.skin; + this.muses = a; + this.mainDiv = window.document.getElementById(c.elementId); + this.mainDiv.style.position = "relative"; + this.statusText = new d.skin.TitleText(this); + this.artistText = new d.skin.TitleText(this); + this.songTitleText = new d.skin.TitleText(this); + this.statusLed = new d.skin.StatusLed(this); + this.volumeControl = new d.skin.VolumeControl(this, this.muses); + this.volumeControl.setVolume(c.volume / 100); + this.playButton = new d.skin.Button(this, "play"); + this.stopButton = new d.skin.Button(this, "stop"); + this.loadSkin(this.skin); + this.statusLed.configured && this.mainDiv.appendChild(this.statusLed.container); + this.statusText.configured && this.mainDiv.appendChild(this.statusText.container); + this.artistText.configured && this.mainDiv.appendChild(this.artistText.container); + this.songTitleText.configured && this.mainDiv.appendChild(this.songTitleText.container); + this.volumeControl.configured && this.mainDiv.appendChild(this.volumeControl.container); + this.mainDiv.appendChild(this.playButton.container); + this.mainDiv.appendChild(this.stopButton.container); + this.stopButton.container.onclick = function(a) { + b.muses.stopAudio(!1) + }; + this.playButton.container.onclick = function(a) { + b.muses.playAudio() + }; + this.showInfo(c.welcome) + }; + d.UI.__name__ = !0; + d.UI.parseInt = function(a, c) { + return null == a ? c : y.parseInt(a) + }; + d.UI.prototype = { + skin: null, + mainDiv: null, + playButton: null, + stopButton: null, + volumeControl: null, + bg: null, + statusText: null, + artistText: null, + songTitleText: null, + statusLed: null, + togglePlayStopEnabled: null, + lastToggleValue: null, + skinFolder: null, + baseURL: null, + skinDomain: null, + title: null, + titleTimer: null, + muses: null, + XmlToLower: function(a) { + for (var c = a.attributes(); c.hasNext();) { + var b = c.next(); + a.set(b.toLowerCase(), a.get(b)) + } + }, + enablePlayStopToggle: function() { + this.togglePlayStopEnabled = !0; + this.togglePlayStop(this.lastToggleValue) + }, + togglePlayStop: function(a) { + this.lastToggleValue = a; + this.togglePlayStopEnabled && (this.playButton.setVisible(!a), this.stopButton.setVisible(a)) + }, + makeAbsolute: function(a) { + return -1 != a.indexOf("://") ? a : "/" == a.charAt(0) ? this.skinDomain + a : this.baseURL + a + }, + getDomainName: function(a) { + a += "/"; + var c = a.indexOf("://"); + if (-1 == c) return ""; + c = a.indexOf("/", c + 3); + return m.substr(a, 0, c) + }, + getDirName: function(a) { + var c = a.lastIndexOf("/"); + return -1 == c ? "" : m.substr(a, 0, c + 1) + }, + loadSkin: function(a) { + var c = + q.Http.requestUrl(a); + this.baseURL = this.getDirName(a); + this.skinDomain = this.getDomainName(a); + a = !1; + for (c = h.parse(c).elements(); c.hasNext();) { + var b = c.next(); + if ("ffmp3-skin" != b.get_nodeName().toLowerCase() && "muses-skin" != b.get_nodeName().toLowerCase()) break; + this.XmlToLower(b); + null == b.get("folder") ? this.skinFolder = "" : this.skinFolder = b.get("folder"); + (a = null == b.get("toggleplaystop") ? !1 : "true" == b.get("toggleplaystop")) && this.enablePlayStopToggle(); + 0 < this.skinFolder.length && "/" != this.skinFolder.charAt(this.skinFolder.length - + 1) && (this.skinFolder += "/"); + this.skinFolder = this.makeAbsolute(this.skinFolder); + for (a = b.elements(); a.hasNext();) switch (b = a.next(), this.XmlToLower(b), b.get_nodeName().toLowerCase()) { + case "bg": + this.configureBG(b); + break; + case "play": + this.playButton.configure(b); + break; + case "stop": + this.stopButton.configure(b); + break; + case "text": + this.statusText.configureText(b, "left"); + break; + case "status": + this.statusLed.configure(b); + break; + case "volume": + this.volumeControl.configure(b); + break; + case "artist": + this.artistText.configureText(b, + "left"); + break; + case "songtitle": + this.songTitleText.configureText(b, "left") + } + } + }, + loadImage: function(a, c) { + a.src = this.skinFolder + c + }, + configureBG: function(a) { + this.bg = new Image; + this.loadImage(this.bg, a.get("image")); + this.bg.style.position = "absolute"; + this.bg.style.left = d.UI.parseInt(a.get("x"), 0) + "px"; + this.bg.style.top = d.UI.parseInt(a.get("y"), 0) + "px"; + this.mainDiv.appendChild(this.bg) + }, + configureButton: function(a, c) { + a.src = this.skinFolder + c.get("image"); + a.style.position = "absolute"; + a.style.left = d.UI.parseInt(c.get("x"), + 0) + "px"; + a.style.top = d.UI.parseInt(c.get("y"), 0) + "px" + }, + setPlaying: function() { + this.showInfo("Play"); + this.statusLed.on(); + this.togglePlayStop(!0) + }, + setStopped: function() { + this.showInfo("Stop"); + this.statusLed.off(); + this.togglePlayStop(!1) + }, + setBuffering: function() { + this.showInfo("Buffering"); + this.statusLed.on(); + this.togglePlayStop(!0) + }, + setError: function() { + this.showInfo("Error"); + this.statusLed.off() + }, + setVolume: function(a) { + this.volumeControl.setVolume(a); + this.showInfo("Volume: " + Math.round(100 * a) + "%") + }, + showInfo: function(a, + c) { + null == c && (c = !0); + null == a ? this.restoreTitle() : (null != this.titleTimer && this.titleTimer.stop(), this.statusText.setText(a), c && (this.titleTimer = new q.Timer(2E3), this.titleTimer.run = p(this, this.restoreTitle))) + }, + restoreTitle: function() { + null != this.titleTimer && this.titleTimer.stop(); + this.statusText.setText(this.title) + }, + __class__: d.UI + }; + d.skin = {}; + d.skin.UIComponent = function(a) { + this.ui = a; + this.configured = !1; + this.container = window.document.createElement("div"); + this.container.style.position = "absolute" + }; + d.skin.UIComponent.__name__ = !0; + d.skin.UIComponent.prototype = { + container: null, + configured: null, + ui: null, + setVisible: function(a) { + this.container.style.display = a ? "block" : "none" + }, + configure: function(a) { + this.configured = !0; + this.container.style.left = d.UI.parseInt(a.get("x"), 0) + "px"; + this.container.style.top = d.UI.parseInt(a.get("y"), 0) + "px"; + null != a.get("width") && (this.container.style.width = d.UI.parseInt(a.get("width"), 0) + "px"); + null != a.get("height") && (this.container.style.height = d.UI.parseInt(a.get("height"), 0) + "px") + }, + appendChild: function(a, + c) { + null == c && (c = !0); + a.style.position = "absolute"; + a.style.left = a.style.top = "0px"; + a.style.display = c ? "block" : "none"; + this.container.appendChild(a) + }, + __class__: d.skin.UIComponent + }; + d.skin.Button = function(a, c) { + var b = this; + d.skin.UIComponent.call(this, a); + this.mouseOverState = new Image; + this.mouseDownState = new Image; + this.noMouseState = new Image; + this.container.title = c; + this.mouseDownState.style.opacity = "0"; + this.mouseOverState.style.opacity = "0"; + this.container.onmouseup = function(a) { + b.mouseDownState.style.opacity = "0"; + b.mouseOverState.style.opacity = "1" + }; + this.container.onmousedown = function(a) { + b.mouseDownState.style.opacity = "1"; + b.mouseOverState.style.opacity = "0" + }; + this.container.onmouseover = function(a) { + b.mouseOverState.style.opacity = "1" + }; + this.container.onmouseout = function(a) { + b.mouseDownState.style.opacity = "0"; + b.mouseOverState.style.opacity = "0" + } + }; + d.skin.Button.__name__ = !0; + d.skin.Button.__super__ = d.skin.UIComponent; + d.skin.Button.prototype = w(d.skin.UIComponent.prototype, { + mouseOverState: null, + mouseDownState: null, + noMouseState: null, + configure: function(a) { + d.skin.UIComponent.prototype.configure.call(this, a); + null != a.get("bgimage") && (this.ui.loadImage(this.noMouseState, a.get("bgimage")), this.appendChild(this.noMouseState)); + null != a.get("clickimage") && (this.ui.loadImage(this.mouseDownState, a.get("clickimage")), this.appendChild(this.mouseDownState)); + this.ui.loadImage(this.mouseOverState, a.get("image")); + this.appendChild(this.mouseOverState) + }, + __class__: d.skin.Button + }); + d.skin.StatusLed = function(a) { + d.skin.UIComponent.call(this, a); + this.playMC = + new Image; + this.stopMC = new Image + }; + d.skin.StatusLed.__name__ = !0; + d.skin.StatusLed.__super__ = d.skin.UIComponent; + d.skin.StatusLed.prototype = w(d.skin.UIComponent.prototype, { + playMC: null, + stopMC: null, + configure: function(a) { + d.skin.UIComponent.prototype.configure.call(this, a); + null != a.get("imageplay") && -1 == a.get("imageplay").indexOf(".swf") && (this.ui.loadImage(this.playMC, a.get("imageplay")), this.appendChild(this.playMC, !1)); + null != a.get("imagestop") && -1 == a.get("imagestop").indexOf(".swf") && (this.ui.loadImage(this.stopMC, + a.get("imagestop")), this.appendChild(this.stopMC, !0)) + }, + on: function() { + this.playMC.style.display = "block"; + this.stopMC.style.display = "none" + }, + off: function() { + this.playMC.style.display = "none"; + this.stopMC.style.display = "block" + }, + __class__: d.skin.StatusLed + }); + d.skin.TitleText = function(a) { + d.skin.UIComponent.call(this, a); + this.container.style.fontFamily = "Silkscreen"; + this.container.style.fontSize = "12px" + }; + d.skin.TitleText.__name__ = !0; + d.skin.TitleText.__super__ = d.skin.UIComponent; + d.skin.TitleText.prototype = w(d.skin.UIComponent.prototype, { + configureText: function(a, b) { + this.configure(a); + switch (a.get("align")) { + case "center": + this.container.style.textAlign = "center"; + break; + case "right": + this.container.style.textAlign = "right"; + break; + default: + this.container.style.textAlign = b + } + this.container.style.padding = "2px"; + this.container.style.whiteSpace = "nowrap"; + this.container.style.fontFamily = a.get("font"); + this.container.style.fontSize = d.UI.parseInt(a.get("size"), 12) + "px"; + this.container.style.color = a.get("color"); + this.container.style.overflow = "hidden" + }, + setText: function(a) { + this.container.innerHTML = + a + }, + __class__: d.skin.TitleText + }); + d.skin.VolumeControl = function(a, b) { + d.skin.UIComponent.call(this, a); + this.muses = b; + this.firstDraw = !0; + this.bars = null; + this.mousePressed = !1; + this.volume = 1; + this.setMode("bars"); + this.draw(this.container); + this.vertMargin = this.horizMargin = this.height = this.width = 0; + this.barStep = 2; + this.barWidth = 1; + this.barColors = this.bgColors = null + }; + d.skin.VolumeControl.__name__ = !0; + d.skin.VolumeControl.__super__ = d.skin.UIComponent; + d.skin.VolumeControl.prototype = w(d.skin.UIComponent.prototype, { + volume: null, + width: null, + height: null, + horizMargin: null, + horizDesp: null, + vertMargin: null, + vertDesp: null, + barStep: null, + barWidth: null, + bgColors: null, + barColors: null, + bars: null, + cover: null, + spriteBar: null, + firstDraw: null, + mode: null, + holder: null, + mousePressed: null, + muses: null, + draw: function(a) {}, + setMode: function(a) { + switch (a.toLowerCase()) { + case "bars": + this.draw = p(this, this.drawBars); + break; + case "holder": + this.draw = p(this, this.drawHolder); + break; + case "vholder": + this.draw = p(this, this.drawVHolder) + } + this.mode = a + }, + drawHolder: function(a) { + this.holder.style.left = + this.volume * (this.width - this.holder.width) + "px" + }, + drawVHolder: function(a) { + this.holder.style.top = (1 - this.volume) * (this.height - this.holder.height) + "px" + }, + drawBars: function(a) { + if (null != this.barColors && 0 != this.barStep && (a = Math.round((this.width - 2 * this.horizMargin) / this.barStep), 0 != a)) { + var b = (this.height - 2 * this.vertMargin + 1) / a, + d = this.height - this.vertMargin, + e = this.horizMargin; + if (null == this.bars) { + this.bars = []; + for (var f = 0; f < a;) { + var g = f++, + h; + h = window.document.createElement("div"); + this.bars.push(h); + this.appendChild(h); + h.style.left = e + g * this.barStep + "px"; + h.style.top = d - g * b + "px"; + h.style.width = Math.round(this.barWidth) + "px"; + h.style.height = Math.ceil(g * b) + "px" + } + } + b = 0; + for (d = Math.round(this.volume * a); b < d;) e = b++, this.bars[e].style.backgroundColor = this.barColors[0]; + for (b = Math.round(this.volume * a); b < a;) d = b++, this.bars[d].style.backgroundColor = this.barColors[1] + } + }, + setVolume: function(a) { + this.volume != a && (this.volume = a, 1 < this.volume && (this.volume = 1), 0 > this.volume && (this.volume = 0), this.muses.setVolume(this.volume), this.draw(this.container)) + }, + getVolume: function() { + return this.volume + }, + mouseDown: function(a) { + var b; + this.mousePressed = !0; + "vholder" != this.mode ? (a = a.layerX, b = this.width) : (a = this.height - a.layerY, b = this.height); + a -= .06 * b; + 0 > a && (a = 0); + a = Math.round(1.06 * a); + a > b && (a = b); + this.setVolume(a / (b - 2)) + }, + mouseUp: function(a) { + this.mousePressed = !1 + }, + mouseMove: function(a) { + this.mousePressed && this.mouseDown(a) + }, + mouseWheel: function(a) { + 0 < a.wheelDelta ? this.setVolume(this.volume + .025) : this.setVolume(this.volume - .025) + }, + configure: function(a) { + d.skin.UIComponent.prototype.configure.call(this, + a); + this.width = d.UI.parseInt(a.get("width"), 0); + this.height = d.UI.parseInt(a.get("height"), 0); + this.barColors = [a.get("color1"), a.get("color2")]; + this.barStep = d.UI.parseInt(a.get("barstep"), 2); + this.barWidth = d.UI.parseInt(a.get("barwidth"), 1); + var b; + b = null != a.get("mode") ? a.get("mode").toLowerCase() : null; + this.setMode(b); + if ("holder" == b || "vholder" == b) this.holder = new Image, this.holder.onload = p(this, this.holderLoad), this.ui.loadImage(this.holder, a.get("holderimage")), this.appendChild(this.holder); + this.draw(this.container); + this.cover = window.document.createElement("div"); + this.cover.onmousedown = p(this, this.mouseDown); + this.cover.onmousemove = p(this, this.mouseMove); + this.cover.onmousewheel = p(this, this.mouseWheel); + this.cover.onmouseup = p(this, this.mouseUp); + this.cover.onmouseout = p(this, this.mouseUp); + this.cover.style.width = this.container.style.width; + this.cover.style.height = this.container.style.height; + this.appendChild(this.cover) + }, + holderLoad: function(a) { + this.holder.style.left = .5 * (this.width - this.holder.width) + "px"; + this.holder.style.top = + .5 * (this.height - this.holder.height) + "px"; + this.draw(this.container) + }, + __class__: d.skin.VolumeControl + }); + var G = 0; + Array.prototype.indexOf && (m.indexOf = function(a, b, d) { + return Array.prototype.indexOf.call(a, b, d) + }); + Math.NaN = Number.NaN; + Math.NEGATIVE_INFINITY = Number.NEGATIVE_INFINITY; + Math.POSITIVE_INFINITY = Number.POSITIVE_INFINITY; + Math.isFinite = function(a) { + return isFinite(a) + }; + Math.isNaN = function(a) { + return isNaN(a) + }; + String.prototype.__class__ = String; + String.__name__ = !0; + Array.__name__ = !0; + Date.prototype.__class__ = + Date; + Date.__name__ = ["Date"]; + var H = { + __name__: ["Int"] + }, + I = { + __name__: ["Dynamic"] + }, + E = Number; + E.__name__ = ["Float"]; + var F = Boolean; + F.__ename__ = ["Bool"]; + var J = { + __name__: ["Class"] + }, + K = {}; + h.Element = "element"; + h.PCData = "pcdata"; + h.CData = "cdata"; + h.Comment = "comment"; + h.DocType = "doctype"; + h.ProcessingInstruction = "processingInstruction"; + h.Document = "document"; + g.objectId = "MRPObject"; + g.playerCounter = 0; + g.__hostPrefix = "hosted"; + g.__hostMidfix = "muses"; + b.Campaign.TYPE_DIRECT = "direct"; + b.Campaign.TYPE_ORGANIC = "organic"; + b.Campaign.TYPE_REFERRAL = + "referral"; + b.Config.ERROR_SEVERITY_SILENCE = 0; + b.Config.ERROR_SEVERITY_TRACE = 1; + b.Config.ERROR_SEVERITY_EXCEPTIONS = 2; + b.CustomVariable.SCOPE_VISITOR = 1; + b.CustomVariable.SCOPE_SESSION = 2; + b.CustomVariable.SCOPE_PAGE = 3; + b.Page.REFERRER_INTERNAL = "0"; + b.Tracker.VERSION = "5.2.5"; + b.URLParser.parts = "source protocol authority userInfo user password host port relative path directory file query anchor".split(" "); + b.internals.X10.OBJECT_KEY_NUM = 1; + b.internals.X10.TYPE_KEY_NUM = 2; + b.internals.X10.LABEL_KEY_NUM = 3; + b.internals.X10.VALUE_VALUE_NUM = + 1; + b.internals.request.Request.TYPE_EVENT = "event"; + b.internals.request.Request.TYPE_TRANSACTION = "tran"; + b.internals.request.Request.TYPE_ITEM = "item"; + b.internals.request.Request.TYPE_SOCIAL = "social"; + b.internals.request.Request.TYPE_CUSTOMVARIABLE = "var"; + b.internals.request.Request.X10_CUSTOMVAR_NAME_PROJECT_ID = "8"; + b.internals.request.Request.X10_CUSTOMVAR_VALUE_PROJECT_ID = "9"; + b.internals.request.Request.X10_CUSTOMVAR_SCOPE_PROJECT_ID = "11"; + b.internals.request.Request.CAMPAIGN_DELIMITER = "|"; + b.internals.request.EventRequest.X10_EVENT_PROJECT_ID = + "5"; + q.xml.Parser.escapes = function(a) { + a = new q.ds.StringMap; + a.set("lt", "<"); + a.set("gt", ">"); + a.set("amp", "&"); + a.set("quot", '"'); + a.set("apos", "'"); + a.set("nbsp", String.fromCharCode(160)); + return a + }(this); + d.Muses.VERSION = "0.2 beta"; + d.Muses.instances = []; + d.Tracker.enabled = !0; + g.main() +})("undefined" != typeof window ? window : exports); +var FlashDetect = new function() { + var n = this; + n.installed = !1; + n.raw = ""; + n.major = -1; + n.minor = -1; + n.revision = -1; + n.revisionStr = ""; + var w = [{ + name: "ShockwaveFlash.ShockwaveFlash.7", + version: function(p) { + return B(p) + } + }, { + name: "ShockwaveFlash.ShockwaveFlash.6", + version: function(p) { + var n = "6,0,21"; + try { + p.AllowScriptAccess = "always", n = B(p) + } catch (m) {} + return n + } + }, { + name: "ShockwaveFlash.ShockwaveFlash", + version: function(p) { + return B(p) + } + }], + B = function(p) { + var n = -1; + try { + n = p.GetVariable("$version") + } catch (m) {} + return n + }; + n.majorAtLeast = function(p) { + return n.major >= + p + }; + n.minorAtLeast = function(p) { + return n.minor >= p + }; + n.revisionAtLeast = function(p) { + return n.revision >= p + }; + n.versionAtLeast = function(p) { + var s = [n.major, n.minor, n.revision], + m = Math.min(s.length, arguments.length); + for (i = 0; i < m; i++) + if (s[i] >= arguments[i]) { + if (!(i + 1 < m && s[i] == arguments[i])) return !0 + } else return !1 + }; + n.FlashDetect = function() { + var p, s, m, x, A; + if (navigator.plugins && 0 < navigator.plugins.length) { + var g = navigator.mimeTypes; + if (g && g["application/x-shockwave-flash"] && g["application/x-shockwave-flash"].enabledPlugin && + g["application/x-shockwave-flash"].enabledPlugin.description) { + p = g = g["application/x-shockwave-flash"].enabledPlugin.description; + var g = p.split(/ +/), + z = g[2].split(/\./), + g = g[3]; + s = parseInt(z[0], 10); + m = parseInt(z[1], 10); + x = g; + A = parseInt(g.replace(/[a-zA-Z]/g, ""), 10) || n.revision; + n.raw = p; + n.major = s; + n.minor = m; + n.revisionStr = x; + n.revision = A; + n.installed = !0 + } + } else if (-1 == navigator.appVersion.indexOf("Mac") && window.execScript) + for (g = -1, z = 0; z < w.length && -1 == g; z++) { + p = -1; + try { + p = new ActiveXObject(w[z].name) + } catch (v) { + p = { + activeXError: !0 + } + } + p.activeXError || + (n.installed = !0, g = w[z].version(p), -1 != g && (p = g, x = p.split(","), s = parseInt(x[0].split(" ")[1], 10), m = parseInt(x[1], 10), A = parseInt(x[2], 10), x = x[2], n.raw = p, n.major = s, n.minor = m, n.revision = A, n.revisionStr = x)) + } + }() +}; +FlashDetect.JS_RELEASE = "1.0.4"; \ No newline at end of file diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/muses.swf b/airtime_mvc/public/js/airtime/embeddableplayer/muses.swf new file mode 100644 index 0000000000000000000000000000000000000000..0a23f07cf1ece131ba9c4b902c855d4ea246ebbf GIT binary patch literal 74589 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 Szwqxq*An{+`1%+A*h7G?D}~+w literal 0 HcmV?d00001 From 7744e6b6560c0c366d4ac3f3401a67fdfe7dc2ec Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Tue, 10 Mar 2015 16:34:06 -0400 Subject: [PATCH 12/60] Delete player css file for now --- airtime_mvc/public/css/embed-player.css | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 airtime_mvc/public/css/embed-player.css diff --git a/airtime_mvc/public/css/embed-player.css b/airtime_mvc/public/css/embed-player.css deleted file mode 100644 index 1f19319e8..000000000 --- a/airtime_mvc/public/css/embed-player.css +++ /dev/null @@ -1,4 +0,0 @@ -#embed_player_preview { - margin-top:20px; - border: 1px solid; -} From 8198d89095575e69214230d6ca1426cf587cf4d9 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Tue, 10 Mar 2015 16:47:08 -0400 Subject: [PATCH 13/60] small change to player form --- .../application/controllers/EmbeddableplayerController.php | 2 +- airtime_mvc/application/forms/EmbeddablePlayer.php | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/airtime_mvc/application/controllers/EmbeddableplayerController.php b/airtime_mvc/application/controllers/EmbeddableplayerController.php index 4536d7e18..6fc2d9e13 100644 --- a/airtime_mvc/application/controllers/EmbeddableplayerController.php +++ b/airtime_mvc/application/controllers/EmbeddableplayerController.php @@ -17,5 +17,5 @@ class EmbeddablePlayerController extends Zend_Controller_Action public function embedCodeAction() { $this->view->layout()->disableLayout(); - } + } } \ No newline at end of file diff --git a/airtime_mvc/application/forms/EmbeddablePlayer.php b/airtime_mvc/application/forms/EmbeddablePlayer.php index 4b9353aad..230037de7 100644 --- a/airtime_mvc/application/forms/EmbeddablePlayer.php +++ b/airtime_mvc/application/forms/EmbeddablePlayer.php @@ -10,7 +10,6 @@ class Application_Form_EmbeddablePlayer extends Zend_Form_SubForm $embedSrc = new Zend_Form_Element_Text('player_embed_src'); $embedSrc->setAttrib("readonly", "readonly"); - $embedSrc->setAttrib("class", "player_embed_src"); $embedSrc->setValue('<iframe frameborder="0" src="http://localhost/embeddableplayer/embed-code"></iframe>'); $embedSrc->removeDecorator('label'); $this->addElement($embedSrc); From 0042fb50fe3ca792e9b73fd39d5189cf42e1d32e Mon Sep 17 00:00:00 2001 From: Albert Santoni <albert.santoni@sourcefabric.org> Date: Tue, 10 Mar 2015 19:15:38 -0400 Subject: [PATCH 14/60] Refactor some stuff related to four streams, Liquidsoap is a piece of shit too - no dynamic variables --- .../application/controllers/ApiController.php | 2 +- .../controllers/PreferenceController.php | 10 +- .../application/models/StreamSetting.php | 203 +++++++++--------- .../generate_liquidsoap_cfg.py | 23 +- .../pypo/liquidsoap_scripts/ls_script.liq | 5 +- python_apps/pypo/listenerstat.py | 4 +- python_apps/pypo/pypofetch.py | 93 +------- python_apps/pypo/pypoliquidsoap.py | 1 + python_apps/pypo/pyponotify.py | 10 +- 9 files changed, 130 insertions(+), 221 deletions(-) diff --git a/airtime_mvc/application/controllers/ApiController.php b/airtime_mvc/application/controllers/ApiController.php index c90b41c22..b07cccd51 100644 --- a/airtime_mvc/application/controllers/ApiController.php +++ b/airtime_mvc/application/controllers/ApiController.php @@ -1308,7 +1308,7 @@ class ApiController extends Zend_Controller_Action } public function getStreamParametersAction() { - $streams = array("s1", "s2", "s3"); + $streams = array("s1", "s2", "s3", "s4"); $stream_params = array(); foreach ($streams as $s) { $stream_params[$s] = diff --git a/airtime_mvc/application/controllers/PreferenceController.php b/airtime_mvc/application/controllers/PreferenceController.php index 6a0ba3541..b06a9e1ed 100644 --- a/airtime_mvc/application/controllers/PreferenceController.php +++ b/airtime_mvc/application/controllers/PreferenceController.php @@ -141,11 +141,7 @@ class PreferenceController extends Zend_Controller_Action $this->view->headScript()->appendFile($baseUrl.'js/airtime/preferences/streamsetting.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); // get current settings - $temp = Application_Model_StreamSetting::getStreamSetting(); - $setting = array(); - foreach ($temp as $t) { - $setting[$t['keyname']] = $t['value']; - } + $setting = Application_Model_StreamSetting::getStreamSetting(); $name_map = array( 'ogg' => 'Ogg Vorbis', @@ -208,6 +204,7 @@ class PreferenceController extends Zend_Controller_Action $s1_data = array(); $s2_data = array(); $s3_data = array(); + $s4_data = array(); $values = array(); foreach($postData as $k=>$v) { $v = explode('=', urldecode($v)); @@ -447,7 +444,8 @@ class PreferenceController extends Zend_Controller_Action public function getAdminPasswordStatusAction() { $out = array(); - for ($i=1; $i<=4; $i++) { + $num_of_stream = intval(Application_Model_Preference::GetNumOfStreams()); + for ($i=1; $i<=$num_of_stream; $i++) { if (Application_Model_StreamSetting::getAdminPass('s'.$i)=='') { $out["s".$i] = false; } else { diff --git a/airtime_mvc/application/models/StreamSetting.php b/airtime_mvc/application/models/StreamSetting.php index c34c09a2e..832849474 100644 --- a/airtime_mvc/application/models/StreamSetting.php +++ b/airtime_mvc/application/models/StreamSetting.php @@ -1,4 +1,7 @@ <?php + +define("MAX_NUM_STREAMS", 4); + class Application_Model_StreamSetting { public static function setValue($key, $value, $type) @@ -41,7 +44,7 @@ class Application_Model_StreamSetting } } - public static function getValue($key) + public static function getValue($key, $default="") { $con = Propel::getConnection(); @@ -59,7 +62,7 @@ class Application_Model_StreamSetting throw new Exception("Error: $msg"); } - return $result ? $result : ""; + return $result ? $result : $default; } /* Returns the id's of all streams that are enabled in an array. An @@ -83,50 +86,62 @@ class Application_Model_StreamSetting return $ids; } - /* Returns only global data as array*/ - public static function getGlobalData() - { - $con = Propel::getConnection(); - $sql = "SELECT * " - ."FROM cc_stream_setting " - ."WHERE keyname IN ('output_sound_device', 'icecast_vorbis_metadata')"; - - $rows = Application_Common_Database::prepareAndExecute($sql, array(), 'all'); - - $data = array(); - - foreach ($rows as $row) { - $data[$row["keyname"]] = $row["value"]; - } - - return $data; - } - /* Returns all information related to a specific stream. An example * of a stream id is 's1' or 's2'. */ public static function getStreamData($p_streamId) { - $con = Propel::getConnection(); - $streamId = pg_escape_string($p_streamId); - $sql = "SELECT * " - ."FROM cc_stream_setting " - ."WHERE keyname LIKE '{$streamId}_%'"; - - $stmt = $con->prepare($sql); - - if ($stmt->execute()) { - $rows = $stmt->fetchAll(); - } else { - $msg = implode(',', $stmt->errorInfo()); - throw new Exception("Error: $msg"); - } + $rows = CcStreamSettingQuery::create() + ->filterByDbKeyName("${p_streamId}_%") + ->find(); + //This is way too much code because someone made only stupid decisions about how + //the layout of this table worked. The git history doesn't lie. $data = array(); - foreach ($rows as $row) { - $data[$row["keyname"]] = $row["value"]; + $key = $row->getDbKeyName(); + $value = $row->getDbValue(); + $type = $row->getDbType(); + //Fix stupid defaults so we end up with proper typing in our JSON + if ($row->getDbType() == "boolean") { + if (empty($value)) { + //In Python, there is no way to tell the difference between ints and booleans, + //which we need to differentiate between for when we're generating the Liquidsoap + //config file. Returning booleans as a string is a workaround that lets us do that. + $value = "false"; + } + $data[$key] = $value; + } + elseif ($row->getDbType() == "integer") { + if (empty($value)) { + $value = 0; + } + $data[$key] = intval($value); + } + else { + $data[$key] = $value; + } } + //Add in defaults in case they don't exist in the database. + $keyPrefix = $p_streamId . '_'; + self::ensureKeyExists($keyPrefix . 'admin_pass', $data); + self::ensureKeyExists($keyPrefix . 'admin_user', $data); + self::ensureKeyExists($keyPrefix . 'bitrate', $data, 128); + self::ensureKeyExists($keyPrefix . 'channels', $data, "stereo"); + self::ensureKeyExists($keyPrefix . 'description', $data); + self::ensureKeyExists($keyPrefix . 'enable', $data, "false"); + self::ensureKeyExists($keyPrefix . 'genre', $data); + self::ensureKeyExists($keyPrefix . 'host', $data); + self::ensureKeyExists($keyPrefix . 'liquidsoap_error', $data, "waiting"); + self::ensureKeyExists($keyPrefix . 'mount', $data); + self::ensureKeyExists($keyPrefix . 'name', $data); + self::ensureKeyExists($keyPrefix . 'output', $data); + self::ensureKeyExists($keyPrefix . 'pass', $data); + self::ensureKeyExists($keyPrefix . 'port', $data, 8000); + self::ensureKeyExists($keyPrefix . 'type', $data); + self::ensureKeyExists($keyPrefix . 'url', $data); + self::ensureKeyExists($keyPrefix . 'user', $data); + return $data; } @@ -134,76 +149,41 @@ class Application_Model_StreamSetting * make data easier to iterate over */ public static function getStreamDataNormalized($p_streamId) { - $con = Propel::getConnection(); - $streamId = pg_escape_string($p_streamId); - $sql = "SELECT * " - ."FROM cc_stream_setting " - ."WHERE keyname LIKE '{$streamId}_%'"; - - $stmt = $con->prepare($sql); - - if ($stmt->execute()) { - $rows = $stmt->fetchAll(); - } else { - $msg = implode(',', $stmt->errorInfo()); - throw new Exception("Error: $msg"); + $settings = self::getStreamData($p_streamId); + foreach ($settings as $key => $value) + { + unset($settings[$key]); + $newKey = substr($key, strlen($p_streamId)+1); //$p_streamId is assumed to be the key prefix. + $settings[$newKey] = $value; } + return $settings; + } - $data = array(); - - foreach ($rows as $row) { - list($id, $key) = explode("_", $row["keyname"], 2); - $data[$key] = $row["value"]; + private static function ensureKeyExists($key, &$array, $default='') + { + if (!array_key_exists($key, $array)) { + $array[$key] = $default; } - - return $data; + return $array; } public static function getStreamSetting() { - $con = Propel::getConnection(); - $sql = "SELECT *" - ." FROM cc_stream_setting" - ." WHERE keyname not like '%_error' AND keyname not like '%_admin_%'"; - - $rows = Application_Common_Database::prepareAndExecute($sql, array(), 'all'); - - $exists = array(); - - foreach ($rows as $r) { - if ($r['keyname'] == 'master_live_stream_port') { - $exists['master_live_stream_port'] = true; - } elseif ($r['keyname'] == 'master_live_stream_mp') { - $exists['master_live_stream_mp'] = true; - } elseif ($r['keyname'] == 'dj_live_stream_port') { - $exists['dj_live_stream_port'] = true; - } elseif ($r['keyname'] == 'dj_live_stream_mp') { - $exists['dj_live_stream_mp'] = true; - } + $settings = array(); + $numStreams = MAX_NUM_STREAMS; + for ($streamIdx = 1; $streamIdx <= $numStreams; $streamIdx++) + { + $settings = array_merge($settings, self::getStreamData("s" . $streamIdx)); } - - if (!isset($exists["master_live_stream_port"])) { - $rows[] = array("keyname" =>"master_live_stream_port", - "value"=>self::getMasterLiveStreamPort(), - "type"=>"integer"); - } - if (!isset($exists["master_live_stream_mp"])) { - $rows[] = array("keyname" =>"master_live_stream_mp", - "value"=>self::getMasterLiveStreamMountPoint(), - "type"=>"string"); - } - if (!isset($exists["dj_live_stream_port"])) { - $rows[] = array("keyname" =>"dj_live_stream_port", - "value"=>self::getDjLiveStreamPort(), - "type"=>"integer"); - } - if (!isset($exists["dj_live_stream_mp"])) { - $rows[] = array("keyname" =>"dj_live_stream_mp", - "value"=>self::getDjLiveStreamMountPoint(), - "type"=>"string"); - } - - return $rows; + $settings["master_live_stream_port"] = self::getMasterLiveStreamPort(); + $settings["master_live_stream_mp"] = self::getMasterLiveStreamMountPoint(); + $settings["dj_live_stream_port"] = self::getDjLiveStreamPort(); + $settings["dj_live_stream_mp"] = self::getDjLiveStreamMountPoint(); + $settings["off_air_meta"] = self::getOffAirMeta(); + $settings["icecast_vorbis_metadata"] = self::getIcecastVorbisMetadata(); + $settings["output_sound_device"] = self::getOutputSoundDevice(); + $settings["output_sound_device_type"] = self::getOutputSoundDeviceType(); + return $settings; } @@ -211,7 +191,10 @@ class Application_Model_StreamSetting { $stream_setting = CcStreamSettingQuery::create()->filterByDbKeyName($key)->findOne(); if (is_null($stream_setting)) { - throw new Exception("Keyname $key does not exist!"); + //throw new Exception("Keyname $key does not exist!"); + $stream_setting = new CcStreamSetting(); + $stream_setting->setDbKeyName($key); + $stream_setting->setDbType(""); } $stream_setting->setDbValue($value); @@ -411,7 +394,7 @@ class Application_Model_StreamSetting public static function getMasterLiveStreamPort() { - return self::getValue("master_live_stream_port"); + return self::getValue("master_live_stream_port", 8001); } public static function setMasterLiveStreamMountPoint($value) @@ -421,7 +404,7 @@ class Application_Model_StreamSetting public static function getMasterLiveStreamMountPoint() { - return self::getValue("master_live_stream_mp"); + return self::getValue("master_live_stream_mp", "/master"); } public static function setDjLiveStreamPort($value) @@ -431,7 +414,7 @@ class Application_Model_StreamSetting public static function getDjLiveStreamPort() { - return self::getValue("dj_live_stream_port"); + return self::getValue("dj_live_stream_port", 8001); } public static function setDjLiveStreamMountPoint($value) @@ -441,7 +424,7 @@ class Application_Model_StreamSetting public static function getDjLiveStreamMountPoint() { - return self::getValue("dj_live_stream_mp"); + return self::getValue("dj_live_stream_mp", "/show"); } public static function getAdminUser($stream){ @@ -488,4 +471,16 @@ class Application_Model_StreamSetting public static function SetListenerStatError($key, $v) { self::setValue($key, $v, 'string'); } + + public static function getIcecastVorbisMetadata() { + return self::getValue("icecast_vorbis_metadata", ""); + } + + public static function getOutputSoundDevice() { + return self::getValue("output_sound_device", "false"); + } + + public static function getOutputSoundDeviceType() { + return self::getValue("output_sound_device_type", ""); + } } diff --git a/python_apps/pypo/liquidsoap_scripts/generate_liquidsoap_cfg.py b/python_apps/pypo/liquidsoap_scripts/generate_liquidsoap_cfg.py index 45bdb46f4..ec5d10cc3 100644 --- a/python_apps/pypo/liquidsoap_scripts/generate_liquidsoap_cfg.py +++ b/python_apps/pypo/liquidsoap_scripts/generate_liquidsoap_cfg.py @@ -10,18 +10,23 @@ def generate_liquidsoap_config(ss): fh.write("################################################\n") fh.write("# THIS FILE IS AUTO GENERATED. DO NOT CHANGE!! #\n") fh.write("################################################\n") + fh.write("# The ignore() lines are to squash unused variable warnings\n") - for d in data: - key = d['keyname'] + for key, value in data.iteritems(): + try: + str_buffer = "%s = %s\n" % (key, int(value)) + except ValueError: + try: # Is it a boolean? + if "true" in value or "false" in value: + str_buffer = "%s = %s\n" % (key, value.lower()) + else: + raise ValueError() # Just drop into the except below + except: #Everything else is a string + str_buffer = "%s = \"%s\"\n" % (key, value) - str_buffer = d[u'keyname'] + " = " - if d[u'type'] == 'string': - val = '"%s"' % d['value'] - else: - val = d[u'value'] - val = val if len(val) > 0 else "0" - str_buffer = "%s = %s\n" % (key, val) fh.write(str_buffer.encode('utf-8')) + # ignore squashes unused variable errors from Liquidsoap + fh.write(("ignore(%s)\n" % key).encode('utf-8')) fh.write('log_file = "/var/log/airtime/pypo-liquidsoap/<script>.log"\n') fh.close() diff --git a/python_apps/pypo/liquidsoap_scripts/ls_script.liq b/python_apps/pypo/liquidsoap_scripts/ls_script.liq index 38f647f7f..ccc3a0776 100644 --- a/python_apps/pypo/liquidsoap_scripts/ls_script.liq +++ b/python_apps/pypo/liquidsoap_scripts/ls_script.liq @@ -104,7 +104,7 @@ server.register(namespace="vars", fun (s) -> begin log("vars.bootup_time") time := s s end) server.register(namespace="streams", "connection_status", - fun (s) -> begin log("streams.connection_status") "1:#{!s1_connected},2:#{!s2_connected},3:#{!s3_connected}:#{!s4_connected}" end) + fun (s) -> begin log("streams.connection_status") "1:#{!s1_connected},2:#{!s2_connected},3:#{!s3_connected},4:#{!s4_connected}" end) server.register(namespace="vars", "default_dj_fade", fun (s) -> begin log("vars.default_dj_fade") default_dj_fade := float_of_string(s) s end) @@ -400,9 +400,9 @@ if s3_enable == true then s3_connected, s3_description, s3_channels) end -%ifdef s4_enable s4_namespace = ref '' if s4_enable == true then + log("Stream 4 Enabled") if s4_output == 'shoutcast' then s4_namespace := "shoutcast_stream_4" else @@ -413,7 +413,6 @@ if s4_enable == true then s4_mount, s4_url, s4_name, s4_genre, s4_user, s, "4", s4_connected, s4_description, s4_channels) end -%endif command = "/usr/lib/airtime/pypo/bin/liquidsoap_scripts/notify.sh --liquidsoap-started &" diff --git a/python_apps/pypo/listenerstat.py b/python_apps/pypo/listenerstat.py index c97ca030d..a7f99e816 100644 --- a/python_apps/pypo/listenerstat.py +++ b/python_apps/pypo/listenerstat.py @@ -118,10 +118,10 @@ class ListenerStat(Thread): else: stats.append(self.get_shoutcast_stats(v)) self.update_listener_stat_error(v["mount"], 'OK') - except Exception, e: + except Exception as e: try: self.update_listener_stat_error(v["mount"], str(e)) - except Exception, e: + except Exception as e: self.logger.error('Exception: %s', e) return stats diff --git a/python_apps/pypo/pypofetch.py b/python_apps/pypo/pypofetch.py index 5564825a3..5f3ce5503 100644 --- a/python_apps/pypo/pypofetch.py +++ b/python_apps/pypo/pypofetch.py @@ -216,98 +216,9 @@ class PypoFetch(Thread): TODO: This function needs to be way shorter, and refactored :/ - MK """ def regenerate_liquidsoap_conf(self, setting): - existing = {} + self.restart_liquidsoap() + self.update_liquidsoap_connection_status() - 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 def update_liquidsoap_connection_status(self): diff --git a/python_apps/pypo/pypoliquidsoap.py b/python_apps/pypo/pypoliquidsoap.py index 62779200f..e1f341ed4 100644 --- a/python_apps/pypo/pypoliquidsoap.py +++ b/python_apps/pypo/pypoliquidsoap.py @@ -15,6 +15,7 @@ class PypoLiquidsoap(): "s1": None, "s2": None, "s3": None, + "s4": None, } self.telnet_liquidsoap = TelnetLiquidsoap(telnet_lock, \ diff --git a/python_apps/pypo/pyponotify.py b/python_apps/pypo/pyponotify.py index 797d1ce9b..46eb76159 100644 --- a/python_apps/pypo/pyponotify.py +++ b/python_apps/pypo/pyponotify.py @@ -83,12 +83,12 @@ class Notify: # @pram time: time that LS started def notify_liquidsoap_status(self, msg, stream_id, time): - logger.debug('#################################################') - logger.debug('# Calling server to update liquidsoap status #') - logger.debug('#################################################') - logger.debug('msg = ' + str(msg)) + logger.info('#################################################') + logger.info('# Calling server to update liquidsoap status #') + logger.info('#################################################') + logger.info('msg = ' + str(msg)) response = self.api_client.notify_liquidsoap_status(msg, stream_id, time) - logger.debug("Response: " + json.dumps(response)) + logger.info("Response: " + json.dumps(response)) def notify_source_status(self, source_name, status): logger.debug('#################################################') From 694430f542b5ac940d19b7601827d517499406c3 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Thu, 12 Mar 2015 15:45:53 -0400 Subject: [PATCH 15/60] SAAS-637: Un-hardcode all player variables --- .../controllers/EmbeddableplayerController.php | 9 +++++++++ .../application/forms/EmbeddablePlayer.php | 18 +++++++++++------- .../scripts/embeddableplayer/embed-code.phtml | 8 ++++---- .../views/scripts/form/embeddableplayer.phtml | 2 +- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/airtime_mvc/application/controllers/EmbeddableplayerController.php b/airtime_mvc/application/controllers/EmbeddableplayerController.php index 6fc2d9e13..fa37e2861 100644 --- a/airtime_mvc/application/controllers/EmbeddableplayerController.php +++ b/airtime_mvc/application/controllers/EmbeddableplayerController.php @@ -17,5 +17,14 @@ class EmbeddablePlayerController extends Zend_Controller_Action public function embedCodeAction() { $this->view->layout()->disableLayout(); + + $request = $this->getRequest(); + + $this->view->mrp_js = Application_Common_HTTPHelper::getStationUrl() . "/js/airtime/embeddableplayer/mrp.js"; + $this->view->muses_swf = Application_Common_HTTPHelper::getStationUrl() . "/js/airtime/embeddableplayer/muses.swf"; + $this->view->skin = Application_Common_HTTPHelper::getStationUrl() . "/js/airtime/embeddableplayer/ffmp3-mcclean.xml"; + $this->view->codec = $request->getParam('codec'); + $this->view->streamURL = $request->getParam('url'); + $this->view->displayMetadata = $request->getParam('display_metadata'); } } \ No newline at end of file diff --git a/airtime_mvc/application/forms/EmbeddablePlayer.php b/airtime_mvc/application/forms/EmbeddablePlayer.php index 230037de7..7a89660ff 100644 --- a/airtime_mvc/application/forms/EmbeddablePlayer.php +++ b/airtime_mvc/application/forms/EmbeddablePlayer.php @@ -8,12 +8,6 @@ class Application_Form_EmbeddablePlayer extends Zend_Form_SubForm array('ViewScript', array('viewScript' => 'form/embeddableplayer.phtml')) )); - $embedSrc = new Zend_Form_Element_Text('player_embed_src'); - $embedSrc->setAttrib("readonly", "readonly"); - $embedSrc->setValue('<iframe frameborder="0" src="http://localhost/embeddableplayer/embed-code"></iframe>'); - $embedSrc->removeDecorator('label'); - $this->addElement($embedSrc); - $displayTrackMetadata = new Zend_Form_Element_Checkbox('player_display_track_metadata'); $displayTrackMetadata->setValue(true); $displayTrackMetadata->setLabel(_('Display track metadata?')); @@ -27,9 +21,19 @@ class Application_Form_EmbeddablePlayer extends Zend_Form_SubForm $streamURL->setMultiOptions( $urlOptions ); - $streamURL->setValue(0); + $streamURL->setValue(array_keys($urlOptions)[0]); $streamURL->setLabel(_('Select stream:')); + $streamURL->setAttrib('codec', array_values($urlOptions)[0]); $this->addElement($streamURL); + $url = $streamURL->getValue(); + $codec = $streamURL->getAttrib('codec'); + + $embedSrc = new Zend_Form_Element_Text('player_embed_src'); + $embedSrc->setAttrib("readonly", "readonly"); + $embedSrc->setValue('<iframe frameborder="0" src="'.Application_Common_HTTPHelper::getStationUrl().'/embeddableplayer/embed-code?url='.$url.'&codec='.$codec.'"></iframe>'); + $embedSrc->removeDecorator('label'); + $this->addElement($embedSrc); + } } \ No newline at end of file diff --git a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml index 74da31357..73db907b7 100644 --- a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml +++ b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml @@ -2,17 +2,17 @@ <html xmlns="http://www.w3.org/1999/xhtml"> <head> - <script src="http://localhost/widgets/muses/self_hosted/mrp.js" type="text/javascript"></script> + <script src="<?php echo $this->mrp_js?>" type="text/javascript"></script> </head> -<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="180" height="60" bgcolor="#FFFFFF"> +<object classid="" width="180" height="60" bgcolor="#FFFFFF"> <param name="movie" value="muses.swf" /> - <param name="flashvars" value="url=http://albertprov1.out.airtime.pro:8000/albertprov1_a&lang=auto&codec=mp3&volume=100&introurl=&tracking=true&jsevents=true&buffering=5&skin=http://localhost/js/airtime/embeddableplayer/ffmp3-mcclean.xml&title=Albert's%20Test Stream" /> + <param name="flashvars" value="url=<?php echo $this->streamURL?>&lang=auto&codec=<?php echo $this->codec?>&volume=100&introurl=&tracking=true&jsevents=true&buffering=5&skin=<?php echo $this->skin?>&title=''" /> <param name="wmode" value="window" /> <param name="allowscriptaccess" value="always" /> <param name="bgcolor" value="#FFFFFF" /> <param name="scale" value="noscale" /> - <embed src="http://localhost/js/airtime/embeddableplayer/muses.swf" flashvars="url=http://albertprov1.out.airtime.pro:8000/albertprov1_a&lang=auto&codec=mp3&volume=100&introurl=&tracking=true&jsevents=true&buffering=5&skin=http://localhost/js/airtime/embeddableplayer/ffmp3-mcclean.xml&title=Albert's%20Test Stream" width="180" scale="noscale" height="60" wmode="window" bgcolor="#FFFFFF" allowscriptaccess="always" type="application/x-shockwave-flash" /> + <embed src="<?php echo $this->muses_swf?>" flashvars="url=<?php echo $this->streamURL?>&lang=auto&codec=<?php echo $this->codec?>&volume=100&introurl=&tracking=true&jsevents=true&buffering=5&skin=<?php echo $this->skin?>&title=''" width="180" scale="noscale" height="60" wmode="window" bgcolor="#FFFFFF" allowscriptaccess="always" type="application/x-shockwave-flash" /> </object> </html> \ No newline at end of file diff --git a/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml b/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml index 568540d7f..d4f481199 100644 --- a/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml +++ b/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml @@ -8,6 +8,6 @@ <?php echo $this->element->getElement('player_stream_url'); ?> <div style="clear:both"></div> - <iframe frameborder="0" src="http://localhost/embeddableplayer/embed-code"></iframe> + <?php echo $this->element->getElement('player_embed_src')->getValue(); ?> </dl> </fieldset> \ No newline at end of file From dc0855de186d1f4bb4a87030afe44283d8da7d52 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Mon, 16 Mar 2015 11:29:24 -0400 Subject: [PATCH 16/60] SAAS-643: Embed Player -> Restrict the setting of an OPUS stream --- .../application/controllers/EmbeddableplayerController.php | 7 ++++++- airtime_mvc/application/forms/EmbeddablePlayer.php | 2 ++ .../application/views/scripts/embeddableplayer/index.phtml | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/airtime_mvc/application/controllers/EmbeddableplayerController.php b/airtime_mvc/application/controllers/EmbeddableplayerController.php index fa37e2861..d7a16104b 100644 --- a/airtime_mvc/application/controllers/EmbeddableplayerController.php +++ b/airtime_mvc/application/controllers/EmbeddableplayerController.php @@ -11,7 +11,12 @@ class EmbeddablePlayerController extends Zend_Controller_Action { $form = new Application_Form_EmbeddablePlayer(); - $this->view->form = $form; + if ($form->getElement('player_stream_url')->getAttrib('numberOfEnabledStreams') > 0) { + $this->view->form = $form; + } else { + $this->view->errorMsg = "You need to enable at least one MP3, AAC, or OGG stream to use this feature."; + } + } public function embedCodeAction() diff --git a/airtime_mvc/application/forms/EmbeddablePlayer.php b/airtime_mvc/application/forms/EmbeddablePlayer.php index 7a89660ff..520a5318b 100644 --- a/airtime_mvc/application/forms/EmbeddablePlayer.php +++ b/airtime_mvc/application/forms/EmbeddablePlayer.php @@ -16,6 +16,7 @@ class Application_Form_EmbeddablePlayer extends Zend_Form_SubForm $streamURL = new Zend_Form_Element_Radio('player_stream_url'); $urlOptions = Array(); foreach(Application_Model_StreamSetting::getEnabledStreamUrls() as $type => $url) { + if ($type == "opus") continue; $urlOptions[$url] = $type; } $streamURL->setMultiOptions( @@ -24,6 +25,7 @@ class Application_Form_EmbeddablePlayer extends Zend_Form_SubForm $streamURL->setValue(array_keys($urlOptions)[0]); $streamURL->setLabel(_('Select stream:')); $streamURL->setAttrib('codec', array_values($urlOptions)[0]); + $streamURL->setAttrib('numberOfEnabledStreams', sizeof($urlOptions)); $this->addElement($streamURL); $url = $streamURL->getValue(); diff --git a/airtime_mvc/application/views/scripts/embeddableplayer/index.phtml b/airtime_mvc/application/views/scripts/embeddableplayer/index.phtml index 0a9e66daa..f762892b4 100644 --- a/airtime_mvc/application/views/scripts/embeddableplayer/index.phtml +++ b/airtime_mvc/application/views/scripts/embeddableplayer/index.phtml @@ -6,6 +6,7 @@ <div style="clear:both"></div> + <?php echo $this->errorMsg; ?> <?php echo $this->form; ?> <br /> From 1f2f8a27a55775c9b76be895421520f70f01dfdb Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Mon, 16 Mar 2015 13:24:58 -0400 Subject: [PATCH 17/60] SAAS-650: Fix up player page UI in Airtime --- .../application/controllers/EmbeddableplayerController.php | 3 +++ airtime_mvc/application/forms/EmbeddablePlayer.php | 5 +++++ .../application/views/scripts/embeddableplayer/index.phtml | 6 ++---- .../application/views/scripts/form/embeddableplayer.phtml | 5 ++++- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/airtime_mvc/application/controllers/EmbeddableplayerController.php b/airtime_mvc/application/controllers/EmbeddableplayerController.php index d7a16104b..ec16317b5 100644 --- a/airtime_mvc/application/controllers/EmbeddableplayerController.php +++ b/airtime_mvc/application/controllers/EmbeddableplayerController.php @@ -9,6 +9,9 @@ class EmbeddablePlayerController extends Zend_Controller_Action public function indexAction() { + $CC_CONFIG = Config::getConfig(); + $baseUrl = Application_Common_OsPath::getBaseDir(); + $this->view->headLink()->appendStylesheet($baseUrl.'css/embeddableplayer.css?'.$CC_CONFIG['airtime_version']); $form = new Application_Form_EmbeddablePlayer(); if ($form->getElement('player_stream_url')->getAttrib('numberOfEnabledStreams') > 0) { diff --git a/airtime_mvc/application/forms/EmbeddablePlayer.php b/airtime_mvc/application/forms/EmbeddablePlayer.php index 520a5318b..a77ae5f1d 100644 --- a/airtime_mvc/application/forms/EmbeddablePlayer.php +++ b/airtime_mvc/application/forms/EmbeddablePlayer.php @@ -34,8 +34,13 @@ class Application_Form_EmbeddablePlayer extends Zend_Form_SubForm $embedSrc = new Zend_Form_Element_Text('player_embed_src'); $embedSrc->setAttrib("readonly", "readonly"); $embedSrc->setValue('<iframe frameborder="0" src="'.Application_Common_HTTPHelper::getStationUrl().'/embeddableplayer/embed-code?url='.$url.'&codec='.$codec.'"></iframe>'); + $embedSrc->setAttrib("class", "embed-player-text-box"); $embedSrc->removeDecorator('label'); $this->addElement($embedSrc); + $previewLabel = new Zend_Form_Element_Text('player_preview_label'); + $previewLabel->setLabel("Preview:"); + $this->addElement($previewLabel); + } } \ No newline at end of file diff --git a/airtime_mvc/application/views/scripts/embeddableplayer/index.phtml b/airtime_mvc/application/views/scripts/embeddableplayer/index.phtml index f762892b4..7a069af6d 100644 --- a/airtime_mvc/application/views/scripts/embeddableplayer/index.phtml +++ b/airtime_mvc/application/views/scripts/embeddableplayer/index.phtml @@ -1,14 +1,12 @@ -<div class="ui-widget ui-widget-content block-shadow simple-formblock clearfix padded-strong preferences"> +<div class="ui-widget ui-widget-content block-shadow simple-formblock embed-player-form clearfix padded-strong preferences"> <h2 style="float:left"><?php echo _("Embeddable Player") ?></h2> <?php $baseUrl = Application_Common_OsPath::getBaseDir(); ?> <form method="post" id="player_form" enctype="multipart/form-data"> - - + <div style="clear:both"></div> <?php echo $this->errorMsg; ?> <?php echo $this->form; ?> - <br /> </form> </div> diff --git a/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml b/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml index d4f481199..5153189e8 100644 --- a/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml +++ b/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml @@ -7,7 +7,10 @@ <?php echo $this->element->getElement('player_stream_url'); ?> + <?php echo $this->element->getElement('player_preview_label')->renderLabel(); ?> <div style="clear:both"></div> - <?php echo $this->element->getElement('player_embed_src')->getValue(); ?> + <div id="embed_player_preview"> + <?php echo $this->element->getElement('player_embed_src')->getValue(); ?> + </div> </dl> </fieldset> \ No newline at end of file From ed891fb145647ad7cd3d222fde6fc15748af36a1 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Wed, 18 Mar 2015 14:23:38 -0400 Subject: [PATCH 18/60] Committing lots of trial and error embed player code changes --- .../EmbeddableplayerController.php | 14 +++++--- .../application/forms/EmbeddablePlayer.php | 4 ++- .../scripts/embeddableplayer/embed-code.phtml | 35 ++++++++++++++----- .../scripts/embeddableplayer/index.phtml | 4 +-- .../views/scripts/form/embeddableplayer.phtml | 22 ++++++++++++ airtime_mvc/public/css/embeddableplayer.css | 14 ++++++++ .../embeddableplayer/embeddableplayer.js | 28 +++++++++++++++ .../public/js/airtime/embeddableplayer/mrp.js | 3 +- 8 files changed, 107 insertions(+), 17 deletions(-) create mode 100644 airtime_mvc/public/css/embeddableplayer.css create mode 100644 airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js diff --git a/airtime_mvc/application/controllers/EmbeddableplayerController.php b/airtime_mvc/application/controllers/EmbeddableplayerController.php index ec16317b5..6c2ccfbd8 100644 --- a/airtime_mvc/application/controllers/EmbeddableplayerController.php +++ b/airtime_mvc/application/controllers/EmbeddableplayerController.php @@ -12,6 +12,10 @@ class EmbeddablePlayerController extends Zend_Controller_Action $CC_CONFIG = Config::getConfig(); $baseUrl = Application_Common_OsPath::getBaseDir(); $this->view->headLink()->appendStylesheet($baseUrl.'css/embeddableplayer.css?'.$CC_CONFIG['airtime_version']); + $this->view->headScript()->appendFile($baseUrl.'js/airtime/embeddableplayer/mrp.js?'.$CC_CONFIG['airtime_version']); + $this->view->headScript()->appendFile($baseUrl.'js/airtime/embeddableplayer/embeddableplayer.js?'.$CC_CONFIG['airtime_version']); + + $form = new Application_Form_EmbeddablePlayer(); if ($form->getElement('player_stream_url')->getAttrib('numberOfEnabledStreams') > 0) { @@ -28,11 +32,13 @@ class EmbeddablePlayerController extends Zend_Controller_Action $request = $this->getRequest(); - $this->view->mrp_js = Application_Common_HTTPHelper::getStationUrl() . "/js/airtime/embeddableplayer/mrp.js"; - $this->view->muses_swf = Application_Common_HTTPHelper::getStationUrl() . "/js/airtime/embeddableplayer/muses.swf"; - $this->view->skin = Application_Common_HTTPHelper::getStationUrl() . "/js/airtime/embeddableplayer/ffmp3-mcclean.xml"; + $this->view->mrp_js = Application_Common_HTTPHelper::getStationUrl() . "js/airtime/embeddableplayer/mrp.js"; + $this->view->muses_swf = Application_Common_HTTPHelper::getStationUrl() . "js/airtime/embeddableplayer/muses.swf"; + $this->view->skin = Application_Common_HTTPHelper::getStationUrl() . "js/airtime/embeddableplayer/ffmp3-mcclean.xml"; $this->view->codec = $request->getParam('codec'); - $this->view->streamURL = $request->getParam('url'); + //$this->view->streamURL = $request->getParam('url'); + //$stream = $request->getParam('stream'); + $this->view->streamURL = "http://127.0.0.1:8000/airtime_128"; $this->view->displayMetadata = $request->getParam('display_metadata'); } } \ No newline at end of file diff --git a/airtime_mvc/application/forms/EmbeddablePlayer.php b/airtime_mvc/application/forms/EmbeddablePlayer.php index a77ae5f1d..febf1379a 100644 --- a/airtime_mvc/application/forms/EmbeddablePlayer.php +++ b/airtime_mvc/application/forms/EmbeddablePlayer.php @@ -22,6 +22,7 @@ class Application_Form_EmbeddablePlayer extends Zend_Form_SubForm $streamURL->setMultiOptions( $urlOptions ); + Logging::info($urlOptions); $streamURL->setValue(array_keys($urlOptions)[0]); $streamURL->setLabel(_('Select stream:')); $streamURL->setAttrib('codec', array_values($urlOptions)[0]); @@ -33,8 +34,9 @@ class Application_Form_EmbeddablePlayer extends Zend_Form_SubForm $embedSrc = new Zend_Form_Element_Text('player_embed_src'); $embedSrc->setAttrib("readonly", "readonly"); - $embedSrc->setValue('<iframe frameborder="0" src="'.Application_Common_HTTPHelper::getStationUrl().'/embeddableplayer/embed-code?url='.$url.'&codec='.$codec.'"></iframe>'); $embedSrc->setAttrib("class", "embed-player-text-box"); + //$embedSrc->setValue('<iframe frameborder="0" src="'.Application_Common_HTTPHelper::getStationUrl().'embeddableplayer/embed-code?url='.$url.'&codec='.$codec.'"></iframe>'); + $embedSrc->setValue('<iframe frameborder="0" src="'.Application_Common_HTTPHelper::getStationUrl().'embeddableplayer/embed-code?stream=stream1&codec='.$codec.'"></iframe>'); $embedSrc->removeDecorator('label'); $this->addElement($embedSrc); diff --git a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml index 73db907b7..cc5e96804 100644 --- a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml +++ b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml @@ -3,16 +3,33 @@ <head> <script src="<?php echo $this->mrp_js?>" type="text/javascript"></script> + <script type="text/javascript"> + + function musesCallback(event,value){ + if (event == "source") { + //MRP.setUrl("http://sourcefabric.out.airtime.pro:8000/sourcefabric_b"); + } + } + </script> </head> +<body> -<object classid="" width="180" height="60" bgcolor="#FFFFFF"> - <param name="movie" value="muses.swf" /> - <param name="flashvars" value="url=<?php echo $this->streamURL?>&lang=auto&codec=<?php echo $this->codec?>&volume=100&introurl=&tracking=true&jsevents=true&buffering=5&skin=<?php echo $this->skin?>&title=''" /> - <param name="wmode" value="window" /> - <param name="allowscriptaccess" value="always" /> - <param name="bgcolor" value="#FFFFFF" /> - <param name="scale" value="noscale" /> - <embed src="<?php echo $this->muses_swf?>" flashvars="url=<?php echo $this->streamURL?>&lang=auto&codec=<?php echo $this->codec?>&volume=100&introurl=&tracking=true&jsevents=true&buffering=5&skin=<?php echo $this->skin?>&title=''" width="180" scale="noscale" height="60" wmode="window" bgcolor="#FFFFFF" allowscriptaccess="always" type="application/x-shockwave-flash" /> -</object> +<script type="text/javascript"> + MRP.insert({ + 'url':"<?php echo $this->streamURL ?>", + 'codec':"<?php echo $this->codec ?>", + 'volume':100, + 'jsevents':true, + 'autoplay':false, + 'buffering':5, + 'title':'test', + 'bgcolor':'#FFFFFF', + 'skin':"<?php echo $this->skin ?>", + 'width':180, + 'height':60 + }); +</script> + +</body> </html> \ No newline at end of file diff --git a/airtime_mvc/application/views/scripts/embeddableplayer/index.phtml b/airtime_mvc/application/views/scripts/embeddableplayer/index.phtml index 7a069af6d..e4c37849b 100644 --- a/airtime_mvc/application/views/scripts/embeddableplayer/index.phtml +++ b/airtime_mvc/application/views/scripts/embeddableplayer/index.phtml @@ -1,9 +1,9 @@ -<div class="ui-widget ui-widget-content block-shadow simple-formblock embed-player-form clearfix padded-strong preferences"> +<div class="ui-widget ui-widget-content block-shadow simple-formblock embed-player-form clearfix padded-strong "> <h2 style="float:left"><?php echo _("Embeddable Player") ?></h2> <?php $baseUrl = Application_Common_OsPath::getBaseDir(); ?> <form method="post" id="player_form" enctype="multipart/form-data"> - + <div style="clear:both"></div> <?php echo $this->errorMsg; ?> <?php echo $this->form; ?> diff --git a/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml b/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml index 5153189e8..dacb80cd7 100644 --- a/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml +++ b/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml @@ -8,9 +8,31 @@ <?php echo $this->element->getElement('player_stream_url'); ?> <?php echo $this->element->getElement('player_preview_label')->renderLabel(); ?> + + <div style="clear:both"></div> + <div id="custom_muses_player"> + <a href="#"><div id="muses_play">play</div></a> + <a href="#"><div id="muses_stop">stop</div></a> + </div> + + <!-- + <div style="clear:both"></div> + <object classid="" width="180" height="60" bgcolor="#FFFFFF"> + <param name="movie" value="muses.swf" /> + <param name="flashvars" value="url=<?php echo $this->streamURL?>&lang=auto&codec=<?php echo $this->codec?>&volume=100&introurl=&tracking=true&jsevents=true&buffering=5&title=''%skin=''" /> + <param name="wmode" value="window" /> + <param name="allowscriptaccess" value="always" /> + <param name="bgcolor" value="#FFFFFF" /> + <param name="scale" value="noscale" /> + <embed src="<?php echo $this->muses_swf?>" flashvars="url=<?php echo $this->streamURL?>&lang=auto&codec=<?php echo $this->codec?>&volume=100&introurl=&tracking=true&jsevents=true&buffering=5&title=''&skin=''" width="180" scale="noscale" height="60" wmode="window" bgcolor="#FFFFFF" allowscriptaccess="always" type="application/x-shockwave-flash" /> + </object> + --> + <div style="clear:both"></div> <div id="embed_player_preview"> <?php echo $this->element->getElement('player_embed_src')->getValue(); ?> </div> + + </dl> </fieldset> \ No newline at end of file diff --git a/airtime_mvc/public/css/embeddableplayer.css b/airtime_mvc/public/css/embeddableplayer.css new file mode 100644 index 000000000..a1e12cbc0 --- /dev/null +++ b/airtime_mvc/public/css/embeddableplayer.css @@ -0,0 +1,14 @@ +.embed-player-text-box { + padding-right: 0px !important; + width: 100% !important; +} +.embed-player-form { + width: 70%; +} +.embed-player-form dd { + width: 100% !important; + float: left; + margin: 0; + padding: 4px 0px 4px 0px; +} + diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js b/airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js new file mode 100644 index 000000000..ece8902a3 --- /dev/null +++ b/airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js @@ -0,0 +1,28 @@ +function generateEmbedSrc() +{ + document.getElementById('embed_player_preview').textContent=""; +} + +function setupPlayer() +{ + MRP.insert({ + 'url':"http://127.0.0.1:8000/airtime_128", + 'codec':"mp3", + 'volume':100, + 'jsevents':true, + 'autoplay':false, + 'buffering':5, + 'title':'test', + 'bgcolor':'#FFFFFF', + 'skin':-1, + 'width':180, + 'height':60 + }); +} + +window.onload = function() { + setupPlayer(); + document.getElementById('player_display_track_metadata').onchange = generateEmbedSrc; + document.getElementById('muses_play').click = MRP.play(); + document.getElementById('muses_stop').click = MRP.stop(); +} diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/mrp.js b/airtime_mvc/public/js/airtime/embeddableplayer/mrp.js index 74bc30836..3308a8f55 100644 --- a/airtime_mvc/public/js/airtime/embeddableplayer/mrp.js +++ b/airtime_mvc/public/js/airtime/embeddableplayer/mrp.js @@ -203,7 +203,8 @@ c + ("&skin=" + g.getSkin(a.skin, !0)), c = c + ("&title=" + a.title), c = c + ("&welcome=" + a.welcome), - b = g.getScriptBaseHREF() + "/muses-hosted.swf", + //b = g.getScriptBaseHREF() + "/muses-hosted.swf", + b = "http://localhost/js/airtime/embeddableplayer/muses.swf", e = 'width="' + a.width + '" height="' + a.height + '" '; null != a.bgcolor && (e += 'bgcolor="' + a.bgcolor + '" '); var f = '<object id="' + a.id + '" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" ' + e + ">", From bafd9eeb6f6cce3c494803869803b326ff253db5 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Wed, 18 Mar 2015 15:03:34 -0400 Subject: [PATCH 19/60] Cleaning up player code --- .../views/scripts/form/embeddableplayer.phtml | 19 ------------- .../embeddableplayer/embeddableplayer.js | 27 +++---------------- 2 files changed, 4 insertions(+), 42 deletions(-) diff --git a/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml b/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml index dacb80cd7..815c9abf0 100644 --- a/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml +++ b/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml @@ -9,25 +9,6 @@ <?php echo $this->element->getElement('player_preview_label')->renderLabel(); ?> - <div style="clear:both"></div> - <div id="custom_muses_player"> - <a href="#"><div id="muses_play">play</div></a> - <a href="#"><div id="muses_stop">stop</div></a> - </div> - - <!-- - <div style="clear:both"></div> - <object classid="" width="180" height="60" bgcolor="#FFFFFF"> - <param name="movie" value="muses.swf" /> - <param name="flashvars" value="url=<?php echo $this->streamURL?>&lang=auto&codec=<?php echo $this->codec?>&volume=100&introurl=&tracking=true&jsevents=true&buffering=5&title=''%skin=''" /> - <param name="wmode" value="window" /> - <param name="allowscriptaccess" value="always" /> - <param name="bgcolor" value="#FFFFFF" /> - <param name="scale" value="noscale" /> - <embed src="<?php echo $this->muses_swf?>" flashvars="url=<?php echo $this->streamURL?>&lang=auto&codec=<?php echo $this->codec?>&volume=100&introurl=&tracking=true&jsevents=true&buffering=5&title=''&skin=''" width="180" scale="noscale" height="60" wmode="window" bgcolor="#FFFFFF" allowscriptaccess="always" type="application/x-shockwave-flash" /> - </object> - --> - <div style="clear:both"></div> <div id="embed_player_preview"> <?php echo $this->element->getElement('player_embed_src')->getValue(); ?> diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js b/airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js index ece8902a3..345e283ed 100644 --- a/airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js +++ b/airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js @@ -1,28 +1,9 @@ +window.onload = function() { + document.getElementById('player_display_track_metadata').onchange = generateEmbedSrc; +} + function generateEmbedSrc() { document.getElementById('embed_player_preview').textContent=""; } -function setupPlayer() -{ - MRP.insert({ - 'url':"http://127.0.0.1:8000/airtime_128", - 'codec':"mp3", - 'volume':100, - 'jsevents':true, - 'autoplay':false, - 'buffering':5, - 'title':'test', - 'bgcolor':'#FFFFFF', - 'skin':-1, - 'width':180, - 'height':60 - }); -} - -window.onload = function() { - setupPlayer(); - document.getElementById('player_display_track_metadata').onchange = generateEmbedSrc; - document.getElementById('muses_play').click = MRP.play(); - document.getElementById('muses_stop').click = MRP.stop(); -} From 875ed0e41ec01a70bdab3d3508233919e09691bc Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Thu, 19 Mar 2015 13:42:19 -0400 Subject: [PATCH 20/60] Got muses working without a skin Had to leave the skin visible but "hide" it by setting the width and height to 1px --- .../scripts/embeddableplayer/embed-code.phtml | 47 ++++++++++++------- .../public/js/airtime/embeddableplayer/mrp.js | 4 +- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml index cc5e96804..26070fabf 100644 --- a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml +++ b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml @@ -10,26 +10,41 @@ //MRP.setUrl("http://sourcefabric.out.airtime.pro:8000/sourcefabric_b"); } } - </script> + + </script> + <style type="text/css"> + #muses_skin{width:1px; height:1px; overflow-x: hidden; overflow-y: hidden;} + </style> </head> + <body> -<script type="text/javascript"> - MRP.insert({ - 'url':"<?php echo $this->streamURL ?>", - 'codec':"<?php echo $this->codec ?>", - 'volume':100, - 'jsevents':true, - 'autoplay':false, - 'buffering':5, - 'title':'test', - 'bgcolor':'#FFFFFF', - 'skin':"<?php echo $this->skin ?>", - 'width':180, - 'height':60 - }); +<div id="muses_skin"> + <script type="text/javascript"> -</script> + + MRP.insert({ + 'url':"<?php echo $this->streamURL ?>", + 'codec':"<?php echo $this->codec ?>", + 'volume':100, + 'jsevents':true, + 'autoplay':false, + 'buffering':5, + 'title':'test', + 'bgcolor':'#FFFFFF', + 'skin':'mcclean', + 'width':180, + 'height':60 + }); + </script> +</div> + +<div id="custom_muses_play" onclick="MRP.play()"> + <a href="#">play</a> +</div> +<div id="custom_muses_stop" onclick="MRP.stop()"> + <a href="#">stop</a> +</div> </body> </html> \ No newline at end of file diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/mrp.js b/airtime_mvc/public/js/airtime/embeddableplayer/mrp.js index 3308a8f55..4377dddd5 100644 --- a/airtime_mvc/public/js/airtime/embeddableplayer/mrp.js +++ b/airtime_mvc/public/js/airtime/embeddableplayer/mrp.js @@ -203,8 +203,8 @@ c + ("&skin=" + g.getSkin(a.skin, !0)), c = c + ("&title=" + a.title), c = c + ("&welcome=" + a.welcome), - //b = g.getScriptBaseHREF() + "/muses-hosted.swf", - b = "http://localhost/js/airtime/embeddableplayer/muses.swf", + b = g.getScriptBaseHREF() + "/muses-hosted.swf", + //b = "http://localhost/js/airtime/embeddableplayer/muses.swf", e = 'width="' + a.width + '" height="' + a.height + '" '; null != a.bgcolor && (e += 'bgcolor="' + a.bgcolor + '" '); var f = '<object id="' + a.id + '" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" ' + e + ">", From 16ddf09d6d20932cdc3bc52b8d9fbc859fd7691b Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Thu, 19 Mar 2015 16:59:05 -0400 Subject: [PATCH 21/60] SAAS-655: Extract muses player events into our own js object --- .../scripts/embeddableplayer/embed-code.phtml | 35 ++++++++++++++----- .../views/scripts/form/embeddableplayer.phtml | 5 +-- .../embeddableplayer/embeddableplayer.js | 2 +- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml index 26070fabf..adba9fd8a 100644 --- a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml +++ b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml @@ -5,10 +5,31 @@ <script src="<?php echo $this->mrp_js?>" type="text/javascript"></script> <script type="text/javascript"> - function musesCallback(event,value){ - if (event == "source") { - //MRP.setUrl("http://sourcefabric.out.airtime.pro:8000/sourcefabric_b"); - } + var MusesPlayer = function() { + this.flashDetect = FlashDetect.versionAtLeast(10, 1) ? true : false; + }; + MusesPlayer.prototype.play = function() { + this.flashDetect ? MRP.play() : musesHTMLPlayClick(); + }; + MusesPlayer.prototype.stop = function() { + this.flashDetect ? MRP.stop() : musesHTMLStopClick(); + }; + //TODO: setVolume, setURL + + var musesPlayer = new MusesPlayer(); + + function musesHTMLPlayClick() { + //child nodes + var cn = document.getElementById("MusesRadioPlayer-HTML5-player-1").childNodes; + var playDiv = cn[4]; + playDiv.onclick(); + } + + function musesHTMLStopClick() { + //child nodes + var cn = document.getElementById("MusesRadioPlayer-HTML5-player-1").childNodes; + var stopDiv = cn[5]; + stopDiv.onclick(); } </script> @@ -21,8 +42,6 @@ <div id="muses_skin"> <script type="text/javascript"> - - MRP.insert({ 'url':"<?php echo $this->streamURL ?>", 'codec':"<?php echo $this->codec ?>", @@ -39,10 +58,10 @@ </script> </div> -<div id="custom_muses_play" onclick="MRP.play()"> +<div id="custom_muses_play" onclick="musesPlayer.play()"> <a href="#">play</a> </div> -<div id="custom_muses_stop" onclick="MRP.stop()"> +<div id="custom_muses_stop" onclick="musesPlayer.stop()"> <a href="#">stop</a> </div> diff --git a/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml b/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml index 815c9abf0..aed5bc181 100644 --- a/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml +++ b/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml @@ -10,10 +10,7 @@ <?php echo $this->element->getElement('player_preview_label')->renderLabel(); ?> <div style="clear:both"></div> - <div id="embed_player_preview"> - <?php echo $this->element->getElement('player_embed_src')->getValue(); ?> - </div> - + <?php echo $this->element->getElement('player_embed_src')->getValue(); ?> </dl> </fieldset> \ No newline at end of file diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js b/airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js index 345e283ed..d228625af 100644 --- a/airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js +++ b/airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js @@ -4,6 +4,6 @@ window.onload = function() { function generateEmbedSrc() { - document.getElementById('embed_player_preview').textContent=""; + } From 20d774874ecef8306d4956e09f490aa6e7c73319 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Fri, 20 Mar 2015 07:35:18 -0400 Subject: [PATCH 22/60] SAAS-655: Extract muses player events into our own js object Added skeleton functions for setVolume and setURL --- .../scripts/embeddableplayer/embed-code.phtml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml index adba9fd8a..8d953d7a4 100644 --- a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml +++ b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml @@ -8,13 +8,23 @@ var MusesPlayer = function() { this.flashDetect = FlashDetect.versionAtLeast(10, 1) ? true : false; }; + MusesPlayer.prototype.play = function() { this.flashDetect ? MRP.play() : musesHTMLPlayClick(); }; + MusesPlayer.prototype.stop = function() { this.flashDetect ? MRP.stop() : musesHTMLStopClick(); }; - //TODO: setVolume, setURL + + MusesPlayer.prototype.setVolume = function(value) { + //this.flashDetect ? MRP.setVolume(value) : null; + //TODO - maybe + }; + + MusesPlayer.prototype.setURL = function() { + //TODO + }; var musesPlayer = new MusesPlayer(); @@ -55,6 +65,8 @@ 'width':180, 'height':60 }); + + musesPlayer.setVolume(50); </script> </div> From 28786e7bf11940db0ff6f072378129134d8c9d47 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Fri, 20 Mar 2015 07:41:10 -0400 Subject: [PATCH 23/60] SAAS-655: Extract muses player events into our own js object Moved player creation into musesPlayer constructor --- .../scripts/embeddableplayer/embed-code.phtml | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml index 8d953d7a4..adab3876e 100644 --- a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml +++ b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml @@ -6,6 +6,20 @@ <script type="text/javascript"> var MusesPlayer = function() { + MRP.insert({ + 'url':"<?php echo $this->streamURL ?>", + 'codec':"<?php echo $this->codec ?>", + 'volume':100, + 'jsevents':true, + 'autoplay':false, + 'buffering':5, + 'title':'test', + 'bgcolor':'#FFFFFF', + 'skin':'mcclean', + 'width':180, + 'height':60 + }); + this.flashDetect = FlashDetect.versionAtLeast(10, 1) ? true : false; }; @@ -26,8 +40,6 @@ //TODO }; - var musesPlayer = new MusesPlayer(); - function musesHTMLPlayClick() { //child nodes var cn = document.getElementById("MusesRadioPlayer-HTML5-player-1").childNodes; @@ -52,21 +64,7 @@ <div id="muses_skin"> <script type="text/javascript"> - MRP.insert({ - 'url':"<?php echo $this->streamURL ?>", - 'codec':"<?php echo $this->codec ?>", - 'volume':100, - 'jsevents':true, - 'autoplay':false, - 'buffering':5, - 'title':'test', - 'bgcolor':'#FFFFFF', - 'skin':'mcclean', - 'width':180, - 'height':60 - }); - - musesPlayer.setVolume(50); + var musesPlayer = new MusesPlayer(); </script> </div> From 0764ca60e6d418e6770594731f9c1b4b29ed3d79 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Fri, 20 Mar 2015 09:09:09 -0400 Subject: [PATCH 24/60] Delete muses skin files --- .../airtime/embeddableplayer/ffmp3-mcclean.xml | 10 ---------- .../embeddableplayer/ffmp3-mcclean/bg.png | Bin 3649 -> 0 bytes .../embeddableplayer/ffmp3-mcclean/holder.png | Bin 830 -> 0 bytes .../embeddableplayer/ffmp3-mcclean/play.gif | Bin 1493 -> 0 bytes .../embeddableplayer/ffmp3-mcclean/playclick.jpg | Bin 1422 -> 0 bytes .../ffmp3-mcclean/statusplay.png | Bin 138 -> 0 bytes .../ffmp3-mcclean/statusstop.png | Bin 136 -> 0 bytes .../embeddableplayer/ffmp3-mcclean/stop.jpg | Bin 1459 -> 0 bytes .../embeddableplayer/ffmp3-mcclean/stopclick.jpg | Bin 1459 -> 0 bytes 9 files changed, 10 deletions(-) delete mode 100644 airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean.xml delete mode 100644 airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/bg.png delete mode 100644 airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/holder.png delete mode 100644 airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/play.gif delete mode 100644 airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/playclick.jpg delete mode 100644 airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/statusplay.png delete mode 100644 airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/statusstop.png delete mode 100644 airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/stop.jpg delete mode 100644 airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/stopclick.jpg diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean.xml b/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean.xml deleted file mode 100644 index 57d7c3eab..000000000 --- a/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean.xml +++ /dev/null @@ -1,10 +0,0 @@ -<ffmp3-skin folder="ffmp3-mcclean"> - <bg image="bg.png" x="0" y="0" /> - <play image="play.gif" x="8" y="29" clickimage="playclick.jpg" /> - <stop image="stop.jpg" x="40" y="29" clickimage="stopclick.jpg" /> - <text x="13" y="9" width="154" height="17" color="#ffffff" font="Arial" size="11" /> - <songtitle x="12" y="83" width="210" height="22" color="#000000" font="Arial" size="12" /> - <artist x="12" y="103" width="210" height="22" color="#000000" font="Arial" size="12" /> - <volume mode="holder" x="71" y="33" width="100" height="17" holderImage="holder.png" /> - <status imagePlay="statusplay.png" imageStop="statusstop.png" x="13" y="34" /> -</ffmp3-skin> \ No newline at end of file diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/bg.png b/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/bg.png deleted file mode 100644 index a399e16be7eeeb3a6a26d2488b98293dbf0eb29e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3649 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 T`Az`^00000NkvXXu0mjf8;ca6 diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/holder.png b/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/holder.png deleted file mode 100644 index 8b61d23458835ad36e762af595eb72011e809860..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 830 zcmV-E1Ht@>P)<h;3K|Lk000e1NJLTq000pH000mO1^@s7hc=|i00004XF*Lt006JZ zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!&`Cr=RCwBA zU;u*e-@pF{(M&+B+||_uVk-j#5aY39$F#trH*em&s;#Yk6(E2>9M-2#pZ<@DiTUs5 z<_6Nk3KC@e_U+rB|3CnuL41G!0tqsF{P^(>2!mC;fBzmN3zp#G;$mQ8W@cvCwQCmx z4-XFmhz$b%fByXW55gceKmZn~90x!U2(C25R!V&a576G0g3plfGI<IiRs<Sa3*12` znN0RRh9`t@=A3&<smB<H!iJyzNo%e3w$_r?nxvHEoTE|-2_Z-+wIOlg%7!}u@BL6? z3_3Q%C;EVccILsH@f+raO_<Ls01ae}!XOMr;g>eLiByUXLZD}H3<Ns4c5{+mz$-Ww zyhX3jn{+8ILOQfdruKhg0xvxN_Xzp^tphdB^G$F9MIWJg?Y;MJ2{IvB+gfWfe~O|w z?1cFmLdcX-==&Z?lAx|@aK=m<W4g9&QB@VJwMf$x&N<|Hj$s%&y)4VOX&OjzeV&D~ zELYErd$<slnx^R&Jr6S$kK;H#;<_mw&X_6jkPmWmXVGcHev0mrz5W6;O~tAa0#R^7 z4k9E)QifAfr!*g6Z6lZ_wT-Q$wi5h-Lu~woyCPzfUND_l<Qqcl(kKW<&CJF%c`)eP zoww|pH><>AoHtGLh`Ssh_kJ8lF-_CoCML@c=Xw5s($A)8zF>J$ZaedR{}z^d*LD3g z48y+bI!Ym^o$I>t%iOkY!F}?`XPpxLx~}V2m|$t!c7x+MOBjX{%q`23Uk1HPKI>S& zqX96XdpahLV=1SmX;ObW3Rjmh=XoxIAP`BC2*WU>!&t|99x|3?`G(hVm!|2aL6&9P zKM$c*i=q&H-*0QIQwF;L9-=6s9y5Ah6c}tr<!q5b9{H?W>wkR*&>~Ns=N+r6`h>XO zn8c#Moa@{tZ{_dA_J;qB8Mp*)fUA{64&N$m-T_}gug1&x0ct_DHu;mWmjD0&07*qo IM6N<$g2~ExeE<Le diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/play.gif b/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/play.gif deleted file mode 100644 index 150591ffc95dfe9d45c6815eea1dce1f50c08941..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1493 zcmWlYc|6p47{`Z@tS(zANoYE05u3!8#EdI7n@T6bW=F3!t=H;M;U$K(vayV#gACF* z29;?j#x>)Z`OSSFznS0Mv_z!5+WmQdpXc*=KF|02?-w10I^Y-Yh;Ts6BM=841p4P5 z2`D^zsPM>7kB;n8c>KWK?;-c~6CZK|ecjYQdaL}y3!=h`A_9xif#s->l2CL>#8Est z=m|QQfC_933vLNV6QhD(9Yei}LU%=lzdnI^c`~~592R#qrSfW0SMnXw{aci~fugV= zad?CXb4+#yt;R${m(D8_VxcQo$#0jT<P2>N4u{Ln&o3w_C@wB8DJdx{E5qaQ6%`fL z)zyz5KhC6NJ!*PXMl3IHEw5~?u5PWux8ZAQY6t{EeSLjnV`Fo3b8BlWi9~8|Z|~^n zc=P5>S63IAOr}sM-QC?iJw5N<y?g)ueKsGLsm490J|_&ls2^^uqc;$k4fSIUy}iAC zF#Xj2J}OLqe?OHv05do^I5adgJUmRJ(dcyg$jHd(=;+wk*!cK3lR3^{FqliQ#sDS* zfE{20Y=FUJF#!NzumA?YX0ka9IIvhO0ARCN95%q=u-LG%*=!Dn!)9~Y93F=U3zx&? z^8hZF2k?0uF6=xWmnY!!`2rZ8kS`Dk1R}n0NdzLHkRuQZcp`yNBoK>40*O>06beO4 zNJJ8$NFo*sMQ}^RLa|sZk-%9hkx3O2u}lK1R3;V5;7Kl#N~JQHOe&R2<uaK<s!+(} za=AhQKT*o0pj-he6rfzCQA$Nhg-ofGgP=mGQYe*55Cl~!m0GP<f*KX5RjV~h4XA?P zf=5UVX(1&Dsnig>hEy7j8iF8=Mgu`wjZUl8LU5}gy;iH&>I^!aL95rmqBX&zUZ*$e zbsG3$5)KAPZ`2tLI{gwx_?QevBkV?_K|g6QPMA!Srpbv3BWx4@nkFVD^%F~cfSH<_ znwgpTH2-mScJ|XeTyy^+5OZ_0b8~a^^9!FpfByRQtIL9g83OSI;f{b=`V$E2Z-|5y z7Y2=j&Q!^oUp5b~bR?&$ySG=*rTi0kmJ~x`bN`edzPC6Bn(~g<^<+3NEKqbzMZoD= zpKFPlhnKo<O+)0A;UXEOYC8L)?d^xLjVE3PZGYR)`r_mh@aAjvuSFMmO#wO=yTRmB z{tZ#QnvSNuQopohR#8gcA9B|)Y{p;9B-{&KX&T_JB==esIcIY5OH$6xLLxb>C!V6s zLi?u3Yr`CU_ud#deB~{%5_Kl_6_)MbsrPE`G|=h=#dJHH7+n%jkZQfQFV8!9w9)VU zkZpzc73@IV3Jr0F6wlZn*k=|$?2#~5YPN01mW`F3$9)!znwJ!h_@0~JwvVn!dSGpS zFk_OvexhvNbbJGydLWkX7DJ9Zo7`7xVQuCo{P{cgHg3Ar#wh2Y;z(jwP{i)0=whn* z@{NEQqN|fSvZp0EddNNs**=0pwq%dE*G0w!ncFlzGh1I*cX?zt@}7QRD|!ooj=^WL z>C2GoGR>_{$Mxs$d|KE}$0VkK7ETq3)X-AQx!M)TRnFI`mS>_*(zlhz*ySTJMXZqT z(>-gorG+O{*2qFzPjkyt0nh78Q=0s&>?4V!P}h9-Y?^C<&kY*VEY#C-U#5-CF4xQ* zt|<G+XIV4?epL|8B|F18#Jl{PnSqcW=U`<YaUIOtdXxAT>(Y7K!hE%7<}9me>;1L@ zbjv-TzK{*U%GBjf)GyPyTW-@xc}^Kz)*e#xDmVAF@i}WPD$^71<T<7GW(_;04@_G* zrP$s<lSK|*!JDwVFh`$aORG?uuho%IPHAnJ4-V5!sd)}4;Z$&U!aA4!m7$5O2lnTE zh?e%Ne|Pb+zZjQlvC`WaGvu&Xlr^y0t6NE>A<Hl9v)zXvSx^?6vT`?FbpPAJ&PQ;y SV?4}fdT<kV)^QC2;r~A$;C_q% diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/playclick.jpg b/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/playclick.jpg deleted file mode 100644 index 48bbd4954a037eefc63f038727b538a96ec2b0da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1422 zcma)5X;4#F6u$Spmjwiym#}7<vQt_?2^gxOWf6?ZqJkJ|*+~=;I}kNu+!}V2HAn!h zr9>f4#u;(JDJTqtB4O1k%Ya}(1}#&S8bMnaHGKg^e|pZ`nLFov-&ya~4e7=KEr7R) z2Ot7~kWip|3UGh1AV~rsz<}Nebko4mSC|~X69`)f3q*k_hLR190YJh5Kx+ZOssJG6 z>qbB(fS)eN2SJzQ9UXy?@DgXq@u?SGE5NCM2|h<54iFroIMlU)E}~FEKN1p|f?@`X z={_<8j6@;R0FfI-NlSJVLr8!I$<_>0x|^N58BR<AK`|-?zJnkTN9h=_VX)&&NWQji z@#*gNoHWHgzw<&59$k;dkY!8ZAdaBGn#PW!-^FZvLqur`wbu43i2;ecfDEzsg+km0 zuxZxMeM90zX)?vbE9$$t`@je#a5#z+tC*TH+?~5EQ7e_b_>-x9#`S&kPuRSJS{~TE z-J)d;bFznkQy~P9gm7Ey5)SV<)IFopc)s%Dns$45MfN8}O*~^KMvTlo(Xa*^C#Yjh zq^dRZc9R#6{TEZ~y+B6v<W7r}%Er>G%lW-3m4c677+7$j3$Dw!dGszx?REASwoDdh zcvbi!4Iz6Y%JvsU2Y;w3Zb*xIVYS($uQ#*2FG?gVAvt&Hz+#96n4g`rWc_{5ZJujW z+f6lxHqTfY3YvZF-wMTy+GZZR+jVrRJh|W<1wtZnLv+GV4o!_;3lewJOG9X_>n#Ia zD9jFF{LSdnFku_}Oa~>Uh$`=&QuZ{6hi7hun2ypN<DA3Gk=Q81JXL{lOhj8s<WAe! zgY{>a1wVe4C7EuKx8c1(GZlPJ=nc&oubv(C(5Uh1T>H9nt1T|giN3-gDP>AA*Xh~~ z_lGOLEDR7+U6iF^%O5|$ohq`nhI0-a=2dT~jykpF-xzIdgx8h7ngVKAT_@`wm8aga z%~?4(+YxNs8j#W%-10_k;L;}Z;KmB_xB7?5@E{pO!7vV$416O|o&P(z@_X<7LzN>V z+G)*vqt)yi`OMK~t5nvSg@Hj7dfS}U0f+HvMXY;#bDmt9Q{6wdw=C4vt$pbD>9=Ey zrxSMV`R*Z|5gmLr!fi*D1NT&5c3C<<U)tYm7H#IS+LD><IW;)Ja<tmO92mdeeYqe` z^t^=%66Bu0KNde3yU9)b?d5ZE&X%Vy)r;HIXKj75a@9G(^gfc4&ylbrbF`lslGmNt z#ecju7-dil)^f^f@?P(MzgIn>yUO+Dwz`t=<D>GRJ0%%>hfM#y{%5rZzhn4c@1<i5 z3S6G$Vq{RniCkcPc-OS-jpO;$#TTM)n6@dE9DKTDNO0QuUJ_>yZmmRaQ_Yvvtd``v z)t&g|ROY_yR6)%nzm3t4Ee}u&SBy@k^;nd*Y)_iW<#Ak!Oq%;x!>Dz^j?hx4>gu`& z*_xsHyegk>&5T%b68yE$_|@&KKCZ>ou?rc>=!=u%OD)s7tAp32uLv~>y5@JZK%`Wy zLPG3wr9POvL|!qU!e;~KB^!aQ8ZmKD1WxzdS^P1mzG6+0Z}GW|ljgYGLXejr>Ix8L hW-=&JW`n^lDRiV&I%`i@Zl%i<#m3Syo`mRs_<xxh-|hea diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/statusplay.png b/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/statusplay.png deleted file mode 100644 index d751aac25ba5107d963191a806f72dbaf13e8bdf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 138 zcmeAS@N?(olHy`uVBq!ia0vp^Od!m`#=yYHy3uwMki(Mh=<CS9u>OQOYu8I4U$VqC zq9iy!t)x7$D3!r6B|j-u!8128JvAsbF{QHbWU38Nk&LH{V@SoVq=X;(zt|KSBpg^A i8Vrr@h*uqEU}11fX1%|yRKOOfj=|H_&t;ucLK6VcjwMR~ diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/statusstop.png b/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/statusstop.png deleted file mode 100644 index e0cce0d1a8407d07b2b8470cfedcbb3b7a0c96b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 136 zcmeAS@N?(olHy`uVBq!ia0vp^Od!m`#=yYHy3uwMki(Mh=<CS9u>OQOYu8I4U$VqC zq9iy!t)x7$D3!r6B|j-u!8128JvAsbF{QHbWU38Nk(8&4V@SoVq#x%Gd|*>(kZ@pe gXfS-ozOjLU!C@|IGjG!oQ=lpaPgg&ebxsLQ0I(4y>;M1& diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/stop.jpg b/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/stop.jpg deleted file mode 100644 index c7fda38d84c849610b89f211a34fd222b515e527..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1459 zcmex=<NpH&0WUXCHwH!~28I+MWcdGvLC~c%IlGd9k%5H)B*^gp9fO)<N`6u*L&^c5 z2qQCtC<`+i7cT<?QzZif^CJcZmO~5-tQ-H|W@uqxBpxs!05cpwwKFj?0%d_1E<r5g z{~-oJ4hAU(8D>U71|~s9W<kdPM;NXE9m~WFbUhrfaj>v5vvV>sGBC0-!bO1$W+oO^ zHg*mH21XV}CKh(M6blnGD;pz&fP#=PyP%=5vxumwATZomnV49(xS^^Tfp!QAv9KBn zD>^C}iLeDGHZI)g6x3uanzTvTIl1|;X{(u=5Can<&^N4blfk-J1QZMv3xxs~UR2s7 z40IF|D>Dl-%wa&UC<=TqbPP<KxKUx@!Hb2B4?q6D#lQnpz$C~l2#l0BGYn;~UJX37 zN^0|zn*Ddz^c_EXTX*|;X9YR)GQ}N-({~+Yn^iK;_&HD)2iVU{oVj1*cU*0);JlI9 z^4awBq51>fy4{RtzLf>K?Y#OoP(|vZ$t%wmSC{MBoxfR%Blm34U;m~atepuMWK7qM zN_!q#&kDM&q<Yk<cbddG&1F&PGWK8v%pe8VBx)mqv)#VVQ1t6~$Kk^ecR1oXOUg^* zC)W={OkrZPXIkf4a&=;D=FF|1N?2E2<mSB((GQYZy~=dA)bA$i*K8q&XI#JOutK3B zvL@nRooP%>&P*ZE2V6qwr!MX4=6(m(!pI2J;@qgIRT3UFN%X+iAT1U(=kt8GW4`zA z-u9hG*VpFoPoHRgqenbbZm+fY6n5)UDT|-1#hyNGjcJ+u-?(RMU$|Jbih0>hzuc)U zGyS66)4!_68e8RFclkOsF4A?!AC)R)<Giz5H_egU^Rn=IwAVAge8tm^YV$e!KJMCZ zDNOQ^Vzlql4RzPeTn<&9I2!N#Wx*}o%)CeUGJ?GWyt)`)ST*?jZe2d(`62mTyAIyk zTU_*cyExyU^Jdpxzt`FEdFeKzvqnviyHqYsP)m9mQLP;ll~;E5R_oK(zjsT|4qdNU zT@oO5H>`zkO3f*+H?wzFZ?b3WvUb^e>(28xAKUh)|MQP8vp?Y+f92QVmDj)56n>BA z|5Wm8A|z;-8SNE!B>A~dXlP4%vtEGr^zsk`eP;HkM4Ly3c^w+3Ox7j8;reL$>g=nB zV*0rn)%SE18l3(!ELy9)VP>I|X~nv-z(t&&9xiBX%ww5)YSVm2e)+6<y3VS-2U3j! zr!P61#ywr{<+@kT-ZpE7FE+Nnv>y_`AZwS*NcnSZflM&3_^wO$b=v3e2{OvsvSRI; zDu^0pkeWq2dym-!7yLeG^Qa_X8s|>0bB}{x@4iv9QS7+D0_%`d9d6DM8^9V6>0Ln1 z!Dmu~0^ENBn&(z5t!!^93T5)Ty3pT2U<tDW+Z)5oJuM>FeE&17@t;}MmCo2V&r0gT zJ+YH}VoGPZwro5wEnpFwr#Hi`9d9@5lqqfPnBm5B@REYld<)sP$ytvRQde>bJ(zhV zZ7G-V4VM25N0;OTYnq+B&K9oo&$gfw?n!|qhj=I0Wt1vBl$Tg^F!q{W2ggk<vHD#5 zHjRpBF6*m2>Xia^`Ch*Y)&TOcz(UPQ3NjrnuL5(WxHhhFnDMeB`G}Vu>#7U(B{sLt j-kf*rV_i&&<6WDi@4L2qSJ@S<aKrcE?3e3T|Gx<UPJI59 diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/stopclick.jpg b/airtime_mvc/public/js/airtime/embeddableplayer/ffmp3-mcclean/stopclick.jpg deleted file mode 100644 index a25e025be1b2ecf0ffd3906fcc2c3cf9f6382873..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1459 zcmex=<NpH&0WUXCHwH!~28I+MWcdGvLC~c%IlGd9k%5H)B*^gp9fO)<N`6u*L&^c5 z2qQCtC<`+i7cT<?QwakD^ECzrmhB7-tQ-H|W@uqxBpxs!05cpwwKFj?0%d_1E<r5g z{~-oJ4hAU(8D>U71|~s9W<kdPM;Oil9m~WFbUhrfaj<hTv#>HUGB7bS!bO1$7FK39 zb`AlcdPZgzgcLIi6Du2o0HcDCFuRems4>uRMn*PfRyKC1W=1Av76w5@Az@a-L=ne8 zqa@?Tg-T6}O^cLOQbf(%gn;Uqm{~dDRsneof&zvt3JW(L6jDqqoTwBetjsFR2n=9m zCN{WRfxb{Q6mU%Z5U5btIC0^@jTayOzs0}<bQqH$vmh`!HeRqZz3;4Ith0f~Ys*u+ zzlmmk8aLj><=To%oqgm!(Z^e_nIR#2;!L0}4xn!tS(#FmqULlZGtIhMayI*X^3PN4 zJHjKor=G8xb#?W1oBG;{$EU8z%{r<Rd(=QH#y)o9U#<J|y8nCy>t_P#zplnFbF5(A z+l`9z78R*Wdzvm+WN>^1mS+aZU(wF-WL|aZ_TwuD%9h{gVpV#x&$DVp{FV({ru#!o zVPdmqS}hj+<NAW;ZiVl1CU5TE`S_}YwKqyJ`)xhMAdr%UDpi#;`COC*4>epl_VK>n zQsq9YTA!T~$#<o?E*<39@SA`BG0UV~wRX!~T3s8&-rV*2&#+M2>-5xb`@x0)Bgmd< z=4-Y2%lg9G=1r2EC$X+$O6JnbTB(!V+4~qG7f&x-+UKGgXK7yQ?j{pDbrrLwSo-bN z-h0C9O8K6Bve7AY@(DHzZqJv_$ojW^#fx2MR@eE92Tyu4>z3S^AD=en{Y<xyd>ilV z<&aVC`!e><B>jKcg$Fd0H{HIo)qkamiFH(hp19Q78$G(qf8YK$b=sRc_2V;hj#diz zu3Ej!y2(2-&@^PVXl+d4t1BLtPKBAyQF=b{<bgu7HP!(u&c-fM&+2@(B26GmVv6LF zNe`BVJ-na2nLpa}UiPLZf5NLjZ2o(`;dg%j^QZY=%j)0!6Mprp-hSHMZ4jR`GukT( zZ=R{Xc!Pc$TWDSFq=#D~g}mn2<!oJIGe=tV_dfN_9euYnGwgm%aN^iH^{#U1mxV&t zj(UBMlX|mq<AM`6=e&2@-cc}B_=I@=-5^F)vl&;t^Y|ROcfDKo;q#or=d*TY?*8+o zv_1V-;bKUff^1wN%scV-+Ktu9Kd$R9KQ(LD<^1^Acq6?F8GeC}Akhj^weV<BkBpL| zrDjRqhhs@B$EEI_P_W!tb8MN%T;+L-I<6&{Eod+)oRBw-UmU0jk<<km9CkE!Fu*-1 zFxxlNKv2n3X3Djq0|D`}Y)(SWT^0(bXH8^S(;ThV@s#JiZQi7N<#%0AGR(<au6=at z^xWnJ8y0dV6^rWhUD@wdp2E{)n(7r1=;@(hGc9>;^#+zJ+V+17`Zj5}uJ7Z0I+uIX zE{PYFetk2m)*HLSy(zGq_l2{|iJtbh=AeaUH;<g`yj8kom)D02m8#Yg?9S|)we6Co z(Yl|@$`7e>Mda<%hx=S$@xh>GuPpA4&bH?18aW5ttXwvAs;ezpW#^c}VUzIn)?DEk kn{NKJ^9{XofU6-j@721uzV2CCj2i+HXA}zk*#G|~0Q5EfJpcdz From 5c4533d7859857e90b44e895543880c7c1dbf24b Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Mon, 23 Mar 2015 10:11:33 -0400 Subject: [PATCH 25/60] SAAS-644: Embed Player -> Update embed src code and preview when an option changes --- .../embeddableplayer/embeddableplayer.js | 37 +++++++++++++++++-- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js b/airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js index d228625af..16cfb95e3 100644 --- a/airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js +++ b/airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js @@ -1,9 +1,38 @@ -window.onload = function() { - document.getElementById('player_display_track_metadata').onchange = generateEmbedSrc; -} - function generateEmbedSrc() { + var $embedCodeParams = "?"; + var $streamMode = getStreamMode(); + if ($streamMode == "b") { + var $stream = $("input[name=player_stream_url]:radio:checked").val(); + $embedCodeParams += "stream-mode=b&stream="+$stream; + } else if ($streamMode == "a") { + $embedCodeParams += "stream-mode=a"; + } + $embedCodeParams += "\""; + $("input[name=player_embed_src]").val(function(index, value) { + return value.replace(/\?.*?"/, $embedCodeParams); + }); } +function getStreamMode() { + return $("input[name=player_stream_mode]:radio:checked").val(); +} + +$(document).ready(function() { + $("#player_stream_mode-element").change(function() { + var $streamMode = getStreamMode(); + if ($streamMode == "a") { + $("#player_stream_url-element input[type='radio']").attr("disabled", "disabled"); + } else if ($streamMode == "b") { + $("#player_stream_url-element input[type='radio']").removeAttr("disabled"); + } + + generateEmbedSrc(); + }); + + $("#player_stream_url-element").change(function() { + generateEmbedSrc(); + }); +}); + From 2ae4d6c1c323828ee1d4ac714226a029d478d346 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Mon, 23 Mar 2015 12:37:22 -0400 Subject: [PATCH 26/60] Embed Player Added stream modes Unhardcoded some values Disabled opus streams --- .../EmbeddableplayerController.php | 11 +++-- .../application/forms/EmbeddablePlayer.php | 44 +++++++++++++------ .../application/models/StreamSetting.php | 19 ++++++++ .../views/scripts/form/embeddableplayer.phtml | 2 + .../embeddableplayer/embeddableplayer.js | 13 ++++-- 5 files changed, 67 insertions(+), 22 deletions(-) diff --git a/airtime_mvc/application/controllers/EmbeddableplayerController.php b/airtime_mvc/application/controllers/EmbeddableplayerController.php index 6c2ccfbd8..8efc33c2b 100644 --- a/airtime_mvc/application/controllers/EmbeddableplayerController.php +++ b/airtime_mvc/application/controllers/EmbeddableplayerController.php @@ -15,7 +15,6 @@ class EmbeddablePlayerController extends Zend_Controller_Action $this->view->headScript()->appendFile($baseUrl.'js/airtime/embeddableplayer/mrp.js?'.$CC_CONFIG['airtime_version']); $this->view->headScript()->appendFile($baseUrl.'js/airtime/embeddableplayer/embeddableplayer.js?'.$CC_CONFIG['airtime_version']); - $form = new Application_Form_EmbeddablePlayer(); if ($form->getElement('player_stream_url')->getAttrib('numberOfEnabledStreams') > 0) { @@ -34,11 +33,11 @@ class EmbeddablePlayerController extends Zend_Controller_Action $this->view->mrp_js = Application_Common_HTTPHelper::getStationUrl() . "js/airtime/embeddableplayer/mrp.js"; $this->view->muses_swf = Application_Common_HTTPHelper::getStationUrl() . "js/airtime/embeddableplayer/muses.swf"; - $this->view->skin = Application_Common_HTTPHelper::getStationUrl() . "js/airtime/embeddableplayer/ffmp3-mcclean.xml"; - $this->view->codec = $request->getParam('codec'); - //$this->view->streamURL = $request->getParam('url'); - //$stream = $request->getParam('stream'); - $this->view->streamURL = "http://127.0.0.1:8000/airtime_128"; + $stream = $request->getParam('stream'); + $streamData = Application_Model_StreamSetting::getEnabledStreamData(); + $selectedStreamData = $streamData[$stream]; + $this->view->streamURL = $selectedStreamData["url"]; + $this->view->codec = $selectedStreamData["codec"]; $this->view->displayMetadata = $request->getParam('display_metadata'); } } \ No newline at end of file diff --git a/airtime_mvc/application/forms/EmbeddablePlayer.php b/airtime_mvc/application/forms/EmbeddablePlayer.php index febf1379a..537abd39a 100644 --- a/airtime_mvc/application/forms/EmbeddablePlayer.php +++ b/airtime_mvc/application/forms/EmbeddablePlayer.php @@ -13,30 +13,48 @@ class Application_Form_EmbeddablePlayer extends Zend_Form_SubForm $displayTrackMetadata->setLabel(_('Display track metadata?')); $this->addElement($displayTrackMetadata); + $streamMode = new Zend_Form_Element_Radio('player_stream_mode'); + $streamMode->setLabel(_('Select Stream:')); + $streamMode->setMultiOptions( + array( + "a" => "Use a mobile stream if possible, when appropriate. Otherwise use the highest quality stream.", + "b" => "Select a stream" + ) + ); + $streamMode->setValue("a"); + $this->addElement($streamMode); + $streamURL = new Zend_Form_Element_Radio('player_stream_url'); + $opusStreamCount = 0; $urlOptions = Array(); - foreach(Application_Model_StreamSetting::getEnabledStreamUrls() as $type => $url) { - if ($type == "opus") continue; - $urlOptions[$url] = $type; + foreach(Application_Model_StreamSetting::getEnabledStreamData() as $stream => $data) { + $urlOptions[$stream] = $data["codec"]." - ".$data["bitrate"]."kbps"; + if ($data["codec"] == "opus") { + $opusStreamCount += 1; + $urlOptions[$stream] .=" - The player does not support Opus streams."; + } } $streamURL->setMultiOptions( $urlOptions ); - Logging::info($urlOptions); - $streamURL->setValue(array_keys($urlOptions)[0]); - $streamURL->setLabel(_('Select stream:')); - $streamURL->setAttrib('codec', array_values($urlOptions)[0]); - $streamURL->setAttrib('numberOfEnabledStreams', sizeof($urlOptions)); - $this->addElement($streamURL); - $url = $streamURL->getValue(); - $codec = $streamURL->getAttrib('codec'); + foreach ($urlOptions as $o => $v) { + if (strpos($v, "opus") !== false) { + continue; + } else { + $streamURL->setValue($o); + break; + } + } + + $streamURL->setAttrib('numberOfEnabledStreams', sizeof($urlOptions)-$opusStreamCount); + $streamURL->setAttrib("disabled", "disabled"); + $this->addElement($streamURL); $embedSrc = new Zend_Form_Element_Text('player_embed_src'); $embedSrc->setAttrib("readonly", "readonly"); $embedSrc->setAttrib("class", "embed-player-text-box"); - //$embedSrc->setValue('<iframe frameborder="0" src="'.Application_Common_HTTPHelper::getStationUrl().'embeddableplayer/embed-code?url='.$url.'&codec='.$codec.'"></iframe>'); - $embedSrc->setValue('<iframe frameborder="0" src="'.Application_Common_HTTPHelper::getStationUrl().'embeddableplayer/embed-code?stream=stream1&codec='.$codec.'"></iframe>'); + $embedSrc->setValue('<iframe frameborder="0" src="'.Application_Common_HTTPHelper::getStationUrl().'embeddableplayer/embed-code?stream-mode=a"></iframe>'); $embedSrc->removeDecorator('label'); $this->addElement($embedSrc); diff --git a/airtime_mvc/application/models/StreamSetting.php b/airtime_mvc/application/models/StreamSetting.php index 3bdac8a31..49ec7a072 100644 --- a/airtime_mvc/application/models/StreamSetting.php +++ b/airtime_mvc/application/models/StreamSetting.php @@ -78,6 +78,25 @@ class Application_Model_StreamSetting return $urls; } + public static function getEnabledStreamData() + { + $streams = Array(); + $streamIds = self::getEnabledStreamIds(); + foreach ($streamIds as $id) { + $streamData = self::getStreamData($id); + $prefix = $id."_"; + $host = $streamData[$prefix."host"]; + $port = $streamData[$prefix."port"]; + $mount = $streamData[$prefix."mount"]; + $streams[$id] = Array( + "url" => "http://$host:$port/$mount", + "codec" => $streamData[$prefix."type"], + "bitrate" => $streamData[$prefix."bitrate"] + ); + } + return $streams; + } + /* Returns the id's of all streams that are enabled in an array. An * example of the array returned in JSON notation is ["s1", "s2", "s3"] */ public static function getEnabledStreamIds() diff --git a/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml b/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml index aed5bc181..7ab6cb227 100644 --- a/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml +++ b/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml @@ -5,6 +5,8 @@ <?php echo $this->element->getElement('player_display_track_metadata'); ?> + <?php echo $this->element->getElement('player_stream_mode'); ?> + <?php echo $this->element->getElement('player_stream_url'); ?> <?php echo $this->element->getElement('player_preview_label')->renderLabel(); ?> diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js b/airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js index 16cfb95e3..a07a293f2 100644 --- a/airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js +++ b/airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js @@ -1,4 +1,4 @@ -function generateEmbedSrc() +function updateEmbedSrcParams() { var $embedCodeParams = "?"; var $streamMode = getStreamMode(); @@ -20,19 +20,26 @@ function getStreamMode() { } $(document).ready(function() { + $("#player_stream_mode-element").change(function() { var $streamMode = getStreamMode(); if ($streamMode == "a") { $("#player_stream_url-element input[type='radio']").attr("disabled", "disabled"); } else if ($streamMode == "b") { $("#player_stream_url-element input[type='radio']").removeAttr("disabled"); + + $("input[name=player_stream_url]").each(function(i, obj) { + if ($(this).parent().text().indexOf("opus") >= 0) { + $(this).attr("disabled", "disabled"); + } + }); } - generateEmbedSrc(); + updateEmbedSrcParams(); }); $("#player_stream_url-element").change(function() { - generateEmbedSrc(); + updateEmbedSrcParams(); }); }); From e32744668f5f1e80f509ebef94de8b847ca8a98d Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Mon, 23 Mar 2015 16:06:35 -0400 Subject: [PATCH 27/60] SAAS-661: Add mobile stream identifier on Stream Settings page --- airtime_mvc/application/forms/EmbeddablePlayer.php | 5 ++++- airtime_mvc/application/forms/StreamSettingSubForm.php | 6 ++++++ airtime_mvc/application/models/StreamSetting.php | 3 ++- .../views/scripts/form/stream-setting-form.phtml | 7 +++++++ .../public/js/airtime/embeddableplayer/embeddableplayer.js | 2 +- 5 files changed, 20 insertions(+), 3 deletions(-) diff --git a/airtime_mvc/application/forms/EmbeddablePlayer.php b/airtime_mvc/application/forms/EmbeddablePlayer.php index 537abd39a..ba7c5fce9 100644 --- a/airtime_mvc/application/forms/EmbeddablePlayer.php +++ b/airtime_mvc/application/forms/EmbeddablePlayer.php @@ -28,7 +28,10 @@ class Application_Form_EmbeddablePlayer extends Zend_Form_SubForm $opusStreamCount = 0; $urlOptions = Array(); foreach(Application_Model_StreamSetting::getEnabledStreamData() as $stream => $data) { - $urlOptions[$stream] = $data["codec"]." - ".$data["bitrate"]."kbps"; + $urlOptions[$stream] = strtoupper($data["codec"])." - ".$data["bitrate"]."kbps"; + if ($data["mobile"]) { + $urlOptions[$stream] .= " - Mobile friendly"; + } if ($data["codec"] == "opus") { $opusStreamCount += 1; $urlOptions[$stream] .=" - The player does not support Opus streams."; diff --git a/airtime_mvc/application/forms/StreamSettingSubForm.php b/airtime_mvc/application/forms/StreamSettingSubForm.php index 874f38dc5..b84adf63d 100644 --- a/airtime_mvc/application/forms/StreamSettingSubForm.php +++ b/airtime_mvc/application/forms/StreamSettingSubForm.php @@ -53,6 +53,12 @@ class Application_Form_StreamSettingSubForm extends Zend_Form_SubForm } $this->addElement($enable); + $mobile = new Zend_Form_Element_Checkbox('mobile'); + $mobile->setLabel(_('Good for mobile?')); + $mobile->setValue($setting[$prefix.'_mobile']); + $mobile->setDecorators(array('ViewHelper')); + $this->addElement($mobile); + $type = new Zend_Form_Element_Select('type'); $type->setLabel(_("Stream Type:")) ->setMultiOptions($stream_types) diff --git a/airtime_mvc/application/models/StreamSetting.php b/airtime_mvc/application/models/StreamSetting.php index a85fad988..19a147798 100644 --- a/airtime_mvc/application/models/StreamSetting.php +++ b/airtime_mvc/application/models/StreamSetting.php @@ -94,7 +94,8 @@ class Application_Model_StreamSetting $streams[$id] = Array( "url" => "http://$host:$port/$mount", "codec" => $streamData[$prefix."type"], - "bitrate" => $streamData[$prefix."bitrate"] + "bitrate" => $streamData[$prefix."bitrate"], + "mobile" => $streamData[$prefix."mobile"] ); } return $streams; diff --git a/airtime_mvc/application/views/scripts/form/stream-setting-form.phtml b/airtime_mvc/application/views/scripts/form/stream-setting-form.phtml index 6b76ab84a..a8d61d247 100644 --- a/airtime_mvc/application/views/scripts/form/stream-setting-form.phtml +++ b/airtime_mvc/application/views/scripts/form/stream-setting-form.phtml @@ -14,6 +14,13 @@ <dd id="<?php echo $s_name?>Enabled-element"> <?php echo $this->element->getElement('enable')?> </dd> + + <dt id="<?php echo $s_name?>Mobile-label"> + <label for="<?php echo $s_name?>Mobile"><?php echo $this->element->getElement('mobile')->getLabel() ?></label> + </dt> + <dd id="<?php echo $s_name?>Mobile-element"> + <?php echo $this->element->getElement('mobile')?> + </dd> <dt id="<?php echo $s_name?>Type-label"> <label for="<?php echo $s_name?>Type"><?php echo $this->element->getElement('type')->getLabel()?></label> diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js b/airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js index a07a293f2..376c64e57 100644 --- a/airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js +++ b/airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js @@ -29,7 +29,7 @@ $(document).ready(function() { $("#player_stream_url-element input[type='radio']").removeAttr("disabled"); $("input[name=player_stream_url]").each(function(i, obj) { - if ($(this).parent().text().indexOf("opus") >= 0) { + if ($(this).parent().text().toLowerCase().indexOf("opus") >= 0) { $(this).attr("disabled", "disabled"); } }); From 17358a761fdef63d08e39343f21f0188402a6d5b Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Wed, 25 Mar 2015 11:44:28 -0400 Subject: [PATCH 28/60] SAAS-663: Integrate player design --- .../EmbeddableplayerController.php | 6 +- .../application/forms/EmbeddablePlayer.php | 10 +- .../scripts/embeddableplayer/embed-code.phtml | 90 ++++++++- .../views/scripts/form/embeddableplayer.phtml | 4 +- .../css/colorpicker/embeddable-player.css | 0 .../css/embed-player-images/airtime_logo.png | Bin 0 -> 1162 bytes .../public/css/embed-player-images/mute.png | Bin 0 -> 1338 bytes .../css/embed-player-images/pause_button.png | Bin 0 -> 1077 bytes .../css/embed-player-images/play_button.png | Bin 0 -> 1590 bytes .../public/css/embed-player-images/unmute.png | Bin 0 -> 1378 bytes ...eplayer.css => embeddable-player-form.css} | 2 + airtime_mvc/public/css/embeddable-player.css | 190 ++++++++++++++++++ .../embeddableplayer/embeddableplayer.js | 20 +- 13 files changed, 307 insertions(+), 15 deletions(-) create mode 100644 airtime_mvc/public/css/colorpicker/embeddable-player.css create mode 100644 airtime_mvc/public/css/embed-player-images/airtime_logo.png create mode 100644 airtime_mvc/public/css/embed-player-images/mute.png create mode 100644 airtime_mvc/public/css/embed-player-images/pause_button.png create mode 100644 airtime_mvc/public/css/embed-player-images/play_button.png create mode 100644 airtime_mvc/public/css/embed-player-images/unmute.png rename airtime_mvc/public/css/{embeddableplayer.css => embeddable-player-form.css} (99%) create mode 100644 airtime_mvc/public/css/embeddable-player.css diff --git a/airtime_mvc/application/controllers/EmbeddableplayerController.php b/airtime_mvc/application/controllers/EmbeddableplayerController.php index 8efc33c2b..2a66d0b7e 100644 --- a/airtime_mvc/application/controllers/EmbeddableplayerController.php +++ b/airtime_mvc/application/controllers/EmbeddableplayerController.php @@ -11,7 +11,7 @@ class EmbeddablePlayerController extends Zend_Controller_Action { $CC_CONFIG = Config::getConfig(); $baseUrl = Application_Common_OsPath::getBaseDir(); - $this->view->headLink()->appendStylesheet($baseUrl.'css/embeddableplayer.css?'.$CC_CONFIG['airtime_version']); + $this->view->headLink()->appendStylesheet($baseUrl.'css/embeddable-player-form.css?'.$CC_CONFIG['airtime_version']); $this->view->headScript()->appendFile($baseUrl.'js/airtime/embeddableplayer/mrp.js?'.$CC_CONFIG['airtime_version']); $this->view->headScript()->appendFile($baseUrl.'js/airtime/embeddableplayer/embeddableplayer.js?'.$CC_CONFIG['airtime_version']); @@ -31,8 +31,12 @@ class EmbeddablePlayerController extends Zend_Controller_Action $request = $this->getRequest(); + $this->view->css = Application_Common_HTTPHelper::getStationUrl() . "css/embeddable-player.css"; $this->view->mrp_js = Application_Common_HTTPHelper::getStationUrl() . "js/airtime/embeddableplayer/mrp.js"; + $this->view->jquery = Application_Common_HTTPHelper::getStationUrl() . "js/libs/jquery-1.10.2.js"; $this->view->muses_swf = Application_Common_HTTPHelper::getStationUrl() . "js/airtime/embeddableplayer/muses.swf"; + $this->view->metadata_api_url = Application_Common_HTTPHelper::getStationUrl() . "api/live-info"; + $this->view->station_name = Application_Model_Preference::GetStationName(); $stream = $request->getParam('stream'); $streamData = Application_Model_StreamSetting::getEnabledStreamData(); $selectedStreamData = $streamData[$stream]; diff --git a/airtime_mvc/application/forms/EmbeddablePlayer.php b/airtime_mvc/application/forms/EmbeddablePlayer.php index ba7c5fce9..e5086716a 100644 --- a/airtime_mvc/application/forms/EmbeddablePlayer.php +++ b/airtime_mvc/application/forms/EmbeddablePlayer.php @@ -17,11 +17,11 @@ class Application_Form_EmbeddablePlayer extends Zend_Form_SubForm $streamMode->setLabel(_('Select Stream:')); $streamMode->setMultiOptions( array( - "a" => "Use a mobile stream if possible, when appropriate. Otherwise use the highest quality stream.", - "b" => "Select a stream" + "auto" => "Auto detect the most appropriate stream to use.", + "manual" => "Select a stream:" ) ); - $streamMode->setValue("a"); + $streamMode->setValue("auto"); $this->addElement($streamMode); $streamURL = new Zend_Form_Element_Radio('player_stream_url'); @@ -42,7 +42,7 @@ class Application_Form_EmbeddablePlayer extends Zend_Form_SubForm ); foreach ($urlOptions as $o => $v) { - if (strpos($v, "opus") !== false) { + if (strpos(strtolower($v), "opus") !== false) { continue; } else { $streamURL->setValue($o); @@ -57,7 +57,7 @@ class Application_Form_EmbeddablePlayer extends Zend_Form_SubForm $embedSrc = new Zend_Form_Element_Text('player_embed_src'); $embedSrc->setAttrib("readonly", "readonly"); $embedSrc->setAttrib("class", "embed-player-text-box"); - $embedSrc->setValue('<iframe frameborder="0" src="'.Application_Common_HTTPHelper::getStationUrl().'embeddableplayer/embed-code?stream-mode=a"></iframe>'); + $embedSrc->setValue('<iframe frameborder="0" width="280" height="230" src="'.Application_Common_HTTPHelper::getStationUrl().'embeddableplayer/embed-code?stream=auto"></iframe>'); $embedSrc->removeDecorator('label'); $this->addElement($embedSrc); diff --git a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml index adab3876e..47e713e42 100644 --- a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml +++ b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml @@ -2,7 +2,9 @@ <html xmlns="http://www.w3.org/1999/xhtml"> <head> + <link rel="stylesheet" href="<?php echo $this->css?>" type="text/css"> <script src="<?php echo $this->mrp_js?>" type="text/javascript"></script> + <script src="<?php echo $this->jquery?>" type="text/javascript"></script> <script type="text/javascript"> var MusesPlayer = function() { @@ -19,27 +21,38 @@ 'width':180, 'height':60 }); + $("p.station_name").html("<?php echo $this->station_name?>"); this.flashDetect = FlashDetect.versionAtLeast(10, 1) ? true : false; + + getMetadata(); }; MusesPlayer.prototype.play = function() { this.flashDetect ? MRP.play() : musesHTMLPlayClick(); + togglePlayStopButton(); }; MusesPlayer.prototype.stop = function() { this.flashDetect ? MRP.stop() : musesHTMLStopClick(); + togglePlayStopButton(); }; MusesPlayer.prototype.setVolume = function(value) { //this.flashDetect ? MRP.setVolume(value) : null; - //TODO - maybe }; MusesPlayer.prototype.setURL = function() { //TODO }; + /*function musesCallback(event,value){ + console.log('event: "'+event+'", value: "'+value+'"'); + }*/ + + /** + * This is a hack to trigger the play button in HTML5 mode + */ function musesHTMLPlayClick() { //child nodes var cn = document.getElementById("MusesRadioPlayer-HTML5-player-1").childNodes; @@ -47,6 +60,9 @@ playDiv.onclick(); } + /** + * This is a hack to trigger the stop button in HTML5 mode + */ function musesHTMLStopClick() { //child nodes var cn = document.getElementById("MusesRadioPlayer-HTML5-player-1").childNodes; @@ -54,7 +70,39 @@ stopDiv.onclick(); } + function togglePlayStopButton() { + document.getElementById("play_button").classList.toggle("hide_button"); + document.getElementById("stop_button").classList.toggle("hide_button"); + } + + // default how often to fetch metadata to 20 seconds + var time_to_next_track_starts = 20000; + + function getMetadata(){ + $.ajax({url: "<?php echo $this->metadata_api_url?>", + data: {type:"interval",limit:"5"}, + dataType: "jsonp", + success: function(data) { + console.log("fetching metadata"); + var current_track_end_time = new Date(data.current.ends); + var current_time = new Date(); + current_time = new Date(current_time.getTime() + current_time.getTimezoneOffset()*60*1000); + time_to_next_track_starts = current_track_end_time - current_time; + // maybe we should set time_to_next_track_starts to + // (10 || 20 || etc.) minutes if its greater than that + // in case of on-the-fly schedule changes + + var artist = data.current.name.split(" - ")[0]; + var track = data.current.name.split(" - ")[1]; + $("p.now_playing").html(artist+"<span>"+track+"</span>"); + $("ul.schedule_list").find("li").html(data.next.name); + } + }); + setTimeout(getMetadata, time_to_next_track_starts); + } + </script> + <style type="text/css"> #muses_skin{width:1px; height:1px; overflow-x: hidden; overflow-y: hidden;} </style> @@ -62,18 +110,58 @@ <body> +<div class="airtime_player"> + + <div class="airtime_header"> + <p class="station_name">fff</p> + <a class="airtime_pro" href="#"><span>airtime.pro</span><img src="http://localhost/css/embed-player-images/airtime_logo.png"></a> + </div> + + <div class="airtime_box"> + + <div class="airtime_button"> + <span id="play_button" class="play_button" onclick="musesPlayer.play()"></span> + <span id="stop_button" class="stop_button hide_button" onclick="musesPlayer.stop()"></span> + </div> + <p class="now_playing"></p> + + </div> + + <div class="airtime_volume"> + + <div class="volume_control"> + <span class="mute"></span> + </div> + + <div class="airtime_volume_bar"> + <div class="airtime_volume_bar_value" style="width: 80%;"></div> + </div> + + </div> + + <div class="airtime_schedule"> + <p class="airtime_next">Next</p> + <ul class="schedule_list"> + <li>John Legend - Ordinary People</li> + </ul> + </div> + +</div> + <div id="muses_skin"> <script type="text/javascript"> var musesPlayer = new MusesPlayer(); </script> </div> +<!-- <div id="custom_muses_play" onclick="musesPlayer.play()"> <a href="#">play</a> </div> <div id="custom_muses_stop" onclick="musesPlayer.stop()"> <a href="#">stop</a> </div> +--> </body> </html> \ No newline at end of file diff --git a/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml b/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml index 7ab6cb227..18305c20b 100644 --- a/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml +++ b/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml @@ -3,12 +3,12 @@ <?php echo $this->element->getElement('player_embed_src'); ?> - <?php echo $this->element->getElement('player_display_track_metadata'); ?> - <?php echo $this->element->getElement('player_stream_mode'); ?> <?php echo $this->element->getElement('player_stream_url'); ?> + <?php //echo $this->element->getElement('player_display_track_metadata'); ?> + <?php echo $this->element->getElement('player_preview_label')->renderLabel(); ?> <div style="clear:both"></div> diff --git a/airtime_mvc/public/css/colorpicker/embeddable-player.css b/airtime_mvc/public/css/colorpicker/embeddable-player.css new file mode 100644 index 000000000..e69de29bb diff --git a/airtime_mvc/public/css/embed-player-images/airtime_logo.png b/airtime_mvc/public/css/embed-player-images/airtime_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..db8e5fb816358773294e4bb922cc58ca07cf69a3 GIT binary patch literal 1162 zcmaJ>TWs4@7<ST;(r!_WCaue;;NWuUF0p;w#3iO#?rkHHu1g~=;sMpMPa2cj$JnQf z+ab|S6Le?~Lr4hiX<{#X+Q^=eiWs1*Du@S$5JEg6ctmL8fg#4_xNABd2$t=0(f57- z|DXS}FU-wOhX)T0G7J+g&g9E<Wa%^XXo$Xlc25X&h>${+%;P#~sSaW?I<6s5G}T2^ zMykH@>gQ;ZVfr|uQYF>Wi;9L#UiC4&XWEp_Fq0|IR<$M~poSI=E6M$`af<_np5#u& zOJd2+p@uQD>Y(}6*^0K>)Dk+EIu0g1g$kI6sK7IqELZW8+_tVl=l-_9fo%wBCb<Vm zRZDXqhaCjs{CHFoAq29_!&qFF;V~eIP!dFX%TX9p;<6%5fSrq@(Hy<5l=D+NvFIwv zH3+d4L1?vFd~2M?&Y}PliG;5qNl}W3x+@k@y{P4mb`|o-)f~en2DX5&sMhc@NpdvP z2PK&HuB_$m91}e-!BcGk@}gf-7g#F&Kh!jL(Jm>Yhxz`euv=NNkx)i1UUoFvxcaCc z%2sj?QVDh{7%z2Cajt<0b{p6Rxp^6koiZ#Px7@?q_)<wJS}sv74HffAjw<kmp(|2u z>Uk(;VXh!a5Ef!tQOspyFazU-oD4G)U2Yz0%O<i&m#aVIN<F#06-=8(&LhV-jr1u8 zn_#<S#psz!W~!&&4p;A;i`bJZ&}0O^vHxmxcZ<5mpYGb0E_RKNEb4ZL+WIFkVbM?G zuVOw^@xHI!Po2CF2+uI>=h!y`?Ew4ao*!<fKN?!Ub@gcd{2{QXb2>dT*x}R1HrEEO z2kssLFAsDkE`>h`ef?*8>5KHpy-?@OS#MxH6!~PIzSY+rW~T$~XZr3g92kA+pcrh& zzq!JIcRN>qPi<{ntOg$ob<Tb_{K~<x*WSDHV=LJ1Ki;>Hj;!AZ!WS-o*uTX-%iWK> zRrvLABQ3GgTIBK*UuA~azn|J<H1O^8{*7Sg@r99W=)87vJiPw;evRpm9eMlH)yB;_ z*ksOtHwt%_uQkuD-P?PUJ1`m2)9;wW>~HTkpVrsL*wiDnz2M`E!X+lSk2zX}hWy8u QpZR}>VqrFaJ-cx3A7~43IsgCw literal 0 HcmV?d00001 diff --git a/airtime_mvc/public/css/embed-player-images/mute.png b/airtime_mvc/public/css/embed-player-images/mute.png new file mode 100644 index 0000000000000000000000000000000000000000..0276df62e7bae7cbe95915c4a590d5d0c0cd1e62 GIT binary patch literal 1338 zcmeAS@N?(olHy`uVBq!ia0vp@Ak4uAB#T}@sR2@wC9V-A!TD(=<%vb942~)JNvR5+ zxryniL8*x;m4zo$Z5SAsl`=yjN+NuHtdjF{^%6m9^eS=-fVvqNZ0suv5|gu2OB9k) z(=+pImEP~(ucVNfVyhHx>TBRz;GCL~=}}db8eHWUl3bOYY?-2DZ>L~WVFffGH?<^D zp&~aYuh^=>Rtapb6_5=Q)>l#hD=EpgRf0Gw!Z$#{Ilm}X!Bo#!H`&0@P{GVh&(Orw z%*;?n!N|bSNZ$a6%ybP+tW3?UjLa3FKnZADQA(Oskc%7CE+EfVDWjyMz)D}gyu4hm z+*mKaC|%#s($W%ShLMpjP=#)BWnM{Qg>GK4GRy>*)Z*l#%z~24{5%DaiHS-1r6smX zK$k+ikXryZHm?{OOuzusuShJ=H`Fr#c?qV_*B8Ii++4Wo;*y|LgnO|XTpUtakg6Y) zTAW{6lnjixG-Z%g1y;^Qsfi`|MIrh5Ij~R+$jC3rFV4s>P;d@5Q_u*{%uC5HFV+OB z_w}{%%quQQ%u7!7bg@+enxL1NnPO#PV&G!rWZ~lMYUF5W=xSo=X5`}P>||tO>|$hK zY2ggh>yn>bnwy$e0@Is<(Cdm*FDNPG765H_NiE7OOHFYr%Fk5*d)X=zw_BWWng`XJ zg4-?5IQ8lS9itD5Sfq%C2?0|NhzU=&Kn^_Nr{)1udl4{MmrBOJWnf@j=jq}YQgJIM zXumg8qQt*<S6-j{;&N0cyRaxXk5fkFJ4dG&>+}r=PbvoZwI6Yel3`VjI<&M+rg_oQ ze~m2~;_U`T6Z%5>URT=hJFrPy?#Dg(^Pm4cn0#Z8W`p>KMneO}Wt&+q*)Lh@qqbSJ z*UjH;@x`*WVY{7t*mNAX&8cT`YH(v&fBm%6!T{-gn?tl@Z*4n!>ggxJi5`yZhYm#P zh;fH&O`Ui9ZQ8DN#`Di#ciUKOBNy**L_qm;%w2<?bd~p}zTMhghd5nIcE+$De)wVW z{rBHfBFldL{r5UXPyKXFvFpc#DHR41DUA*d!ZLj9v;CI8zM1olCpNG9=%cw2&Z?7q zSzjEok>d{zohp3dLak`Qg!r{#rk;<=AJ6?^so5Yn!=u3Yqqb)7ngETObCZ0SILu2F z5A`d4yO6cDOz?J&*=%X0<sMh$_{}$5cv;faeDJ}<K1b!qX{GbLmR|Z=Rm&PX@6*pe zM)sC6+!6;X{naMx-^~j*_npkKWEZoJlLGI;<c-H}ua|FJ*Wp>V_~MPjtp3UqPt++* z+##YJ@K*fOvpeQOk5@hVf3dpZ(8-*$+ZmV{rv8~4yV-Z!6i{jA>FVdQ&MBb@0Lzx( A<NyEw literal 0 HcmV?d00001 diff --git a/airtime_mvc/public/css/embed-player-images/pause_button.png b/airtime_mvc/public/css/embed-player-images/pause_button.png new file mode 100644 index 0000000000000000000000000000000000000000..8719664daa1948c4189455f7743002f6d1b948c6 GIT binary patch literal 1077 zcmaJ=TWHfz7!LJ<PQ0PQymA(GDA=S)+swt)*)^>e%^Y2EohVzDoUWlJCnjgM8~C88 zsGuTVL2)P|_@Lk(L{XU{h&K>@P|-(WA}E^=;)HoQscrSa8j_p~-}n9h`Ty@6XlY(M zwQ61!MNw1Z>!bu3tI0Qi(nRu4zuUNp40CZbg<DY?=M)=KVGVUb5I2-=n1G7byZsSt zpr~=Qo=oACyg^iv!6+_<$r~17Q&dA^-cr;o#Gngy>t=`^zBNb#T?^4|LD?r;5tz}} z_1UnsuQ{pqWz~9(Zd?r-@*)v1AXY%$*k(FnK13IEMKX7{SsD}|I2)pWCzX<0Km^$k z1R1|q^>G{!1cnO)1%X=ucpt~JKJp4)E+7U4k*@>AhbGZ%EiERbSTPn^h3E{%mdLWX zT#m{48Dw{}Tz!4LtHJYLg77-MCRXxZ(^*_nkf5X5x`lOQ0#{M#LOnP{lT3df!LZ7* zrc*2vDHxkqES6(@?vYABS^huNFv@5LC*Yra|0(Pwdo9Q&po4mBl{7BB*bQZg5gRHP z*-3=9m8#g1K^Qq1WPwPl0G74sriOCP@&aC##klEU#Z+Nj3eiM?(REF%3&c2{Z}LT= zQ9s8;15JD^x^iVWCisGNejy-~xDrx(3~1sKSNp@|Dso*b7#4{vL0jJewU~_zP&l%v zSIi|Gt*BSzY87({$0~AJk__uM_OC{lwupP&Y1y`9Q8qp_iQ8>r>-5>vCb<%q<5D=8 zfAjI=wW|AI#=bi#?&4lNdGyD#p_Yd)YP<`V3=J-Qom_u(50e|IS@84Q&EDB+a_vj~ zEx0h{us;(Xmmcv<_bq+iHSNf`gPv)t-kY1VC$GrEJ3Ed&ne3U~o}JUL57wTV^>mD0 z#6P(GC2JoNZ%Dv1bN`z0^ynEdd*FQS*P6SJpATKS{=LIF-hO+cHDT+(aKpWY{bQ%M jB%kj3e5Ui&=JD0krx}eWKCD;(+>0EKHcMBVI(Pp9Z|`BS literal 0 HcmV?d00001 diff --git a/airtime_mvc/public/css/embed-player-images/play_button.png b/airtime_mvc/public/css/embed-player-images/play_button.png new file mode 100644 index 0000000000000000000000000000000000000000..bff38adbc4fb4fb1da43231742f93568d8827ae2 GIT binary patch literal 1590 zcmeAS@N?(olHy`uVBq!ia0vp^5<o1?!3HGL9{Yy@DajJoh?3y^w370~qErUQl>DSr z1<%~X^wgl##FWaylc_cg49rTIArU1JzCKpT`MG+DAT@dwxdlMo3=B5*6$OdO*{LN8 zNvY|XdA3ULckfqH$V{<S3ODsN@GWpo&B*kqDoPEm@(W3>%1*XSQL?vFu&J;D8jzb> zlBiITo0C^;Rbi_HHrEQs1_|pcDS(xfWZNo192Makpx~Tel&WB=XRMoSU}&gdW~OIo zVrph)sH0$HU}&Uo07PcGh9*{~W>!Y#3Q(W~w5=#5%__*n4QdyVXRDM^Qc_^0uU}qX zu2*iXmtT~wZ)j<02{OaTNEfI=x41H|B(Xv_uUHvof=g;~a#3bMNoIbY0?5R~r2Ntn zTP2`NAzsKWfE$}v3=Jk=fazBx7U&!58GyV5Q|Rl9UukYGTy=3tP%6T`SPd=?sVqp< z4@xc0FD*(2MqHXQ$f^P>=c3falKi5O{QMkPC<bKYm*f{`<QFJ72b(EqglFcZ<d+v~ zg4O%_T6yLbmn7yTr+T{BDgjN<%gju%GIVrxG;%hwbapi|H#BrLv2=7bbF(xwFn4q| zb9J?Fg6Vb1PcF?(%`1WFO+n~&#HkmQ6mkoIHoK%2WtOF;xE1B+DuBIgm5JLe<~YrR z>P^Az77Ltu^?{Dj2SqGWM8kxDsRzV_CtDx~p72xifT_I*n5?_z_V+U|Fs<@*aSW-r zwPvb+hftx&@#pjZ|DUi>&Zp4%ri~7Z?k08S#d|7FIy=5|Ji0q}PsIlj7g@0-O#+TK zE$w`=RxEQDCC4`35>wMyxioisQf%p+nR8$Mj;`B$ByFAbxdX}1{yuyD`^UZSfA4+Z zIsCzZ+q7t9Pp3Gi-jAAn+<wcA4W)X^G&An|FuX6@t$!<L+jO~p^;N50Sw~JYT_ZgC zEW-x9>DpOaqqrM%9#&p`SYYt@V+FIpr+*Ez3K@(#qI9M)De$~AyOBEOja5TDd-Fjx z-Pt~yjiwxIQs7{fIQ-e>aG}i3`HULvn{*G~pP-WI@csRHR)vF+)2=EW5u0#Q>)V`A zk*Bs_5;N4g5*zAXDoC(jP@VF2+TFbA7jqXa%VcU@(Z+b(vB6@)b1swJY<sH?KP;HN z<)=w)8q<pj`yCu#6f-YS{qg#OyHd2;)~{z8IByDcJI}Ydb&ECk+Co8Z?`0BfJ{Q>S zg~J3Mt0mM<@m*5L|MlTB+m$R%|0J5&FJ8)?=<%ddK_KFwwl-s7+8JqE=@*9<#GH;> zzg?2A{rp7E12Zix87Dd|ys$WLd-c}quis7x6Z7d>o2ay3W2#s7)1sf;HJA47KH)Q4 zx}o{I;#9A@xh7Ki556VZ3;f8ikYZclx?Q-_W%1>gHitRo6JJU*{Wo6D!rXM~V8RE_ zy>az({Faw*_#&I#{Qs@JC`0ZKt@-EguMW|g|KMNCU$+k{H)p6ZRK@tHP5wUn?6d40 z)f;qK=4H;}QopiOzWb=$mZ-Js?!Q+rz2}nf(j@XV!`q0t`~_0;!&YByznN3keXa7m zhcQR{=cdPfmyY^W&OZJ0-Ic7Z{RQ<CqvnS^f2r<r`OQopHR=B2_Tt?~Z+{R^6#V6A z_>d!O*5<o;`dy1=9Qa(xWKvwSfy3jOpnGY+$&Y5jAKV)l8Oo~ed#YJn(*l*`p00i_ I>zopr01<p)kpKVy literal 0 HcmV?d00001 diff --git a/airtime_mvc/public/css/embed-player-images/unmute.png b/airtime_mvc/public/css/embed-player-images/unmute.png new file mode 100644 index 0000000000000000000000000000000000000000..83959783518ac07d6c764d8dbef9895e4f2280f1 GIT binary patch literal 1378 zcmeAS@N?(olHy`uVBq!ia0vp@Ak4uAB#T}@sR2@wC9V-A!TD(=<%vb942~)JNvR5+ zxryniL8*x;m4zo$Z5SAsl`=yjN+NuHtdjF{^%6m9^eS=-fVvqNZ0suv5|gu2OB9k) z(=+pImEP~(ucVNfVyhHx>TBRz;GCL~=}}db8eHWUl3bOYY?-2DZ>L~WVFffGH?<^D zp&~aYuh^=>Rtapb6_5=Q)>l#hD=EpgRf0Gw!Z$#{Ilm}X!Bo#!H`&0@P{GVh&(Orw z%*;?n!N|bSNZ$a6%ybP+tW3?UjLa3FKnZADQA(Oskc%7CE+EfVDWjyMz)D}gyu4hm z+*mKaC|%#s($W%ShLMpjP=#)BWnM{Qg>GK4GRy>*)Z*l#%z~24{5%DaiHS-1r6smX zK$k+ikXryZHm?{OOuzusuShJ=H`Fr#c?qV_*B8Ii++4Wo;*y|LgnO|XTpUtakg6Y) zTAW{6lnjixG-Z%g1y;^Qsfi`|MIrh5Ij~R+$jC3rFV4s>P;d@5Q_u*{%uC5HFV+OB z_w}{%%quQQ%u7!7bg@+enxL1NnPTN)Wa8vxXyM}Q>S}6e=xXBVYVK%h;cDUN>|)~T z?BWE|>yn>bnwy$e0@Is<(Cdm*FDNPG765H_NiE7OOHFYr%Fk5*d)X=zw_BWWng`XJ zg4-?5IQ8lS9itD5Sfq%C2?0|NhzU=&Kn^_Nr{)1udl4{MH(XTjWnf^u=IP=XQgJI~ z(p~SULW$$`^Mcjm`zCb-+^bY|mRPek_J~49$G-*>4)&*KSLAwyo0TbE>fPBZ))P44 z$&ITe)33${2x)t;-M+exwXl5B8RI8kDvvn4IMDw5`=^@u+dmhFXo;HsQ5E83e#|Pp zE8*ei`WxvkcmE#?UU?-+Md;_HyzSGwjy`&+5Fq|B_|55w3wii_IAWAORPEIg;i{c} zU}wxbPaU!916A^TM=q-OCAciU_%X%k=jFF$+WYUXkI}n6ZQbklJ8w5$|IqcIeNv{* z{PX`e=Kf~)D{f2Fn15cq$(Duvj{M=|8Tn?jgMVoHdL6#FWXjG2gB47T4N8}zS?2HD z)!;cp?XcxHWeeAo#|jgtX3m;+`K8b1{Zbrt2RFMd{<thaV+LP)$zl`1ug6awoVqOY zb%dpLqyGkx{^Qki_MS-FY+kPPawW67Tz|T(wyN%7E}sWmqV%S_zf9g1qIK58{!*_V z_oeGM<{T2y`BJslJ>~AvvYF4y)`o4?J-y3s`DNitePL_E<Tve*`Lph2)!u)*FTdR5 zeBpoXgN4UU+f<zce*B;M>D&Y9oPhnR6QkB%D>ka-Z9gm><fk_Ir$X-G{dNCsdVSx# tyL;MU#pww4_x{&(7eya_|0b+~k>RkyOxZ90<}3r1dY-O+F6*2UngH(63Mc>o literal 0 HcmV?d00001 diff --git a/airtime_mvc/public/css/embeddableplayer.css b/airtime_mvc/public/css/embeddable-player-form.css similarity index 99% rename from airtime_mvc/public/css/embeddableplayer.css rename to airtime_mvc/public/css/embeddable-player-form.css index a1e12cbc0..9a6fe20d8 100644 --- a/airtime_mvc/public/css/embeddableplayer.css +++ b/airtime_mvc/public/css/embeddable-player-form.css @@ -12,3 +12,5 @@ padding: 4px 0px 4px 0px; } + + diff --git a/airtime_mvc/public/css/embeddable-player.css b/airtime_mvc/public/css/embeddable-player.css new file mode 100644 index 000000000..6c770c693 --- /dev/null +++ b/airtime_mvc/public/css/embeddable-player.css @@ -0,0 +1,190 @@ +.airtime_player { + max-width: 270px; + position: relative; + font-family: Arial, Helvetica, sans-serif; + color: #fff; + background: rgba(53, 53, 53, 0.9); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: 0 3px 5px rgba(0,0,0,0.1) inset,0 1px 0 rgba(255,255,255,0.1),0 0 1px #000 inset; + -moz-box-shadow: 0 3px 5px rgba(0,0,0,0.1) inset,0 1px 0 rgba(255,255,255,0.1),0 0 1px #000 inset; + box-shadow: 0 3px 5px rgba(0,0,0,0.1) inset,0 1px 0 rgba(255,255,255,0.1),0 0 1px #000 inset; +} + +.airtime_header { + background: rgba(53, 53, 53, 0.9); + -webkit-box-shadow: 0 3px 5px rgba(0,0,0,0.1) inset,0 1px 0 rgba(255,255,255,0.1),0 0 1px #000 inset; + -moz-box-shadow: 0 3px 5px rgba(0,0,0,0.1) inset,0 1px 0 rgba(255,255,255,0.1),0 0 1px #000 inset; + box-shadow: 0 3px 5px rgba(0,0,0,0.1) inset,0 1px 0 rgba(255,255,255,0.1),0 0 1px #000 inset; + height: 37px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.airtime_box { + margin-top: 15px; + float: left; + width: 100%; +} +.station_name { + font-size: 14px; + padding-top: 10px; + padding-left: 20px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + padding-right: 30px; +} +.airtime_pro { + position: absolute; + right: 15px; + top: 10px; + color: #fff; + font-size: 12px; + text-decoration: none; +} +.airtime_pro span { + display: inline-block; + vertical-align: 2px; + margin-right: 5px; +} +.airtime_pro:hover span { + display: inline-block; + vertical-align: 2px; + margin-right: 5px; +} +.airtime_box .airtime_button { + text-indent: -9999px; + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + border-radius: 2px; + background: rgb(100,100,100); + background: -moz-linear-gradient(top, rgba(107, 107, 107, 1) 0%, rgba(88,88,88,1) 100%); + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(100,100,100,1)), color-stop(100%,rgba(88,88,88,1))); + background: -webkit-linear-gradient(top, rgba(107, 107, 107, 1) 0%,rgba(88,88,88,1) 100%); + background: -o-linear-gradient(top, rgba(107, 107, 107, 1) 0%,rgba(88,88,88,1) 100%); + background: -ms-linear-gradient(top, rgba(107, 107, 107, 1) 0%,rgba(88,88,88,1) 100%); + background: linear-gradient(to bottom, rgba(107, 107, 107, 1) 0%,rgba(88,88,88,1) 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#646464', endColorstr='#585858',GradientType=0 ); + -webkit-box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.2) inset; + -moz-box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.2) inset; + box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.2) inset; + width: 47px; + height: 47px; + display: inline-block; + float: left; + cursor: pointer; + margin-left: 20px; + margin-right: 15px; +} + +.airtime_box .airtime_button:hover { + background: rgb(147,147,147); + background: -moz-linear-gradient(top, rgba(147,147,147,1) 0%, rgba(117,117,117,1) 100%); + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(147,147,147,1)), color-stop(100%,rgba(117,117,117,1))); + background: -webkit-linear-gradient(top, rgba(147,147,147,1) 0%,rgba(117,117,117,1) 100%); + background: -o-linear-gradient(top, rgba(147,147,147,1) 0%,rgba(117,117,117,1) 100%); + background: -ms-linear-gradient(top, rgba(147,147,147,1) 0%,rgba(117,117,117,1) 100%); + background: linear-gradient(to bottom, rgba(147,147,147,1) 0%,rgba(117,117,117,1) 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#939393', endColorstr='#757575',GradientType=0 ); +} + +.airtime_box .airtime_button .play_button { + display: block; + background: url("embed-player-images/play_button.png") center no-repeat; + width: 47px; + height: 47px; +} + +.airtime_box .airtime_button .stop_button { + display: block; + background: url("embed-player-images/pause_button.png") center no-repeat; + width: 47px; + height: 47px; +} + +.hide_button { + display:none !important; +} + +.now_playing { + margin-top: 8px; + margin-left: 15px; + margin-right: 15px; + display: block; + font-size: 14px; + color: #fff; + width: auto; +} + +.now_playing span { + display: block; + color: #aaaaaa; +} + +.airtime_volume { + padding: 10px 0px 15px 0px; + clear: both; +} + +.airtime_volume .volume_control { + margin-left: 55px; + float: left; + display: inline-block; +} + +.airtime_volume .mute { + background: url("embed-player-images/mute.png") center no-repeat; + display: block; + margin-top: -4px; + width: 15px; + height: 15px; + cursor: pointer; +} + +.airtime_volume_bar { + border-color: #262526 #262526 #5E5E5E; + border-style: solid; + border-width: 1px; + background-color: #393939; + width: auto; + height: 5px; + cursor: pointer; + margin-left: 80px; + margin-right: 40px; +} + +.airtime_volume_bar_value { + background-color: #ff9122; + width: 0px; + height: 5px; +} + +.airtime_schedule { + margin: 0px 20px 10px 20px; + padding-top: 10px; + font-size: 14px; + color: #aaaaaa; + border-top: 1px solid rgba(255, 255, 255, 0.1); +} + +.airtime_next { + float: left; + margin: 0px; +} + +.schedule_list { + list-style: none; + padding-left: 0px; + padding-bottom: 10px; + margin-top: 0px; + margin-left: 60px; + line-height: 130%; +} + +.schedule_list li { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} \ No newline at end of file diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js b/airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js index 376c64e57..be4bbc7c3 100644 --- a/airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js +++ b/airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js @@ -2,17 +2,25 @@ function updateEmbedSrcParams() { var $embedCodeParams = "?"; var $streamMode = getStreamMode(); - if ($streamMode == "b") { + if ($streamMode == "manual") { var $stream = $("input[name=player_stream_url]:radio:checked").val(); - $embedCodeParams += "stream-mode=b&stream="+$stream; - } else if ($streamMode == "a") { - $embedCodeParams += "stream-mode=a"; + $embedCodeParams += "stream="+$stream; + } else if ($streamMode == "auto") { + $embedCodeParams += "stream=auto"; } $embedCodeParams += "\""; $("input[name=player_embed_src]").val(function(index, value) { return value.replace(/\?.*?"/, $embedCodeParams); }); + + updatePlayerIframeSrc($("input[name=player_embed_src]").val()); +} + +function updatePlayerIframeSrc(iframe_text) { + var $player_iframe = $("#player_form iframe"); + var player_iframe_src = iframe_text.match(/http.*?"/)[0].slice(0, -1); + $player_iframe.attr('src', player_iframe_src); } function getStreamMode() { @@ -23,9 +31,9 @@ $(document).ready(function() { $("#player_stream_mode-element").change(function() { var $streamMode = getStreamMode(); - if ($streamMode == "a") { + if ($streamMode == "auto") { $("#player_stream_url-element input[type='radio']").attr("disabled", "disabled"); - } else if ($streamMode == "b") { + } else if ($streamMode == "manual") { $("#player_stream_url-element input[type='radio']").removeAttr("disabled"); $("input[name=player_stream_url]").each(function(i, obj) { From 3d2b189dbadfe28a651716d9f8c703edc6cd39be Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Wed, 25 Mar 2015 11:47:23 -0400 Subject: [PATCH 29/60] removed logging statement --- .../application/views/scripts/embeddableplayer/embed-code.phtml | 1 - 1 file changed, 1 deletion(-) diff --git a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml index 47e713e42..f650ed509 100644 --- a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml +++ b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml @@ -83,7 +83,6 @@ data: {type:"interval",limit:"5"}, dataType: "jsonp", success: function(data) { - console.log("fetching metadata"); var current_track_end_time = new Date(data.current.ends); var current_time = new Date(); current_time = new Date(current_time.getTime() + current_time.getTimezoneOffset()*60*1000); From d0f7f820a78516c096acaa61b2b9c077eea4389b Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Fri, 27 Mar 2015 16:34:04 -0400 Subject: [PATCH 30/60] SAAS-675: Implement "auto" mode --- .../EmbeddableplayerController.php | 25 ++- .../scripts/embeddableplayer/embed-code.phtml | 150 ++++++++++++++---- 2 files changed, 143 insertions(+), 32 deletions(-) diff --git a/airtime_mvc/application/controllers/EmbeddableplayerController.php b/airtime_mvc/application/controllers/EmbeddableplayerController.php index 2a66d0b7e..2d70c08a2 100644 --- a/airtime_mvc/application/controllers/EmbeddableplayerController.php +++ b/airtime_mvc/application/controllers/EmbeddableplayerController.php @@ -39,9 +39,26 @@ class EmbeddablePlayerController extends Zend_Controller_Action $this->view->station_name = Application_Model_Preference::GetStationName(); $stream = $request->getParam('stream'); $streamData = Application_Model_StreamSetting::getEnabledStreamData(); - $selectedStreamData = $streamData[$stream]; - $this->view->streamURL = $selectedStreamData["url"]; - $this->view->codec = $selectedStreamData["codec"]; - $this->view->displayMetadata = $request->getParam('display_metadata'); + + if ($stream == "auto") { + $this->view->playerMode = "auto"; + $availableMobileStreams = array(); + $availableDesktopStreams = array(); + foreach ($streamData as $s) { + if ($s["mobile"]) { + array_push($availableMobileStreams, $s); + } else if (!$s["mobile"]) { + array_push($availableDesktopStreams, $s); + } + } + $this->view->availableMobileStreams = json_encode($availableMobileStreams); + $this->view->availableDesktopStreams = json_encode($availableDesktopStreams); + } else { + $this->view->playerMode = "manual"; + $selectedStreamData = $streamData[$stream]; + $this->view->streamURL = $selectedStreamData["url"]; + $this->view->codec = $selectedStreamData["codec"]; + } + //$this->view->displayMetadata = $request->getParam('display_metadata'); } } \ No newline at end of file diff --git a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml index f650ed509..940850be9 100644 --- a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml +++ b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml @@ -8,19 +8,42 @@ <script type="text/javascript"> var MusesPlayer = function() { - MRP.insert({ - 'url':"<?php echo $this->streamURL ?>", - 'codec':"<?php echo $this->codec ?>", - 'volume':100, - 'jsevents':true, - 'autoplay':false, - 'buffering':5, - 'title':'test', - 'bgcolor':'#FFFFFF', - 'skin':'mcclean', - 'width':180, - 'height':60 - }); + this.mobileDetect = this.mobileDetect(); + this.availableMobileStreamQueue = <?php echo $this->availableMobileStreams?>; + this.availableDesktopStreamQueue = <?php echo $this->availableDesktopStreams?>; + this.playerMode = "<?php echo $this->playerMode ?>"; + + if (this.playerMode == "manual") { + MRP.insert({ + 'url': "<?php echo $this->streamURL ?>", + 'codec': "<?php echo $this->codec ?>", + 'volume': 100, + 'jsevents': true, + 'autoplay': false, + 'buffering': 5, + 'title': 'test', + 'bgcolor': '#FFFFFF', + 'skin': 'mcclean', + 'width': 180, + 'height': 60 + }); + } else if (this.playerMode == "auto") { + var stream = this.getNextAvailableStream(); + MRP.insert({ + 'url': stream["url"], + 'codec': stream["codec"], + 'volume': 100, + 'jsevents': true, + 'autoplay': false, + 'buffering': 5, + 'title': 'test', + 'bgcolor': '#FFFFFF', + 'skin': 'mcclean', + 'width': 180, + 'height': 60 + }); + } + $("p.station_name").html("<?php echo $this->station_name?>"); this.flashDetect = FlashDetect.versionAtLeast(10, 1) ? true : false; @@ -28,6 +51,49 @@ getMetadata(); }; + MusesPlayer.prototype.mobileDetect = function() { + if ( screen.width <= 760) { + return true; + } else { + return false; + } + } + + MusesPlayer.prototype.getNextAvailableStream = function() { + if (this.mobileDetect && this.availableMobileStreamQueue.length > 0) { + return this.getNextAvailableMobileStream(); + } + + if (!this.mobileDetect && this.availableDesktopStreamQueue.length > 0) { + return this.getNextAvailableDesktopStream(); + } + + //if we get to this point there are no available streams for the + //type of device the client has connected with so just return + //the next available stream - first we'll try the desktop streams + var desktopStream = this.getNextAvailableDesktopStream(); + if (desktopStream) { + return desktopStream; + } else { + return this.getNextAvailableMobileStream(); + } + + } + + MusesPlayer.prototype.getNextAvailableMobileStream = function() { + var stream = this.availableMobileStreamQueue.shift(); + //add to end of queue + this.availableMobileStreamQueue.push(stream); + return stream; + } + + MusesPlayer.prototype.getNextAvailableDesktopStream = function() { + var stream = this.availableDesktopStreamQueue.shift(); + //add to end of queue + this.availableDesktopStreamQueue.push(stream); + return stream; + } + MusesPlayer.prototype.play = function() { this.flashDetect ? MRP.play() : musesHTMLPlayClick(); togglePlayStopButton(); @@ -46,9 +112,20 @@ //TODO }; - /*function musesCallback(event,value){ - console.log('event: "'+event+'", value: "'+value+'"'); - }*/ + function musesCallback(event,value){ + switch (event) { + case "loadComplete": + // no source URL is set + if (value === "0") { + console.log("loadComplete failed"); + } + case "ioError": + // connection limit reached or problem connecting to stream + if (value === "0") { + console.log("ioError"); + } + } + } /** * This is a hack to trigger the play button in HTML5 mode @@ -83,18 +160,35 @@ data: {type:"interval",limit:"5"}, dataType: "jsonp", success: function(data) { - var current_track_end_time = new Date(data.current.ends); - var current_time = new Date(); - current_time = new Date(current_time.getTime() + current_time.getTimezoneOffset()*60*1000); - time_to_next_track_starts = current_track_end_time - current_time; - // maybe we should set time_to_next_track_starts to - // (10 || 20 || etc.) minutes if its greater than that - // in case of on-the-fly schedule changes + //console.log("hello"); + + if (data.current === null) { + $("p.now_playing").html("Offline"); + } else { + var artist = data.current.name.split(" - ")[0]; + var track = data.current.name.split(" - ")[1]; + $("p.now_playing").html(artist + "<span>" + track + "</span>"); + + var current_track_end_time = new Date(data.current.ends); + var current_time = new Date(); + //convert current_time to UTC to match the timezone of time_to_next_track_starts + current_time = new Date(current_time.getTime() + current_time.getTimezoneOffset() * 60 * 1000); + //TODO stop the first settimeout from executing!! + time_to_next_track_starts = current_track_end_time - current_time; + //console.log((time_to_next_track_starts/1000)/60); + // maybe we should set time_to_next_track_starts to + // (10 || 20 || etc.) minutes if its greater than that + // in case of on-the-fly schedule changes + + + } + + if (data.next === null) { + $("ul.schedule_list").find("li").html("Nothing scheduled"); + } else { + $("ul.schedule_list").find("li").html(data.next.name); + } - var artist = data.current.name.split(" - ")[0]; - var track = data.current.name.split(" - ")[1]; - $("p.now_playing").html(artist+"<span>"+track+"</span>"); - $("ul.schedule_list").find("li").html(data.next.name); } }); setTimeout(getMetadata, time_to_next_track_starts); @@ -141,7 +235,7 @@ <div class="airtime_schedule"> <p class="airtime_next">Next</p> <ul class="schedule_list"> - <li>John Legend - Ordinary People</li> + <li></li> </ul> </div> From 778df97d3cb13c39e4ee1d14ecc2a07c6068741c Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Tue, 31 Mar 2015 16:51:14 -0400 Subject: [PATCH 31/60] SAAS-662: Make player auto-connect if there is a problem with the stream Kind of working in HTML5 mode --- .../EmbeddableplayerController.php | 8 +- .../scripts/embeddableplayer/embed-code.phtml | 101 +++++++----------- .../public/js/airtime/embeddableplayer/mrp.js | 1 + 3 files changed, 44 insertions(+), 66 deletions(-) diff --git a/airtime_mvc/application/controllers/EmbeddableplayerController.php b/airtime_mvc/application/controllers/EmbeddableplayerController.php index 2d70c08a2..6a00df36b 100644 --- a/airtime_mvc/application/controllers/EmbeddableplayerController.php +++ b/airtime_mvc/application/controllers/EmbeddableplayerController.php @@ -39,11 +39,11 @@ class EmbeddablePlayerController extends Zend_Controller_Action $this->view->station_name = Application_Model_Preference::GetStationName(); $stream = $request->getParam('stream'); $streamData = Application_Model_StreamSetting::getEnabledStreamData(); + $availableMobileStreams = array(); + $availableDesktopStreams = array(); if ($stream == "auto") { $this->view->playerMode = "auto"; - $availableMobileStreams = array(); - $availableDesktopStreams = array(); foreach ($streamData as $s) { if ($s["mobile"]) { array_push($availableMobileStreams, $s); @@ -51,14 +51,14 @@ class EmbeddablePlayerController extends Zend_Controller_Action array_push($availableDesktopStreams, $s); } } - $this->view->availableMobileStreams = json_encode($availableMobileStreams); - $this->view->availableDesktopStreams = json_encode($availableDesktopStreams); } else { $this->view->playerMode = "manual"; $selectedStreamData = $streamData[$stream]; $this->view->streamURL = $selectedStreamData["url"]; $this->view->codec = $selectedStreamData["codec"]; } + $this->view->availableMobileStreams = json_encode($availableMobileStreams); + $this->view->availableDesktopStreams = json_encode($availableDesktopStreams); //$this->view->displayMetadata = $request->getParam('display_metadata'); } } \ No newline at end of file diff --git a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml index 940850be9..5c1a57b74 100644 --- a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml +++ b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml @@ -9,45 +9,35 @@ var MusesPlayer = function() { this.mobileDetect = this.mobileDetect(); - this.availableMobileStreamQueue = <?php echo $this->availableMobileStreams?>; - this.availableDesktopStreamQueue = <?php echo $this->availableDesktopStreams?>; this.playerMode = "<?php echo $this->playerMode ?>"; + this.flashDetect = FlashDetect.versionAtLeast(10, 1) ? true : false; + this.settings = { + 'volume': 100, + 'jsevents': true, + 'autoplay': false, + 'buffering': 5, + 'title': 'test', + 'bgcolor': '#FFFFFF', + 'skin': 'mcclean', + 'width': 180, + 'height': 60 + }; if (this.playerMode == "manual") { - MRP.insert({ - 'url': "<?php echo $this->streamURL ?>", - 'codec': "<?php echo $this->codec ?>", - 'volume': 100, - 'jsevents': true, - 'autoplay': false, - 'buffering': 5, - 'title': 'test', - 'bgcolor': '#FFFFFF', - 'skin': 'mcclean', - 'width': 180, - 'height': 60 - }); + this.settings.url = "<?php echo $this->streamURL ?>"; + this.settings.codec = "<?php echo $this->codec ?>"; + MRP.insert(this.settings); } else if (this.playerMode == "auto") { + this.availableMobileStreamQueue = <?php echo $this->availableMobileStreams?>; + this.availableDesktopStreamQueue = <?php echo $this->availableDesktopStreams?>; var stream = this.getNextAvailableStream(); - MRP.insert({ - 'url': stream["url"], - 'codec': stream["codec"], - 'volume': 100, - 'jsevents': true, - 'autoplay': false, - 'buffering': 5, - 'title': 'test', - 'bgcolor': '#FFFFFF', - 'skin': 'mcclean', - 'width': 180, - 'height': 60 - }); + this.settings.url = stream["url"]; + this.settings.codec = stream["codec"]; + MRP.insert(this.settings); } $("p.station_name").html("<?php echo $this->station_name?>"); - this.flashDetect = FlashDetect.versionAtLeast(10, 1) ? true : false; - getMetadata(); }; @@ -95,7 +85,7 @@ } MusesPlayer.prototype.play = function() { - this.flashDetect ? MRP.play() : musesHTMLPlayClick(); + this.flashDetect ? MRP.play() : musesHTMLPlayClick();; togglePlayStopButton(); }; @@ -108,43 +98,39 @@ //this.flashDetect ? MRP.setVolume(value) : null; }; - MusesPlayer.prototype.setURL = function() { - //TODO + MusesPlayer.prototype.setURL = function(url) { + MRP.setUrl(url); }; - function musesCallback(event,value){ + // detects errors in FLASH mode + function musesCallback(event,value) { switch (event) { - case "loadComplete": - // no source URL is set - if (value === "0") { - console.log("loadComplete failed"); - } case "ioError": // connection limit reached or problem connecting to stream if (value === "0") { console.log("ioError"); + var stream = musesPlayer.getNextAvailableStream(); + musesPlayer.setURL(stream["url"]); + musesPlayer.play(); } } } - /** - * This is a hack to trigger the play button in HTML5 mode - */ function musesHTMLPlayClick() { - //child nodes - var cn = document.getElementById("MusesRadioPlayer-HTML5-player-1").childNodes; - var playDiv = cn[4]; - playDiv.onclick(); + MRP.html.audio.src = MRP.html.src; + + MRP.html.audio.play(); + + // detects errors in HTML5 mode + MRP.html.audio.addEventListener('error', function failed(e) { + var stream = musesPlayer.getNextAvailableStream(); + MRP.html.audio.src = stream["url"]; + MRP.html.audio.play(); + }, true); } - /** - * This is a hack to trigger the stop button in HTML5 mode - */ function musesHTMLStopClick() { - //child nodes - var cn = document.getElementById("MusesRadioPlayer-HTML5-player-1").childNodes; - var stopDiv = cn[5]; - stopDiv.onclick(); + MRP.html.audio.pause(); } function togglePlayStopButton() { @@ -247,14 +233,5 @@ </script> </div> -<!-- -<div id="custom_muses_play" onclick="musesPlayer.play()"> - <a href="#">play</a> -</div> -<div id="custom_muses_stop" onclick="musesPlayer.stop()"> - <a href="#">stop</a> -</div> ---> - </body> </html> \ No newline at end of file diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/mrp.js b/airtime_mvc/public/js/airtime/embeddableplayer/mrp.js index 4377dddd5..ca7140dc1 100644 --- a/airtime_mvc/public/js/airtime/embeddableplayer/mrp.js +++ b/airtime_mvc/public/js/airtime/embeddableplayer/mrp.js @@ -2298,6 +2298,7 @@ this.ui = new d.UI(this, a); a.autoplay && (a = window.navigator.userAgent.toLowerCase(), -1 == a.indexOf("iphone") && -1 == a.indexOf("ipad") && -1 == a.indexOf("ipod") && this.playAudio()) + n.MRP.html = this; }; d.Muses.__name__ = !0; d.Muses.initTimer = function(a) { From 15a8e3fa1f5e8fb2accfa58d63e6cfd814ced198 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Tue, 31 Mar 2015 17:20:42 -0400 Subject: [PATCH 32/60] Moved html5 error listener to MusesPlayer constructor --- .../scripts/embeddableplayer/embed-code.phtml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml index 5c1a57b74..076b6c38f 100644 --- a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml +++ b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml @@ -39,6 +39,16 @@ $("p.station_name").html("<?php echo $this->station_name?>"); getMetadata(); + + // detects errors in HTML5 mode + if (!this.flashDetect) { + MRP.html.audio.addEventListener('error', function failed(e) { + var stream = musesPlayer.getNextAvailableStream(); + console.log(stream); + MRP.html.audio.src = stream["url"]; + MRP.html.audio.play(); + }, true); + } }; MusesPlayer.prototype.mobileDetect = function() { @@ -120,13 +130,6 @@ MRP.html.audio.src = MRP.html.src; MRP.html.audio.play(); - - // detects errors in HTML5 mode - MRP.html.audio.addEventListener('error', function failed(e) { - var stream = musesPlayer.getNextAvailableStream(); - MRP.html.audio.src = stream["url"]; - MRP.html.audio.play(); - }, true); } function musesHTMLStopClick() { From 57cd2eda13dd00aa98c587859cb1d7ab20218c48 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Tue, 31 Mar 2015 18:21:25 -0400 Subject: [PATCH 33/60] bad merge --- airtime_mvc/application/configs/ACL.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/airtime_mvc/application/configs/ACL.php b/airtime_mvc/application/configs/ACL.php index fe6e4d3d6..1386d1dee 100644 --- a/airtime_mvc/application/configs/ACL.php +++ b/airtime_mvc/application/configs/ACL.php @@ -71,11 +71,8 @@ $ccAcl->allow('G', 'index') ->allow('A', 'user') ->allow('A', 'systemstatus') ->allow('A', 'preference') -<<<<<<< HEAD ->allow('A', 'embeddableplayer') -======= ->allow('S', 'thank-you') ->>>>>>> saas ->allow('S', 'billing'); From c1038cd92fe0b28ead6b39afa476e7f53b5e1531 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Wed, 1 Apr 2015 11:25:58 -0400 Subject: [PATCH 34/60] SAAS-706: Remove volume bar from player --- .../views/scripts/embeddableplayer/embed-code.phtml | 5 +++-- airtime_mvc/public/css/embeddable-player.css | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml index 076b6c38f..0af07296e 100644 --- a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml +++ b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml @@ -209,7 +209,7 @@ </div> - <div class="airtime_volume"> + <!--<div class="airtime_volume"> <div class="volume_control"> <span class="mute"></span> @@ -219,7 +219,8 @@ <div class="airtime_volume_bar_value" style="width: 80%;"></div> </div> - </div> + </div>--> + <div style="clear:both"></div> <div class="airtime_schedule"> <p class="airtime_next">Next</p> diff --git a/airtime_mvc/public/css/embeddable-player.css b/airtime_mvc/public/css/embeddable-player.css index 6c770c693..be1a06d9d 100644 --- a/airtime_mvc/public/css/embeddable-player.css +++ b/airtime_mvc/public/css/embeddable-player.css @@ -162,7 +162,7 @@ } .airtime_schedule { - margin: 0px 20px 10px 20px; + margin: 10px 20px 10px 20px; padding-top: 10px; font-size: 14px; color: #aaaaaa; From eaeed9be77a04b3f95164b479f1ce7de091d7794 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Wed, 1 Apr 2015 12:50:49 -0400 Subject: [PATCH 35/60] SAAS-669: Long station names overlap with link to airtime.pro --- .../views/scripts/embeddableplayer/embed-code.phtml | 3 ++- airtime_mvc/public/css/embeddable-player.css | 12 ++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml index 0af07296e..d2aa129db 100644 --- a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml +++ b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml @@ -196,7 +196,6 @@ <div class="airtime_header"> <p class="station_name">fff</p> - <a class="airtime_pro" href="#"><span>airtime.pro</span><img src="http://localhost/css/embed-player-images/airtime_logo.png"></a> </div> <div class="airtime_box"> @@ -227,8 +226,10 @@ <ul class="schedule_list"> <li></li> </ul> + <a class="airtime_pro" href="https://www.airtime.pro/">Powered by Airtime Pro<span class="airtime_pro_logo"></span></a> </div> + </div> <div id="muses_skin"> diff --git a/airtime_mvc/public/css/embeddable-player.css b/airtime_mvc/public/css/embeddable-player.css index be1a06d9d..88f3ee187 100644 --- a/airtime_mvc/public/css/embeddable-player.css +++ b/airtime_mvc/public/css/embeddable-player.css @@ -37,13 +37,15 @@ padding-right: 30px; } .airtime_pro { - position: absolute; - right: 15px; - top: 10px; color: #fff; - font-size: 12px; + font-size: 10px; text-decoration: none; } +.airtime_pro_logo { + background: url("embed-player-images/airtime_logo.png") center no-repeat; + width: 16px; + height: 16px; +} .airtime_pro span { display: inline-block; vertical-align: 2px; @@ -167,6 +169,7 @@ font-size: 14px; color: #aaaaaa; border-top: 1px solid rgba(255, 255, 255, 0.1); + padding-bottom: 5px; } .airtime_next { @@ -180,6 +183,7 @@ padding-bottom: 10px; margin-top: 0px; margin-left: 60px; + margin-bottom: 0px; line-height: 130%; } From 0bffa07ed9129995b3420d184432733e267e22e7 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Wed, 1 Apr 2015 12:59:31 -0400 Subject: [PATCH 36/60] SAAS-669: Long station names overlap with link to airtime.pro --- airtime_mvc/public/css/embeddable-player.css | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/airtime_mvc/public/css/embeddable-player.css b/airtime_mvc/public/css/embeddable-player.css index 88f3ee187..30ce8beac 100644 --- a/airtime_mvc/public/css/embeddable-player.css +++ b/airtime_mvc/public/css/embeddable-player.css @@ -45,8 +45,9 @@ background: url("embed-player-images/airtime_logo.png") center no-repeat; width: 16px; height: 16px; + display:inline-block; } -.airtime_pro span { +/*.airtime_pro span { display: inline-block; vertical-align: 2px; margin-right: 5px; @@ -55,7 +56,7 @@ display: inline-block; vertical-align: 2px; margin-right: 5px; -} +}*/ .airtime_box .airtime_button { text-indent: -9999px; -webkit-border-radius: 2px; From 353bfe86a5f9c7ac915503dff569d1530a83438d Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Wed, 1 Apr 2015 13:05:08 -0400 Subject: [PATCH 37/60] Addded default for mobile stream setting --- airtime_mvc/application/models/StreamSetting.php | 1 + 1 file changed, 1 insertion(+) diff --git a/airtime_mvc/application/models/StreamSetting.php b/airtime_mvc/application/models/StreamSetting.php index 19a147798..c51995d6d 100644 --- a/airtime_mvc/application/models/StreamSetting.php +++ b/airtime_mvc/application/models/StreamSetting.php @@ -177,6 +177,7 @@ class Application_Model_StreamSetting self::ensureKeyExists($keyPrefix . 'type', $data); self::ensureKeyExists($keyPrefix . 'url', $data); self::ensureKeyExists($keyPrefix . 'user', $data); + self::ensureKeyExists($keyPrefix . 'mobile', $data); return $data; } From 06f43ef2d7c3420d57831f8fdd0366790a39e17d Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Thu, 2 Apr 2015 12:45:05 -0400 Subject: [PATCH 38/60] CSS modifications Added fixed width and height to player. Floated Airtime badge to the right --- airtime_mvc/public/css/embeddable-player.css | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/airtime_mvc/public/css/embeddable-player.css b/airtime_mvc/public/css/embeddable-player.css index 30ce8beac..420d12ede 100644 --- a/airtime_mvc/public/css/embeddable-player.css +++ b/airtime_mvc/public/css/embeddable-player.css @@ -1,5 +1,6 @@ .airtime_player { - max-width: 270px; + width: 270px; + height: 172px; position: relative; font-family: Arial, Helvetica, sans-serif; color: #fff; @@ -40,6 +41,8 @@ color: #fff; font-size: 10px; text-decoration: none; + display:inline-block; + float:right; } .airtime_pro_logo { background: url("embed-player-images/airtime_logo.png") center no-repeat; @@ -47,6 +50,7 @@ height: 16px; display:inline-block; } + /*.airtime_pro span { display: inline-block; vertical-align: 2px; From fdd8cdfaeaf5ff9ffc77fe43877461d5fd52a100 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Thu, 2 Apr 2015 14:35:13 -0400 Subject: [PATCH 39/60] Fixed Airtime badge spacing --- .../views/scripts/embeddableplayer/embed-code.phtml | 2 +- airtime_mvc/public/css/embeddable-player.css | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml index d2aa129db..d1047885c 100644 --- a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml +++ b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml @@ -226,8 +226,8 @@ <ul class="schedule_list"> <li></li> </ul> - <a class="airtime_pro" href="https://www.airtime.pro/">Powered by Airtime Pro<span class="airtime_pro_logo"></span></a> </div> + <a class="airtime_pro" href="https://www.airtime.pro/">Powered by Airtime Pro<span class="airtime_pro_logo"></span></a> </div> diff --git a/airtime_mvc/public/css/embeddable-player.css b/airtime_mvc/public/css/embeddable-player.css index 420d12ede..261a6496f 100644 --- a/airtime_mvc/public/css/embeddable-player.css +++ b/airtime_mvc/public/css/embeddable-player.css @@ -1,6 +1,6 @@ .airtime_player { width: 270px; - height: 172px; + height: 180px; position: relative; font-family: Arial, Helvetica, sans-serif; color: #fff; @@ -38,6 +38,7 @@ padding-right: 30px; } .airtime_pro { + margin: 0px 20px; color: #fff; font-size: 10px; text-decoration: none; @@ -169,12 +170,13 @@ } .airtime_schedule { - margin: 10px 20px 10px 20px; + margin: 10px 20px 5px 20px; padding-top: 10px; font-size: 14px; color: #aaaaaa; border-top: 1px solid rgba(255, 255, 255, 0.1); - padding-bottom: 5px; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + padding-bottom: 0px; } .airtime_next { From 4ab4fe5cb9886dc0e24e8104091d96cba22e7e15 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Thu, 2 Apr 2015 15:00:46 -0400 Subject: [PATCH 40/60] Fixed player css so long artist and track names don't overflow --- airtime_mvc/public/css/embeddable-player.css | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/airtime_mvc/public/css/embeddable-player.css b/airtime_mvc/public/css/embeddable-player.css index 261a6496f..3c9ef0f08 100644 --- a/airtime_mvc/public/css/embeddable-player.css +++ b/airtime_mvc/public/css/embeddable-player.css @@ -1,6 +1,6 @@ .airtime_player { width: 270px; - height: 180px; + height: 185px; position: relative; font-family: Arial, Helvetica, sans-serif; color: #fff; @@ -123,12 +123,19 @@ display: block; font-size: 14px; color: #fff; - width: auto; + width: 170px; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; } .now_playing span { display: block; color: #aaaaaa; + width: 170px; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; } .airtime_volume { From 369a59c564ad1212360acfc85053eeaedf6f0bbf Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Thu, 2 Apr 2015 15:13:34 -0400 Subject: [PATCH 41/60] Changed mobile option text on streams page --- airtime_mvc/application/forms/StreamSettingSubForm.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airtime_mvc/application/forms/StreamSettingSubForm.php b/airtime_mvc/application/forms/StreamSettingSubForm.php index b84adf63d..8d1f96f43 100644 --- a/airtime_mvc/application/forms/StreamSettingSubForm.php +++ b/airtime_mvc/application/forms/StreamSettingSubForm.php @@ -54,7 +54,7 @@ class Application_Form_StreamSettingSubForm extends Zend_Form_SubForm $this->addElement($enable); $mobile = new Zend_Form_Element_Checkbox('mobile'); - $mobile->setLabel(_('Good for mobile?')); + $mobile->setLabel(_('Mobile:')); $mobile->setValue($setting[$prefix.'_mobile']); $mobile->setDecorators(array('ViewHelper')); $this->addElement($mobile); From e8c8fde8bde50325d7c1c81b5a64146ec02b7752 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Thu, 2 Apr 2015 15:27:01 -0400 Subject: [PATCH 42/60] Fix airtime.pro link on player --- .../application/views/scripts/embeddableplayer/embed-code.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml index d1047885c..4ab35b768 100644 --- a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml +++ b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml @@ -227,7 +227,7 @@ <li></li> </ul> </div> - <a class="airtime_pro" href="https://www.airtime.pro/">Powered by Airtime Pro<span class="airtime_pro_logo"></span></a> + <a class="airtime_pro" target="_blank" href="https://www.airtime.pro/">Powered by Airtime Pro<span class="airtime_pro_logo"></span></a> </div> From faee0fba98123587b5ae9646cca4b93ef902c1c9 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Thu, 2 Apr 2015 16:54:57 -0400 Subject: [PATCH 43/60] SAAS-673: Warn if public api is not enabled under Preferences --- .../controllers/EmbeddableplayerController.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/airtime_mvc/application/controllers/EmbeddableplayerController.php b/airtime_mvc/application/controllers/EmbeddableplayerController.php index 6a00df36b..3552ee47e 100644 --- a/airtime_mvc/application/controllers/EmbeddableplayerController.php +++ b/airtime_mvc/application/controllers/EmbeddableplayerController.php @@ -17,10 +17,15 @@ class EmbeddablePlayerController extends Zend_Controller_Action $form = new Application_Form_EmbeddablePlayer(); - if ($form->getElement('player_stream_url')->getAttrib('numberOfEnabledStreams') > 0) { + $apiEnabled = Application_Model_Preference::GetAllow3rdPartyApi(); + $numEnabledStreams = $form->getElement('player_stream_url')->getAttrib('numberOfEnabledStreams'); + + if ($numEnabledStreams > 0 && $apiEnabled) { $this->view->form = $form; } else { - $this->view->errorMsg = "You need to enable at least one MP3, AAC, or OGG stream to use this feature."; + $this->view->errorMsg = "To configure and use the embeddable player you must:<br><br> + 1. Enable at least one MP3, AAC, or OGG stream under System -> Streams<br> + 2. Enable the Public Airtime API under System -> Preferences"; } } From 481d21ff70a6bfd77dde5d4835d7d0ab01c01850 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Tue, 7 Apr 2015 11:34:21 -0400 Subject: [PATCH 44/60] SAAS-662: Make player auto-connect if there is a problem with the stream HTML5 error handling --- .../scripts/embeddableplayer/embed-code.phtml | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml index 4ab35b768..66117609a 100644 --- a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml +++ b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml @@ -15,7 +15,7 @@ 'volume': 100, 'jsevents': true, 'autoplay': false, - 'buffering': 5, + 'buffering': 0, 'title': 'test', 'bgcolor': '#FFFFFF', 'skin': 'mcclean', @@ -26,27 +26,37 @@ if (this.playerMode == "manual") { this.settings.url = "<?php echo $this->streamURL ?>"; this.settings.codec = "<?php echo $this->codec ?>"; - MRP.insert(this.settings); } else if (this.playerMode == "auto") { this.availableMobileStreamQueue = <?php echo $this->availableMobileStreams?>; this.availableDesktopStreamQueue = <?php echo $this->availableDesktopStreams?>; var stream = this.getNextAvailableStream(); this.settings.url = stream["url"]; this.settings.codec = stream["codec"]; - MRP.insert(this.settings); } + MRP.insert(this.settings); + $("p.station_name").html("<?php echo $this->station_name?>"); getMetadata(); - // detects errors in HTML5 mode + // detects events in HTML5 mode if (!this.flashDetect) { + MRP.html.audio.addEventListener('error', function failed(e) { + console.log("HTML error"); var stream = musesPlayer.getNextAvailableStream(); - console.log(stream); - MRP.html.audio.src = stream["url"]; - MRP.html.audio.play(); + var audio = $(MRP.html.audio); + audio.src = stream["url"]; + audio[0].load(); + audio[0].play(); + }, true); + + MRP.html.audio.addEventListener('pause', function paused(e) { + //this is when pause happens + console.log("HTML paused"); + //src = MRP.html.audio.src; + //MRP.html.audio.src = ""; }, true); } }; @@ -109,6 +119,7 @@ }; MusesPlayer.prototype.setURL = function(url) { + console.log("setURL"); MRP.setUrl(url); }; From f6d5b34cca21493931e8243476a5dbecd0685221 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Tue, 7 Apr 2015 17:49:21 -0400 Subject: [PATCH 45/60] Embed player - code review fixes --- .../application/controllers/ApiController.php | 2 +- .../EmbeddableplayerController.php | 17 +++- .../application/forms/EmbeddablePlayer.php | 19 +++-- .../application/models/StreamSetting.php | 16 ---- .../scripts/embeddableplayer/embed-code.phtml | 83 ++++++++++++++----- 5 files changed, 89 insertions(+), 48 deletions(-) diff --git a/airtime_mvc/application/controllers/ApiController.php b/airtime_mvc/application/controllers/ApiController.php index 80429db08..5ecd5f1da 100644 --- a/airtime_mvc/application/controllers/ApiController.php +++ b/airtime_mvc/application/controllers/ApiController.php @@ -523,7 +523,7 @@ class ApiController extends Zend_Controller_Action $result["description"] = Application_Model_Preference::GetStationDescription(); $result["timezone"] = Application_Model_Preference::GetDefaultTimezone(); $result["locale"] = Application_Model_Preference::GetDefaultLocale(); - $result["enabled_stream_urls"] = Application_Model_StreamSetting::getEnabledStreamUrls(); + $result["stream_data"] = Application_Model_StreamSetting::getEnabledStreamData(); // used by caller to determine if the airtime they are running or widgets in use is out of date. $result['AIRTIME_API_VERSION'] = AIRTIME_API_VERSION; diff --git a/airtime_mvc/application/controllers/EmbeddableplayerController.php b/airtime_mvc/application/controllers/EmbeddableplayerController.php index 3552ee47e..43e8be2d5 100644 --- a/airtime_mvc/application/controllers/EmbeddableplayerController.php +++ b/airtime_mvc/application/controllers/EmbeddableplayerController.php @@ -30,18 +30,29 @@ class EmbeddablePlayerController extends Zend_Controller_Action } + /** + * This is the action that is called to insert the player onto a web page. + * It passes all the js and css files to the view, as well as all the + * stream customization information. + * + * The view for this action contains all the inline javascript needed to + * create the player. + */ public function embedCodeAction() { $this->view->layout()->disableLayout(); + $CC_CONFIG = Config::getConfig(); + $request = $this->getRequest(); - $this->view->css = Application_Common_HTTPHelper::getStationUrl() . "css/embeddable-player.css"; - $this->view->mrp_js = Application_Common_HTTPHelper::getStationUrl() . "js/airtime/embeddableplayer/mrp.js"; + $this->view->css = Application_Common_HTTPHelper::getStationUrl() . "css/embeddable-player.css?".$CC_CONFIG['airtime_version']; + $this->view->mrp_js = Application_Common_HTTPHelper::getStationUrl() . "js/airtime/embeddableplayer/mrp.js?".$CC_CONFIG['airtime_version']; $this->view->jquery = Application_Common_HTTPHelper::getStationUrl() . "js/libs/jquery-1.10.2.js"; $this->view->muses_swf = Application_Common_HTTPHelper::getStationUrl() . "js/airtime/embeddableplayer/muses.swf"; $this->view->metadata_api_url = Application_Common_HTTPHelper::getStationUrl() . "api/live-info"; - $this->view->station_name = Application_Model_Preference::GetStationName(); + $this->view->station_name = addslashes(Application_Model_Preference::GetStationName()); + $stream = $request->getParam('stream'); $streamData = Application_Model_StreamSetting::getEnabledStreamData(); $availableMobileStreams = array(); diff --git a/airtime_mvc/application/forms/EmbeddablePlayer.php b/airtime_mvc/application/forms/EmbeddablePlayer.php index e5086716a..ca8ca45c9 100644 --- a/airtime_mvc/application/forms/EmbeddablePlayer.php +++ b/airtime_mvc/application/forms/EmbeddablePlayer.php @@ -1,5 +1,7 @@ <?php +define("OPUS", "opus"); + class Application_Form_EmbeddablePlayer extends Zend_Form_SubForm { public function init() @@ -8,17 +10,19 @@ class Application_Form_EmbeddablePlayer extends Zend_Form_SubForm array('ViewScript', array('viewScript' => 'form/embeddableplayer.phtml')) )); + /* We will use this option in the future $displayTrackMetadata = new Zend_Form_Element_Checkbox('player_display_track_metadata'); $displayTrackMetadata->setValue(true); $displayTrackMetadata->setLabel(_('Display track metadata?')); $this->addElement($displayTrackMetadata); + */ $streamMode = new Zend_Form_Element_Radio('player_stream_mode'); $streamMode->setLabel(_('Select Stream:')); $streamMode->setMultiOptions( array( - "auto" => "Auto detect the most appropriate stream to use.", - "manual" => "Select a stream:" + "auto" => _("Auto detect the most appropriate stream to use."), + "manual" => _("Select a stream:") ) ); $streamMode->setValue("auto"); @@ -30,19 +34,20 @@ class Application_Form_EmbeddablePlayer extends Zend_Form_SubForm foreach(Application_Model_StreamSetting::getEnabledStreamData() as $stream => $data) { $urlOptions[$stream] = strtoupper($data["codec"])." - ".$data["bitrate"]."kbps"; if ($data["mobile"]) { - $urlOptions[$stream] .= " - Mobile friendly"; + $urlOptions[$stream] .= _(" - Mobile friendly"); } - if ($data["codec"] == "opus") { + if ($data["codec"] == OPUS) { $opusStreamCount += 1; - $urlOptions[$stream] .=" - The player does not support Opus streams."; + $urlOptions[$stream] .= _(" - The player does not support Opus streams."); } } $streamURL->setMultiOptions( $urlOptions ); + // Set default value to the first non-opus stream we find foreach ($urlOptions as $o => $v) { - if (strpos(strtolower($v), "opus") !== false) { + if (strpos(strtolower($v), OPUS) !== false) { continue; } else { $streamURL->setValue($o); @@ -62,7 +67,7 @@ class Application_Form_EmbeddablePlayer extends Zend_Form_SubForm $this->addElement($embedSrc); $previewLabel = new Zend_Form_Element_Text('player_preview_label'); - $previewLabel->setLabel("Preview:"); + $previewLabel->setLabel(_("Preview:")); $this->addElement($previewLabel); } diff --git a/airtime_mvc/application/models/StreamSetting.php b/airtime_mvc/application/models/StreamSetting.php index c51995d6d..688ddc397 100644 --- a/airtime_mvc/application/models/StreamSetting.php +++ b/airtime_mvc/application/models/StreamSetting.php @@ -65,22 +65,6 @@ class Application_Model_StreamSetting return $result ? $result : $default; } - public static function getEnabledStreamUrls() - { - $urls = Array(); - $streamIds = Application_Model_StreamSetting::getEnabledStreamIds(); - foreach ($streamIds as $id) { - $prefix = $id."_"; - $streamData = Application_Model_StreamSetting::getStreamData($id); - $host = $streamData[$prefix."host"]; - $port = $streamData[$prefix."port"]; - $mount = $streamData[$prefix."mount"]; - $type = $streamData[$prefix."type"]; - $urls[$type] = "http://$host:$port/$mount"; - } - return $urls; - } - public static function getEnabledStreamData() { $streams = Array(); diff --git a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml index 66117609a..f079a4564 100644 --- a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml +++ b/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml @@ -7,6 +7,17 @@ <script src="<?php echo $this->jquery?>" type="text/javascript"></script> <script type="text/javascript"> + var MAX_MOBILE_SCREEN_WIDTH = 760; + + // We are creating a custom player object that acts as a wrapper + // around the player object we get from Muses. We are doing this + // for a couple reasons: + // + // 1. It will be easier to swap out Muses for a different player engine + // in the future - if we ever decide to do that. + // 2. We had to add in some custom behaviour depending on the player + // customizations and whether or not the player is in Flash or HTML5 + // mode. var MusesPlayer = function() { this.mobileDetect = this.mobileDetect(); this.playerMode = "<?php echo $this->playerMode ?>"; @@ -34,11 +45,12 @@ this.settings.codec = stream["codec"]; } + // Create the Muses player object MRP.insert(this.settings); - $("p.station_name").html("<?php echo $this->station_name?>"); + $("p.station_name").html(htmlEscape("<?php echo $this->station_name?>")); - getMetadata(); + attachStreamMetadataToPlayer(); // detects events in HTML5 mode if (!this.flashDetect) { @@ -62,13 +74,14 @@ }; MusesPlayer.prototype.mobileDetect = function() { - if ( screen.width <= 760) { - return true; - } else { - return false; - } + return (screen.width <= MAX_MOBILE_SCREEN_WIDTH); } + // This function is called if an error occurs while a client is + // attempting to connect to a stream (An error would occur if + // the streams listener count has been maxed out or if the stream is down). + // It checks if the client is a mobile device or not and returns the next + // best available stream. MusesPlayer.prototype.getNextAvailableStream = function() { if (this.mobileDetect && this.availableMobileStreamQueue.length > 0) { return this.getNextAvailableMobileStream(); @@ -78,9 +91,9 @@ return this.getNextAvailableDesktopStream(); } - //if we get to this point there are no available streams for the - //type of device the client has connected with so just return - //the next available stream - first we'll try the desktop streams + // If we get to this point there are no available streams for the + // type of device the client has connected with so just return + // the next available stream - first we'll try the desktop streams var desktopStream = this.getNextAvailableDesktopStream(); if (desktopStream) { return desktopStream; @@ -90,6 +103,8 @@ } + // Gets and returns the next available mobile stream from the queue, + // but adds it back to the end of the queue to be recycled. MusesPlayer.prototype.getNextAvailableMobileStream = function() { var stream = this.availableMobileStreamQueue.shift(); //add to end of queue @@ -97,6 +112,8 @@ return stream; } + // Gets and returns the next available desktop stream from the queue, + // but adds it back to the end of the queue to be recycled. MusesPlayer.prototype.getNextAvailableDesktopStream = function() { var stream = this.availableDesktopStreamQueue.shift(); //add to end of queue @@ -114,10 +131,6 @@ togglePlayStopButton(); }; - MusesPlayer.prototype.setVolume = function(value) { - //this.flashDetect ? MRP.setVolume(value) : null; - }; - MusesPlayer.prototype.setURL = function(url) { console.log("setURL"); MRP.setUrl(url); @@ -129,7 +142,6 @@ case "ioError": // connection limit reached or problem connecting to stream if (value === "0") { - console.log("ioError"); var stream = musesPlayer.getNextAvailableStream(); musesPlayer.setURL(stream["url"]); musesPlayer.play(); @@ -137,14 +149,23 @@ } } + // Triggers the play function on the Muses player object in HTML5 mode function musesHTMLPlayClick() { + /*if (MRP.html === undefined) { + console.log("inserting player"); + MRP.insert(musesPlayer.settings); + }*/ MRP.html.audio.src = MRP.html.src; MRP.html.audio.play(); } + // Triggers the stop function on the Muses player object in HTML5 mode + // NOTE: The HTML5 audio element doesn't have stop functionality. It + // can only be paused. function musesHTMLStopClick() { MRP.html.audio.pause(); + //delete MRP.html; } function togglePlayStopButton() { @@ -155,12 +176,15 @@ // default how often to fetch metadata to 20 seconds var time_to_next_track_starts = 20000; - function getMetadata(){ + // Fetches the streams metadata from the Airtime live-info API + // and attaches it to the player UI. + // + // The metadata is fetched when the current track is about to end. + function attachStreamMetadataToPlayer(){ $.ajax({url: "<?php echo $this->metadata_api_url?>", data: {type:"interval",limit:"5"}, dataType: "jsonp", success: function(data) { - //console.log("hello"); if (data.current === null) { $("p.now_playing").html("Offline"); @@ -191,14 +215,30 @@ } }); - setTimeout(getMetadata, time_to_next_track_starts); + setTimeout(attachStreamMetadataToPlayer, time_to_next_track_starts); + } + + function htmlEscape(str) { + return String(str) + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(/'/g, ''') + .replace(/</g, '<') + .replace(/>/g, '>'); } </script> <style type="text/css"> + /* + We have to have the default Muses skin displayed on the page or else + the player will not work. Setting the display to none and hidden does + not work. It has to be "visible" on the page. As a hacky work around we + set the height and width to 1px so users will not see it. + */ #muses_skin{width:1px; height:1px; overflow-x: hidden; overflow-y: hidden;} </style> + </head> <body> @@ -219,7 +259,8 @@ </div> - <!--<div class="airtime_volume"> + <!-- + <div class="airtime_volume"> <div class="volume_control"> <span class="mute"></span> @@ -229,7 +270,8 @@ <div class="airtime_volume_bar_value" style="width: 80%;"></div> </div> - </div>--> + </div> + --> <div style="clear:both"></div> <div class="airtime_schedule"> @@ -240,7 +282,6 @@ </div> <a class="airtime_pro" target="_blank" href="https://www.airtime.pro/">Powered by Airtime Pro<span class="airtime_pro_logo"></span></a> - </div> <div id="muses_skin"> From 23bf866211187ac341eb6fbe2e44945b3614594f Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Wed, 8 Apr 2015 14:01:57 -0400 Subject: [PATCH 46/60] SAAS-712: UI Improvements --- airtime_mvc/application/configs/ACL.php | 4 +- .../application/configs/navigation.php | 7 ++-- ...yerController.php => PlayerController.php} | 20 +++++----- .../{EmbeddablePlayer.php => Player.php} | 10 ++--- .../{embeddableplayer.phtml => player.phtml} | 27 +++++++++----- .../index.phtml => player/customize.phtml} | 4 +- .../embed-code.phtml => player/index.phtml} | 0 .../public/css/embeddable-player-form.css | 16 -------- airtime_mvc/public/css/player-form.css | 35 ++++++++++++++++++ .../css/{embeddable-player.css => player.css} | 0 .../{embeddableplayer => player}/mrp.js | 0 .../{embeddableplayer => player}/muses.swf | Bin .../embeddableplayer.js => player/player.js} | 8 +++- 13 files changed, 81 insertions(+), 50 deletions(-) rename airtime_mvc/application/controllers/{EmbeddableplayerController.php => PlayerController.php} (82%) rename airtime_mvc/application/forms/{EmbeddablePlayer.php => Player.php} (86%) rename airtime_mvc/application/views/scripts/form/{embeddableplayer.phtml => player.phtml} (55%) rename airtime_mvc/application/views/scripts/{embeddableplayer/index.phtml => player/customize.phtml} (84%) rename airtime_mvc/application/views/scripts/{embeddableplayer/embed-code.phtml => player/index.phtml} (100%) delete mode 100644 airtime_mvc/public/css/embeddable-player-form.css create mode 100644 airtime_mvc/public/css/player-form.css rename airtime_mvc/public/css/{embeddable-player.css => player.css} (100%) rename airtime_mvc/public/js/airtime/{embeddableplayer => player}/mrp.js (100%) rename airtime_mvc/public/js/airtime/{embeddableplayer => player}/muses.swf (100%) rename airtime_mvc/public/js/airtime/{embeddableplayer/embeddableplayer.js => player/player.js} (89%) diff --git a/airtime_mvc/application/configs/ACL.php b/airtime_mvc/application/configs/ACL.php index 1386d1dee..c58986f1e 100644 --- a/airtime_mvc/application/configs/ACL.php +++ b/airtime_mvc/application/configs/ACL.php @@ -38,7 +38,7 @@ $ccAcl->add(new Zend_Acl_Resource('library')) ->add(new Zend_Acl_Resource('billing')) ->add(new Zend_Acl_Resource('thank-you')) ->add(new Zend_Acl_Resource('provisioning')) - ->add(new Zend_Acl_Resource('embeddableplayer')); + ->add(new Zend_Acl_Resource('player')); /** Creating permissions */ $ccAcl->allow('G', 'index') @@ -71,7 +71,7 @@ $ccAcl->allow('G', 'index') ->allow('A', 'user') ->allow('A', 'systemstatus') ->allow('A', 'preference') - ->allow('A', 'embeddableplayer') + ->allow('A', 'player') ->allow('S', 'thank-you') ->allow('S', 'billing'); diff --git a/airtime_mvc/application/configs/navigation.php b/airtime_mvc/application/configs/navigation.php index 01c8aa385..1aeab45d5 100644 --- a/airtime_mvc/application/configs/navigation.php +++ b/airtime_mvc/application/configs/navigation.php @@ -87,11 +87,10 @@ $pages = array( 'resource' => 'listenerstat' ), array( - 'label' => _('Embeddable Player'), + 'label' => _('Player'), 'module' => 'default', - 'controller' => 'embeddableplayer', - 'action' => 'index', - 'resource' => 'embeddableplayer' + 'controller' => 'player', + 'action' => 'customize' ) ) ), diff --git a/airtime_mvc/application/controllers/EmbeddableplayerController.php b/airtime_mvc/application/controllers/PlayerController.php similarity index 82% rename from airtime_mvc/application/controllers/EmbeddableplayerController.php rename to airtime_mvc/application/controllers/PlayerController.php index 43e8be2d5..2775d1c55 100644 --- a/airtime_mvc/application/controllers/EmbeddableplayerController.php +++ b/airtime_mvc/application/controllers/PlayerController.php @@ -1,21 +1,20 @@ <?php -class EmbeddablePlayerController extends Zend_Controller_Action +class PlayerController extends Zend_Controller_Action { public function init() { } - public function indexAction() + public function customizeAction() { $CC_CONFIG = Config::getConfig(); $baseUrl = Application_Common_OsPath::getBaseDir(); - $this->view->headLink()->appendStylesheet($baseUrl.'css/embeddable-player-form.css?'.$CC_CONFIG['airtime_version']); - $this->view->headScript()->appendFile($baseUrl.'js/airtime/embeddableplayer/mrp.js?'.$CC_CONFIG['airtime_version']); - $this->view->headScript()->appendFile($baseUrl.'js/airtime/embeddableplayer/embeddableplayer.js?'.$CC_CONFIG['airtime_version']); + $this->view->headLink()->appendStylesheet($baseUrl.'css/player-form.css?'.$CC_CONFIG['airtime_version']); + $this->view->headScript()->appendFile($baseUrl.'js/airtime/player/player.js?'.$CC_CONFIG['airtime_version']); - $form = new Application_Form_EmbeddablePlayer(); + $form = new Application_Form_Player(); $apiEnabled = Application_Model_Preference::GetAllow3rdPartyApi(); $numEnabledStreams = $form->getElement('player_stream_url')->getAttrib('numberOfEnabledStreams'); @@ -38,18 +37,19 @@ class EmbeddablePlayerController extends Zend_Controller_Action * The view for this action contains all the inline javascript needed to * create the player. */ - public function embedCodeAction() + public function indexAction() { $this->view->layout()->disableLayout(); $CC_CONFIG = Config::getConfig(); + $request = $this->getRequest(); - $this->view->css = Application_Common_HTTPHelper::getStationUrl() . "css/embeddable-player.css?".$CC_CONFIG['airtime_version']; - $this->view->mrp_js = Application_Common_HTTPHelper::getStationUrl() . "js/airtime/embeddableplayer/mrp.js?".$CC_CONFIG['airtime_version']; + $this->view->css = Application_Common_HTTPHelper::getStationUrl() . "css/player.css?".$CC_CONFIG['airtime_version']; + $this->view->mrp_js = Application_Common_HTTPHelper::getStationUrl() . "js/airtime/player/mrp.js?".$CC_CONFIG['airtime_version']; $this->view->jquery = Application_Common_HTTPHelper::getStationUrl() . "js/libs/jquery-1.10.2.js"; - $this->view->muses_swf = Application_Common_HTTPHelper::getStationUrl() . "js/airtime/embeddableplayer/muses.swf"; + $this->view->muses_swf = Application_Common_HTTPHelper::getStationUrl() . "js/airtime/player/muses.swf"; $this->view->metadata_api_url = Application_Common_HTTPHelper::getStationUrl() . "api/live-info"; $this->view->station_name = addslashes(Application_Model_Preference::GetStationName()); diff --git a/airtime_mvc/application/forms/EmbeddablePlayer.php b/airtime_mvc/application/forms/Player.php similarity index 86% rename from airtime_mvc/application/forms/EmbeddablePlayer.php rename to airtime_mvc/application/forms/Player.php index ca8ca45c9..4cad32e53 100644 --- a/airtime_mvc/application/forms/EmbeddablePlayer.php +++ b/airtime_mvc/application/forms/Player.php @@ -2,12 +2,12 @@ define("OPUS", "opus"); -class Application_Form_EmbeddablePlayer extends Zend_Form_SubForm +class Application_Form_Player extends Zend_Form_SubForm { public function init() { $this->setDecorators(array( - array('ViewScript', array('viewScript' => 'form/embeddableplayer.phtml')) + array('ViewScript', array('viewScript' => 'form/player.phtml')) )); /* We will use this option in the future @@ -56,14 +56,14 @@ class Application_Form_EmbeddablePlayer extends Zend_Form_SubForm } $streamURL->setAttrib('numberOfEnabledStreams', sizeof($urlOptions)-$opusStreamCount); - $streamURL->setAttrib("disabled", "disabled"); + $streamURL->removeDecorator('label'); $this->addElement($streamURL); $embedSrc = new Zend_Form_Element_Text('player_embed_src'); $embedSrc->setAttrib("readonly", "readonly"); $embedSrc->setAttrib("class", "embed-player-text-box"); - $embedSrc->setValue('<iframe frameborder="0" width="280" height="230" src="'.Application_Common_HTTPHelper::getStationUrl().'embeddableplayer/embed-code?stream=auto"></iframe>'); - $embedSrc->removeDecorator('label'); + $embedSrc->setLabel(_("Embeddable code:")); + $embedSrc->setValue('<iframe frameborder="0" width="280" height="210" src="'.Application_Common_HTTPHelper::getStationUrl().'player?stream=auto"></iframe>'); $this->addElement($embedSrc); $previewLabel = new Zend_Form_Element_Text('player_preview_label'); diff --git a/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml b/airtime_mvc/application/views/scripts/form/player.phtml similarity index 55% rename from airtime_mvc/application/views/scripts/form/embeddableplayer.phtml rename to airtime_mvc/application/views/scripts/form/player.phtml index 18305c20b..c47deaccf 100644 --- a/airtime_mvc/application/views/scripts/form/embeddableplayer.phtml +++ b/airtime_mvc/application/views/scripts/form/player.phtml @@ -1,18 +1,27 @@ <fieldset class="padded"> <dl class="zend_form"> - <?php echo $this->element->getElement('player_embed_src'); ?> - - <?php echo $this->element->getElement('player_stream_mode'); ?> - - <?php echo $this->element->getElement('player_stream_url'); ?> - - <?php //echo $this->element->getElement('player_display_track_metadata'); ?> - <?php echo $this->element->getElement('player_preview_label')->renderLabel(); ?> <div style="clear:both"></div> - <?php echo $this->element->getElement('player_embed_src')->getValue(); ?> + <div class="player-preview"> + <?php echo $this->element->getElement('player_embed_src')->getValue(); ?> + </div> + + <div id="player_instructions"> + Customize the player by configuring the options below. Once you are happy with the player + copy the embeddable code below into your websites HTML. + </div> + + <?php echo $this->element->getElement('player_stream_mode')->render(); ?> + + <?php echo $this->element->getElement('player_stream_url'); ?> + + <?php echo $this->element->getElement('player_embed_src')->render(); ?> + + <?php //echo $this->element->getElement('player_display_track_metadata'); ?> + + </dl> </fieldset> \ No newline at end of file diff --git a/airtime_mvc/application/views/scripts/embeddableplayer/index.phtml b/airtime_mvc/application/views/scripts/player/customize.phtml similarity index 84% rename from airtime_mvc/application/views/scripts/embeddableplayer/index.phtml rename to airtime_mvc/application/views/scripts/player/customize.phtml index e4c37849b..56c70f8a4 100644 --- a/airtime_mvc/application/views/scripts/embeddableplayer/index.phtml +++ b/airtime_mvc/application/views/scripts/player/customize.phtml @@ -1,9 +1,9 @@ <div class="ui-widget ui-widget-content block-shadow simple-formblock embed-player-form clearfix padded-strong "> - <h2 style="float:left"><?php echo _("Embeddable Player") ?></h2> + <?php $baseUrl = Application_Common_OsPath::getBaseDir(); ?> <form method="post" id="player_form" enctype="multipart/form-data"> - + <h2 style="float:left"><?php echo _("Embeddable Player") ?></h2> <div style="clear:both"></div> <?php echo $this->errorMsg; ?> <?php echo $this->form; ?> diff --git a/airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml b/airtime_mvc/application/views/scripts/player/index.phtml similarity index 100% rename from airtime_mvc/application/views/scripts/embeddableplayer/embed-code.phtml rename to airtime_mvc/application/views/scripts/player/index.phtml diff --git a/airtime_mvc/public/css/embeddable-player-form.css b/airtime_mvc/public/css/embeddable-player-form.css deleted file mode 100644 index 9a6fe20d8..000000000 --- a/airtime_mvc/public/css/embeddable-player-form.css +++ /dev/null @@ -1,16 +0,0 @@ -.embed-player-text-box { - padding-right: 0px !important; - width: 100% !important; -} -.embed-player-form { - width: 70%; -} -.embed-player-form dd { - width: 100% !important; - float: left; - margin: 0; - padding: 4px 0px 4px 0px; -} - - - diff --git a/airtime_mvc/public/css/player-form.css b/airtime_mvc/public/css/player-form.css new file mode 100644 index 000000000..663a304cf --- /dev/null +++ b/airtime_mvc/public/css/player-form.css @@ -0,0 +1,35 @@ +.embed-player-text-box { + padding-right: 0px !important; + width: 100% !important; +} +.embed-player-form { + width: 98%; +} +.embed-player-form dd { + width: 100% !important; + float: left; + margin: 0; + padding: 4px 0px 4px 0px; +} +.player-preview { + width: 100%; +} +.player-preview iframe { + margin: 0 auto; + display: block; +} +#player_form { + width: 50%; + margin: 0 auto; +} +#player_instructions { + border-bottom: 1px solid; + padding-bottom: 10px; + font-size: 14px; + font-weight: bold; + color: #5b5b5b; + margin-bottom: 10px; +} + + + diff --git a/airtime_mvc/public/css/embeddable-player.css b/airtime_mvc/public/css/player.css similarity index 100% rename from airtime_mvc/public/css/embeddable-player.css rename to airtime_mvc/public/css/player.css diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/mrp.js b/airtime_mvc/public/js/airtime/player/mrp.js similarity index 100% rename from airtime_mvc/public/js/airtime/embeddableplayer/mrp.js rename to airtime_mvc/public/js/airtime/player/mrp.js diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/muses.swf b/airtime_mvc/public/js/airtime/player/muses.swf similarity index 100% rename from airtime_mvc/public/js/airtime/embeddableplayer/muses.swf rename to airtime_mvc/public/js/airtime/player/muses.swf diff --git a/airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js b/airtime_mvc/public/js/airtime/player/player.js similarity index 89% rename from airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js rename to airtime_mvc/public/js/airtime/player/player.js index be4bbc7c3..ea5fb5588 100644 --- a/airtime_mvc/public/js/airtime/embeddableplayer/embeddableplayer.js +++ b/airtime_mvc/public/js/airtime/player/player.js @@ -29,12 +29,16 @@ function getStreamMode() { $(document).ready(function() { + $("#player_stream_url-element").hide(); + $("#player_stream_mode-element").change(function() { var $streamMode = getStreamMode(); + if ($streamMode == "auto") { - $("#player_stream_url-element input[type='radio']").attr("disabled", "disabled"); + $("#player_stream_url-element").hide(); + } else if ($streamMode == "manual") { - $("#player_stream_url-element input[type='radio']").removeAttr("disabled"); + $("#player_stream_url-element").show(); $("input[name=player_stream_url]").each(function(i, obj) { if ($(this).parent().text().toLowerCase().indexOf("opus") >= 0) { From 94a7643ebc62082034abfd6d35e19b4cda140e0f Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Wed, 8 Apr 2015 15:00:02 -0400 Subject: [PATCH 47/60] Escape stream urls in the player js --- .../application/views/scripts/player/index.phtml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/airtime_mvc/application/views/scripts/player/index.phtml b/airtime_mvc/application/views/scripts/player/index.phtml index f079a4564..3f198e642 100644 --- a/airtime_mvc/application/views/scripts/player/index.phtml +++ b/airtime_mvc/application/views/scripts/player/index.phtml @@ -35,20 +35,21 @@ }; if (this.playerMode == "manual") { - this.settings.url = "<?php echo $this->streamURL ?>"; + this.settings.url = htmlEscape("<?php echo $this->streamURL ?>"); this.settings.codec = "<?php echo $this->codec ?>"; } else if (this.playerMode == "auto") { this.availableMobileStreamQueue = <?php echo $this->availableMobileStreams?>; this.availableDesktopStreamQueue = <?php echo $this->availableDesktopStreams?>; var stream = this.getNextAvailableStream(); - this.settings.url = stream["url"]; + this.settings.url = htmlEscape(stream["url"]); this.settings.codec = stream["codec"]; } // Create the Muses player object MRP.insert(this.settings); - $("p.station_name").html(htmlEscape("<?php echo $this->station_name?>")); + var station_name = htmlEscape("<?php echo $this->station_name?>"); + $("p.station_name").html(station_name); attachStreamMetadataToPlayer(); @@ -132,7 +133,6 @@ }; MusesPlayer.prototype.setURL = function(url) { - console.log("setURL"); MRP.setUrl(url); }; @@ -143,7 +143,7 @@ // connection limit reached or problem connecting to stream if (value === "0") { var stream = musesPlayer.getNextAvailableStream(); - musesPlayer.setURL(stream["url"]); + musesPlayer.setURL(htmlEscape(stream["url"])); musesPlayer.play(); } } From 78b2631980a7c6569cb41a38db6cad6e1a33ebce Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Wed, 8 Apr 2015 15:19:22 -0400 Subject: [PATCH 48/60] Remove stream url js escaping and do it on server side instead --- .../controllers/PlayerController.php | 4 ++-- .../views/scripts/player/index.phtml | 18 ++++-------------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/airtime_mvc/application/controllers/PlayerController.php b/airtime_mvc/application/controllers/PlayerController.php index 2775d1c55..92501bbd4 100644 --- a/airtime_mvc/application/controllers/PlayerController.php +++ b/airtime_mvc/application/controllers/PlayerController.php @@ -51,7 +51,7 @@ class PlayerController extends Zend_Controller_Action $this->view->jquery = Application_Common_HTTPHelper::getStationUrl() . "js/libs/jquery-1.10.2.js"; $this->view->muses_swf = Application_Common_HTTPHelper::getStationUrl() . "js/airtime/player/muses.swf"; $this->view->metadata_api_url = Application_Common_HTTPHelper::getStationUrl() . "api/live-info"; - $this->view->station_name = addslashes(Application_Model_Preference::GetStationName()); + $this->view->station_name = json_encode(Application_Model_Preference::GetStationName()); $stream = $request->getParam('stream'); $streamData = Application_Model_StreamSetting::getEnabledStreamData(); @@ -70,7 +70,7 @@ class PlayerController extends Zend_Controller_Action } else { $this->view->playerMode = "manual"; $selectedStreamData = $streamData[$stream]; - $this->view->streamURL = $selectedStreamData["url"]; + $this->view->streamURL = json_encode($selectedStreamData["url"]); $this->view->codec = $selectedStreamData["codec"]; } $this->view->availableMobileStreams = json_encode($availableMobileStreams); diff --git a/airtime_mvc/application/views/scripts/player/index.phtml b/airtime_mvc/application/views/scripts/player/index.phtml index 3f198e642..ee19b337c 100644 --- a/airtime_mvc/application/views/scripts/player/index.phtml +++ b/airtime_mvc/application/views/scripts/player/index.phtml @@ -35,21 +35,20 @@ }; if (this.playerMode == "manual") { - this.settings.url = htmlEscape("<?php echo $this->streamURL ?>"); + this.settings.url = "<?php echo $this->streamURL ?>"; this.settings.codec = "<?php echo $this->codec ?>"; } else if (this.playerMode == "auto") { this.availableMobileStreamQueue = <?php echo $this->availableMobileStreams?>; this.availableDesktopStreamQueue = <?php echo $this->availableDesktopStreams?>; var stream = this.getNextAvailableStream(); - this.settings.url = htmlEscape(stream["url"]); + this.settings.url = stream["url"]; this.settings.codec = stream["codec"]; } // Create the Muses player object MRP.insert(this.settings); - var station_name = htmlEscape("<?php echo $this->station_name?>"); - $("p.station_name").html(station_name); + $("p.station_name").html("<?php echo $this->station_name?>"); attachStreamMetadataToPlayer(); @@ -143,7 +142,7 @@ // connection limit reached or problem connecting to stream if (value === "0") { var stream = musesPlayer.getNextAvailableStream(); - musesPlayer.setURL(htmlEscape(stream["url"])); + musesPlayer.setURL(stream["url"]); musesPlayer.play(); } } @@ -218,15 +217,6 @@ setTimeout(attachStreamMetadataToPlayer, time_to_next_track_starts); } - function htmlEscape(str) { - return String(str) - .replace(/&/g, '&') - .replace(/"/g, '"') - .replace(/'/g, ''') - .replace(/</g, '<') - .replace(/>/g, '>'); - } - </script> <style type="text/css"> From 4429117f6a9d2ba30a1e858bc63cc115ec9813a8 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Thu, 9 Apr 2015 09:43:15 -0400 Subject: [PATCH 49/60] SAAS-712: UI Improvements --- .../controllers/PlayerController.php | 2 +- airtime_mvc/application/forms/Player.php | 29 +++++++---- .../views/scripts/form/player.phtml | 6 ++- .../views/scripts/player/index.phtml | 11 ++++- airtime_mvc/public/css/player-form.css | 16 ++++++- .../public/js/airtime/player/player.js | 48 ++++++++++++++++++- 6 files changed, 97 insertions(+), 15 deletions(-) diff --git a/airtime_mvc/application/controllers/PlayerController.php b/airtime_mvc/application/controllers/PlayerController.php index 92501bbd4..8d51219fe 100644 --- a/airtime_mvc/application/controllers/PlayerController.php +++ b/airtime_mvc/application/controllers/PlayerController.php @@ -51,7 +51,7 @@ class PlayerController extends Zend_Controller_Action $this->view->jquery = Application_Common_HTTPHelper::getStationUrl() . "js/libs/jquery-1.10.2.js"; $this->view->muses_swf = Application_Common_HTTPHelper::getStationUrl() . "js/airtime/player/muses.swf"; $this->view->metadata_api_url = Application_Common_HTTPHelper::getStationUrl() . "api/live-info"; - $this->view->station_name = json_encode(Application_Model_Preference::GetStationName()); + $this->view->player_title = json_encode($request->getParam('title')); $stream = $request->getParam('stream'); $streamData = Application_Model_StreamSetting::getEnabledStreamData(); diff --git a/airtime_mvc/application/forms/Player.php b/airtime_mvc/application/forms/Player.php index 4cad32e53..01fc62d75 100644 --- a/airtime_mvc/application/forms/Player.php +++ b/airtime_mvc/application/forms/Player.php @@ -10,12 +10,23 @@ class Application_Form_Player extends Zend_Form_SubForm array('ViewScript', array('viewScript' => 'form/player.phtml')) )); - /* We will use this option in the future - $displayTrackMetadata = new Zend_Form_Element_Checkbox('player_display_track_metadata'); - $displayTrackMetadata->setValue(true); - $displayTrackMetadata->setLabel(_('Display track metadata?')); - $this->addElement($displayTrackMetadata); - */ + $displayTitle = new Zend_Form_Element_Checkbox('player_display_title'); + $displayTitle->setValue(true); + $displayTitle->setLabel(_('Display title?')); + //$displayTitle->addDecorator('Label', array('placement' => 'APPEND')); + $displayTitle->setDecorators(array( + 'ViewHelper', + 'Errors', + 'Label' + )); + $displayTitle->addDecorator('Label', array('class' => 'player-checkbox', 'placement' => 'APPEND')); + $this->addElement($displayTitle); + + $title = new Zend_Form_Element_Text('player_title'); + $title->setValue(_('Now Playing')); + $title->setLabel(_('Title:')); + $title->removeDecorator('DtDdWrapper'); + $this->addElement($title); $streamMode = new Zend_Form_Element_Radio('player_stream_mode'); $streamMode->setLabel(_('Select Stream:')); @@ -59,11 +70,13 @@ class Application_Form_Player extends Zend_Form_SubForm $streamURL->removeDecorator('label'); $this->addElement($streamURL); - $embedSrc = new Zend_Form_Element_Text('player_embed_src'); + $embedSrc = new Zend_Form_Element_Textarea('player_embed_src'); $embedSrc->setAttrib("readonly", "readonly"); $embedSrc->setAttrib("class", "embed-player-text-box"); + $embedSrc->setAttrib('cols', '40') + ->setAttrib('rows', '4'); $embedSrc->setLabel(_("Embeddable code:")); - $embedSrc->setValue('<iframe frameborder="0" width="280" height="210" src="'.Application_Common_HTTPHelper::getStationUrl().'player?stream=auto"></iframe>'); + $embedSrc->setValue('<iframe frameborder="0" width="280" height="210" src="'.Application_Common_HTTPHelper::getStationUrl().'player?stream=auto&title=Now Playing"></iframe>'); $this->addElement($embedSrc); $previewLabel = new Zend_Form_Element_Text('player_preview_label'); diff --git a/airtime_mvc/application/views/scripts/form/player.phtml b/airtime_mvc/application/views/scripts/form/player.phtml index c47deaccf..e9bb8f3ed 100644 --- a/airtime_mvc/application/views/scripts/form/player.phtml +++ b/airtime_mvc/application/views/scripts/form/player.phtml @@ -9,10 +9,14 @@ </div> <div id="player_instructions"> - Customize the player by configuring the options below. Once you are happy with the player + Customize the player by configuring the options below. When you are done copy the embeddable code below into your websites HTML. </div> + <?php echo $this->element->getElement('player_display_title'); ?> + + <?php echo $this->element->getElement('player_title')->render(); ?> + <?php echo $this->element->getElement('player_stream_mode')->render(); ?> <?php echo $this->element->getElement('player_stream_url'); ?> diff --git a/airtime_mvc/application/views/scripts/player/index.phtml b/airtime_mvc/application/views/scripts/player/index.phtml index ee19b337c..a2732b3c4 100644 --- a/airtime_mvc/application/views/scripts/player/index.phtml +++ b/airtime_mvc/application/views/scripts/player/index.phtml @@ -35,7 +35,7 @@ }; if (this.playerMode == "manual") { - this.settings.url = "<?php echo $this->streamURL ?>"; + this.settings.url = '<?php echo $this->streamURL ?>'; this.settings.codec = "<?php echo $this->codec ?>"; } else if (this.playerMode == "auto") { this.availableMobileStreamQueue = <?php echo $this->availableMobileStreams?>; @@ -48,7 +48,14 @@ // Create the Muses player object MRP.insert(this.settings); - $("p.station_name").html("<?php echo $this->station_name?>"); + // Configure player title + var player_title = <?php echo $this->player_title?>; + if (player_title === null) { + $(".airtime_header").hide(); + $(".airtime_player").css('height', '150px'); + } else { + $("p.station_name").html(player_title); + } attachStreamMetadataToPlayer(); diff --git a/airtime_mvc/public/css/player-form.css b/airtime_mvc/public/css/player-form.css index 663a304cf..0ab842bb6 100644 --- a/airtime_mvc/public/css/player-form.css +++ b/airtime_mvc/public/css/player-form.css @@ -19,9 +19,12 @@ display: block; } #player_form { - width: 50%; + width: 40%; margin: 0 auto; } +#player_form dd { + margin-bottom: 10px; +} #player_instructions { border-bottom: 1px solid; padding-bottom: 10px; @@ -30,6 +33,17 @@ color: #5b5b5b; margin-bottom: 10px; } +.player-checkbox { + clear: left; + color: #5b5b5b; + float: left; + font-size: 13px; + font-weight: bold; + margin: 0; + min-width: 90px; + padding: 4px 0; + text-align: left; +} diff --git a/airtime_mvc/public/js/airtime/player/player.js b/airtime_mvc/public/js/airtime/player/player.js index ea5fb5588..a2bb17808 100644 --- a/airtime_mvc/public/js/airtime/player/player.js +++ b/airtime_mvc/public/js/airtime/player/player.js @@ -8,13 +8,19 @@ function updateEmbedSrcParams() } else if ($streamMode == "auto") { $embedCodeParams += "stream=auto"; } + + var playerTitle = getPlayerTitle(); + if (playerTitle !== null) { + $embedCodeParams += "&title="+playerTitle; + } + $embedCodeParams += "\""; - $("input[name=player_embed_src]").val(function(index, value) { + $("textarea[name=player_embed_src]").val(function(index, value) { return value.replace(/\?.*?"/, $embedCodeParams); }); - updatePlayerIframeSrc($("input[name=player_embed_src]").val()); + updatePlayerIframeSrc($("textarea[name=player_embed_src]").val()); } function updatePlayerIframeSrc(iframe_text) { @@ -27,10 +33,19 @@ function getStreamMode() { return $("input[name=player_stream_mode]:radio:checked").val(); } +function getPlayerTitle() { + if ($("#player_display_title").prop("checked")) { + return $("input[name=player_title]").val(); + } else { + return null; + } +} + $(document).ready(function() { $("#player_stream_url-element").hide(); + // stream mode change event $("#player_stream_mode-element").change(function() { var $streamMode = getStreamMode(); @@ -50,8 +65,37 @@ $(document).ready(function() { updateEmbedSrcParams(); }); + // stream url change event $("#player_stream_url-element").change(function() { updateEmbedSrcParams(); }); + + // display title checkbox change event + $("#player_display_title").change(function() { + if ($(this).prop("checked")) { + $("#player_title-label").show(); + $("#player_title-element").show(); + } else { + $("#player_title-label").hide(); + $("#player_title-element").hide(); + } + updateEmbedSrcParams(); + }); + + // title textbox change event + // setup before functions + var typingTimer; + var doneTypingInterval = 3000; + + // on keyup, start the countdown + $("input[name=player_title]").keyup(function(){ + clearTimeout(typingTimer); + typingTimer = setTimeout(updateEmbedSrcParams, doneTypingInterval); + }); + + // on keydown, clear the countdown + $("input[name=player_title]").keydown(function(){ + clearTimeout(typingTimer); + }); }); From 84231f811a61b27bb5e62c7fbd09f3b99afe744a Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Thu, 9 Apr 2015 12:25:27 -0400 Subject: [PATCH 50/60] SAAS-720: Player can't be accessed unless a user has a valid session --- .../controllers/EmbedController.php | 56 +++++++++++++++++++ .../controllers/PlayerController.php | 51 +---------------- .../controllers/plugins/Acl_plugin.php | 3 +- airtime_mvc/application/forms/Player.php | 4 +- .../index.phtml => embed/player.phtml} | 0 5 files changed, 61 insertions(+), 53 deletions(-) create mode 100644 airtime_mvc/application/controllers/EmbedController.php rename airtime_mvc/application/views/scripts/{player/index.phtml => embed/player.phtml} (100%) diff --git a/airtime_mvc/application/controllers/EmbedController.php b/airtime_mvc/application/controllers/EmbedController.php new file mode 100644 index 000000000..11f2cc68e --- /dev/null +++ b/airtime_mvc/application/controllers/EmbedController.php @@ -0,0 +1,56 @@ +<?php + +class EmbedController extends Zend_Controller_Action +{ + public function init() + { + + } + + /** + * This is the action that is called to insert the player onto a web page. + * It passes all the js and css files to the view, as well as all the + * stream customization information. + * + * The view for this action contains all the inline javascript needed to + * create the player. + */ + public function playerAction() + { + $this->view->layout()->disableLayout(); + + $CC_CONFIG = Config::getConfig(); + + $request = $this->getRequest(); + + $this->view->css = Application_Common_HTTPHelper::getStationUrl() . "css/player.css?".$CC_CONFIG['airtime_version']; + $this->view->mrp_js = Application_Common_HTTPHelper::getStationUrl() . "js/airtime/player/mrp.js?".$CC_CONFIG['airtime_version']; + $this->view->jquery = Application_Common_HTTPHelper::getStationUrl() . "js/libs/jquery-1.10.2.js"; + $this->view->muses_swf = Application_Common_HTTPHelper::getStationUrl() . "js/airtime/player/muses.swf"; + $this->view->metadata_api_url = Application_Common_HTTPHelper::getStationUrl() . "api/live-info"; + $this->view->player_title = json_encode($request->getParam('title')); + + $stream = $request->getParam('stream'); + $streamData = Application_Model_StreamSetting::getEnabledStreamData(); + $availableMobileStreams = array(); + $availableDesktopStreams = array(); + + if ($stream == "auto") { + $this->view->playerMode = "auto"; + foreach ($streamData as $s) { + if ($s["mobile"]) { + array_push($availableMobileStreams, $s); + } else if (!$s["mobile"]) { + array_push($availableDesktopStreams, $s); + } + } + } else { + $this->view->playerMode = "manual"; + $selectedStreamData = $streamData[$stream]; + $this->view->streamURL = json_encode($selectedStreamData["url"]); + $this->view->codec = $selectedStreamData["codec"]; + } + $this->view->availableMobileStreams = json_encode($availableMobileStreams); + $this->view->availableDesktopStreams = json_encode($availableDesktopStreams); + } +} diff --git a/airtime_mvc/application/controllers/PlayerController.php b/airtime_mvc/application/controllers/PlayerController.php index 8d51219fe..bb3fbc2d3 100644 --- a/airtime_mvc/application/controllers/PlayerController.php +++ b/airtime_mvc/application/controllers/PlayerController.php @@ -28,53 +28,4 @@ class PlayerController extends Zend_Controller_Action } } - - /** - * This is the action that is called to insert the player onto a web page. - * It passes all the js and css files to the view, as well as all the - * stream customization information. - * - * The view for this action contains all the inline javascript needed to - * create the player. - */ - public function indexAction() - { - $this->view->layout()->disableLayout(); - - $CC_CONFIG = Config::getConfig(); - - - $request = $this->getRequest(); - - $this->view->css = Application_Common_HTTPHelper::getStationUrl() . "css/player.css?".$CC_CONFIG['airtime_version']; - $this->view->mrp_js = Application_Common_HTTPHelper::getStationUrl() . "js/airtime/player/mrp.js?".$CC_CONFIG['airtime_version']; - $this->view->jquery = Application_Common_HTTPHelper::getStationUrl() . "js/libs/jquery-1.10.2.js"; - $this->view->muses_swf = Application_Common_HTTPHelper::getStationUrl() . "js/airtime/player/muses.swf"; - $this->view->metadata_api_url = Application_Common_HTTPHelper::getStationUrl() . "api/live-info"; - $this->view->player_title = json_encode($request->getParam('title')); - - $stream = $request->getParam('stream'); - $streamData = Application_Model_StreamSetting::getEnabledStreamData(); - $availableMobileStreams = array(); - $availableDesktopStreams = array(); - - if ($stream == "auto") { - $this->view->playerMode = "auto"; - foreach ($streamData as $s) { - if ($s["mobile"]) { - array_push($availableMobileStreams, $s); - } else if (!$s["mobile"]) { - array_push($availableDesktopStreams, $s); - } - } - } else { - $this->view->playerMode = "manual"; - $selectedStreamData = $streamData[$stream]; - $this->view->streamURL = json_encode($selectedStreamData["url"]); - $this->view->codec = $selectedStreamData["codec"]; - } - $this->view->availableMobileStreams = json_encode($availableMobileStreams); - $this->view->availableDesktopStreams = json_encode($availableDesktopStreams); - //$this->view->displayMetadata = $request->getParam('display_metadata'); - } -} \ No newline at end of file +} diff --git a/airtime_mvc/application/controllers/plugins/Acl_plugin.php b/airtime_mvc/application/controllers/plugins/Acl_plugin.php index 7ea1336d0..9eef38fdb 100644 --- a/airtime_mvc/application/controllers/plugins/Acl_plugin.php +++ b/airtime_mvc/application/controllers/plugins/Acl_plugin.php @@ -118,7 +118,8 @@ class Zend_Controller_Plugin_Acl extends Zend_Controller_Plugin_Abstract "locale", "upgrade", 'whmcs-login', - "provisioning" + "provisioning", + "embed" ))) { $this->setRoleName("G"); diff --git a/airtime_mvc/application/forms/Player.php b/airtime_mvc/application/forms/Player.php index 01fc62d75..5e1ac4009 100644 --- a/airtime_mvc/application/forms/Player.php +++ b/airtime_mvc/application/forms/Player.php @@ -76,7 +76,7 @@ class Application_Form_Player extends Zend_Form_SubForm $embedSrc->setAttrib('cols', '40') ->setAttrib('rows', '4'); $embedSrc->setLabel(_("Embeddable code:")); - $embedSrc->setValue('<iframe frameborder="0" width="280" height="210" src="'.Application_Common_HTTPHelper::getStationUrl().'player?stream=auto&title=Now Playing"></iframe>'); + $embedSrc->setValue('<iframe frameborder="0" width="280" height="210" src="'.Application_Common_HTTPHelper::getStationUrl().'embed/player?stream=auto&title=Now Playing"></iframe>'); $this->addElement($embedSrc); $previewLabel = new Zend_Form_Element_Text('player_preview_label'); @@ -84,4 +84,4 @@ class Application_Form_Player extends Zend_Form_SubForm $this->addElement($previewLabel); } -} \ No newline at end of file +} diff --git a/airtime_mvc/application/views/scripts/player/index.phtml b/airtime_mvc/application/views/scripts/embed/player.phtml similarity index 100% rename from airtime_mvc/application/views/scripts/player/index.phtml rename to airtime_mvc/application/views/scripts/embed/player.phtml From f238faa9374122bee7e9b556707ee0ac82963e85 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Thu, 9 Apr 2015 14:22:50 -0400 Subject: [PATCH 51/60] SAAS-712: UI Improvements --- airtime_mvc/application/forms/Player.php | 18 +++++-------- .../views/scripts/embed/player.phtml | 2 +- .../views/scripts/form/player.phtml | 6 ++--- airtime_mvc/public/css/player-form.css | 27 ++++++++++++++----- airtime_mvc/public/css/player.css | 10 +++++-- .../public/js/airtime/player/player.js | 11 ++------ 6 files changed, 39 insertions(+), 35 deletions(-) diff --git a/airtime_mvc/application/forms/Player.php b/airtime_mvc/application/forms/Player.php index 5e1ac4009..a1dbff341 100644 --- a/airtime_mvc/application/forms/Player.php +++ b/airtime_mvc/application/forms/Player.php @@ -10,22 +10,15 @@ class Application_Form_Player extends Zend_Form_SubForm array('ViewScript', array('viewScript' => 'form/player.phtml')) )); - $displayTitle = new Zend_Form_Element_Checkbox('player_display_title'); - $displayTitle->setValue(true); - $displayTitle->setLabel(_('Display title?')); - //$displayTitle->addDecorator('Label', array('placement' => 'APPEND')); - $displayTitle->setDecorators(array( + $title = new Zend_Form_Element_Text('player_title'); + $title->setValue(_('Now Playing')); + $title->setLabel(_('Title:')); + $title->setDecorators(array( 'ViewHelper', 'Errors', 'Label' )); - $displayTitle->addDecorator('Label', array('class' => 'player-checkbox', 'placement' => 'APPEND')); - $this->addElement($displayTitle); - - $title = new Zend_Form_Element_Text('player_title'); - $title->setValue(_('Now Playing')); - $title->setLabel(_('Title:')); - $title->removeDecorator('DtDdWrapper'); + $title->addDecorator('Label', array('class' => 'player-title')); $this->addElement($title); $streamMode = new Zend_Form_Element_Radio('player_stream_mode'); @@ -76,6 +69,7 @@ class Application_Form_Player extends Zend_Form_SubForm $embedSrc->setAttrib('cols', '40') ->setAttrib('rows', '4'); $embedSrc->setLabel(_("Embeddable code:")); + $embedSrc->setDescription(_("Copy this code and paste it into your website's HTML to embed the player in your site.")); $embedSrc->setValue('<iframe frameborder="0" width="280" height="210" src="'.Application_Common_HTTPHelper::getStationUrl().'embed/player?stream=auto&title=Now Playing"></iframe>'); $this->addElement($embedSrc); diff --git a/airtime_mvc/application/views/scripts/embed/player.phtml b/airtime_mvc/application/views/scripts/embed/player.phtml index a2732b3c4..0695ba202 100644 --- a/airtime_mvc/application/views/scripts/embed/player.phtml +++ b/airtime_mvc/application/views/scripts/embed/player.phtml @@ -277,7 +277,7 @@ <li></li> </ul> </div> - <a class="airtime_pro" target="_blank" href="https://www.airtime.pro/">Powered by Airtime Pro<span class="airtime_pro_logo"></span></a> + <a class="airtime_pro" target="_blank" href="https://www.airtime.pro/">Powered by <span class="airtime-pro-orange">Airtime Pro</span></a> </div> diff --git a/airtime_mvc/application/views/scripts/form/player.phtml b/airtime_mvc/application/views/scripts/form/player.phtml index e9bb8f3ed..bedd3f099 100644 --- a/airtime_mvc/application/views/scripts/form/player.phtml +++ b/airtime_mvc/application/views/scripts/form/player.phtml @@ -9,12 +9,10 @@ </div> <div id="player_instructions"> - Customize the player by configuring the options below. When you are done - copy the embeddable code below into your websites HTML. + Customize the player by configuring the options below. When you are done, + copy the embeddable code below and paste it into your website's HTML. </div> - <?php echo $this->element->getElement('player_display_title'); ?> - <?php echo $this->element->getElement('player_title')->render(); ?> <?php echo $this->element->getElement('player_stream_mode')->render(); ?> diff --git a/airtime_mvc/public/css/player-form.css b/airtime_mvc/public/css/player-form.css index 0ab842bb6..69654c9f1 100644 --- a/airtime_mvc/public/css/player-form.css +++ b/airtime_mvc/public/css/player-form.css @@ -3,7 +3,8 @@ width: 100% !important; } .embed-player-form { - width: 98%; + width: 40%; + margin: 0 auto; } .embed-player-form dd { width: 100% !important; @@ -19,7 +20,7 @@ display: block; } #player_form { - width: 40%; + width: 100%; margin: 0 auto; } #player_form dd { @@ -29,20 +30,32 @@ border-bottom: 1px solid; padding-bottom: 10px; font-size: 14px; - font-weight: bold; - color: #5b5b5b; + /*font-weight: bold;*/ + color: #333; margin-bottom: 10px; } -.player-checkbox { +.player-title { clear: left; color: #5b5b5b; float: left; font-size: 13px; font-weight: bold; - margin: 0; - min-width: 90px; + width: 40px; padding: 4px 0; text-align: left; + margin-top: 5px; + margin-bottom: 17px; +} +#player_title { + clear: left; +} +#player_stream_url-element { + margin-left:30px; +} +#player_embed_src-element p { + margin: 0px; + font-size: 13px; + color: #333; } diff --git a/airtime_mvc/public/css/player.css b/airtime_mvc/public/css/player.css index 3c9ef0f08..cdb76e01f 100644 --- a/airtime_mvc/public/css/player.css +++ b/airtime_mvc/public/css/player.css @@ -38,9 +38,9 @@ padding-right: 30px; } .airtime_pro { - margin: 0px 20px; + margin: 6px 20px; color: #fff; - font-size: 10px; + font-size: 11px; text-decoration: none; display:inline-block; float:right; @@ -189,6 +189,7 @@ .airtime_next { float: left; margin: 0px; + margin-top: 1px; } .schedule_list { @@ -199,10 +200,15 @@ margin-left: 60px; margin-bottom: 0px; line-height: 130%; + height: 20px; } .schedule_list li { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; +} + +.airtime-pro-orange { + color: #ff5d1a; } \ No newline at end of file diff --git a/airtime_mvc/public/js/airtime/player/player.js b/airtime_mvc/public/js/airtime/player/player.js index a2bb17808..db2fbb8bb 100644 --- a/airtime_mvc/public/js/airtime/player/player.js +++ b/airtime_mvc/public/js/airtime/player/player.js @@ -9,10 +9,7 @@ function updateEmbedSrcParams() $embedCodeParams += "stream=auto"; } - var playerTitle = getPlayerTitle(); - if (playerTitle !== null) { - $embedCodeParams += "&title="+playerTitle; - } + $embedCodeParams += "&title="+getPlayerTitle(); $embedCodeParams += "\""; @@ -34,11 +31,7 @@ function getStreamMode() { } function getPlayerTitle() { - if ($("#player_display_title").prop("checked")) { - return $("input[name=player_title]").val(); - } else { - return null; - } + return $("input[name=player_title]").val(); } $(document).ready(function() { From 9e62c71690678a1965a7453a84650ba6d3590314 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Fri, 10 Apr 2015 10:52:40 -0400 Subject: [PATCH 52/60] Fix player's manual mode --- .../controllers/EmbedController.php | 3 ++- .../views/scripts/embed/player.phtml | 18 +----------------- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/airtime_mvc/application/controllers/EmbedController.php b/airtime_mvc/application/controllers/EmbedController.php index 11f2cc68e..3212c8e5d 100644 --- a/airtime_mvc/application/controllers/EmbedController.php +++ b/airtime_mvc/application/controllers/EmbedController.php @@ -37,6 +37,7 @@ class EmbedController extends Zend_Controller_Action if ($stream == "auto") { $this->view->playerMode = "auto"; + $this->view->streamURL = json_encode(""); foreach ($streamData as $s) { if ($s["mobile"]) { array_push($availableMobileStreams, $s); @@ -44,7 +45,7 @@ class EmbedController extends Zend_Controller_Action array_push($availableDesktopStreams, $s); } } - } else { + } elseif (!empty($stream)) { $this->view->playerMode = "manual"; $selectedStreamData = $streamData[$stream]; $this->view->streamURL = json_encode($selectedStreamData["url"]); diff --git a/airtime_mvc/application/views/scripts/embed/player.phtml b/airtime_mvc/application/views/scripts/embed/player.phtml index 0695ba202..c7c5560b1 100644 --- a/airtime_mvc/application/views/scripts/embed/player.phtml +++ b/airtime_mvc/application/views/scripts/embed/player.phtml @@ -35,7 +35,7 @@ }; if (this.playerMode == "manual") { - this.settings.url = '<?php echo $this->streamURL ?>'; + this.settings.url = <?php echo $this->streamURL ?>; this.settings.codec = "<?php echo $this->codec ?>"; } else if (this.playerMode == "auto") { this.availableMobileStreamQueue = <?php echo $this->availableMobileStreams?>; @@ -63,7 +63,6 @@ if (!this.flashDetect) { MRP.html.audio.addEventListener('error', function failed(e) { - console.log("HTML error"); var stream = musesPlayer.getNextAvailableStream(); var audio = $(MRP.html.audio); audio.src = stream["url"]; @@ -72,8 +71,6 @@ }, true); MRP.html.audio.addEventListener('pause', function paused(e) { - //this is when pause happens - console.log("HTML paused"); //src = MRP.html.audio.src; //MRP.html.audio.src = ""; }, true); @@ -256,19 +253,6 @@ </div> - <!-- - <div class="airtime_volume"> - - <div class="volume_control"> - <span class="mute"></span> - </div> - - <div class="airtime_volume_bar"> - <div class="airtime_volume_bar_value" style="width: 80%;"></div> - </div> - - </div> - --> <div style="clear:both"></div> <div class="airtime_schedule"> From 4d7d48d6fbb06a8e83a58770cc0eb5cb0b3bcd71 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Fri, 10 Apr 2015 10:55:39 -0400 Subject: [PATCH 53/60] Player - fix height --- airtime_mvc/application/forms/Player.php | 2 +- airtime_mvc/public/css/player.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/airtime_mvc/application/forms/Player.php b/airtime_mvc/application/forms/Player.php index a1dbff341..5a24397d7 100644 --- a/airtime_mvc/application/forms/Player.php +++ b/airtime_mvc/application/forms/Player.php @@ -70,7 +70,7 @@ class Application_Form_Player extends Zend_Form_SubForm ->setAttrib('rows', '4'); $embedSrc->setLabel(_("Embeddable code:")); $embedSrc->setDescription(_("Copy this code and paste it into your website's HTML to embed the player in your site.")); - $embedSrc->setValue('<iframe frameborder="0" width="280" height="210" src="'.Application_Common_HTTPHelper::getStationUrl().'embed/player?stream=auto&title=Now Playing"></iframe>'); + $embedSrc->setValue('<iframe frameborder="0" width="280" height="216" src="'.Application_Common_HTTPHelper::getStationUrl().'embed/player?stream=auto&title=Now Playing"></iframe>'); $this->addElement($embedSrc); $previewLabel = new Zend_Form_Element_Text('player_preview_label'); diff --git a/airtime_mvc/public/css/player.css b/airtime_mvc/public/css/player.css index cdb76e01f..47cdd0994 100644 --- a/airtime_mvc/public/css/player.css +++ b/airtime_mvc/public/css/player.css @@ -1,6 +1,6 @@ .airtime_player { width: 270px; - height: 185px; + height: 191px; position: relative; font-family: Arial, Helvetica, sans-serif; color: #fff; From caa591bd5ec9ca9e95046ba15ade1e1a2908e87e Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Fri, 10 Apr 2015 11:07:42 -0400 Subject: [PATCH 54/60] Player - set fixed height for metadata area --- airtime_mvc/public/css/player.css | 1 + 1 file changed, 1 insertion(+) diff --git a/airtime_mvc/public/css/player.css b/airtime_mvc/public/css/player.css index 47cdd0994..f8a3102c7 100644 --- a/airtime_mvc/public/css/player.css +++ b/airtime_mvc/public/css/player.css @@ -27,6 +27,7 @@ margin-top: 15px; float: left; width: 100%; + height: 52px; } .station_name { font-size: 14px; From c80d4376011709d05a2d0089b1599d5a3aaeb8c7 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Fri, 10 Apr 2015 15:25:49 -0400 Subject: [PATCH 55/60] Fixed player HTML5 error handling --- .../views/scripts/embed/player.phtml | 56 +++++++++++++++++-- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/airtime_mvc/application/views/scripts/embed/player.phtml b/airtime_mvc/application/views/scripts/embed/player.phtml index c7c5560b1..b964c75bc 100644 --- a/airtime_mvc/application/views/scripts/embed/player.phtml +++ b/airtime_mvc/application/views/scripts/embed/player.phtml @@ -63,11 +63,55 @@ if (!this.flashDetect) { MRP.html.audio.addEventListener('error', function failed(e) { - var stream = musesPlayer.getNextAvailableStream(); - var audio = $(MRP.html.audio); - audio.src = stream["url"]; - audio[0].load(); - audio[0].play(); + switch (e.target.error.code) { + case e.target.error.MEDIA_ERR_NETWORK: + // If there is a network error keep retrying to connect + // to a stream. + var stream; + if (musesPlayer.playerMode == "auto") { + var nextAvailableStream = musesPlayer.getNextAvailableStream(); + stream = nextAvailableStream["url"]; + } else { + stream = musesPlayer.settings.url; + } + var audio = $(MRP.html.audio); + audio.src = stream; + audio[0].load(); + audio[0].play(); + break; + case e.target.error.MEDIA_ERR_DECODE: + // If there was a corruption error or a problem with the browser + // display an error and stop playback. + togglePlayStopButton(); + clearTimeout(metadataTimer); + $("p.now_playing").html("Error - Try again later"); + break; + case e.target.error.MEDIA_ERR_SRC_NOT_SUPPORTED: + // If in auto mode and the current stream format is not supported + // or the max number of listeners has been reached + // retry connection with the next available stream. + if (musesPlayer.playerMode == "auto") { + var nextAvailableStream = musesPlayer.getNextAvailableStream(); + var audio = $(MRP.html.audio); + audio.src = nextAvailableStream["url"];; + audio[0].load(); + audio[0].play(); + } else { + // If in manual mode and the current stream format is not supported + // or the max number of listeners has been reached + // display an error and stop play back. + togglePlayStopButton(); + clearTimeout(metadataTimer); + $("p.now_playing").html("Error - Try again later"); + } + break; + default: + togglePlayStopButton(); + clearTimeout(metadataTimer); + $("p.now_playing").html("Error - Try again later"); + break; + } + }, true); MRP.html.audio.addEventListener('pause', function paused(e) { @@ -218,8 +262,8 @@ } }); - setTimeout(attachStreamMetadataToPlayer, time_to_next_track_starts); } + var metadataTimer = setTimeout(attachStreamMetadataToPlayer, time_to_next_track_starts); </script> From 1d79f13716dd4151fd530586c8075d0fb7aa42af Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Fri, 10 Apr 2015 16:06:19 -0400 Subject: [PATCH 56/60] Player HTML5 error handling fix --- airtime_mvc/application/views/scripts/embed/player.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airtime_mvc/application/views/scripts/embed/player.phtml b/airtime_mvc/application/views/scripts/embed/player.phtml index b964c75bc..da64ab0e5 100644 --- a/airtime_mvc/application/views/scripts/embed/player.phtml +++ b/airtime_mvc/application/views/scripts/embed/player.phtml @@ -93,7 +93,7 @@ if (musesPlayer.playerMode == "auto") { var nextAvailableStream = musesPlayer.getNextAvailableStream(); var audio = $(MRP.html.audio); - audio.src = nextAvailableStream["url"];; + audio[0].src = nextAvailableStream["url"];; audio[0].load(); audio[0].play(); } else { From b8e8a1a983764ec738fdf6b5d8b1dfb8ba2a568c Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Fri, 10 Apr 2015 16:44:51 -0400 Subject: [PATCH 57/60] Player HTML5 error handling fix --- airtime_mvc/application/views/scripts/embed/player.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airtime_mvc/application/views/scripts/embed/player.phtml b/airtime_mvc/application/views/scripts/embed/player.phtml index da64ab0e5..34e39803a 100644 --- a/airtime_mvc/application/views/scripts/embed/player.phtml +++ b/airtime_mvc/application/views/scripts/embed/player.phtml @@ -75,7 +75,7 @@ stream = musesPlayer.settings.url; } var audio = $(MRP.html.audio); - audio.src = stream; + audio[0].src = stream; audio[0].load(); audio[0].play(); break; From 63c3236e7ab7b741bf293ba34d15e1b8085b08b6 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Fri, 10 Apr 2015 17:38:39 -0400 Subject: [PATCH 58/60] Player metadata fix --- .../views/scripts/embed/player.phtml | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/airtime_mvc/application/views/scripts/embed/player.phtml b/airtime_mvc/application/views/scripts/embed/player.phtml index 34e39803a..0531b702b 100644 --- a/airtime_mvc/application/views/scripts/embed/player.phtml +++ b/airtime_mvc/application/views/scripts/embed/player.phtml @@ -220,8 +220,9 @@ document.getElementById("stop_button").classList.toggle("hide_button"); } - // default how often to fetch metadata to 20 seconds - var time_to_next_track_starts = 20000; + // variables for updating the player's metadata + var time_to_next_track_starts; + var metadataTimer; // Fetches the streams metadata from the Airtime live-info API // and attaches it to the player UI. @@ -235,23 +236,19 @@ if (data.current === null) { $("p.now_playing").html("Offline"); + time_to_next_track_starts = 20000; } else { var artist = data.current.name.split(" - ")[0]; var track = data.current.name.split(" - ")[1]; + console.log(artist); + console.log(track); $("p.now_playing").html(artist + "<span>" + track + "</span>"); var current_track_end_time = new Date(data.current.ends); var current_time = new Date(); //convert current_time to UTC to match the timezone of time_to_next_track_starts current_time = new Date(current_time.getTime() + current_time.getTimezoneOffset() * 60 * 1000); - //TODO stop the first settimeout from executing!! time_to_next_track_starts = current_track_end_time - current_time; - //console.log((time_to_next_track_starts/1000)/60); - // maybe we should set time_to_next_track_starts to - // (10 || 20 || etc.) minutes if its greater than that - // in case of on-the-fly schedule changes - - } if (data.next === null) { @@ -262,8 +259,10 @@ } }); + console.log(time_to_next_track_starts); + // Add 3 seconds to the timeout so Airtime has time to update the metadata before we fetch it + metadataTimer = setTimeout(attachStreamMetadataToPlayer, time_to_next_track_starts+3000); } - var metadataTimer = setTimeout(attachStreamMetadataToPlayer, time_to_next_track_starts); </script> From 988fcae9899edd8fc4bccf95560d23ea507ea91a Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Fri, 10 Apr 2015 18:17:22 -0400 Subject: [PATCH 59/60] Remove console.log --- airtime_mvc/application/views/scripts/embed/player.phtml | 3 --- 1 file changed, 3 deletions(-) diff --git a/airtime_mvc/application/views/scripts/embed/player.phtml b/airtime_mvc/application/views/scripts/embed/player.phtml index 0531b702b..37a3221f2 100644 --- a/airtime_mvc/application/views/scripts/embed/player.phtml +++ b/airtime_mvc/application/views/scripts/embed/player.phtml @@ -240,8 +240,6 @@ } else { var artist = data.current.name.split(" - ")[0]; var track = data.current.name.split(" - ")[1]; - console.log(artist); - console.log(track); $("p.now_playing").html(artist + "<span>" + track + "</span>"); var current_track_end_time = new Date(data.current.ends); @@ -259,7 +257,6 @@ } }); - console.log(time_to_next_track_starts); // Add 3 seconds to the timeout so Airtime has time to update the metadata before we fetch it metadataTimer = setTimeout(attachStreamMetadataToPlayer, time_to_next_track_starts+3000); } From 24b6751eebc2bd77d45f4b84a14760569d909265 Mon Sep 17 00:00:00 2001 From: drigato <denise.rigato@sourcefabric.org> Date: Mon, 13 Apr 2015 09:18:36 -0400 Subject: [PATCH 60/60] Remove unused code from player --- airtime_mvc/application/views/scripts/embed/player.phtml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/airtime_mvc/application/views/scripts/embed/player.phtml b/airtime_mvc/application/views/scripts/embed/player.phtml index 37a3221f2..3d7d312e1 100644 --- a/airtime_mvc/application/views/scripts/embed/player.phtml +++ b/airtime_mvc/application/views/scripts/embed/player.phtml @@ -113,11 +113,6 @@ } }, true); - - MRP.html.audio.addEventListener('pause', function paused(e) { - //src = MRP.html.audio.src; - //MRP.html.audio.src = ""; - }, true); } };