diff --git a/airtime_mvc/application/common/TuneIn.php b/airtime_mvc/application/common/TuneIn.php new file mode 100644 index 000000000..6040fb184 --- /dev/null +++ b/airtime_mvc/application/common/TuneIn.php @@ -0,0 +1,48 @@ +setLastPlayedTime($now); + + // Push metadata to TuneIn + if (Application_Model_Preference::getTuneinEnabled()) { + $filePropelOrm = $file->getPropelOrm(); + $title = urlencode($filePropelOrm->getDbTrackTitle()); + $artist = urlencode($filePropelOrm->getDbArtistName()); + Application_Common_TuneIn::sendMetadataToTunein($title, $artist); + } } } else { // webstream diff --git a/airtime_mvc/application/controllers/PreferenceController.php b/airtime_mvc/application/controllers/PreferenceController.php index 0d7543a37..1a67b556d 100644 --- a/airtime_mvc/application/controllers/PreferenceController.php +++ b/airtime_mvc/application/controllers/PreferenceController.php @@ -56,14 +56,19 @@ class PreferenceController extends Zend_Controller_Action Application_Model_Preference::SetStationLogo($imagePath); } - Application_Model_Preference::SetUploadToSoundcloudOption($values["UploadToSoundcloudOption"]); + Application_Model_Preference::setTuneinEnabled($values["enable_tunein"]); + Application_Model_Preference::setTuneinStationId($values["tunein_station_id"]); + Application_Model_Preference::setTuneinPartnerKey($values["tunein_partner_key"]); + Application_Model_Preference::setTuneinPartnerId($values["tunein_partner_id"]); + + /*Application_Model_Preference::SetUploadToSoundcloudOption($values["UploadToSoundcloudOption"]); Application_Model_Preference::SetSoundCloudDownloadbleOption($values["SoundCloudDownloadbleOption"]); Application_Model_Preference::SetSoundCloudUser($values["SoundCloudUser"]); Application_Model_Preference::SetSoundCloudPassword($values["SoundCloudPassword"]); Application_Model_Preference::SetSoundCloudTags($values["SoundCloudTags"]); Application_Model_Preference::SetSoundCloudGenre($values["SoundCloudGenre"]); Application_Model_Preference::SetSoundCloudTrackType($values["SoundCloudTrackType"]); - Application_Model_Preference::SetSoundCloudLicense($values["SoundCloudLicense"]); + Application_Model_Preference::SetSoundCloudLicense($values["SoundCloudLicense"]);*/ $this->view->statusMsg = "
". _("Preferences updated.")."
"; $this->view->form = $form; diff --git a/airtime_mvc/application/controllers/ScheduleController.php b/airtime_mvc/application/controllers/ScheduleController.php index 452788b23..0df1c389c 100644 --- a/airtime_mvc/application/controllers/ScheduleController.php +++ b/airtime_mvc/application/controllers/ScheduleController.php @@ -1,4 +1,5 @@ addSubForm($general_pref, 'preferences_general'); + //tunein form + $tuneinPreferences = new Application_Form_TuneInPreferences(); + $this->addSubForm($tuneinPreferences, 'preferences_tunein'); + $soundcloud_pref = new Application_Form_SoundcloudPreferences(); $this->addSubForm($soundcloud_pref, 'preferences_soundcloud'); diff --git a/airtime_mvc/application/forms/TuneInPreferences.php b/airtime_mvc/application/forms/TuneInPreferences.php new file mode 100644 index 000000000..7eee0d56f --- /dev/null +++ b/airtime_mvc/application/forms/TuneInPreferences.php @@ -0,0 +1,144 @@ +setDecorators(array( + array('ViewScript', array('viewScript' => 'form/preferences_tunein.phtml')) + )); + + $enableTunein = new Zend_Form_Element_Checkbox("enable_tunein"); + $enableTunein->setDecorators(array( + 'ViewHelper', + 'Errors', + 'Label' + )); + $enableTunein->addDecorator('Label', array('class' => 'enable-tunein')); + $enableTunein->setLabel(_("Push metadata to your station on TuneIn?")); + $enableTunein->setValue(Application_Model_Preference::getTuneinEnabled()); + $enableTunein->setAttrib("class", "block-display"); + $this->addElement($enableTunein); + + $tuneinStationId = new Zend_Form_Element_Text("tunein_station_id"); + $tuneinStationId->setLabel(_("Station ID:")); + $tuneinStationId->setValue(Application_Model_Preference::getTuneinStationId()); + $tuneinStationId->setAttrib("class", "input_text"); + $this->addElement($tuneinStationId); + + $tuneinPartnerKey = new Zend_Form_Element_Text("tunein_partner_key"); + $tuneinPartnerKey->setLabel(_("Partner Key:")); + $tuneinPartnerKey->setValue(Application_Model_Preference::getTuneinPartnerKey()); + $tuneinPartnerKey->setAttrib("class", "input_text"); + $this->addElement($tuneinPartnerKey); + + $tuneinPartnerId = new Zend_Form_Element_Text("tunein_partner_id"); + $tuneinPartnerId->setLabel(_("Partner Id:")); + $tuneinPartnerId->setValue(Application_Model_Preference::getTuneinPartnerId()); + $tuneinPartnerId->setAttrib("class", "input_text"); + $this->addElement($tuneinPartnerId); + } + + public function isValid($data) + { + $valid = true; + // Make request to TuneIn API to test the settings are valid. + // TuneIn does not have an API to make test requests to check if + // the credentials are correct. Therefore we will make a request + // with the commercial flag set to true, which removes the metadata + // from the station on TuneIn. After that, and if the test request + // succeeds, we will make another request with the real metadata. + if ($data["enable_tunein"]) { + $credentialsQryStr = "?partnerId=".$data["tunein_partner_id"]."&partnerKey=".$data["tunein_partner_key"]."&id=".$data["tunein_station_id"]; + $commercialFlagQryStr = "&commercial=true"; + + $metadata = Application_Model_Schedule::getCurrentPlayingTrack(); + + if (is_null($metadata)) { + $qryStr = $credentialsQryStr . $commercialFlagQryStr; + } else { + $metadata["artist"] = empty($metadata["artist"]) ? "n/a" : $metadata["artist"]; + $metadata["title"] = empty($metadata["title"]) ? "n/a" : $metadata["title"]; + $metadataQryStr = "&artist=" . $metadata["artist"] . "&title=" . $metadata["title"]; + + $qryStr = $credentialsQryStr . $metadataQryStr; + } + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, TUNEIN_API_URL . $qryStr); + curl_setopt($ch, CURLOPT_FAILONERROR, 1); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_TIMEOUT, 30); + + $xmlData = curl_exec($ch); + if (curl_error($ch)) { + Logging::error("Failed to reach TuneIn: ". curl_errno($ch)." - ". curl_error($ch) . " - " . curl_getinfo($ch, CURLINFO_EFFECTIVE_URL)); + if (curl_error($ch) == "The requested URL returned error: 403 Forbidden") { + $this->getElement("enable_tunein")->setErrors(array(_("Invalid TuneIn Settings. Please ensure your TuneIn settings are correct and try again."))); + + $valid = false; + } + } + curl_close($ch); + + if ($valid) { + $xmlObj = new SimpleXMLElement($xmlData); + if (!$xmlObj || $xmlObj->head->status != "200") { + $valid = false; + } else if ($xmlObj->head->status == "200") { + $valid = true; + + // Make another request to TuneIn to update the metadata right away + // and to turn off the commercial flag. + + /*$metadata = Application_Model_Schedule::getCurrentPlayingTrack(); + + if (!is_null($metadata)) { + + Logging::info($metadata); + // Replace empty strings with "n/a" since the TuneIn API will complain + // and return an error that title and/or artist is not set. + $metadata["artist"] = empty($metadata["artist"]) ? "n/a" : $metadata["artist"]; + $metadata["title"] = empty($metadata["title"]) ? "n/a" : $metadata["title"]; + Logging::info($metadata); + + $metadataQryStr = "&artist=" . $metadata["artist"] . "&title=" . $metadata["title"]; + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, TUNEIN_API_URL . $qry_str . "&commercial=false" . $metadataQryStr); + curl_setopt($ch, CURLOPT_FAILONERROR, 1); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_TIMEOUT, 30); + + $xmlData = curl_exec($ch); + Logging::info($xmlData); + if (curl_error($ch)) { + Logging::error("Failed to reach TuneIn: " . curl_errno($ch) . " - " . curl_error($ch) . " - " . curl_getinfo($ch, CURLINFO_EFFECTIVE_URL)); + } + + curl_close($ch); + $xmlObj = new SimpleXMLElement($xmlData); + if (!$xmlObj || $xmlObj->head->status != "200") { + Logging::error("Failed updating metadata on TuneIn"); + } + }*/ + + } + } + } else { + $valid = true; + } + + if (!$valid) { + // Set values to what the user entered since the form is invalid so they + // don't have to enter in the values again and can see what they entered. + $this->getElement("enable_tunein")->setValue($data["enable_tunein"]); + $this->getElement("tunein_partner_key")->setValue($data["tunein_partner_key"]); + $this->getElement("tunein_partner_id")->setValue($data["tunein_partner_id"]); + $this->getElement("tunein_station_id")->setValue($data["tunein_station_id"]); + } + + return $valid; + } +} diff --git a/airtime_mvc/application/models/Preference.php b/airtime_mvc/application/models/Preference.php index 64263b94c..1387d326b 100644 --- a/airtime_mvc/application/models/Preference.php +++ b/airtime_mvc/application/models/Preference.php @@ -1453,4 +1453,44 @@ class Application_Model_Preference { return self::getValue("provisioning_status"); } + + public static function setTuneinEnabled($value) + { + self::setValue("tunein_enabled", $value); + } + + public static function getTuneinEnabled() + { + return self::getValue("tunein_enabled"); + } + + public static function setTuneinPartnerKey($value) + { + self::setValue("tunein_partner_key", $value); + } + + public static function getTuneinPartnerKey() + { + return self::getValue("tunein_partner_key"); + } + + public static function setTuneinPartnerId($value) + { + self::setValue("tunein_partner_id", $value); + } + + public static function getTuneinPartnerId() + { + return self::getValue("tunein_partner_id"); + } + + public static function setTuneinStationId($value) + { + self::setValue("tunein_station_id", $value); + } + + public static function getTuneinStationId() + { + return self::getValue("tunein_station_id"); + } } diff --git a/airtime_mvc/application/models/Schedule.php b/airtime_mvc/application/models/Schedule.php index 2993dc03d..66d00661b 100644 --- a/airtime_mvc/application/models/Schedule.php +++ b/airtime_mvc/application/models/Schedule.php @@ -56,6 +56,29 @@ SQL; return $real_streams; } + + /** + * Returns an array with 2 elements: artist and title name of the track that is currently playing. + * Elements will be set to null if metadata is not set for those fields. + * + * Returns null if no track is currently playing. + * + * Data is based on GetPlayOrderRange() in this class. + */ + public static function getCurrentPlayingTrack() + { + $currentScheduleInfo = self::GetPlayOrderRange(); + if (empty($currentScheduleInfo["tracks"]["current"])) { + return null; + } + + $currentTrackArray = explode(" - ", $currentScheduleInfo["tracks"]["current"]["name"]); + $currentTrackMetadata = array( + "artist" => empty($currentTrackArray[0]) ? null : urlencode($currentTrackArray[0]), + "title" => empty($currentTrackArray[1]) ? null : urlencode($currentTrackArray[1]) + ); + return $currentTrackMetadata; + } /** * Returns data related to the scheduled items. diff --git a/airtime_mvc/application/views/scripts/form/preferences.phtml b/airtime_mvc/application/views/scripts/form/preferences.phtml index cef632f57..d200bf627 100644 --- a/airtime_mvc/application/views/scripts/form/preferences.phtml +++ b/airtime_mvc/application/views/scripts/form/preferences.phtml @@ -2,20 +2,22 @@ element->getElement('csrf') ?> element->getSubform('preferences_general') ?> - -

