diff --git a/airtime_mvc/application/Bootstrap.php b/airtime_mvc/application/Bootstrap.php index e64c74136..863ae1ecc 100644 --- a/airtime_mvc/application/Bootstrap.php +++ b/airtime_mvc/application/Bootstrap.php @@ -33,7 +33,7 @@ require_once "TaskManager.php"; require_once "UsabilityHints.php"; require_once "MediaType.php"; require_once __DIR__.'/models/formatters/LengthFormatter.php'; -require_once __DIR__.'/services/CeleryService.php'; +require_once __DIR__.'/services/CeleryManager.php'; require_once __DIR__.'/services/SoundcloudService.php'; require_once __DIR__.'/forms/helpers/ValidationTypes.php'; require_once __DIR__.'/forms/helpers/CustomDecorators.php'; diff --git a/airtime_mvc/application/common/TaskManager.php b/airtime_mvc/application/common/TaskManager.php index c1687defa..0275d08f3 100644 --- a/airtime_mvc/application/common/TaskManager.php +++ b/airtime_mvc/application/common/TaskManager.php @@ -237,14 +237,14 @@ class CeleryTask implements AirtimeTask { * @return bool true if there are pending tasks in ThirdPartyTrackReferences */ public function shouldBeRun() { - return !CeleryService::isBrokerTaskQueueEmpty(); + return !CeleryManager::isBrokerTaskQueueEmpty(); } /** * Poll the task queue for any completed Celery tasks */ public function run() { - CeleryService::pollBrokerTaskQueue(); + CeleryManager::pollBrokerTaskQueue(); } } \ No newline at end of file diff --git a/airtime_mvc/application/controllers/ShowbuilderController.php b/airtime_mvc/application/controllers/ShowbuilderController.php index df3d01294..619f515b8 100644 --- a/airtime_mvc/application/controllers/ShowbuilderController.php +++ b/airtime_mvc/application/controllers/ShowbuilderController.php @@ -56,6 +56,7 @@ class ShowbuilderController extends Zend_Controller_Action $this->view->headScript()->appendFile($baseUrl.'js/libs/dropzone.min.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/timepicker/jquery.ui.timepicker.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); + $this->view->headScript()->appendFile($baseUrl.'js/airtime/showbuilder/tabs.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/airtime/showbuilder/builder.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/airtime/showbuilder/main_builder.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); diff --git a/airtime_mvc/application/services/CeleryService.php b/airtime_mvc/application/services/CeleryManager.php similarity index 99% rename from airtime_mvc/application/services/CeleryService.php rename to airtime_mvc/application/services/CeleryManager.php index b05692410..07ab7fa42 100644 --- a/airtime_mvc/application/services/CeleryService.php +++ b/airtime_mvc/application/services/CeleryManager.php @@ -2,7 +2,7 @@ require_once "CeleryServiceFactory.php"; -class CeleryService { +class CeleryManager { /** * @var int milliseconds (for compatibility with celery) until we consider a message to have timed out diff --git a/airtime_mvc/application/services/ThirdPartyCeleryService.php b/airtime_mvc/application/services/ThirdPartyCeleryService.php index 2a7b12606..24e9a5800 100644 --- a/airtime_mvc/application/services/ThirdPartyCeleryService.php +++ b/airtime_mvc/application/services/ThirdPartyCeleryService.php @@ -24,6 +24,30 @@ abstract class ThirdPartyCeleryService extends ThirdPartyService { */ protected static $_CELERY_DELETE_TASK_NAME; + /** + * Execute a Celery task with the given name and data parameters + * + * FIXME: Currently, downloads will not create task reference rows because they + * don't have a valid file identifier - this means that we will never know if there + * is an issue with the download before the callback to /rest/media is called! + * + * @param string $taskName the name of the celery task to execute + * @param array $data the data array to send as task parameters + * @param int $fileId the unique identifier for the file involved in the task + */ + protected function _executeTask($taskName, $data, $fileId) { + try { + $brokerTaskId = CeleryManager::sendCeleryMessage($taskName, + static::$_CELERY_EXCHANGE_NAME, + $data); + if (!empty($fileId)) { + $this->_createTaskReference($fileId, $brokerTaskId, $taskName); + } + } catch (Exception $e) { + Logging::info("Invalid request: " . $e->getMessage()); + } + } + /** * Upload the file with the given identifier to a third-party service * @@ -36,20 +60,13 @@ abstract class ThirdPartyCeleryService extends ThirdPartyService { 'token' => $this->_accessToken, 'file_path' => $file->getFilePaths()[0] ); - try { - $brokerTaskId = CeleryService::sendCeleryMessage(static::$_CELERY_UPLOAD_TASK_NAME, - static::$_CELERY_EXCHANGE_NAME, - $data); - $this->_createTaskReference($fileId, $brokerTaskId, static::$_CELERY_UPLOAD_TASK_NAME); - } catch (Exception $e) { - Logging::info("Invalid request: " . $e->getMessage()); - } + $this->_executeTask(static::$_CELERY_UPLOAD_TASK_NAME, $data, $fileId); } /** * Given a track identifier, download a track from a third-party service. * - * @param int|null $trackId a track identifier + * @param int $trackId a track identifier */ public function download($trackId) { $namespace = new Zend_Session_Namespace('csrf_namespace'); @@ -59,13 +76,9 @@ abstract class ThirdPartyCeleryService extends ThirdPartyService { 'token' => $this->_accessToken, 'track_id' => $trackId ); - try { - CeleryService::sendCeleryMessage(static::$_CELERY_DOWNLOAD_TASK_NAME, - static::$_CELERY_EXCHANGE_NAME, - $data); - } catch (Exception $e) { - Logging::info("Invalid request: " . $e->getMessage()); - } + // FIXME + Logging::warn("FIXME: we can't create a task reference without a valid file ID"); + $this->_executeTask(static::$_CELERY_DOWNLOAD_TASK_NAME, $data, null); } /** @@ -85,14 +98,7 @@ abstract class ThirdPartyCeleryService extends ThirdPartyService { 'token' => $this->_accessToken, 'track_id' => $serviceId ); - try { - $brokerTaskId = CeleryService::sendCeleryMessage(static::$_CELERY_DELETE_TASK_NAME, - static::$_CELERY_EXCHANGE_NAME, - $data); - $this->_createTaskReference($fileId, $brokerTaskId, static::$_CELERY_DELETE_TASK_NAME); - } catch (Exception $e) { - Logging::info("Invalid request: " . $e->getMessage()); - } + $this->_executeTask(static::$_CELERY_DELETE_TASK_NAME, $data, $fileId); } /** @@ -108,19 +114,19 @@ abstract class ThirdPartyCeleryService extends ThirdPartyService { * @throws PropelException */ protected function _createTaskReference($fileId, $brokerTaskId, $taskName) { - $trackId = $this->createTrackReference($fileId); + $trackReferenceId = $this->createTrackReference($fileId); $task = new CeleryTasks(); $task->setDbTaskId($brokerTaskId); $task->setDbName($taskName); $utc = new DateTimeZone("UTC"); $task->setDbDispatchTime(new DateTime("now", $utc)); $task->setDbStatus(CELERY_PENDING_STATUS); - $task->setDbTrackReference($trackId); + $task->setDbTrackReference($trackReferenceId); $task->save(); } /** - * Update a CeleryTasks object for a completed upload + * Update a CeleryTasks object for a completed task * TODO: should we have a database layer class to handle Propel operations? * * @param $trackId int ThirdPartyTrackReferences identifier diff --git a/airtime_mvc/application/services/ThirdPartyService.php b/airtime_mvc/application/services/ThirdPartyService.php index d3293f18c..523e0e5cc 100644 --- a/airtime_mvc/application/services/ThirdPartyService.php +++ b/airtime_mvc/application/services/ThirdPartyService.php @@ -54,7 +54,7 @@ abstract class ThirdPartyService { } /** - * Remove a ThirdPartyTrackReferences from the database. + * Remove a ThirdPartyTrackReferences row from the database. * This is necessary if the track was removed from the service * or the foreign id in our database is incorrect * diff --git a/airtime_mvc/public/css/dashboard.css b/airtime_mvc/public/css/dashboard.css index d6a668d48..bf0a3ec2e 100644 --- a/airtime_mvc/public/css/dashboard.css +++ b/airtime_mvc/public/css/dashboard.css @@ -481,6 +481,14 @@ li.ui-state-default { overflow-x: hidden; /* Show the y-direction scrollbar (magic!) */ } +.tab-name { + float: left; + max-width: 150px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + .inner_editor_wrapper { max-height: 60%; overflow-x: hidden; diff --git a/airtime_mvc/public/js/airtime/common/common.js b/airtime_mvc/public/js/airtime/common/common.js index 70d931ae5..4fe28eae7 100644 --- a/airtime_mvc/public/js/airtime/common/common.js +++ b/airtime_mvc/public/js/airtime/common/common.js @@ -289,3 +289,18 @@ function getUsabilityHint() { }); } +function setupTextScrolling(parent, selector) { + parent.on("mouseenter", selector, function () { + var sw = $(this)[0].scrollWidth - parseFloat($(this).css("textIndent")), iw = $(this).innerWidth(); + if (sw > iw) { + $(this).stop().animate({ + textIndent: "-" + (sw + 1 - iw) + "px" + }, sw * 8); + } + }); + parent.on("mouseleave", selector, function () { + $(this).stop().animate({ + textIndent: "0" + }, 500); + }); +} \ No newline at end of file diff --git a/airtime_mvc/public/js/airtime/library/library.js b/airtime_mvc/public/js/airtime/library/library.js index cb03e112b..b79ebbdd0 100644 --- a/airtime_mvc/public/js/airtime/library/library.js +++ b/airtime_mvc/public/js/airtime/library/library.js @@ -76,35 +76,35 @@ var AIRTIME = (function(AIRTIME) { mod.placeholder = function(mediaType) { switch (mediaType) { // TODO: remove duplication in a nice way? - case MediaTypeEnum.FILE: + case mod.MediaTypeEnum.FILE: return { "media": "tracks", "icon": "icon-music", "subtext": "Click 'Upload' to add some now.", "href": "http://sourcefabric.booktype.pro/airtime-pro-for-broadcasters/add-media/" }; - case MediaTypeEnum.PLAYLIST: + case mod.MediaTypeEnum.PLAYLIST: return { "media": "playlists", "icon": "icon-list", "subtext": "Click 'New' to create one now.", "href": "http://sourcefabric.booktype.pro/airtime-pro-for-broadcasters/library/" }; - case MediaTypeEnum.BLOCK: + case mod.MediaTypeEnum.BLOCK: return { "media": "smart blocks", "icon": "icon-time", "subtext": "Click 'New' to create one now.", "href": "http://sourcefabric.booktype.pro/airtime-pro-for-broadcasters/library/" }; - case MediaTypeEnum.WEBSTREAM: + case mod.MediaTypeEnum.WEBSTREAM: return { "media": "webstreams", "icon": "icon-random", "subtext": "Click 'New' to create one now.", "href": "http://sourcefabric.booktype.pro/airtime-pro-for-broadcasters/library/" }; - case MediaTypeEnum.PODCAST: + case mod.MediaTypeEnum.PODCAST: return { "media": "podcasts", "icon": "icon-headphones", @@ -448,7 +448,7 @@ var AIRTIME = (function(AIRTIME) { openTabObjectIds.each(function(i, el) { var v = parseInt($(el).val()); if ($.inArray(v, mediaIds) > -1) { - AIRTIME.playlist.closeTab($(el).closest(".pl-content").attr("data-tab-id")); + AIRTIME.tabs.closeTab($(el).closest(".pl-content").attr("data-tab-id")); } }); @@ -710,24 +710,6 @@ var AIRTIME = (function(AIRTIME) { } else if (!aData.is_playlist) { $(nRow).find("td.library_is_playlist").html(''); } - - // add the play function to the library_type td - $(nRow).find('td.library_type').click(function () { - if (aData.ftype === 'playlist' && aData.length !== '0.0') { - open_playlist_preview(aData.audioFile, 0); - } else if (aData.ftype === 'audioclip') { - if (isAudioSupported(aData.mime)) { - open_audio_preview(aData.ftype, aData.id); - } - } else if (aData.ftype == 'stream') { - if (isAudioSupported(aData.mime)) { - open_audio_preview(aData.ftype, aData.id); - } - } else if (aData.ftype == 'block' && aData.bl_type == 'static') { - open_block_preview(aData.audioFile, 0); - } - return false; - }); }, // remove any selected nodes before the draw. "fnPreDrawCallback": function (oSettings) { @@ -985,6 +967,26 @@ var AIRTIME = (function(AIRTIME) { } }); + // add the play function to the library_type td + $libTable.on("click", "td.library_type", function () { + var aData = $(this).parent().data().aData; + + if (aData.ftype === 'playlist' && aData.length !== '0.0') { + open_playlist_preview(aData.audioFile, 0); + } else if (aData.ftype === 'audioclip') { + if (isAudioSupported(aData.mime)) { + open_audio_preview(aData.ftype, aData.id); + } + } else if (aData.ftype == 'stream') { + if (isAudioSupported(aData.mime)) { + open_audio_preview(aData.ftype, aData.id); + } + } else if (aData.ftype == 'block' && aData.bl_type == 'static') { + open_block_preview(aData.audioFile, 0); + } + return false; + }); + $libTable.find("tbody").on("mousedown", "tr[class*='lib'] > td:not(.library_checkbox, .dataTables_empty)", function(ev) { var $tr = $(this).parent(), // Get the ID of the selected row diff --git a/airtime_mvc/public/js/airtime/library/plupload.js b/airtime_mvc/public/js/airtime/library/plupload.js index 21a92de9f..01b9fefe2 100644 --- a/airtime_mvc/public/js/airtime/library/plupload.js +++ b/airtime_mvc/public/js/airtime/library/plupload.js @@ -157,27 +157,12 @@ $(document).ready(function () { getUsabilityHint(); } }); - }, - "fnCreatedRow": function(nRow) { - $(nRow).find("td").hover( - function () { - var sw = $(this)[0].scrollWidth, iw = $(this).innerWidth(); - if (sw > iw) { - $(this).stop().animate({ - textIndent: "-" + (sw - iw) + "px" - }, sw * 8); - } - }, - function () { - $(this).stop().animate({ - textIndent: "0" - }, 500); - } - ); } }); }; + setupTextScrolling($("#recent_uploads"), "td"); + self.isRecentUploadsRefreshTimerActive = false; self.startRefreshingRecentUploads = function () { diff --git a/airtime_mvc/public/js/airtime/library/spl.js b/airtime_mvc/public/js/airtime/library/spl.js index 394a1bb1a..5ad412bcd 100644 --- a/airtime_mvc/public/js/airtime/library/spl.js +++ b/airtime_mvc/public/js/airtime/library/spl.js @@ -12,9 +12,7 @@ var AIRTIME = (function(AIRTIME){ viewport, $lib, $pl, - widgetHeight, - $tabCount = 0, - $openTabs = {}; + widgetHeight; function isTimeValid(time) { //var regExpr = new RegExp("^\\d{2}[:]\\d{2}[:]\\d{2}([.]\\d{1,6})?$"); @@ -356,17 +354,6 @@ var AIRTIME = (function(AIRTIME){ } } - function updateActiveTabName(newTabName) { - /* - var nameElement = $(this); - //remove any newlines if user somehow snuck them in (easy to do if dragging/dropping text) - nameElement.text(nameElement.text().replace("\n", "")); - - var name = $pl.find(".playlist_name_display").val(); - */ - $(".nav.nav-tabs .active a > span.tab-name").text(newTabName); - } - function redrawLib() { var dt = $lib.find("#library_display").dataTable(); @@ -394,7 +381,7 @@ var AIRTIME = (function(AIRTIME){ setCueEvents(); setFadeEvents(); mod.setModified(json.modified); - updateActiveTabName(json.name); + AIRTIME.tabs.setActiveTabName(json.name); AIRTIME.playlist.validatePlaylistElements(); redrawLib(); @@ -425,10 +412,10 @@ var AIRTIME = (function(AIRTIME){ $('.zend_form + .spl-no-margin > div:has(*:visible):last').css('margin-left', 0); } - function getId(pl) { + mod.getId = function(pl) { pl = (pl === undefined) ? $pl : pl; return parseInt(pl.find(".obj_id").val(), 10); - } + }; mod.getModified = function(pl) { pl = (pl === undefined) ? $pl : pl; @@ -443,115 +430,6 @@ var AIRTIME = (function(AIRTIME){ $pl.find(".title_obj_name").text(title); } - function setTabName(name) { - var id = $pl.data("tab-id"); - $("#show_builder").find("[data-tab-id='" + id + "']").find(".tab-name").text(name); - } - - /* - * Should all be moved to builder.js eventually - */ - function buildNewTab(json) { - AIRTIME.library.selectNone(); - - var tabId = $openTabs[json.type + json.id]; - if (tabId !== undefined) { - AIRTIME.showbuilder.switchTab($("#pl-tab-content-" + tabId), $("#pl-tab-" + tabId)); - return undefined; - } - $tabCount++; - - var wrapper = "