From 9302027a2b164bc20159efa17ef4451028f918b5 Mon Sep 17 00:00:00 2001 From: Duncan Sommerville Date: Thu, 22 Oct 2015 12:12:41 -0400 Subject: [PATCH] Fix issue with automatic ingest; shift episode ingest to PodcastEpisodesController --- airtime_mvc/application/Bootstrap.php | 2 ++ .../application/common/PodcastManager.php | 7 ++--- .../common/enum/HttpRequestType.php | 12 ++++++++ .../rest/controllers/PodcastController.php | 29 ++++--------------- .../controllers/PodcastEpisodesController.php | 16 ++++++---- .../services/PodcastEpisodeService.php | 14 +++++++++ .../application/services/PodcastService.php | 22 ++++++++++++++ .../public/js/airtime/library/podcast.js | 10 +++++-- 8 files changed, 75 insertions(+), 37 deletions(-) create mode 100644 airtime_mvc/application/common/enum/HttpRequestType.php diff --git a/airtime_mvc/application/Bootstrap.php b/airtime_mvc/application/Bootstrap.php index 18ad96c9e..391de011f 100644 --- a/airtime_mvc/application/Bootstrap.php +++ b/airtime_mvc/application/Bootstrap.php @@ -38,7 +38,9 @@ require_once "Auth.php"; require_once 'Preference.php'; require_once 'Locale.php'; /* Enums */ +require_once "Enum.php"; require_once "MediaType.php"; +require_once "HttpRequestType.php"; /* Interfaces */ require_once "OAuth2.php"; require_once "OAuth2Controller.php"; diff --git a/airtime_mvc/application/common/PodcastManager.php b/airtime_mvc/application/common/PodcastManager.php index 7336fd09d..d9b16bc35 100644 --- a/airtime_mvc/application/common/PodcastManager.php +++ b/airtime_mvc/application/common/PodcastManager.php @@ -5,7 +5,7 @@ class PodcastManager { /** * @var int how often, in seconds, to check for and ingest new podcast episodes */ - private static $_PODCAST_POLL_INTERVAL_SECONDS = 3600; // 1 hour + private static $_PODCAST_POLL_INTERVAL_SECONDS = 30; // 1 hour /** * Check whether $_PODCAST_POLL_INTERVAL_SECONDS have passed since the last call to @@ -50,13 +50,12 @@ class PodcastManager { $podcastArray = Application_Service_PodcastService::getPodcastById($podcast->getDbPodcastId()); $episodeList = $podcastArray["episodes"]; $episodes = array(); - // A bit hacky... sort the episodes by publication date to get the most recent - usort($episodeList, array(static::class, "_sortByEpisodePubDate")); for ($i = 0; $i < sizeof($episodeList); $i++) { $episodeData = $episodeList[$i]; + $ingestTimestamp = $podcast->getDbAutoIngestTimestamp(); // If the publication date of this episode is before the ingest timestamp, we don't need to ingest it // Since we're sorting by publication date, we can break - if ($episodeData["pub_date"] < $podcast->getDbAutoIngestTimestamp()) break; + if ($episodeData["pub_date"] < $ingestTimestamp) continue; $episode = PodcastEpisodesQuery::create()->findOneByDbEpisodeGuid($episodeData["guid"]); // Make sure there's no existing episode placeholder or import, and that the data is non-empty if (empty($episode) && !empty($episodeData)) { diff --git a/airtime_mvc/application/common/enum/HttpRequestType.php b/airtime_mvc/application/common/enum/HttpRequestType.php new file mode 100644 index 000000000..59b1a25b3 --- /dev/null +++ b/airtime_mvc/application/common/enum/HttpRequestType.php @@ -0,0 +1,12 @@ +view->layout()->disableLayout(); @@ -17,7 +12,6 @@ class Rest_PodcastController extends Zend_Rest_Controller // Remove reliance on .phtml files to render requests $this->_helper->viewRenderer->setNoRender(true); $this->view->setScriptPath(APPLICATION_PATH . 'views/scripts/'); - $this->_service = new Application_Service_PodcastEpisodeService(); } public function indexAction() @@ -120,11 +114,6 @@ class Rest_PodcastController extends Zend_Rest_Controller try { $requestData = json_decode($this->getRequest()->getRawBody(), true); - // Create placeholders in PodcastEpisodes so we know these episodes are being downloaded - // to prevent the user from trying to download them again while Celery is running - $episodes = $this->_service->addPodcastEpisodePlaceholders($requestData["podcast"]["id"], - $requestData["podcast"]["episodes"]); - $this->_service->downloadEpisodes($episodes); $podcast = Application_Service_PodcastService::updatePodcastFromArray($id, $requestData); $this->getResponse() @@ -167,7 +156,7 @@ class Rest_PodcastController extends Zend_Rest_Controller * Endpoint for performing bulk actions (deleting multiple podcasts, opening multiple editors) */ public function bulkAction() { - if ($this->_request->getMethod() != "POST") { + if ($this->_request->getMethod() != HttpRequestType::POST) { $this->getResponse() ->setHttpResponseCode(405) ->appendBody("ERROR: Method not accepted"); @@ -175,28 +164,20 @@ class Rest_PodcastController extends Zend_Rest_Controller } $ids = $this->_getParam('ids', []); - $method = $this->_getParam('method', 'GET'); + $method = $this->_getParam('method', HttpRequestType::GET); $responseBody = []; switch($method) { - case "DELETE": + case HttpRequestType::DELETE: foreach($ids as $id) { Application_Service_PodcastService::deletePodcastById($id); } // XXX: do we need this to be more descriptive? $responseBody = "Successfully deleted podcasts"; break; - case "GET": + case HttpRequestType::GET: foreach($ids as $id) { - // Check the StationPodcast table rather than checking - // the station podcast ID key in preferences for extensibility - $podcast = StationPodcastQuery::create()->findOneByDbPodcastId($id); - $path = $podcast ? 'podcast/station_podcast.phtml' : 'podcast/podcast.phtml'; - $podcast = Application_Service_PodcastService::getPodcastById($id); - $responseBody[] = array( - "podcast"=>json_encode($podcast), - "html"=>$this->view->render($path), - ); + $responseBody[] = Application_Service_PodcastService::buildPodcastEditorResponse($id, $this->view); } break; } diff --git a/airtime_mvc/application/modules/rest/controllers/PodcastEpisodesController.php b/airtime_mvc/application/modules/rest/controllers/PodcastEpisodesController.php index 605bbb521..b906362aa 100644 --- a/airtime_mvc/application/modules/rest/controllers/PodcastEpisodesController.php +++ b/airtime_mvc/application/modules/rest/controllers/PodcastEpisodesController.php @@ -2,12 +2,19 @@ class Rest_PodcastEpisodesController extends Zend_Rest_Controller { + + /** + * @var Application_Service_PodcastEpisodeService + */ + protected $_service; + public function init() { $this->view->layout()->disableLayout(); // Remove reliance on .phtml files to render requests $this->_helper->viewRenderer->setNoRender(true); + $this->_service = new Application_Service_PodcastEpisodeService(); } public function indexAction() @@ -62,13 +69,13 @@ class Rest_PodcastEpisodesController extends Zend_Rest_Controller public function postAction() { - Logging::info("episodes post"); //If we do get an episode ID on a POST, then that doesn't make any sense //since POST is only for creating. if ($episodeId = $this->_getParam('episode_id', false)) { $resp = $this->getResponse(); $resp->setHttpResponseCode(400); - $resp->appendBody("ERROR: Episode ID should not be specified when using POST. POST is only used for importing podcast episodes, and an episode ID will be chosen by Airtime"); + $resp->appendBody("ERROR: Episode ID should not be specified when using POST. POST is only used for " + . "importing podcast episodes, and an episode ID will be chosen by Airtime"); return; } @@ -80,10 +87,7 @@ class Rest_PodcastEpisodesController extends Zend_Rest_Controller try { $requestData = json_decode($this->getRequest()->getRawBody(), true); - //$requestData = $this->getRequest()->getPost(); - - $episode = PodcastEpisodes::create($id, $requestData); - + $episode = $this->_service->importEpisode($id, $requestData["episode"]); $this->getResponse() ->setHttpResponseCode(201) ->appendBody(json_encode($episode)); diff --git a/airtime_mvc/application/services/PodcastEpisodeService.php b/airtime_mvc/application/services/PodcastEpisodeService.php index db38df1e5..6da33e9f9 100644 --- a/airtime_mvc/application/services/PodcastEpisodeService.php +++ b/airtime_mvc/application/services/PodcastEpisodeService.php @@ -25,6 +25,20 @@ class Application_Service_PodcastEpisodeService extends Application_Service_Thir self::DOWNLOAD => 'podcast-download' ]; + /** + * Utility function to import and download a single episode + * + * @param int $podcastId ID of the podcast the episode should belong to + * @param array $episode array of episode data to store + * + * @return PodcastEpisodes the stored PodcastEpisodes object + */ + public function importEpisode($podcastId, $episode) { + $e = $this->addPlaceholder($podcastId, $episode); + $this->_download($e->getDbId(), $e->getDbDownloadUrl()); + return $e; + } + /** * Given an array of episodes, store them in the database as placeholder objects until * they can be processed by Celery diff --git a/airtime_mvc/application/services/PodcastService.php b/airtime_mvc/application/services/PodcastService.php index 2ba264ff8..57d5ae304 100644 --- a/airtime_mvc/application/services/PodcastService.php +++ b/airtime_mvc/application/services/PodcastService.php @@ -291,6 +291,28 @@ class Application_Service_PodcastService } } + /** + * Build a response with podcast data and embedded HTML to load on the frontend + * + * @param int $podcastId ID of the podcast to build a response for + * @param Zend_View_Interface $view Zend view object to render the response HTML + * + * @return array the response array containing the podcast data and editor HTML + * + * @throws PodcastNotFoundException + */ + public static function buildPodcastEditorResponse($podcastId, $view) { + // Check the StationPodcast table rather than checking + // the station podcast ID key in preferences for extensibility + $podcast = StationPodcastQuery::create()->findOneByDbPodcastId($podcastId); + $path = $podcast ? 'podcast/station_podcast.phtml' : 'podcast/podcast.phtml'; + $podcast = Application_Service_PodcastService::getPodcastById($podcastId); + return array( + "podcast" => json_encode($podcast), + "html" => $view->render($path), + ); + } + /** * Updates a Podcast object with the given metadata * diff --git a/airtime_mvc/public/js/airtime/library/podcast.js b/airtime_mvc/public/js/airtime/library/podcast.js index 5dcee4af1..c13209b2a 100644 --- a/airtime_mvc/public/js/airtime/library/podcast.js +++ b/airtime_mvc/public/js/airtime/library/podcast.js @@ -21,9 +21,13 @@ var AIRTIME = (function (AIRTIME) { tab.setName($scope.podcast.title); $scope.savePodcast = function() { - var podcastData = $scope.podcast; // Copy the podcast in scope so we can modify it - podcastData.episodes = episodeTable.getSelectedRows(); - $http.put(endpoint + $scope.podcast.id, { csrf_token: jQuery("#csrf").val(), podcast: podcastData }) + var episodes = episodeTable.getSelectedRows(), + csrf = jQuery("#csrf").val(); + // TODO: Should we implement a batch endpoint for this instead? + jQuery.each(episodes, function() { + $http.post(endpoint + $scope.podcast.id + '/episodes', { csrf_token: csrf, episode: this }); + }); + $http.put(endpoint + $scope.podcast.id, { csrf_token: csrf, podcast: $scope.podcast }) .success(function() { episodeTable.reload($scope.podcast.id); AIRTIME.library.podcastDataTable.fnDraw();