- -
- element->getSubform('preferences_soundcloud') ?> +

+
+ element->getSubform('preferences_tunein') ?>
+ + +

element->getSubform('preferences_danger') ?>

+ element->submit->render() ?> diff --git a/airtime_mvc/application/views/scripts/form/preferences_tunein.phtml b/airtime_mvc/application/views/scripts/form/preferences_tunein.phtml new file mode 100644 index 000000000..d5fd34dd5 --- /dev/null +++ b/airtime_mvc/application/views/scripts/form/preferences_tunein.phtml @@ -0,0 +1,18 @@ +
+
+ + element->getElement("enable_tunein")->hasErrors()) { + echo $this->element->getElement('enable_tunein')->renderErrors(); + } + ?> + element->getElement('enable_tunein')->renderViewHelper() ?> + element->getElement('enable_tunein')->renderLabel() ?> + + element->getElement('tunein_station_id')->render() ?> + + element->getElement('tunein_partner_id')->render() ?> + + element->getElement('tunein_partner_key')->render() ?> + +
+
\ No newline at end of file diff --git a/airtime_mvc/public/css/styles.css b/airtime_mvc/public/css/styles.css index 8bb4c4ba9..4331af511 100644 --- a/airtime_mvc/public/css/styles.css +++ b/airtime_mvc/public/css/styles.css @@ -3257,3 +3257,7 @@ dd .stream-status { padding-bottom: 0px; padding-top: 13px; } + +.enable-tunein { + font-weight:bold; +} diff --git a/airtime_mvc/public/js/airtime/preferences/preferences.js b/airtime_mvc/public/js/airtime/preferences/preferences.js index 8894020bc..252a64a99 100644 --- a/airtime_mvc/public/js/airtime/preferences/preferences.js +++ b/airtime_mvc/public/js/airtime/preferences/preferences.js @@ -58,6 +58,30 @@ function setMailServerInputReadonly() { setMsAuthenticationFieldsReadonly(requiresAuthCB); } +function setTuneInSettingsListener() { + var enableTunein = $("#enable_tunein"); + enableTunein.click(function(event){ + setTuneInSettingsReadonly(); + }); +} + +function setTuneInSettingsReadonly() { + var enableTunein = $("#enable_tunein"); + var stationId = $("#tunein_station_id"); + var partnerKey = $("#tunein_partner_key"); + var partnerId = $("#tunein_partner_id"); + + if (enableTunein.is(':checked')) { + stationId.removeAttr("readonly"); + partnerKey.removeAttr("readonly"); + partnerId.removeAttr("readonly"); + } else { + stationId.attr("readonly", "readonly"); + partnerKey.attr("readonly", "readonly"); + partnerId.attr("readonly", "readonly"); + } +} + /* * Enable/disable mail server authentication fields */ @@ -131,6 +155,10 @@ $(document).ready(function() { return false; }).next().hide(); + if ($("#tunein-settings").find(".errors").length > 0) { + $(".collapsible-content#tunein-settings").show(); + } + /* No longer using AJAX for this form. Zend + our code makes it needlessly hard to deal with. -- Albert $('#pref_save').live('click', function() { var data = $('#pref_form').serialize(); @@ -154,4 +182,6 @@ $(document).ready(function() { setConfigureMailServerListener(); setEnableSystemEmailsListener(); setCollapsibleWidgetJsCode(); + setTuneInSettingsReadonly(); + setTuneInSettingsListener(); });