diff --git a/airtime_mvc/application/Bootstrap.php b/airtime_mvc/application/Bootstrap.php
index 26a6843c7..e64c74136 100644
--- a/airtime_mvc/application/Bootstrap.php
+++ b/airtime_mvc/application/Bootstrap.php
@@ -31,6 +31,7 @@ require_once "Auth.php";
require_once "interface/OAuth2.php";
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/SoundcloudService.php';
diff --git a/airtime_mvc/application/airtime-boot.php b/airtime_mvc/application/airtime-boot.php
index 08a5b9869..5ed3cb4bc 100644
--- a/airtime_mvc/application/airtime-boot.php
+++ b/airtime_mvc/application/airtime-boot.php
@@ -36,6 +36,8 @@ set_include_path(implode(PATH_SEPARATOR, array(
)));
set_include_path(APPLICATION_PATH . 'common' . PATH_SEPARATOR . get_include_path());
+set_include_path(APPLICATION_PATH . 'common/enum' . PATH_SEPARATOR . get_include_path());
+set_include_path(APPLICATION_PATH . 'common/interface' . PATH_SEPARATOR . get_include_path());
//Propel classes.
set_include_path(APPLICATION_PATH . 'models' . PATH_SEPARATOR . get_include_path());
diff --git a/airtime_mvc/application/common/enum/Enum.php b/airtime_mvc/application/common/enum/Enum.php
new file mode 100644
index 000000000..b0d34fbc3
--- /dev/null
+++ b/airtime_mvc/application/common/enum/Enum.php
@@ -0,0 +1,47 @@
+getConstants();
+ }
+ return self::$constCacheArray[$calledClass];
+ }
+
+ public static function isValidName($name, $strict = false) {
+ $constants = self::getConstants();
+
+ if ($strict) {
+ return array_key_exists($name, $constants);
+ }
+
+ $keys = array_map('strtolower', array_keys($constants));
+ return in_array(strtolower($name), $keys);
+ }
+
+ public static function isValidValue($value) {
+ $values = array_values(self::getConstants());
+ return in_array($value, $values, $strict = true);
+ }
+
+ public static function getDefault() {
+ return static::__default;
+ }
+
+}
\ No newline at end of file
diff --git a/airtime_mvc/application/common/enum/MediaType.php b/airtime_mvc/application/common/enum/MediaType.php
new file mode 100644
index 000000000..69843c0d7
--- /dev/null
+++ b/airtime_mvc/application/common/enum/MediaType.php
@@ -0,0 +1,14 @@
+_service->upload($id);
}
+ /**
+ * Download the file with the given id from a third-party service
+ *
+ * @return void
+ *
+ * @throws Zend_Controller_Response_Exception thrown if download fails for any reason
+ */
+ public function downloadAction() {
+ $request = $this->getRequest();
+ $id = $request->getParam('id');
+ $this->_service->download($id);
+ }
+
/**
* Delete the file with the given id from a third-party service
*
diff --git a/airtime_mvc/application/layouts/scripts/layout.phtml b/airtime_mvc/application/layouts/scripts/layout.phtml
index e2e486415..83b55c87f 100644
--- a/airtime_mvc/application/layouts/scripts/layout.phtml
+++ b/airtime_mvc/application/layouts/scripts/layout.phtml
@@ -66,22 +66,21 @@ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
-
+
-
navigation()->menu(); ?>
@@ -90,7 +89,6 @@ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
totalSpace-$disk->totalFreeSpace;
$total = $disk->totalSpace;
diff --git a/airtime_mvc/application/models/StoredFile.php b/airtime_mvc/application/models/StoredFile.php
index bad64204f..847586d03 100644
--- a/airtime_mvc/application/models/StoredFile.php
+++ b/airtime_mvc/application/models/StoredFile.php
@@ -806,21 +806,17 @@ SQL;
$unionTable = "({$plTable} UNION {$blTable} UNION {$fileTable} UNION {$streamTable}) AS RESULTS";
//choose which table we need to select data from.
- // TODO : use constants instead of numbers -- RG
switch ($type) {
- case 0:
- $fromTable = $unionTable;
- break;
- case 1:
+ case MediaType::FILE:
$fromTable = $fileTable." AS File"; //need an alias for the table if it's standalone.
break;
- case 2:
+ case MediaType::PLAYLIST:
$fromTable = $plTable." AS Playlist"; //need an alias for the table if it's standalone.
break;
- case 3:
+ case MediaType::BLOCK:
$fromTable = $blTable." AS Block"; //need an alias for the table if it's standalone.
break;
- case 4:
+ case MediaType::WEBSTREAM:
$fromTable = $streamTable." AS StreamTable"; //need an alias for the table if it's standalone.
break;
default:
diff --git a/airtime_mvc/application/services/CeleryService.php b/airtime_mvc/application/services/CeleryService.php
index 8e9091290..b05692410 100644
--- a/airtime_mvc/application/services/CeleryService.php
+++ b/airtime_mvc/application/services/CeleryService.php
@@ -77,8 +77,7 @@ class CeleryService {
$c = self::_setupCeleryExchange($config, self::$_CELERY_RESULTS_EXCHANGE, $queue);
$message = $c->getAsyncResultMessage($task->getDbName(), $task->getDbTaskId());
- // If the message isn't ready yet (Celery hasn't finished the task),
- // only throw an exception if the message has timed out.
+ // If the message isn't ready yet (Celery hasn't finished the task), throw an exception.
if ($message == FALSE) {
if (self::_checkMessageTimeout($task)) {
// If the task times out, mark it as failed. We don't want to remove the
diff --git a/airtime_mvc/application/services/SoundcloudService.php b/airtime_mvc/application/services/SoundcloudService.php
index 8e946375e..16be037bc 100644
--- a/airtime_mvc/application/services/SoundcloudService.php
+++ b/airtime_mvc/application/services/SoundcloudService.php
@@ -19,6 +19,7 @@ class SoundcloudService extends ThirdPartyCeleryService implements OAuth2 {
*/
protected static $_SERVICE_NAME = SOUNDCLOUD_SERVICE_NAME; // SoundCloud service name constant from constants.php
+ // TODO: Make these constants
/**
* @var string exchange name for SoundCloud tasks
*/
@@ -29,6 +30,11 @@ class SoundcloudService extends ThirdPartyCeleryService implements OAuth2 {
*/
protected static $_CELERY_UPLOAD_TASK_NAME = 'soundcloud-upload';
+ /**
+ * @var string celery task name for third party uploads
+ */
+ protected static $_CELERY_DOWNLOAD_TASK_NAME = 'soundcloud-download';
+
/**
* @var string celery task name for third party deletions
*/
diff --git a/airtime_mvc/application/services/ThirdPartyCeleryService.php b/airtime_mvc/application/services/ThirdPartyCeleryService.php
index 5dbc1ebad..78b0a09ce 100644
--- a/airtime_mvc/application/services/ThirdPartyCeleryService.php
+++ b/airtime_mvc/application/services/ThirdPartyCeleryService.php
@@ -14,6 +14,11 @@ abstract class ThirdPartyCeleryService extends ThirdPartyService {
*/
protected static $_CELERY_UPLOAD_TASK_NAME;
+ /**
+ * @var string celery task name for third-party uploads
+ */
+ protected static $_CELERY_DOWNLOAD_TASK_NAME;
+
/**
* @var string celery task name for third-party deletion
*/
@@ -41,6 +46,31 @@ abstract class ThirdPartyCeleryService extends ThirdPartyService {
}
}
+ /**
+ * Given a SoundCloud track identifier, download a track from SoundCloud.
+ *
+ * If no track identifier is given, download all tracks for the currently
+ * authenticated SoundCloud user.
+ *
+ * @param int|null $trackId a SoundCloud track identifier
+ */
+ public function download($trackId = null) {
+ $namespace = new Zend_Session_Namespace('csrf_namespace');
+ $csrfToken = $namespace->authtoken;
+ $data = array(
+ 'callback_url' => 'http' . (empty($_SERVER['HTTPS']) ? '' : 's') . '://' . $_SERVER['HTTP_HOST'] . '/media/post>csrf_token=' . $csrfToken,
+ '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());
+ }
+ }
+
/**
* Delete the file with the given identifier from a third-party service
*
@@ -52,7 +82,7 @@ abstract class ThirdPartyCeleryService extends ThirdPartyService {
public function delete($fileId) {
$serviceId = $this->getServiceId($fileId);
if ($serviceId == 0) {
- throw new ServiceNotFoundException("No service found for file with ID $fileId");
+ throw new ServiceNotFoundException("No service ID found for file with ID $fileId");
}
$data = array(
'token' => $this->_accessToken,
diff --git a/airtime_mvc/application/services/ThirdPartyService.php b/airtime_mvc/application/services/ThirdPartyService.php
index 5af1eb0e4..d3293f18c 100644
--- a/airtime_mvc/application/services/ThirdPartyService.php
+++ b/airtime_mvc/application/services/ThirdPartyService.php
@@ -124,6 +124,13 @@ abstract class ThirdPartyService {
*/
abstract function upload($fileId);
+ /**
+ * Download the file with the given identifier from a third-party service
+ *
+ * @param int $trackId the third-party service track identifier
+ */
+ abstract function download($trackId);
+
/**
* Delete the file with the given identifier from a third-party service
*
diff --git a/airtime_mvc/application/views/scripts/partialviews/dashboard-sub-nav.php b/airtime_mvc/application/views/scripts/partialviews/dashboard-sub-nav.php
new file mode 100644
index 000000000..fe9428f1e
--- /dev/null
+++ b/airtime_mvc/application/views/scripts/partialviews/dashboard-sub-nav.php
@@ -0,0 +1,30 @@
+
+
+
+
+
diff --git a/airtime_mvc/application/views/scripts/showbuilder/builderDialog.phtml b/airtime_mvc/application/views/scripts/showbuilder/builderDialog.phtml
index fb346cc94..9ac17f639 100644
--- a/airtime_mvc/application/views/scripts/showbuilder/builderDialog.phtml
+++ b/airtime_mvc/application/views/scripts/showbuilder/builderDialog.phtml
@@ -1,30 +1,7 @@
csrf ?>
diff --git a/airtime_mvc/application/views/scripts/showbuilder/index.phtml b/airtime_mvc/application/views/scripts/showbuilder/index.phtml
index b3f90508b..d36794257 100644
--- a/airtime_mvc/application/views/scripts/showbuilder/index.phtml
+++ b/airtime_mvc/application/views/scripts/showbuilder/index.phtml
@@ -15,6 +15,7 @@
diff --git a/airtime_mvc/public/ajax/library_placeholders.json b/airtime_mvc/public/ajax/library_placeholders.json
deleted file mode 100644
index d1fa5f8af..000000000
--- a/airtime_mvc/public/ajax/library_placeholders.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
- "1": {
- "media": "tracks",
- "icon": "icon-music",
- "subtext": "Click 'Upload' to add some now.",
- "href": "http://sourcefabric.booktype.pro/airtime-pro-for-broadcasters/add-media/"
- },
- "2": {
- "media": "playlists",
- "icon": "icon-list",
- "subtext": "Click 'New' to create one now.",
- "href": "http://sourcefabric.booktype.pro/airtime-pro-for-broadcasters/library/"
- },
- "3": {
- "media": "smart blocks",
- "icon": "icon-time",
- "subtext": "Click 'New' to create one now.",
- "href": "http://sourcefabric.booktype.pro/airtime-pro-for-broadcasters/library/"
-
- },
- "4": {
- "media": "webstreams",
- "icon": "icon-random",
- "subtext": "Click 'New' to create one now.",
- "href": "http://sourcefabric.booktype.pro/airtime-pro-for-broadcasters/library/"
- },
- "unauthorized": "You don't have permission to view the library."
-}
\ No newline at end of file
diff --git a/airtime_mvc/public/css/dashboard.css b/airtime_mvc/public/css/dashboard.css
index 4cb70ee68..d6a668d48 100644
--- a/airtime_mvc/public/css/dashboard.css
+++ b/airtime_mvc/public/css/dashboard.css
@@ -716,3 +716,15 @@ th.library_checkbox {
background-color: #FF5D1A !important;
height: 38px !important;
}
+
+/* ~~~~~~~~~~~~~~~~
+ Podcasts
+ ~~~~~~~~~~~~~~~~ */
+
+#podcast_table {
+ display: none;
+}
+
+/* ~~~~~~~~~~~~~~~~
+ END Podcasts
+ ~~~~~~~~~~~~~~~~ */
diff --git a/airtime_mvc/public/css/styles.css b/airtime_mvc/public/css/styles.css
index 9f4294be8..0780d3b44 100644
--- a/airtime_mvc/public/css/styles.css
+++ b/airtime_mvc/public/css/styles.css
@@ -3598,7 +3598,7 @@ button.btn-icon-text > i.icon-white {
.media_type_selector a {
text-decoration: none;
color: #cecece;
- padding: 10px 10px 10px 10px;
+ padding: 10px 0 10px 10px;
display: block;
}
diff --git a/airtime_mvc/public/js/airtime/library/events/library_showbuilder.js b/airtime_mvc/public/js/airtime/library/events/library_showbuilder.js
index c6d8f8bbd..4be63d7c9 100644
--- a/airtime_mvc/public/js/airtime/library/events/library_showbuilder.js
+++ b/airtime_mvc/public/js/airtime/library/events/library_showbuilder.js
@@ -80,22 +80,20 @@ var AIRTIME = (function(AIRTIME) {
var libEmpty = $('#library_empty');
if (emptyRow.length > 0) {
emptyRow.hide();
- var mediaType = parseInt($('.media_type_selector.selected').attr('data-selection-id')),
+ var mediaType = parseInt($('.media_type_selector.selected').data('selection-id')),
img = $('#library_empty_image');
// Remove all classes for when we change between empty media types
img.removeClass(function() {
return $( this ).attr( "class" );
});
- // TODO: once the new manual pages are added, change links!
- $.getJSON( "ajax/library_placeholders.json", function( data ) {
- var opts = data[mediaType];
- img.addClass("icon-white " + opts.icon);
- $('#library_empty_text').html(
- $.i18n._("You haven't added any " + opts.media + ".")
- + "
" + $.i18n._(opts.subtext)
- + "
" + $.i18n._("Learn about " + opts.media) + " "
- );
- });
+
+ var opts = AIRTIME.library.placeholder(mediaType);
+ img.addClass("icon-white " + opts.icon);
+ $('#library_empty_text').html(
+ $.i18n._("You haven't added any " + opts.media + ".")
+ + "
" + $.i18n._(opts.subtext)
+ + "
" + $.i18n._("Learn about " + opts.media) + " "
+ );
libEmpty.show();
} else {
@@ -333,13 +331,13 @@ var AIRTIME = (function(AIRTIME) {
return;
}
- var selection = $(".media_type_selector.selected").attr("data-selection-id");
+ var selection = $(".media_type_selector.selected").data("selection-id");
- if (selection == 2) {
+ if (selection == AIRTIME.library.MediaTypeEnum.PLAYLIST) {
AIRTIME.playlist.fnNew();
- } else if (selection == 3) {
+ } else if (selection == AIRTIME.library.MediaTypeEnum.BLOCK) {
AIRTIME.playlist.fnNewBlock();
- } else if (selection == 4) {
+ } else if (selection == AIRTIME.library.MediaTypeEnum.WEBSTREAM) {
AIRTIME.playlist.fnWsNew();
}
});
diff --git a/airtime_mvc/public/js/airtime/library/library.js b/airtime_mvc/public/js/airtime/library/library.js
index 2c6fe6ad9..760998b00 100644
--- a/airtime_mvc/public/js/airtime/library/library.js
+++ b/airtime_mvc/public/js/airtime/library/library.js
@@ -59,6 +59,67 @@ var AIRTIME = (function(AIRTIME) {
}
mod = AIRTIME.library;
+ /* ############################################
+ CONFIGURATION
+ ############################################ */
+
+ mod.MediaTypeEnum = Object.freeze({
+ DEFAULT: 1,
+ FILE: 1,
+ PLAYLIST: 2,
+ BLOCK: 3,
+ WEBSTREAM: 4,
+ PODCAST: 5
+ });
+
+ // TODO: once the new manual pages are added, change links!
+ mod.placeholder = function(mediaType) {
+ switch (mediaType) {
+ // TODO: remove duplication in a nice way?
+ case 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:
+ 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:
+ 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:
+ 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:
+ return {
+ "media": "podcasts",
+ "icon": "icon-headphones",
+ "subtext": "Click 'Add' to create one now.",
+ "href": "http://sourcefabric.booktype.pro/airtime-pro-for-broadcasters/library/"
+ };
+ default:
+ break;
+ }
+ };
+
+ /* ############################################
+ END CONFIGURATION
+ ############################################ */
+
mod.getChosenItemsLength = function(){
var cItem,
selected,
@@ -199,10 +260,10 @@ var AIRTIME = (function(AIRTIME) {
};
mod.checkNewButton = function() {
- var selected = $(".media_type_selector.selected").attr("data-selection-id"),
+ var selected = $(".media_type_selector.selected").data("selection-id"),
check = false;
- if (selected != 1) {
+ if (selected != AIRTIME.library.MediaTypeEnum.FILE) {
check = true;
}
@@ -455,131 +516,13 @@ var AIRTIME = (function(AIRTIME) {
var colReorderMap = new Array();
- $libTable = $libContent.find("table");
+ $libTable = $("#library_display");
- function getTableHeight() {
- return $libContent.height() - 175;
- }
+ /* ############################################
+ DATATABLES
+ ############################################ */
- function setColumnFilter(oTable){
- // TODO : remove this dirty hack once js is refactored
- if (!oTable.fnSettings()) { return ; }
- var aoCols = oTable.fnSettings().aoColumns;
- var colsForAdvancedSearch = new Array();
- var advanceSearchDiv = $("div#advanced_search");
- advanceSearchDiv.empty();
- $.each(aoCols, function(i,ele){
- if (ele.bSearchable) {
- var currentColId = ele._ColReorder_iOrigCol;
-
- var inputClass = 'filter_column filter_number_text';
- var labelStyle = "style='margin-right:35px;'";
- if (libraryColumnTypes[ele.mDataProp] != "s") {
- inputClass = 'filterColumn filter_number_range';
- labelStyle = "";
- }
-
- if (ele.bVisible) {
- advanceSearchDiv.append(
- "
" +
- "
"+ele.sTitle+" " +
- "
" +
- "
");
- } else {
- advanceSearchDiv.append(
- "
" +
- "
"+ele.sTitle+" " +
- "
" +
- "
");
- }
-
- if (libraryColumnTypes[ele.mDataProp] == "s") {
- var obj = { sSelector: "#"+ele.mDataProp }
- } else {
- var obj = { sSelector: "#"+ele.mDataProp, type: "number-range" }
- }
- colsForAdvancedSearch.push(obj);
- } else {
- colsForAdvancedSearch.push(null);
- }
- });
-
- oTable.columnFilter({
- aoColumns: colsForAdvancedSearch,
- bUseColVis: true,
- sPlaceHolder: "head:before"
- }
- );
- }
-
- function setFilterElement(iColumn, bVisible){
- var actualId = colReorderMap[iColumn];
- var selector = "div#advanced_search_col_"+actualId;
- var $el = $(selector);
-
- if (bVisible) {
- $el.show();
- } else {
- $el.hide();
- }
- }
-
- function getLibraryDatatableStrings() {
- //Set up the datatables string translation table with different strings depending on
- //whether you're viewing files, playlists, smart blocks, etc.
- var type = parseInt($(".media_type_selector.selected").attr("data-selection-id"));
- type = (type === undefined) ? 1 : type;
-
- //FIXME: The code that calls this function doesn't work as intended because you can't
- // change the oLanguage property of a datatable dynamically. :(
-
- switch (type) {
- /*
- case 0:
- return getDatatablesStrings({
- "sEmptyTable": $.i18n._("No files found"),
- });
- break;
- case 1:
- return getDatatablesStrings({
- "sEmptyTable": $.i18n._("No playlists found"),
- });
- break;
- case 2:
- return getDatatablesStrings({
- "sEmptyTable": $.i18n._("No smart blocks found"),
- });
- break;*/
- default:
- return getDatatablesStrings({
- "sEmptyTable": $.i18n._(""),
- "sZeroRecords": $.i18n._("No matching results found.")
- //"oPaginate": {
- // "sFirst": "<<",
- // "sLast": ">>",
- // "sNext": ">",
- // "sPrevious": "<"
- //}
- });
- break;
- }
-
- }
-
- function handleAjaxError(r) {
- // If the request was denied due to permissioning
- if (r.status === 403) {
- // Hide the processing div
- $("#library_display_wrapper").find(".dt-process-rel").hide();
- $.getJSON( "ajax/library_placeholders.json", function( data ) {
- $('#library_empty_text').text($.i18n._(data.unauthorized));
- }) ;
-
- $('#library_empty').show();
- }
- }
-
- oTable = $libTable.dataTable( {
+ mod.libraryDataTable = $libTable.dataTable({
// put hidden columns at the top to insure they can never be visible
// on the table through column reordering.
@@ -688,10 +631,10 @@ var AIRTIME = (function(AIRTIME) {
oData.iCreate = parseInt(oData.iCreate, 10);
},
- "sAjaxSource": baseUrl+"Library/contents-feed",
+ "sAjaxSource": baseUrl + "Library/contents-feed",
"sAjaxDataProp": "files",
- "fnServerData": function ( sSource, aoData, fnCallback ) {
+ "fnServerData": function (sSource, aoData, fnCallback) {
/*
* The real validation check is done in
* dataTables.columnFilter.js We also need to check it here
@@ -702,13 +645,13 @@ var AIRTIME = (function(AIRTIME) {
var advSearchFields = $("div#advanced_search").children(':visible');
var advSearchValid = validateAdvancedSearch(advSearchFields);
var type;
- aoData.push( { name: "format", value: "json"} );
- aoData.push( { name: "advSearch", value: advSearchValid} );
+ aoData.push({name: "format", value: "json"});
+ aoData.push({name: "advSearch", value: advSearchValid});
// push whether to search files/playlists or all.
- type = $(".media_type_selector.selected").attr("data-selection-id");
- type = (type === undefined) ? 1 : type;
- aoData.push( { name: "type", value: type} );
+ type = $(".media_type_selector.selected").data("selection-id");
+ type = (type === undefined) ? AIRTIME.library.MediaTypeEnum.DEFAULT : type;
+ aoData.push({name: "type", value: type});
//getUsabilityHint();
@@ -719,7 +662,7 @@ var AIRTIME = (function(AIRTIME) {
"data": aoData,
"success": fnCallback,
"error": handleAjaxError
- }).done(function(data) {
+ }).done(function (data) {
if (data.iTotalRecords > data.iTotalDisplayRecords) {
$('#filter_message').text(
$.i18n._("Filtering out ") + (data.iTotalRecords - data.iTotalDisplayRecords)
@@ -736,25 +679,25 @@ var AIRTIME = (function(AIRTIME) {
});
},
"fnRowCallback": AIRTIME.library.fnRowCallback,
- "fnCreatedRow": function( nRow, aData, iDataIndex ) {
+ "fnCreatedRow": function (nRow, aData, iDataIndex) {
// add checkbox
- $(nRow).find('td.library_checkbox').html("
");
+ $(nRow).find('td.library_checkbox').html("
");
$(nRow).find('td.library_actions')
.text("...")
- .on('click', function(e) {
+ .on('click', function (e) {
$(this).contextMenu({x: $(e.target).offset().left, y: $(e.target).offset().top})
}).html("
...
");
// add audio preview image/button
if (aData.ftype === "audioclip") {
- $(nRow).find('td.library_type').html('
');
+ $(nRow).find('td.library_type').html('
');
} else if (aData.ftype === "playlist") {
- $(nRow).find('td.library_type').html('
');
+ $(nRow).find('td.library_type').html('
');
} else if (aData.ftype === "block") {
- $(nRow).find('td.library_type').html('
');
+ $(nRow).find('td.library_type').html('
');
} else if (aData.ftype === "stream") {
- $(nRow).find('td.library_type').html('
');
+ $(nRow).find('td.library_type').html('
');
}
if (aData.is_scheduled) {
@@ -769,8 +712,8 @@ var AIRTIME = (function(AIRTIME) {
}
// add the play function to the library_type td
- $(nRow).find('td.library_type').click(function(){
- if (aData.ftype === 'playlist' && aData.length !== '0.0'){
+ $(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)) {
@@ -787,7 +730,7 @@ var AIRTIME = (function(AIRTIME) {
});
},
// remove any selected nodes before the draw.
- "fnPreDrawCallback": function( oSettings ) {
+ "fnPreDrawCallback": function (oSettings) {
// make sure any dragging helpers are removed or else they'll be
// stranded on the screen.
@@ -822,6 +765,143 @@ var AIRTIME = (function(AIRTIME) {
});
+ /* TODO: implement podcast datatable
+ * mod.podcastDataTable = $("#podcast_table").dataTable({});
+ */
+ mod.podcastDataTable = mod.libraryDataTable;
+
+ /* ############################################
+ END DATATABLES
+ ############################################ */
+
+ function getTableHeight() {
+ return $libContent.height() - 175;
+ }
+
+ function setColumnFilter(oTable){
+ // TODO : remove this dirty hack once js is refactored
+ if (!oTable.fnSettings()) { return ; }
+ var aoCols = oTable.fnSettings().aoColumns;
+ var colsForAdvancedSearch = new Array();
+ var advanceSearchDiv = $("div#advanced_search");
+ advanceSearchDiv.empty();
+ $.each(aoCols, function(i,ele){
+ if (ele.bSearchable) {
+ var currentColId = ele._ColReorder_iOrigCol;
+
+ var inputClass = 'filter_column filter_number_text';
+ var labelStyle = "style='margin-right:35px;'";
+ if (libraryColumnTypes[ele.mDataProp] != "s") {
+ inputClass = 'filterColumn filter_number_range';
+ labelStyle = "";
+ }
+
+ if (ele.bVisible) {
+ advanceSearchDiv.append(
+ "
" +
+ "
"+ele.sTitle+" " +
+ "
" +
+ "
");
+ } else {
+ advanceSearchDiv.append(
+ "
" +
+ "
"+ele.sTitle+" " +
+ "
" +
+ "
");
+ }
+
+ if (libraryColumnTypes[ele.mDataProp] == "s") {
+ var obj = { sSelector: "#"+ele.mDataProp }
+ } else {
+ var obj = { sSelector: "#"+ele.mDataProp, type: "number-range" }
+ }
+ colsForAdvancedSearch.push(obj);
+ } else {
+ colsForAdvancedSearch.push(null);
+ }
+ });
+
+ oTable.columnFilter({
+ aoColumns: colsForAdvancedSearch,
+ bUseColVis: true,
+ sPlaceHolder: "head:before"
+ }
+ );
+ }
+
+ function setFilterElement(iColumn, bVisible){
+ var actualId = colReorderMap[iColumn];
+ var selector = "div#advanced_search_col_"+actualId;
+ var $el = $(selector);
+
+ if (bVisible) {
+ $el.show();
+ } else {
+ $el.hide();
+ }
+ }
+
+ function getLibraryDatatableStrings() {
+ //Set up the datatables string translation table with different strings depending on
+ //whether you're viewing files, playlists, smart blocks, etc.
+ var type = parseInt($(".media_type_selector.selected").data("selection-id"));
+ type = (type === undefined) ? AIRTIME.library.MediaTypeEnum.DEFAULT : type;
+
+ //FIXME: The code that calls this function doesn't work as intended because you can't
+ // change the oLanguage property of a datatable dynamically. :(
+
+ switch (type) {
+ /*
+ case 0:
+ return getDatatablesStrings({
+ "sEmptyTable": $.i18n._("No files found"),
+ });
+ break;
+ case 1:
+ return getDatatablesStrings({
+ "sEmptyTable": $.i18n._("No playlists found"),
+ });
+ break;
+ case 2:
+ return getDatatablesStrings({
+ "sEmptyTable": $.i18n._("No smart blocks found"),
+ });
+ break;*/
+ default:
+ return getDatatablesStrings({
+ "sEmptyTable": $.i18n._(""),
+ "sZeroRecords": $.i18n._("No matching results found.")
+ //"oPaginate": {
+ // "sFirst": "<<",
+ // "sLast": ">>",
+ // "sNext": ">",
+ // "sPrevious": "<"
+ //}
+ });
+ break;
+ }
+
+ }
+
+ function handleAjaxError(r) {
+ // If the request was denied due to permissioning
+ if (r.status === 403) {
+ // Hide the processing div
+ $("#library_display_wrapper").find(".dt-process-rel").hide();
+ $('#library_empty_text').text($.i18n._("You don't have permission to view the library."));
+
+ $('#library_empty').show();
+ }
+ }
+
+ var selected = $("a[href$='"+location.hash+"']");
+ if (selected.parent().data("selection-id") == AIRTIME.library.MediaTypeEnum.PODCAST) {
+ $("#library_display_wrapper").hide();
+ oTable = mod.podcastDataTable.show();
+ } else {
+ oTable = mod.libraryDataTable;
+ }
+
setColumnFilter(oTable);
oTable.fnSetFilteringDelay(350);
diff --git a/airtime_mvc/public/js/airtime/library/plupload.js b/airtime_mvc/public/js/airtime/library/plupload.js
index b8f1d2a32..21a92de9f 100644
--- a/airtime_mvc/public/js/airtime/library/plupload.js
+++ b/airtime_mvc/public/js/airtime/library/plupload.js
@@ -37,90 +37,13 @@ $(document).ready(function () {
self.recentUploadsTable.fnDraw(); //Only works because we're using bServerSide
//In DataTables 1.10 and greater, we can use .fnAjaxReload()
});
- this.on("complete", function() {
+
+ this.on("queuecomplete", function() {
uploadProgress = false;
});
}
};
- /*
- var uploader = new plupload.Uploader({
- runtimes: 'html5, flash, html4',
- browse_button: 'pickfiles',
- container: $("#container"),
- url : baseUrl+'rest/media',
- filters : [
- {title: "Audio Files", extensions: "ogg,mp3,oga,flac,wav,m4a,mp4,opus,aac,oga,mp1,mp2,wma,au"}
- ],
- multipart_params : {
- "csrf_token" : $("#csrf").attr('value')
- },
-
- init: {
- PostInit: function() {
- document.getElementById('filelist').innerHTML = '';
-
- document.getElementById('uploadfiles').onclick = function() {
- uploader.start();
- return false;
- };
- },
-
- FilesAdded: function(up, files) {
- plupload.each(files, function(file) {
- document.getElementById('filelist').innerHTML += '
' + file.name + ' (' + plupload.formatSize(file.size) + ')
';
- });
- },
-
- UploadProgress: function(up, file) {
- document.getElementById(file.id).getElementsByTagName('b')[0].innerHTML = '
' + file.percent + "% ";
- },
-
- Error: function(up, err) {
- document.getElementById('console').innerHTML += "\nError #" + err.code + ": " + err.message;
- }
- }
- });
-
- uploader.init();
- */
-
-
- /*
- $("#plupload_files").pluploadQueue({
- // General settings
- runtimes : 'gears, html5, html4',
- url : baseUrl+'rest/media',
- //chunk_size : '5mb', //Disabling chunking since we're using the File Upload REST API now
- unique_names : 'true',
- multiple_queues : 'true',
- filters : [
- {title: "Audio Files", extensions: "ogg,mp3,oga,flac,wav,m4a,mp4,opus,aac,oga,mp1,mp2,wma,au"}
- ],
- multipart_params : {
- "csrf_token" : $("#csrf").attr('value'),
- }
- });
-
- uploader = $("#plupload_files").pluploadQueue();
-
- uploader.bind('FileUploaded', function(up, file, json)
- {
- //Refresh the upload table:
- self.recentUploadsTable.fnDraw(); //Only works because we're using bServerSide
- //In DataTables 1.10 and greater, we can use .fnAjaxReload()
- });
-
- var uploadProgress = false;
-
- uploader.bind('QueueChanged', function(){
- uploadProgress = (uploader.files.length > 0);
- });
-
- uploader.bind('UploadComplete', function(){
- uploadProgress = false;
- });*/
-
$(window).bind('beforeunload', function () {
if (uploadProgress) {
return sprintf($.i18n._("You are currently uploading files. %sGoing to another screen will cancel the upload process. %sAre you sure you want to leave the page?"),
@@ -173,11 +96,11 @@ $(document).ready(function () {
});
self.setupRecentUploadsTable = function () {
- recentUploadsTable = $("#recent_uploads_table").dataTable({
+ return $("#recent_uploads_table").dataTable({
"bJQueryUI": true,
"bProcessing": false,
"bServerSide": true,
- "sAjaxSource": '/Plupload/recent-uploads/format/json',
+ "sAjaxSource": '/plupload/recent-uploads/format/json',
"sAjaxDataProp": 'files',
"bSearchable": false,
"bInfo": true,
@@ -221,11 +144,13 @@ $(document).ready(function () {
areAnyFileImportsPending = true;
}
}
+
if (areAnyFileImportsPending) {
//alert("pending uploads, starting refresh on timer");
self.startRefreshingRecentUploads();
- } else {
+ } else if (self.isRecentUploadsRefreshTimerActive) {
self.stopRefreshingRecentUploads();
+ self.recentUploadsTable.fnDraw();
}
// Update usability hint - in common.js
@@ -239,7 +164,7 @@ $(document).ready(function () {
var sw = $(this)[0].scrollWidth, iw = $(this).innerWidth();
if (sw > iw) {
$(this).stop().animate({
- textIndent: "-" + (sw + 2 - iw) + "px"
+ textIndent: "-" + (sw - iw) + "px"
}, sw * 8);
}
},
@@ -251,37 +176,41 @@ $(document).ready(function () {
);
}
});
-
- return recentUploadsTable;
};
+ self.isRecentUploadsRefreshTimerActive = false;
+
self.startRefreshingRecentUploads = function () {
- if (self.isRecentUploadsRefreshTimerActive()) { //Prevent multiple timers from running
- return;
+ if (!self.isRecentUploadsRefreshTimerActive) { //Prevent multiple timers from running
+ self.recentUploadsRefreshTimer = setInterval(function() {
+ self.recentUploadsTable.fnDraw();
+ }, 3000);
+ self.isRecentUploadsRefreshTimerActive = true;
}
- self.recentUploadsRefreshTimer = setInterval("self.recentUploadsTable.fnDraw()", 3000);
- };
-
- self.isRecentUploadsRefreshTimerActive = function () {
- return (self.recentUploadsRefreshTimer != null);
};
self.stopRefreshingRecentUploads = function () {
clearInterval(self.recentUploadsRefreshTimer);
- self.recentUploadsRefreshTimer = null;
+ self.isRecentUploadsRefreshTimerActive = false;
};
$("#upload_status_all").click(function () {
- self.uploadFilter = "all";
- self.recentUploadsTable.fnPageChange(0).fnDraw();
+ if (self.uploadFilter !== "all") {
+ self.uploadFilter = "all";
+ self.recentUploadsTable.fnPageChange(0).fnDraw();
+ }
});
$("#upload_status_pending").click(function () {
- self.uploadFilter = "pending";
- self.recentUploadsTable.fnPageChange(0).fnDraw();
+ if (self.uploadFilter !== "pending") {
+ self.uploadFilter = "pending";
+ self.recentUploadsTable.fnPageChange(0).fnDraw();
+ }
});
$("#upload_status_failed").click(function () {
- self.uploadFilter = "failed";
- self.recentUploadsTable.fnPageChange(0).fnDraw();
+ if (self.uploadFilter !== "failed") {
+ self.uploadFilter = "failed";
+ self.recentUploadsTable.fnPageChange(0).fnDraw();
+ }
});
//Create the recent uploads table.
diff --git a/airtime_mvc/public/js/airtime/showbuilder/main_builder.js b/airtime_mvc/public/js/airtime/showbuilder/main_builder.js
index e114d677e..6ea091420 100644
--- a/airtime_mvc/public/js/airtime/showbuilder/main_builder.js
+++ b/airtime_mvc/public/js/airtime/showbuilder/main_builder.js
@@ -137,8 +137,24 @@ AIRTIME = (function(AIRTIME) {
});
$(window).on('hashchange', function() {
- var selected = $("a[href$='"+location.hash+"']");
- var dashboardLink = $(".media_type_selector:first");
+ var selected = $("a[href$='"+location.hash+"']"),
+ dashboardLink = $(".media_type_selector:first"),
+ t;
+
+ if (selected.parent().data("selection-id") == AIRTIME.library.MediaTypeEnum.PODCAST) {
+ $("#library_display_wrapper").hide();
+ $("#podcast_table").show();
+
+ t = AIRTIME.library.podcastDataTable;
+ } else {
+ if (typeof oTable === 'undefined') {
+ oTable = AIRTIME.library.libraryDataTable;
+ }
+
+ $("#library_display_wrapper").show();
+ $("#podcast_table").hide();
+ t = oTable;
+ }
dashboardLink.find("a").attr("href", selected.attr("href"));
AIRTIME.library.selectNone();
@@ -146,7 +162,7 @@ AIRTIME = (function(AIRTIME) {
$(this).removeClass("selected");
});
selected.parent().addClass("selected");
- oTable.fnDraw();
+ t.fnDraw();
$("#library_filter").text(selected.text());
// Highlight the dashboard link
dashboardLink.addClass("highlight");
diff --git a/python_apps/airtime-celery/airtime-celery/tasks.py b/python_apps/airtime-celery/airtime-celery/tasks.py
index 27554241a..74ce00fb3 100644
--- a/python_apps/airtime-celery/airtime-celery/tasks.py
+++ b/python_apps/airtime-celery/airtime-celery/tasks.py
@@ -1,6 +1,7 @@
import os
import json
import urllib2
+import requests
import soundcloud
from celery import Celery
from celery.utils.log import get_task_logger
@@ -33,12 +34,37 @@ def soundcloud_upload(data, token, file_path):
data['asset_data'].close()
return json.dumps(track.fields())
+
+@celery.task(name='soundcloud-download', acks_late=True)
+def soundcloud_download(token, callback_url, track_id=None):
+ """
+ This is in stasis
+
+ :param token: OAuth2 client access token
+ :param track_id: SoundCloud track identifier
+ :rtype: None
+ """
+ client = soundcloud.Client(access_token=token)
+ try:
+ tracks = client.get('/me/tracks') if track_id is None else {client.get('/tracks/%s' % track_id)}
+ for track in tracks:
+ if track.downloadable:
+ track_file = client.get(track.download_url)
+ with track_file as f:
+ requests.post(callback_url, data=f)
+ except Exception as e:
+ logger.info('Error during file download: {0}'.format(e.message))
+ logger.info(str(e))
+ raise e
+
+
@celery.task(name='soundcloud-delete', acks_late=True)
def soundcloud_delete(token, track_id):
"""
Delete a file from SoundCloud
- :param token: OAuth2 client access token
+ :param token: OAuth2 client access token
+ :param track_id: SoundCloud track identifier
:return: the SoundCloud response object
:rtype: dict
diff --git a/python_apps/airtime-celery/setup.py b/python_apps/airtime-celery/setup.py
index 1f26c373e..58f92cdf0 100644
--- a/python_apps/airtime-celery/setup.py
+++ b/python_apps/airtime-celery/setup.py
@@ -35,7 +35,7 @@ def postinst():
os.chmod('/var/log/airtime', 0775)
# Create the Celery user
- call(['adduser', '--no-create-home', '--home', '/var/lib/celery', '--gecos', '', '--disabled-login', 'celery'])
+ call(['adduser', '--no-create-home', '--gecos', '', '--disabled-login', '--firstuid', '1', '--lastuid', '999', 'celery'])
# Add celery to the www-data group
call(['usermod', '-G', 'www-data', '-a', 'celery'])