From 532bd1ea8553dba359e0e82584da63e6d4a58b19 Mon Sep 17 00:00:00 2001 From: drigato Date: Mon, 25 May 2015 15:37:45 -0400 Subject: [PATCH 1/9] SAAS-772: Send metadata to Tunein Made Improvement so Airtime always makes a request to TuneIn every 4 minutes so TuneIn does not turn metadata off --- airtime_mvc/application/common/TuneIn.php | 26 +++++++------------ .../application/controllers/ApiController.php | 25 ++++++++++++++++++ .../controllers/ScheduleController.php | 7 ----- .../application/forms/TuneInPreferences.php | 1 + airtime_mvc/application/models/Preference.php | 10 +++++++ python_apps/api_clients/api_client.py | 4 +++ python_apps/pypo/pypofetch.py | 12 +++++++-- 7 files changed, 59 insertions(+), 26 deletions(-) diff --git a/airtime_mvc/application/common/TuneIn.php b/airtime_mvc/application/common/TuneIn.php index 21d0f8684..cbaf51871 100644 --- a/airtime_mvc/application/common/TuneIn.php +++ b/airtime_mvc/application/common/TuneIn.php @@ -17,12 +17,20 @@ class Application_Common_TuneIn curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_TIMEOUT, 30); - curl_exec($ch); + $xmlResponse = 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)); } curl_close($ch); + $xmlObj = new SimpleXMLElement($xmlResponse); + if (!$xmlObj || $xmlObj->head->status != "200") { + Logging::info("Error occurred pushing metadata to TuneIn:"); + Logging::info($xmlResponse); + } else if ($xmlObj->head->status == "200") { + Application_Model_Preference::setLastTuneinMetadataUpdate(time()); + } + } private static function getCredentialsQueryString() { @@ -33,20 +41,4 @@ class Application_Common_TuneIn return "?partnerId=".$tuneInPartnerID."&partnerKey=".$tuneInPartnerKey."&id=".$tuneInStationID; } - public static function updateOfflineMetadata() { - $credQryStr = self::getCredentialsQueryString(); - - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, TUNEIN_API_URL . $credQryStr . "&commercial=true"); - curl_setopt($ch, CURLOPT_FAILONERROR, 1); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_TIMEOUT, 30); - - 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)); - } - curl_close($ch); - } - } diff --git a/airtime_mvc/application/controllers/ApiController.php b/airtime_mvc/application/controllers/ApiController.php index 38c453d9e..490800085 100644 --- a/airtime_mvc/application/controllers/ApiController.php +++ b/airtime_mvc/application/controllers/ApiController.php @@ -1515,5 +1515,30 @@ class ApiController extends Zend_Controller_Action $this->_helper->json($result); } + + /** + * This function is called from PYPO (pypofetch) every 2 minutes and updates + * metadata on TuneIn if we haven't done so in the last 4 minutes. We have + * to do this because TuneIn turns off metadata if it has not received a + * request within 5 minutes. This is necessary for long tracks > 5 minutes. + */ + public function updateMetadataOnTuneinAction() + { + if (!Application_Model_Preference::getTuneinEnabled()) { + $this->_helper->json->sendJson(array(0)); + } + + $lastTuneInMetadataUpdate = Application_Model_Preference::geLastTuneinMetadataUpdate(); + if (time() - $lastTuneInMetadataUpdate >= 240) { + $metadata = $metadata = Application_Model_Schedule::getCurrentPlayingTrack(); + if (!is_null($metadata)) { + Application_Common_TuneIn::sendMetadataToTunein( + $metadata["title"], + $metadata["artist"] + ); + } + } + $this->_helper->json->sendJson(array(1)); + } } diff --git a/airtime_mvc/application/controllers/ScheduleController.php b/airtime_mvc/application/controllers/ScheduleController.php index 8d7aa0587..1890ac200 100644 --- a/airtime_mvc/application/controllers/ScheduleController.php +++ b/airtime_mvc/application/controllers/ScheduleController.php @@ -315,13 +315,6 @@ class ScheduleController extends Zend_Controller_Action { $range = Application_Model_Schedule::GetPlayOrderRangeOld(); - // If there is no current track playing update TuneIn so it doesn't - // display outdated metadata - //TODO: find a better solution for this so we don't spam the station on TuneIn - /*if (is_null($range["current"]) && Application_Model_Preference::getTuneinEnabled()) { - Application_Common_TuneIn::updateOfflineMetadata(); - }*/ - $show = Application_Model_Show::getCurrentShow(); /* Convert all UTC times to localtime before sending back to user. */ diff --git a/airtime_mvc/application/forms/TuneInPreferences.php b/airtime_mvc/application/forms/TuneInPreferences.php index d4eebc32c..ad133255a 100644 --- a/airtime_mvc/application/forms/TuneInPreferences.php +++ b/airtime_mvc/application/forms/TuneInPreferences.php @@ -87,6 +87,7 @@ class Application_Form_TuneInPreferences extends Zend_Form_SubForm if (!$xmlObj || $xmlObj->head->status != "200") { $valid = false; } else if ($xmlObj->head->status == "200") { + Application_Model_Preference::setLastTuneinMetadataUpdate(time()); $valid = true; } } diff --git a/airtime_mvc/application/models/Preference.php b/airtime_mvc/application/models/Preference.php index 1387d326b..541870fde 100644 --- a/airtime_mvc/application/models/Preference.php +++ b/airtime_mvc/application/models/Preference.php @@ -1493,4 +1493,14 @@ class Application_Model_Preference { return self::getValue("tunein_station_id"); } + + public static function geLastTuneinMetadataUpdate() + { + return self::getValue("last_tunein_metadata_update"); + } + + public static function setLastTuneinMetadataUpdate($value) + { + self::setValue("last_tunein_metadata_update", $value); + } } diff --git a/python_apps/api_clients/api_client.py b/python_apps/api_clients/api_client.py index 6c12e6fb6..0b74ed5c8 100644 --- a/python_apps/api_clients/api_client.py +++ b/python_apps/api_clients/api_client.py @@ -83,6 +83,7 @@ api_config['push_stream_stats'] = 'push-stream-stats/api_key/%%api_key%%/format/ api_config['update_stream_setting_table'] = 'update-stream-setting-table/api_key/%%api_key%%/format/json' api_config['get_files_without_silan_value'] = 'get-files-without-silan-value/api_key/%%api_key%%' api_config['update_cue_values_by_silan'] = 'update-cue-values-by-silan/api_key/%%api_key%%' +api_config['update_metadata_on_tunein'] = 'update-metadata-on-tunein/api_key/%%api_key%%' @@ -530,6 +531,9 @@ class AirtimeApiClient(object): #TODO self.logger.error(str(e)) + def update_metadata_on_tunein(self): + self.services.update_metadata_on_tunein() + class InvalidContentType(Exception): pass diff --git a/python_apps/pypo/pypofetch.py b/python_apps/pypo/pypofetch.py index 5f3ce5503..68bfaea91 100644 --- a/python_apps/pypo/pypofetch.py +++ b/python_apps/pypo/pypofetch.py @@ -14,7 +14,7 @@ import traceback import pure from Queue import Empty -from threading import Thread +from threading import Thread, Timer from subprocess import Popen, PIPE from api_clients import api_client @@ -447,6 +447,12 @@ class PypoFetch(Thread): return success + # This function makes a request to Airtime to see if we need to + # push metadata to TuneIn. We have to do this because TuneIn turns + # off metadata if it does not receive a request every 5 minutes. + def update_metadata_on_tunein(self): + self.api_client.update_metadata_on_tunein() + Timer(120, self.update_metadata_on_tunein).start() def main(self): #Make sure all Liquidsoap queues are empty. This is important in the @@ -458,8 +464,10 @@ class PypoFetch(Thread): self.set_bootstrap_variables() + self.update_metadata_on_tunein() + # Bootstrap: since we are just starting up, we need to grab the - # most recent schedule. After that we fetch the schedule every 30 + # most recent schedule. After that we fetch the schedule every 8 # minutes or wait for schedule updates to get pushed. success = self.persistent_manual_schedule_fetch(max_attempts=5) From 1fa45bfc48ad7d25c6c78b0f24657af9f5d89081 Mon Sep 17 00:00:00 2001 From: Duncan Sommerville Date: Wed, 27 May 2015 14:01:49 -0400 Subject: [PATCH 2/9] SAAS-777 - Changed deleteAllFilesAction to remove files and any current/future schedule items --- .../controllers/PreferenceController.php | 59 +++++++++++-------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/airtime_mvc/application/controllers/PreferenceController.php b/airtime_mvc/application/controllers/PreferenceController.php index 3e7618e9f..710952ef8 100644 --- a/airtime_mvc/application/controllers/PreferenceController.php +++ b/airtime_mvc/application/controllers/PreferenceController.php @@ -480,31 +480,42 @@ class PreferenceController extends Zend_Controller_Action return; } - $user = Application_Model_User::getCurrentUser(); - $playlists = $blocks = $streams = []; + $this->deleteFutureScheduleItems(); + $this->deleteCloudFiles(); + $this->deleteStoredFiles(); - $allPlaylists = CcPlaylistQuery::create()->find(); - foreach ($allPlaylists as $p) { - $playlists[] = $p->getDbId(); + $this->getResponse() + ->setHttpResponseCode(200) + ->appendBody("OK"); + } + + private function deleteFutureScheduleItems() { + $utcTimezone = new DateTimeZone("UTC"); + $nowDateTime = new DateTime("now", $utcTimezone); + $scheduleItems = CcScheduleQuery::create() + ->filterByDbEnds($nowDateTime->format("Y-m-d H:i:s"), Criteria::GREATER_THAN) + ->find(); + + // Delete all the schedule items + foreach ($scheduleItems as $i) { + // If this is the currently playing track, cancel the current show + if ($i->isCurrentItem()) { + $instanceId = $i->getDbInstanceId(); + $instance = CcShowInstancesQuery::create()->findPk($instanceId); + $showId = $instance->getDbShowId(); + + // From ScheduleController + $scheduler = new Application_Model_Scheduler(); + $scheduler->cancelShow($showId); + Application_Model_StoredFile::updatePastFilesIsScheduled(); + } + + $i->delete(); } + } - $allBlocks = CcBlockQuery::create()->find(); - foreach ($allBlocks as $b) { - $blocks[] = $b->getDbId(); - } - - $allStreams = CcWebstreamQuery::create()->find(); - foreach ($allStreams as $s) { - $streams[] = $s->getDbId(); - } - - // Delete all playlists, blocks, and streams - Application_Model_Playlist::deletePlaylists($playlists, $user->getId()); - Application_Model_Block::deleteBlocks($blocks, $user->getId()); - Application_Model_Webstream::deleteStreams($streams, $user->getId()); - + private function deleteCloudFiles() { try { - // Delete all the cloud files $CC_CONFIG = Config::getConfig(); foreach ($CC_CONFIG["supportedStorageBackends"] as $storageBackend) { @@ -514,7 +525,9 @@ class PreferenceController extends Zend_Controller_Action } catch(Exception $e) { Logging::info($e->getMessage()); } + } + private function deleteStoredFiles() { // Delete all files from the database $files = CcFilesQuery::create()->find(); foreach ($files as $file) { @@ -523,10 +536,6 @@ class PreferenceController extends Zend_Controller_Action // every S3 file we delete. $storedFile->delete(true); } - - $this->getResponse() - ->setHttpResponseCode(200) - ->appendBody("OK"); } } From b50aa155879cefe4191f66dbbac390abdc27bdd4 Mon Sep 17 00:00:00 2001 From: Duncan Sommerville Date: Thu, 14 May 2015 10:51:53 -0400 Subject: [PATCH 3/9] Retun 200 from ProvisioningHelper when a database exists to accommodate recreating terminated stations --- .../application/common/ProvisioningHelper.php | 37 +++++++++++++------ 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/airtime_mvc/application/common/ProvisioningHelper.php b/airtime_mvc/application/common/ProvisioningHelper.php index 580c20e52..605617105 100644 --- a/airtime_mvc/application/common/ProvisioningHelper.php +++ b/airtime_mvc/application/common/ProvisioningHelper.php @@ -10,7 +10,7 @@ class ProvisioningHelper // Parameter values private $dbuser, $dbpass, $dbname, $dbhost, $dbowner, $apikey; private $instanceId; - private $station_name, $description; + private $stationName, $description; public function __construct($apikey) { @@ -40,18 +40,14 @@ class ProvisioningHelper if ($this->dbhost && !empty($this->dbhost)) { $this->setNewDatabaseConnection(); - //if ($this->checkDatabaseExists()) { - // throw new Exception("ERROR: Airtime database already exists"); - //} - if (!$this->checkDatabaseExists()) { - throw new Exception("ERROR: $this->dbname database does not exist."); + throw new DatabaseDoesNotExistException("ERROR: $this->dbname database does not exist."); } //We really want to do this check because all the Propel-generated SQL starts with "DROP TABLE IF EXISTS". //If we don't check, then a second call to this API endpoint would wipe all the tables! if ($this->checkTablesExist()) { - throw new Exception("ERROR: airtime tables already exists"); + throw new DatabaseAlreadyExistsException(); } $this->createDatabaseTables(); @@ -63,11 +59,19 @@ class ProvisioningHelper //All we need to do is create the database tables. $this->initializePrefs(); - } catch (Exception $e) { + } catch (DatabaseDoesNotExistException $e) { http_response_code(400); Logging::error($e->getMessage()); echo $e->getMessage() . PHP_EOL; return; + } catch (DatabaseAlreadyExistsException $e) { + // When we recreate a terminated instance, the process will fail + // if we return a 40x response here. In order to circumvent this, + // just return a 200; we still avoid dropping the existing tables + http_response_code(200); + Logging::info($e->getMessage()); + echo $e->getMessage() . PHP_EOL; + return; } http_response_code(201); @@ -108,7 +112,7 @@ class ProvisioningHelper $this->dbowner = $_POST['dbowner']; $this->instanceId = $_POST['instanceid']; - $this->station_name = $_POST['station_name']; + $this->stationName = $_POST['station_name']; $this->description = $_POST['description']; } @@ -194,8 +198,8 @@ class ProvisioningHelper * Initialize preference values passed from the dashboard (if any exist) */ private function initializePrefs() { - if ($this->station_name) { - Application_Model_Preference::SetStationName($this->station_name); + if ($this->stationName) { + Application_Model_Preference::SetStationName($this->stationName); } if ($this->description) { Application_Model_Preference::SetStationDescription($this->description); @@ -203,3 +207,14 @@ class ProvisioningHelper } } + +class DatabaseAlreadyExistsException extends Exception { + private static $_defaultMessage = "ERROR: airtime tables already exists"; + public function __construct($message = null, $code = 0, Exception $previous = null) { + $message = _((is_null($message) ? self::$_defaultMessage : $message)); + parent::__construct($message, $code, $previous); + } +} + +class DatabaseDoesNotExistException extends Exception {} + From 182b12e0e31acb7d95471b450995e0611b03ca39 Mon Sep 17 00:00:00 2001 From: drigato Date: Thu, 28 May 2015 09:47:58 -0400 Subject: [PATCH 4/9] SAAS-794: Schedule/get-current-playlist can take too long to respond Fix for incorrect previous/next metadata when tracks are shuffled around in current playing show. --- airtime_mvc/application/models/Schedule.php | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/airtime_mvc/application/models/Schedule.php b/airtime_mvc/application/models/Schedule.php index d2e114243..75123e5d3 100644 --- a/airtime_mvc/application/models/Schedule.php +++ b/airtime_mvc/application/models/Schedule.php @@ -199,7 +199,6 @@ SQL; $currentMedia["ends"] = $currentMedia["show_ends"]; } - $currentMediaScheduleId = $currentMedia["id"]; $currentMediaFileId = $currentMedia["file_id"]; $currentMediaStreamId = $currentMedia["stream_id"]; if (isset($currentMediaFileId)) { @@ -234,9 +233,10 @@ SQL; ); $previousMedia = CcScheduleQuery::create() - ->filterByDbId($currentMediaScheduleId-1) + ->filterByDbStarts($currentMedia["starts"], Criteria::LESS_THAN) + ->filterByDbId($currentMedia["id"], Criteria::NOT_EQUAL) ->filterByDbPlayoutStatus(0, Criteria::GREATER_THAN) - ->orderByDbStarts() + ->orderByDbStarts(Criteria::DESC) ->findOne(); if (isset($previousMedia)) { $previousMediaFileId = $previousMedia->getDbFileId(); @@ -253,10 +253,6 @@ SQL; $previousWebstream = CcWebstreamQuery::create() ->filterByDbId($previousMediaStreamId) ->findOne(); - /*$previousWebstreamMetadata = CcWebstreamMetadataQuery::create() - ->filterByDbInstanceId($previousMedia->getDbInstanceId()) - ->orderByDbStartTime(Criteria::DESC) - ->findOne();*/ $previousMediaName = $previousWebstream->getDbName(); } else { $previousMediaType = null; @@ -270,8 +266,9 @@ SQL; } $nextMedia = CcScheduleQuery::create() - ->filterByDbId($currentMediaScheduleId+1) - ->orderByDbStarts() + ->filterByDbStarts($currentMedia["starts"], Criteria::GREATER_THAN) + ->filterByDbId($currentMedia["id"], Criteria::NOT_EQUAL) + ->orderByDbStarts(Criteria::ASC) ->findOne(); if (isset($nextMedia)) { $nextMediaFileId = $nextMedia->getDbFileId(); @@ -287,10 +284,6 @@ SQL; $nextWebstream = CcWebstreamQuery::create() ->filterByDbId($nextMediaStreamId) ->findOne(); - /*$nextWebstreamMetadata = CcWebstreamMetadataQuery::create() - ->filterByDbInstanceId($nextMedia->getDbInstanceId()) - ->orderByDbStartTime(Criteria::DESC) - ->findOne();*/ $nextMediaName = $nextWebstream->getDbName(); } else { $nextMediaType = null; From 018bb3a6488c5cf61a6c8aa03d480d2342962aca Mon Sep 17 00:00:00 2001 From: drigato Date: Thu, 28 May 2015 11:54:23 -0400 Subject: [PATCH 5/9] SAAS-823: Tunein preference check box issue --- airtime_mvc/application/controllers/PreferenceController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/airtime_mvc/application/controllers/PreferenceController.php b/airtime_mvc/application/controllers/PreferenceController.php index 3a0df1505..c1d1e5f29 100644 --- a/airtime_mvc/application/controllers/PreferenceController.php +++ b/airtime_mvc/application/controllers/PreferenceController.php @@ -72,6 +72,7 @@ class PreferenceController extends Zend_Controller_Action Application_Model_Preference::SetSoundCloudLicense($values["SoundCloudLicense"]);*/ $this->view->statusMsg = "
". _("Preferences updated.")."
"; + $form = new Application_Form_Preferences(); $this->view->form = $form; //$this->_helper->json->sendJson(array("valid"=>"true", "html"=>$this->view->render('preference/index.phtml'))); } else { From 450300ddd8fa5aaf24e80b1f0c5da311fdda12f0 Mon Sep 17 00:00:00 2001 From: drigato Date: Thu, 28 May 2015 12:00:20 -0400 Subject: [PATCH 6/9] Small TuneIn validation fix Was not setting error message when station id is invalid --- airtime_mvc/application/forms/TuneInPreferences.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/airtime_mvc/application/forms/TuneInPreferences.php b/airtime_mvc/application/forms/TuneInPreferences.php index ad133255a..c4cab8fb6 100644 --- a/airtime_mvc/application/forms/TuneInPreferences.php +++ b/airtime_mvc/application/forms/TuneInPreferences.php @@ -18,7 +18,6 @@ class Application_Form_TuneInPreferences extends Zend_Form_SubForm $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"); @@ -76,7 +75,6 @@ class Application_Form_TuneInPreferences extends Zend_Form_SubForm 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; } } @@ -85,6 +83,7 @@ class Application_Form_TuneInPreferences extends Zend_Form_SubForm if ($valid) { $xmlObj = new SimpleXMLElement($xmlData); if (!$xmlObj || $xmlObj->head->status != "200") { + $this->getElement("enable_tunein")->setErrors(array(_("Invalid TuneIn Settings. Please ensure your TuneIn settings are correct and try again."))); $valid = false; } else if ($xmlObj->head->status == "200") { Application_Model_Preference::setLastTuneinMetadataUpdate(time()); From 187ef926ff792fb35bda75260880ff5b14ef18b2 Mon Sep 17 00:00:00 2001 From: Albert Santoni Date: Thu, 28 May 2015 15:28:51 -0400 Subject: [PATCH 7/9] Allow public APIs to be accessed with the API key regardless of the public API setting. --- .../application/controllers/ApiController.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/airtime_mvc/application/controllers/ApiController.php b/airtime_mvc/application/controllers/ApiController.php index 11f6f7889..9bb118ace 100644 --- a/airtime_mvc/application/controllers/ApiController.php +++ b/airtime_mvc/application/controllers/ApiController.php @@ -72,6 +72,7 @@ class ApiController extends Zend_Controller_Action print _('You are not allowed to access this resource.'); exit; } + return true; } public function versionAction() @@ -156,7 +157,7 @@ class ApiController extends Zend_Controller_Action */ public function liveInfoAction() { - if (Application_Model_Preference::GetAllow3rdPartyApi()) { + if (Application_Model_Preference::GetAllow3rdPartyApi() || $this->checkAuth()) { // disable the view and the layout $this->view->layout()->disableLayout(); $this->_helper->viewRenderer->setNoRender(true); @@ -251,7 +252,7 @@ class ApiController extends Zend_Controller_Action */ public function liveInfoV2Action() { - if (Application_Model_Preference::GetAllow3rdPartyApi()) { + if (Application_Model_Preference::GetAllow3rdPartyApi() || $this->checkAuth()) { // disable the view and the layout $this->view->layout()->disableLayout(); $this->_helper->viewRenderer->setNoRender(true); @@ -359,7 +360,7 @@ class ApiController extends Zend_Controller_Action public function weekInfoAction() { - if (Application_Model_Preference::GetAllow3rdPartyApi()) { + if (Application_Model_Preference::GetAllow3rdPartyApi() || $this->checkAuth()) { // disable the view and the layout $this->view->layout()->disableLayout(); $this->_helper->viewRenderer->setNoRender(true); @@ -478,7 +479,7 @@ class ApiController extends Zend_Controller_Action */ public function showLogoAction() { - if (Application_Model_Preference::GetAllow3rdPartyApi()) { + if (Application_Model_Preference::GetAllow3rdPartyApi() || $this->checkAuth()) { $request = $this->getRequest(); $showId = $request->getParam('id'); @@ -509,7 +510,7 @@ class ApiController extends Zend_Controller_Action */ public function stationMetadataAction() { - if (Application_Model_Preference::GetAllow3rdPartyApi()) { + if (Application_Model_Preference::GetAllow3rdPartyApi() || $this->checkAuth()) { // disable the view and the layout $this->view->layout()->disableLayout(); $this->_helper->viewRenderer->setNoRender(true); @@ -548,7 +549,7 @@ class ApiController extends Zend_Controller_Action */ public function stationLogoAction() { - if (Application_Model_Preference::GetAllow3rdPartyApi()) { + if (Application_Model_Preference::GetAllow3rdPartyApi() || $this->checkAuth()) { // disable the view and the layout $this->view->layout()->disableLayout(); $this->_helper->viewRenderer->setNoRender(true); From a8d2290d006545c7bd3852f683317c2566996a34 Mon Sep 17 00:00:00 2001 From: Albert Santoni Date: Fri, 29 May 2015 16:43:27 -0400 Subject: [PATCH 8/9] SAAS-834: Login with WHMCS account doesn't work on suspended stations --- airtime_mvc/application/controllers/WhmcsLoginController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airtime_mvc/application/controllers/WhmcsLoginController.php b/airtime_mvc/application/controllers/WhmcsLoginController.php index 0b8a00a83..221f4452d 100644 --- a/airtime_mvc/application/controllers/WhmcsLoginController.php +++ b/airtime_mvc/application/controllers/WhmcsLoginController.php @@ -239,7 +239,7 @@ class WHMCS_Auth_Adapter implements Zend_Auth_Adapter_Interface { } else { - if ($product["status"] === "Active") { + if (($product["status"] === "Active") || ($product["status"] === "Suspended")) { $airtimeProduct = $product; $subdomain = ''; From 700fddbada46e2a1eab4879a0e7dd29e9be36aef Mon Sep 17 00:00:00 2001 From: Albert Santoni Date: Tue, 2 Jun 2015 14:51:20 -0400 Subject: [PATCH 9/9] SAAS-818: If the network connection is interrupted, pypo sometimes can't recover --- python_apps/pypo/pypofetch.py | 19 +++++++++++++++---- python_apps/pypo/pypofile.py | 8 ++++---- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/python_apps/pypo/pypofetch.py b/python_apps/pypo/pypofetch.py index 68bfaea91..ecfa557bd 100644 --- a/python_apps/pypo/pypofetch.py +++ b/python_apps/pypo/pypofetch.py @@ -477,6 +477,7 @@ class PypoFetch(Thread): loops = 1 while True: self.logger.info("Loop #%s", loops) + manual_fetch_needed = False try: """ our simple_queue.get() requires a timeout, in which case we @@ -492,17 +493,26 @@ class PypoFetch(Thread): Currently we are checking every POLL_INTERVAL seconds """ - message = self.fetch_queue.get(block=True, timeout=self.listener_timeout) + manual_fetch_needed = False self.handle_message(message) - except Empty, e: + except Empty as e: self.logger.info("Queue timeout. Fetching schedule manually") - self.persistent_manual_schedule_fetch(max_attempts=5) - except Exception, e: + manual_fetch_needed = True + except Exception as e: top = traceback.format_exc() self.logger.error('Exception: %s', e) self.logger.error("traceback: %s", top) + try: + if manual_fetch_needed: + self.persistent_manual_schedule_fetch(max_attempts=5) + except Exception as e: + top = traceback.format_exc() + self.logger.error('Failed to manually fetch the schedule.') + self.logger.error('Exception: %s', e) + self.logger.error("traceback: %s", top) + loops += 1 def run(self): @@ -510,3 +520,4 @@ class PypoFetch(Thread): Entry point of the thread """ self.main() + self.logger.info('PypoFetch thread exiting') diff --git a/python_apps/pypo/pypofile.py b/python_apps/pypo/pypofile.py index 3e5a400e5..1d04345df 100644 --- a/python_apps/pypo/pypofile.py +++ b/python_apps/pypo/pypofile.py @@ -35,6 +35,7 @@ class PypoFile(Thread): self.media_queue = schedule_queue self.media = None self.cache_dir = os.path.join(config["cache_dir"], "scheduler") + self._config = self.read_config_file(CONFIG_PATH) def copy_file(self, media_item): """ @@ -65,11 +66,9 @@ class PypoFile(Thread): if do_copy: self.logger.debug("copying from %s to local cache %s" % (src, dst)) try: - config = self.read_config_file(CONFIG_PATH) CONFIG_SECTION = "general" - username = config.get(CONFIG_SECTION, 'api_key') - - host = config.get(CONFIG_SECTION, 'base_url') + username = self._config.get(CONFIG_SECTION, 'api_key') + host = self._config.get(CONFIG_SECTION, 'base_url') url = "http://%s/rest/media/%s/download" % (host, media_item["id"]) with open(dst, "wb") as handle: response = requests.get(url, auth=requests.auth.HTTPBasicAuth(username, ''), stream=True, verify=False) @@ -210,3 +209,4 @@ class PypoFile(Thread): Entry point of the thread """ self.main() +