Add SoundCloud delete functionality and fixes; implement TaskManager to run background jobs
This commit is contained in:
parent
706d7db2b2
commit
3902c8c746
|
@ -27,6 +27,7 @@ require_once "ProvisioningHelper.php";
|
||||||
require_once "GoogleAnalytics.php";
|
require_once "GoogleAnalytics.php";
|
||||||
require_once "Timezone.php";
|
require_once "Timezone.php";
|
||||||
require_once "Auth.php";
|
require_once "Auth.php";
|
||||||
|
require_once "TaskManager.php";
|
||||||
require_once __DIR__.'/services/SoundcloudService.php';
|
require_once __DIR__.'/services/SoundcloudService.php';
|
||||||
require_once __DIR__.'/forms/helpers/ValidationTypes.php';
|
require_once __DIR__.'/forms/helpers/ValidationTypes.php';
|
||||||
require_once __DIR__.'/forms/helpers/CustomDecorators.php';
|
require_once __DIR__.'/forms/helpers/CustomDecorators.php';
|
||||||
|
@ -123,15 +124,12 @@ class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
|
||||||
$view->headScript()->appendScript("var COMPANY_NAME = '" . COMPANY_NAME . "';");
|
$view->headScript()->appendScript("var COMPANY_NAME = '" . COMPANY_NAME . "';");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function _initUpgrade() {
|
protected function _initTasks() {
|
||||||
/* We need to wrap this here so that we aren't checking when we're running the unit test suite
|
/* We need to wrap this here so that we aren't checking when we're running the unit test suite
|
||||||
*/
|
*/
|
||||||
if (getenv("AIRTIME_UNIT_TEST") != 1) {
|
if (getenv("AIRTIME_UNIT_TEST") != 1) {
|
||||||
//This will do the upgrade too if it's needed...
|
//This will do the upgrade too if it's needed...
|
||||||
if (UpgradeManager::checkIfUpgradeIsNeeded()) {
|
TaskManager::getInstance()->runTasks();
|
||||||
$upgradeManager = new UpgradeManager();
|
|
||||||
$upgradeManager->doUpgrade();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,160 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class TaskManager
|
||||||
|
*/
|
||||||
|
final class TaskManager {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array tasks to be run
|
||||||
|
*/
|
||||||
|
protected $_taskList = [
|
||||||
|
AirtimeTask::SOUNDCLOUD,
|
||||||
|
AirtimeTask::UPGRADE
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var TaskManager singleton instance object
|
||||||
|
*/
|
||||||
|
protected static $_instance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private constructor so class is uninstantiable
|
||||||
|
*/
|
||||||
|
private function __construct() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the singleton instance of this class
|
||||||
|
*
|
||||||
|
* @return TaskManager the TaskManager instance
|
||||||
|
*/
|
||||||
|
public static function getInstance() {
|
||||||
|
if (!self::$_instance) {
|
||||||
|
self::$_instance = new TaskManager();
|
||||||
|
}
|
||||||
|
return self::$_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run all tasks that need to be run
|
||||||
|
*/
|
||||||
|
public function runTasks() {
|
||||||
|
foreach ($this->_taskList as $task) {
|
||||||
|
$task = TaskFactory::getTask($task);
|
||||||
|
assert(is_subclass_of($task, 'AirtimeTask')); // Sanity check
|
||||||
|
/** @var $task AirtimeTask */
|
||||||
|
if ($task && $task->shouldBeRun()) $task->run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface AirtimeTask Interface for task operations - also acts as task type ENUM
|
||||||
|
*/
|
||||||
|
interface AirtimeTask {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PHP doesn't have ENUMs so declare them as interface constants
|
||||||
|
* Task types - values don't really matter as long as they're unique
|
||||||
|
*/
|
||||||
|
|
||||||
|
const SOUNDCLOUD = "soundcloud";
|
||||||
|
const UPGRADE = "upgrade";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the task should be run
|
||||||
|
*
|
||||||
|
* @return bool true if the task needs to be run, otherwise false
|
||||||
|
*/
|
||||||
|
public function shouldBeRun();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the task
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function run();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class TaskFactory Factory class to abstract task instantiation
|
||||||
|
*/
|
||||||
|
class TaskFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an AirtimeTask based on a task type
|
||||||
|
*
|
||||||
|
* @param $task string the task type; uses AirtimeTask constants as an ENUM
|
||||||
|
*
|
||||||
|
* @return AirtimeTask|null return a task of the given type or null if no corresponding
|
||||||
|
* task exists or is implemented
|
||||||
|
*/
|
||||||
|
public static function getTask($task) {
|
||||||
|
switch($task) {
|
||||||
|
case AirtimeTask::SOUNDCLOUD:
|
||||||
|
return new SoundcloudUploadTask();
|
||||||
|
case AirtimeTask::UPGRADE:
|
||||||
|
return new UpgradeTask();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class UpgradeTask
|
||||||
|
*/
|
||||||
|
class UpgradeTask implements AirtimeTask {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the current Airtime schema version to see if an upgrade should be run
|
||||||
|
*
|
||||||
|
* @return bool true if an upgrade is needed
|
||||||
|
*/
|
||||||
|
public function shouldBeRun() {
|
||||||
|
return UpgradeManager::checkIfUpgradeIsNeeded();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run all upgrades above the current schema version
|
||||||
|
*/
|
||||||
|
public function run() {
|
||||||
|
UpgradeManager::doUpgrade();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class SoundcloudUploadTask
|
||||||
|
*/
|
||||||
|
class SoundcloudUploadTask implements AirtimeTask {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var SoundcloudService
|
||||||
|
*/
|
||||||
|
protected $_service;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->_service = new SoundcloudService();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the ThirdPartyTrackReferences table to see if there are any pending SoundCloud tasks
|
||||||
|
*
|
||||||
|
* @return bool true if there are pending tasks in ThirdPartyTrackReferences
|
||||||
|
*/
|
||||||
|
public function shouldBeRun() {
|
||||||
|
return !$this->_service->isBrokerTaskQueueEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Poll the task queue for any completed Celery tasks
|
||||||
|
*/
|
||||||
|
public function run() {
|
||||||
|
$this->_service->pollBrokerTaskQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -83,9 +83,20 @@ define('UI_BLOCK_SESSNAME', 'BLOCK');*/
|
||||||
|
|
||||||
|
|
||||||
// Soundcloud contants
|
// Soundcloud contants
|
||||||
define('SOUNDCLOUD_NOT_UPLOADED_YET' , -1);
|
/**
|
||||||
define('SOUNDCLOUD_PROGRESS' , -2);
|
* @var string status string for pending Celery tasks
|
||||||
define('SOUNDCLOUD_ERROR' , -3);
|
*/
|
||||||
|
define('CELERY_PENDING_STATUS', 'PENDING');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string status string for successful Celery tasks
|
||||||
|
*/
|
||||||
|
define('CELERY_SUCCESS_STATUS', 'SUCCESS');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string status string for failed Celery tasks
|
||||||
|
*/
|
||||||
|
define('CELERY_FAILED_STATUS', 'FAILED');
|
||||||
|
|
||||||
|
|
||||||
//WHMCS integration
|
//WHMCS integration
|
||||||
|
|
|
@ -74,7 +74,6 @@ class ErrorController extends Zend_Controller_Action {
|
||||||
* 404 error - route or controller
|
* 404 error - route or controller
|
||||||
*/
|
*/
|
||||||
public function error404Action() {
|
public function error404Action() {
|
||||||
Logging::info("404!");
|
|
||||||
$this->_helper->viewRenderer('error-404');
|
$this->_helper->viewRenderer('error-404');
|
||||||
$this->getResponse()->setHttpResponseCode(404);
|
$this->getResponse()->setHttpResponseCode(404);
|
||||||
$this->view->message = _('Page not found.');
|
$this->view->message = _('Page not found.');
|
||||||
|
|
|
@ -277,13 +277,11 @@ class LibraryController extends Zend_Controller_Action
|
||||||
|
|
||||||
$serviceId = $soundcloudService->getServiceId($id);
|
$serviceId = $soundcloudService->getServiceId($id);
|
||||||
if (!is_null($file) && $serviceId != 0) {
|
if (!is_null($file) && $serviceId != 0) {
|
||||||
$menu["soundcloud"]["items"]["view"] = array("name" => _("View on Soundcloud"), "icon" => "soundcloud", "url" => $baseUrl."soundcloud/view-on-sound-cloud/id/{$id}");
|
$menu["soundcloud"]["items"]["view"] = array("name" => _("View track"), "icon" => "soundcloud", "url" => $baseUrl."soundcloud/view-on-sound-cloud/id/{$id}");
|
||||||
$text = _("Re-upload to SoundCloud");
|
$menu["soundcloud"]["items"]["upload"] = array("name" => _("Remove track"), "icon" => "soundcloud", "url" => $baseUrl."soundcloud/delete/id/{$id}");
|
||||||
} else {
|
} else {
|
||||||
$text = _("Upload to SoundCloud");
|
$menu["soundcloud"]["items"]["upload"] = array("name" => _("Upload track"), "icon" => "soundcloud", "url" => $baseUrl."soundcloud/upload/id/{$id}");
|
||||||
}
|
}
|
||||||
|
|
||||||
$menu["soundcloud"]["items"]["upload"] = array("name" => $text, "icon" => "soundcloud", "url" => $baseUrl."soundcloud/upload/id/{$id}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($menu)) {
|
if (empty($menu)) {
|
||||||
|
|
|
@ -33,13 +33,8 @@ class SoundcloudController extends ThirdPartyController {
|
||||||
$soundcloudLink = $this->_service->getLinkToFile($id);
|
$soundcloudLink = $this->_service->getLinkToFile($id);
|
||||||
header('Location: ' . $soundcloudLink);
|
header('Location: ' . $soundcloudLink);
|
||||||
} catch (Soundcloud\Exception\InvalidHttpResponseCodeException $e) {
|
} catch (Soundcloud\Exception\InvalidHttpResponseCodeException $e) {
|
||||||
// If we end up here it means the track was removed from SoundCloud
|
|
||||||
// or the foreign id in our database is incorrect, so we should just
|
|
||||||
// get rid of the database record
|
|
||||||
Logging::warn("Error retrieving track data from SoundCloud: " . $e->getMessage());
|
|
||||||
$this->_service->removeTrackReference($id);
|
|
||||||
// Redirect to a 404 so the user knows something went wrong
|
// Redirect to a 404 so the user knows something went wrong
|
||||||
header('Location: ' . $this->_baseUrl . 'error/error-404'); // Redirect back to the Preference page
|
header('Location: ' . $this->_baseUrl . 'error/error-404');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,17 @@ abstract class ThirdPartyController extends Zend_Controller_Action {
|
||||||
header('Location: ' . $auth_url);
|
header('Location: ' . $auth_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the previously saved request token from the preferences
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function deauthorizeAction() {
|
||||||
|
$function = $this->_SERVICE_TOKEN_ACCESSOR;
|
||||||
|
Application_Model_Preference::$function("");
|
||||||
|
header('Location: ' . $this->_baseUrl . 'Preference'); // Redirect back to the Preference page
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when user successfully completes third-party authorization
|
* Called when user successfully completes third-party authorization
|
||||||
* Store the returned request token for future requests
|
* Store the returned request token for future requests
|
||||||
|
@ -67,25 +78,16 @@ abstract class ThirdPartyController extends Zend_Controller_Action {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear the previously saved request token from the preferences
|
* Delete the file with the given id from a third-party service
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
|
||||||
public function deauthorizeAction() {
|
|
||||||
Application_Model_Preference::$this->_SERVICE_TOKEN_ACCESSOR("");
|
|
||||||
header('Location: ' . $this->_baseUrl . 'Preference'); // Redirect back to the Preference page
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Poll the task queue for completed tasks associated with this service
|
|
||||||
* Optionally accepts a specific task name as a parameter
|
|
||||||
*
|
*
|
||||||
* @return void
|
* @throws Zend_Controller_Response_Exception thrown if deletion fails for any reason
|
||||||
*/
|
*/
|
||||||
public function pollBrokerTaskQueueAction() {
|
public function deleteAction() {
|
||||||
$request = $this->getRequest();
|
$request = $this->getRequest();
|
||||||
$taskName = $request->getParam('task');
|
$id = $request->getParam('id');
|
||||||
$this->_service->pollBrokerTaskQueue($taskName);
|
$this->_service->delete($id);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -14,8 +14,7 @@ class UpgradeController extends Zend_Controller_Action
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$upgradeManager = new UpgradeManager();
|
$didWePerformAnUpgrade = UpgradeManager::doUpgrade();
|
||||||
$didWePerformAnUpgrade = $upgradeManager->doUpgrade();
|
|
||||||
|
|
||||||
if (!$didWePerformAnUpgrade) {
|
if (!$didWePerformAnUpgrade) {
|
||||||
$this->getResponse()
|
$this->getResponse()
|
||||||
|
|
|
@ -9,12 +9,15 @@ class Application_Model_RabbitMq
|
||||||
/**
|
/**
|
||||||
* @var int milliseconds (for compatibility with celery) until we consider a message to have timed out
|
* @var int milliseconds (for compatibility with celery) until we consider a message to have timed out
|
||||||
*/
|
*/
|
||||||
public static $_CELERY_MESSAGE_TIMEOUT = 300000; // 5 minutes
|
public static $_CELERY_MESSAGE_TIMEOUT = 600000; // 10 minutes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* We have to use celeryresults (the default results exchange) because php-celery doesn't support
|
||||||
|
* named results exchanges.
|
||||||
|
*
|
||||||
* @var string exchange for celery task results
|
* @var string exchange for celery task results
|
||||||
*/
|
*/
|
||||||
public static $_CELERY_RESULTS_EXCHANGE = 'airtime-results';
|
public static $_CELERY_RESULTS_EXCHANGE = 'celeryresults';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a flag to push the schedule at the end of the request.
|
* Sets a flag to push the schedule at the end of the request.
|
||||||
|
@ -90,7 +93,7 @@ class Application_Model_RabbitMq
|
||||||
* @throws CeleryException when no message is found
|
* @throws CeleryException when no message is found
|
||||||
*/
|
*/
|
||||||
public static function sendCeleryMessage($task, $exchange, $data) {
|
public static function sendCeleryMessage($task, $exchange, $data) {
|
||||||
$config = parse_ini_file($this->_getRmqConfigPath(), true);
|
$config = parse_ini_file(self::_getRmqConfigPath(), true);
|
||||||
$queue = $routingKey = $exchange;
|
$queue = $routingKey = $exchange;
|
||||||
$c = self::_setupCeleryExchange($config, $exchange, $queue); // Use the exchange name for the queue
|
$c = self::_setupCeleryExchange($config, $exchange, $queue); // Use the exchange name for the queue
|
||||||
$result = $c->PostTask($task, $data, true, $routingKey); // and routing key
|
$result = $c->PostTask($task, $data, true, $routingKey); // and routing key
|
||||||
|
@ -109,8 +112,8 @@ class Application_Model_RabbitMq
|
||||||
* @throws CeleryException when no message is found
|
* @throws CeleryException when no message is found
|
||||||
*/
|
*/
|
||||||
public static function getAsyncResultMessage($task, $id) {
|
public static function getAsyncResultMessage($task, $id) {
|
||||||
$config = parse_ini_file($this->_getRmqConfigPath(), true);
|
$config = parse_ini_file(self::_getRmqConfigPath(), true);
|
||||||
$queue = self::$_CELERY_RESULTS_EXCHANGE . "." . $config["stationId"];
|
$queue = self::$_CELERY_RESULTS_EXCHANGE . "." . $task;
|
||||||
$c = self::_setupCeleryExchange($config, self::$_CELERY_RESULTS_EXCHANGE, $queue);
|
$c = self::_setupCeleryExchange($config, self::$_CELERY_RESULTS_EXCHANGE, $queue);
|
||||||
$message = $c->getAsyncResultMessage($task, $id);
|
$message = $c->getAsyncResultMessage($task, $id);
|
||||||
|
|
||||||
|
@ -157,7 +160,7 @@ class Application_Model_RabbitMq
|
||||||
self::sendMessage($exchange, 'direct', true, $data);
|
self::sendMessage($exchange, 'direct', true, $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function _getRmqConfigPath() {
|
private static function _getRmqConfigPath() {
|
||||||
//Hack for Airtime Pro. The RabbitMQ settings for communicating with airtime_analyzer are global
|
//Hack for Airtime Pro. The RabbitMQ settings for communicating with airtime_analyzer are global
|
||||||
//and shared between all instances on Airtime Pro.
|
//and shared between all instances on Airtime Pro.
|
||||||
$CC_CONFIG = Config::getConfig();
|
$CC_CONFIG = Config::getConfig();
|
||||||
|
@ -177,7 +180,7 @@ class Application_Model_RabbitMq
|
||||||
public static function SendMessageToAnalyzer($tmpFilePath, $importedStorageDirectory, $originalFilename,
|
public static function SendMessageToAnalyzer($tmpFilePath, $importedStorageDirectory, $originalFilename,
|
||||||
$callbackUrl, $apiKey, $storageBackend, $filePrefix)
|
$callbackUrl, $apiKey, $storageBackend, $filePrefix)
|
||||||
{
|
{
|
||||||
$config = parse_ini_file($this->_getRmqConfigPath(), true);
|
$config = parse_ini_file(self::_getRmqConfigPath(), true);
|
||||||
$conn = new AMQPConnection($config["rabbitmq"]["host"],
|
$conn = new AMQPConnection($config["rabbitmq"]["host"],
|
||||||
$config["rabbitmq"]["port"],
|
$config["rabbitmq"]["port"],
|
||||||
$config["rabbitmq"]["user"],
|
$config["rabbitmq"]["user"],
|
||||||
|
|
|
@ -17,28 +17,28 @@ class SoundcloudService extends ThirdPartyService {
|
||||||
/**
|
/**
|
||||||
* @var string service name to store in ThirdPartyTrackReferences database
|
* @var string service name to store in ThirdPartyTrackReferences database
|
||||||
*/
|
*/
|
||||||
protected $_SERVICE_NAME = 'SoundCloud';
|
protected static $_SERVICE_NAME = 'SoundCloud';
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string base URI for SoundCloud tracks
|
|
||||||
*/
|
|
||||||
protected $_THIRD_PARTY_TRACK_URI = 'http://api.soundcloud.com/tracks/';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string exchange name for SoundCloud tasks
|
* @var string exchange name for SoundCloud tasks
|
||||||
*/
|
*/
|
||||||
protected $_CELERY_EXCHANGE_NAME = 'soundcloud-uploads';
|
protected static $_CELERY_EXCHANGE_NAME = 'soundcloud';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string celery task name for third party uploads
|
* @var string celery task name for third party uploads
|
||||||
*/
|
*/
|
||||||
protected $_CELERY_UPLOAD_TASK_NAME = 'upload-to-soundcloud';
|
protected static $_CELERY_UPLOAD_TASK_NAME = 'soundcloud-upload';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string celery task name for third party deletions
|
||||||
|
*/
|
||||||
|
protected static $_CELERY_DELETE_TASK_NAME = 'soundcloud-delete';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array Application_Model_Preference functions for SoundCloud and their
|
* @var array Application_Model_Preference functions for SoundCloud and their
|
||||||
* associated API parameter keys so that we can call them dynamically
|
* associated API parameter keys so that we can call them dynamically
|
||||||
*/
|
*/
|
||||||
private $_SOUNDCLOUD_PREF_FUNCTIONS = array(
|
private static $_SOUNDCLOUD_PREF_FUNCTIONS = array(
|
||||||
"getDefaultSoundCloudLicenseType" => "license",
|
"getDefaultSoundCloudLicenseType" => "license",
|
||||||
"getDefaultSoundCloudSharingType" => "sharing"
|
"getDefaultSoundCloudSharingType" => "sharing"
|
||||||
);
|
);
|
||||||
|
@ -71,7 +71,7 @@ class SoundcloudService extends ThirdPartyService {
|
||||||
$trackArray = array(
|
$trackArray = array(
|
||||||
'title' => $file->getName(),
|
'title' => $file->getName(),
|
||||||
);
|
);
|
||||||
foreach ($this->_SOUNDCLOUD_PREF_FUNCTIONS as $func => $param) {
|
foreach (self::$_SOUNDCLOUD_PREF_FUNCTIONS as $func => $param) {
|
||||||
$val = Application_Model_Preference::$func();
|
$val = Application_Model_Preference::$func();
|
||||||
if (!empty($val)) {
|
if (!empty($val)) {
|
||||||
$trackArray[$param] = $val;
|
$trackArray[$param] = $val;
|
||||||
|
@ -84,6 +84,7 @@ class SoundcloudService extends ThirdPartyService {
|
||||||
/**
|
/**
|
||||||
* Update a ThirdPartyTrackReferences object for a completed upload
|
* Update a ThirdPartyTrackReferences object for a completed upload
|
||||||
* TODO: should we have a database layer class to handle Propel operations?
|
* TODO: should we have a database layer class to handle Propel operations?
|
||||||
|
* TODO: break this function up, it's a bit of a beast
|
||||||
*
|
*
|
||||||
* @param $fileId int local CcFiles identifier
|
* @param $fileId int local CcFiles identifier
|
||||||
* @param $track object third-party service track object
|
* @param $track object third-party service track object
|
||||||
|
@ -94,14 +95,18 @@ class SoundcloudService extends ThirdPartyService {
|
||||||
*/
|
*/
|
||||||
protected function _addOrUpdateTrackReference($fileId, $track, $status) {
|
protected function _addOrUpdateTrackReference($fileId, $track, $status) {
|
||||||
$ref = ThirdPartyTrackReferencesQuery::create()
|
$ref = ThirdPartyTrackReferencesQuery::create()
|
||||||
->filterByDbService($this->_SERVICE_NAME)
|
->filterByDbService(static::$_SERVICE_NAME)
|
||||||
->findOneByDbFileId($fileId);
|
->findOneByDbFileId($fileId);
|
||||||
if (is_null($ref)) {
|
if (is_null($ref)) {
|
||||||
$ref = new ThirdPartyTrackReferences();
|
$ref = new ThirdPartyTrackReferences();
|
||||||
|
} // If this was a delete task, just remove the record and return
|
||||||
|
else if ($ref->getDbBrokerTaskName() == static::$_CELERY_DELETE_TASK_NAME) {
|
||||||
|
$ref->delete();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
$ref->setDbService($this->_SERVICE_NAME);
|
$ref->setDbService(static::$_SERVICE_NAME);
|
||||||
// Only set the SoundCloud fields if the task was successful
|
// Only set the SoundCloud fields if the task was successful
|
||||||
if ($status == $this->_SUCCESS_STATUS) {
|
if ($status == CELERY_SUCCESS_STATUS) {
|
||||||
// TODO: fetch any additional SoundCloud parameters we want to store
|
// TODO: fetch any additional SoundCloud parameters we want to store
|
||||||
$ref->setDbForeignId($track->id); // SoundCloud identifier
|
$ref->setDbForeignId($track->id); // SoundCloud identifier
|
||||||
}
|
}
|
||||||
|
@ -124,12 +129,23 @@ class SoundcloudService extends ThirdPartyService {
|
||||||
* @param int $fileId the local CcFiles identifier
|
* @param int $fileId the local CcFiles identifier
|
||||||
*
|
*
|
||||||
* @return string the link to the remote file
|
* @return string the link to the remote file
|
||||||
|
*
|
||||||
|
* @throws Soundcloud\Exception\InvalidHttpResponseCodeException when SoundCloud returns a 4xx/5xx response
|
||||||
*/
|
*/
|
||||||
public function getLinkToFile($fileId) {
|
public function getLinkToFile($fileId) {
|
||||||
$serviceId = $this->getServiceId($fileId);
|
$serviceId = $this->getServiceId($fileId);
|
||||||
// If we don't find a record for the file we'll get 0 back for the id
|
// If we don't find a record for the file we'll get 0 back for the id
|
||||||
if ($serviceId == 0) { return ''; }
|
if ($serviceId == 0) { return ''; }
|
||||||
$track = json_decode($this->_client->get('tracks/' . $serviceId));
|
try {
|
||||||
|
$track = json_decode($this->_client->get('tracks/' . $serviceId));
|
||||||
|
} catch (Soundcloud\Exception\InvalidHttpResponseCodeException $e) {
|
||||||
|
// If we end up here it means the track was removed from SoundCloud
|
||||||
|
// or the foreign id in our database is incorrect, so we should just
|
||||||
|
// get rid of the database record
|
||||||
|
Logging::warn("Error retrieving track data from SoundCloud: " . $e->getMessage());
|
||||||
|
$this->removeTrackReference($fileId);
|
||||||
|
throw $e; // Throw the exception up to the controller so we can redirect to a 404
|
||||||
|
}
|
||||||
return $track->permalink_url;
|
return $track->permalink_url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +168,7 @@ class SoundcloudService extends ThirdPartyService {
|
||||||
// in the redirect. This allows us to create a singular script to redirect
|
// in the redirect. This allows us to create a singular script to redirect
|
||||||
// back to any station the request comes from.
|
// back to any station the request comes from.
|
||||||
$url = urlencode('http'.(empty($_SERVER['HTTPS'])?'':'s').'://'.$_SERVER['HTTP_HOST'].'/soundcloud/redirect');
|
$url = urlencode('http'.(empty($_SERVER['HTTPS'])?'':'s').'://'.$_SERVER['HTTP_HOST'].'/soundcloud/redirect');
|
||||||
return $this->_client->getAuthorizeUrl(array("state" => $url));
|
return $this->_client->getAuthorizeUrl(array("state" => $url, "scope" => "non-expiring"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -162,7 +178,7 @@ class SoundcloudService extends ThirdPartyService {
|
||||||
*/
|
*/
|
||||||
public function requestNewAccessToken($code) {
|
public function requestNewAccessToken($code) {
|
||||||
// Get a non-expiring access token
|
// Get a non-expiring access token
|
||||||
$response = $this->_client->accessToken($code, $postData = array('scope' => 'non-expiring'));
|
$response = $this->_client->accessToken($code);
|
||||||
$accessToken = $response['access_token'];
|
$accessToken = $response['access_token'];
|
||||||
Application_Model_Preference::setSoundCloudRequestToken($accessToken);
|
Application_Model_Preference::setSoundCloudRequestToken($accessToken);
|
||||||
$this->_accessToken = $accessToken;
|
$this->_accessToken = $accessToken;
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ServiceNotFoundException
|
||||||
|
*/
|
||||||
|
class ServiceNotFoundException extends Exception {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class ThirdPartyService generic superclass for third-party services
|
* Class ThirdPartyService generic superclass for third-party services
|
||||||
|
* TODO: decouple the media/track-specific functions into ThirdPartyMediaService class?
|
||||||
*/
|
*/
|
||||||
abstract class ThirdPartyService {
|
abstract class ThirdPartyService {
|
||||||
|
|
||||||
|
@ -13,44 +19,32 @@ abstract class ThirdPartyService {
|
||||||
/**
|
/**
|
||||||
* @var string service name to store in ThirdPartyTrackReferences database
|
* @var string service name to store in ThirdPartyTrackReferences database
|
||||||
*/
|
*/
|
||||||
protected $_SERVICE_NAME;
|
protected static $_SERVICE_NAME;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string base URI for third-party tracks
|
* @var string base URI for third-party tracks
|
||||||
*/
|
*/
|
||||||
protected $_THIRD_PARTY_TRACK_URI;
|
protected static $_THIRD_PARTY_TRACK_URI;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string broker exchange name for third party tasks
|
* @var string broker exchange name for third party tasks
|
||||||
*/
|
*/
|
||||||
protected $_CELERY_EXCHANGE_NAME = 'default';
|
protected static $_CELERY_EXCHANGE_NAME;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string celery task name for third party uploads
|
* @var string celery task name for third party uploads
|
||||||
*/
|
*/
|
||||||
protected $_CELERY_UPLOAD_TASK_NAME = 'upload';
|
protected static $_CELERY_UPLOAD_TASK_NAME;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string status string for pending tasks
|
* @var string celery task name for third party deletion
|
||||||
*/
|
*/
|
||||||
protected $_PENDING_STATUS = 'PENDING';
|
protected static $_CELERY_DELETE_TASK_NAME;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string status string for successful tasks
|
|
||||||
*/
|
|
||||||
protected $_SUCCESS_STATUS = 'SUCCESS';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string status string for failed tasks
|
|
||||||
*/
|
|
||||||
protected $_FAILED_STATUS = 'FAILED';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upload the file with the given identifier to a third-party service
|
* Upload the file with the given identifier to a third-party service
|
||||||
*
|
*
|
||||||
* @param int $fileId the local CcFiles identifier
|
* @param int $fileId the local CcFiles identifier
|
||||||
*
|
|
||||||
* @throws Exception thrown when the upload fails for any reason
|
|
||||||
*/
|
*/
|
||||||
public function upload($fileId) {
|
public function upload($fileId) {
|
||||||
$file = Application_Model_StoredFile::RecallById($fileId);
|
$file = Application_Model_StoredFile::RecallById($fileId);
|
||||||
|
@ -60,11 +54,40 @@ abstract class ThirdPartyService {
|
||||||
'file_path' => $file->getFilePaths()[0]
|
'file_path' => $file->getFilePaths()[0]
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
$brokerTaskId = Application_Model_RabbitMq::sendCeleryMessage($this->_CELERY_UPLOAD_TASK_NAME,
|
$brokerTaskId = Application_Model_RabbitMq::sendCeleryMessage(static::$_CELERY_UPLOAD_TASK_NAME,
|
||||||
$this->_CELERY_EXCHANGE_NAME,
|
static::$_CELERY_EXCHANGE_NAME,
|
||||||
$data);
|
$data);
|
||||||
$this->_createTaskReference($fileId, $brokerTaskId, $this->_CELERY_UPLOAD_TASK_NAME);
|
$this->_createTaskReference($fileId, $brokerTaskId, static::$_CELERY_UPLOAD_TASK_NAME);
|
||||||
} catch(Exception $e) {
|
} catch (Exception $e) {
|
||||||
|
Logging::info("Invalid request: " . $e->getMessage());
|
||||||
|
// We should only get here if we have an access token, so attempt to refresh
|
||||||
|
$this->accessTokenRefresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the file with the given identifier from a third-party service
|
||||||
|
*
|
||||||
|
* @param int $fileId the local CcFiles identifier
|
||||||
|
*
|
||||||
|
* @throws ServiceNotFoundException when a $fileId with no corresponding
|
||||||
|
* service identifier is given
|
||||||
|
*/
|
||||||
|
public function delete($fileId) {
|
||||||
|
$serviceId = $this->getServiceId($fileId);
|
||||||
|
if ($serviceId == 0) {
|
||||||
|
throw new ServiceNotFoundException("No service found for file with ID $fileId");
|
||||||
|
}
|
||||||
|
$data = array(
|
||||||
|
'token' => $this->_accessToken,
|
||||||
|
'track_id' => $serviceId
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
$brokerTaskId = Application_Model_RabbitMq::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());
|
Logging::info("Invalid request: " . $e->getMessage());
|
||||||
// We should only get here if we have an access token, so attempt to refresh
|
// We should only get here if we have an access token, so attempt to refresh
|
||||||
$this->accessTokenRefresh();
|
$this->accessTokenRefresh();
|
||||||
|
@ -86,18 +109,18 @@ abstract class ThirdPartyService {
|
||||||
protected function _createTaskReference($fileId, $brokerTaskId, $taskName) {
|
protected function _createTaskReference($fileId, $brokerTaskId, $taskName) {
|
||||||
// First, check if the track already has an entry in the database
|
// First, check if the track already has an entry in the database
|
||||||
$ref = ThirdPartyTrackReferencesQuery::create()
|
$ref = ThirdPartyTrackReferencesQuery::create()
|
||||||
->filterByDbService($this->_SERVICE_NAME)
|
->filterByDbService(static::$_SERVICE_NAME)
|
||||||
->findOneByDbFileId($fileId);
|
->findOneByDbFileId($fileId);
|
||||||
if (is_null($ref)) {
|
if (is_null($ref)) {
|
||||||
$ref = new ThirdPartyTrackReferences();
|
$ref = new ThirdPartyTrackReferences();
|
||||||
}
|
}
|
||||||
$ref->setDbService($this->_SERVICE_NAME);
|
$ref->setDbService(static::$_SERVICE_NAME);
|
||||||
$ref->setDbBrokerTaskId($brokerTaskId);
|
$ref->setDbBrokerTaskId($brokerTaskId);
|
||||||
$ref->setDbBrokerTaskName($taskName);
|
$ref->setDbBrokerTaskName($taskName);
|
||||||
$utc = new DateTimeZone("UTC");
|
$utc = new DateTimeZone("UTC");
|
||||||
$ref->setDbBrokerTaskDispatchTime(new DateTime("now", $utc));
|
$ref->setDbBrokerTaskDispatchTime(new DateTime("now", $utc));
|
||||||
$ref->setDbFileId($fileId);
|
$ref->setDbFileId($fileId);
|
||||||
$ref->setDbStatus($this->_PENDING_STATUS);
|
$ref->setDbStatus(CELERY_PENDING_STATUS);
|
||||||
$ref->save();
|
$ref->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +136,7 @@ abstract class ThirdPartyService {
|
||||||
*/
|
*/
|
||||||
public function removeTrackReference($fileId) {
|
public function removeTrackReference($fileId) {
|
||||||
$ref = ThirdPartyTrackReferencesQuery::create()
|
$ref = ThirdPartyTrackReferencesQuery::create()
|
||||||
->filterByDbService($this->_SERVICE_NAME)
|
->filterByDbService(static::$_SERVICE_NAME)
|
||||||
->findOneByDbFileId($fileId);
|
->findOneByDbFileId($fileId);
|
||||||
$ref->delete();
|
$ref->delete();
|
||||||
}
|
}
|
||||||
|
@ -128,9 +151,9 @@ abstract class ThirdPartyService {
|
||||||
*/
|
*/
|
||||||
public function getServiceId($fileId) {
|
public function getServiceId($fileId) {
|
||||||
$ref = ThirdPartyTrackReferencesQuery::create()
|
$ref = ThirdPartyTrackReferencesQuery::create()
|
||||||
->filterByDbService($this->_SERVICE_NAME)
|
->filterByDbService(static::$_SERVICE_NAME)
|
||||||
->findOneByDbFileId($fileId); // There shouldn't be duplicates!
|
->findOneByDbFileId($fileId); // There shouldn't be duplicates!
|
||||||
return is_null($ref) ? 0 : $ref->getDbForeignId();
|
return empty($ref) ? 0 : $ref->getDbForeignId();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -143,7 +166,24 @@ abstract class ThirdPartyService {
|
||||||
*/
|
*/
|
||||||
public function getLinkToFile($fileId) {
|
public function getLinkToFile($fileId) {
|
||||||
$serviceId = $this->getServiceId($fileId);
|
$serviceId = $this->getServiceId($fileId);
|
||||||
return $serviceId > 0 ? $this->_THIRD_PARTY_TRACK_URI . $serviceId : '';
|
return $serviceId > 0 ? static::$_THIRD_PARTY_TRACK_URI . $serviceId : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check to see if there are any pending tasks for this service
|
||||||
|
*
|
||||||
|
* @param string $taskName
|
||||||
|
*
|
||||||
|
* @return bool true if there are any pending tasks, otherwise false
|
||||||
|
*/
|
||||||
|
public function isBrokerTaskQueueEmpty($taskName="") {
|
||||||
|
$query = ThirdPartyTrackReferencesQuery::create()
|
||||||
|
->filterByDbService(static::$_SERVICE_NAME);
|
||||||
|
if (!empty($taskName)) {
|
||||||
|
$query->filterByDbBrokerTaskName($taskName);
|
||||||
|
}
|
||||||
|
$result = $query->findOneByDbStatus(CELERY_PENDING_STATUS);
|
||||||
|
return empty($result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -154,18 +194,22 @@ abstract class ThirdPartyService {
|
||||||
* @param string $taskName the name of the task to poll for
|
* @param string $taskName the name of the task to poll for
|
||||||
*/
|
*/
|
||||||
public function pollBrokerTaskQueue($taskName="") {
|
public function pollBrokerTaskQueue($taskName="") {
|
||||||
$pendingTasks = $this->_getPendingTasks($taskName);
|
$pendingTasks = static::_getPendingTasks($taskName);
|
||||||
foreach ($pendingTasks as $task) {
|
foreach ($pendingTasks as $task) {
|
||||||
try {
|
try {
|
||||||
$message = $this->_getTaskMessage($task);
|
$message = static::_getTaskMessage($task);
|
||||||
$this->_addOrUpdateTrackReference($task->getDbFileId(), json_decode($message->result), $message->status);
|
static::_addOrUpdateTrackReference($task->getDbFileId(), json_decode($message->result), $message->status);
|
||||||
} catch(CeleryException $e) {
|
} catch (CeleryException $e) {
|
||||||
Logging::info("Couldn't retrieve task message for task " . $task->getDbBrokerTaskName()
|
// Fail silently unless the message has timed out; often we end up here when
|
||||||
. " with ID " . $task->getDbBrokerTaskId() . ": " . $e->getMessage());
|
// the Celery task takes a while to execute
|
||||||
if ($this->_checkMessageTimeout($task)) {
|
if (static::_checkMessageTimeout($task)) {
|
||||||
$task->setDbStatus($this->_FAILED_STATUS);
|
Logging::info($e->getMessage());
|
||||||
|
$task->setDbStatus(CELERY_FAILED_STATUS);
|
||||||
$task->save();
|
$task->save();
|
||||||
}
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// Sometimes we might catch a json_decode error and end up here
|
||||||
|
Logging::info($e->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,8 +224,8 @@ abstract class ThirdPartyService {
|
||||||
*/
|
*/
|
||||||
protected function _getPendingTasks($taskName) {
|
protected function _getPendingTasks($taskName) {
|
||||||
$query = ThirdPartyTrackReferencesQuery::create()
|
$query = ThirdPartyTrackReferencesQuery::create()
|
||||||
->filterByDbService($this->_SERVICE_NAME)
|
->filterByDbService(static::$_SERVICE_NAME)
|
||||||
->filterByDbStatus($this->_PENDING_STATUS)
|
->filterByDbStatus(CELERY_PENDING_STATUS)
|
||||||
->filterByDbBrokerTaskId('', Criteria::NOT_EQUAL);
|
->filterByDbBrokerTaskId('', Criteria::NOT_EQUAL);
|
||||||
if (!empty($taskName)) {
|
if (!empty($taskName)) {
|
||||||
$query->filterByDbBrokerTaskName($taskName);
|
$query->filterByDbBrokerTaskName($taskName);
|
||||||
|
@ -198,7 +242,7 @@ abstract class ThirdPartyService {
|
||||||
*
|
*
|
||||||
* @throws CeleryException when the result message for this task no longer exists
|
* @throws CeleryException when the result message for this task no longer exists
|
||||||
*/
|
*/
|
||||||
protected function _getTaskMessage($task) {
|
protected static function _getTaskMessage($task) {
|
||||||
$message = Application_Model_RabbitMq::getAsyncResultMessage($task->getDbBrokerTaskName(),
|
$message = Application_Model_RabbitMq::getAsyncResultMessage($task->getDbBrokerTaskName(),
|
||||||
$task->getDbBrokerTaskId());
|
$task->getDbBrokerTaskId());
|
||||||
return json_decode($message['body']);
|
return json_decode($message['body']);
|
||||||
|
@ -212,7 +256,7 @@ abstract class ThirdPartyService {
|
||||||
* @return bool true if the dispatch time is empty or it's been more than our timeout time
|
* @return bool true if the dispatch time is empty or it's been more than our timeout time
|
||||||
* since the message was dispatched, otherwise false
|
* since the message was dispatched, otherwise false
|
||||||
*/
|
*/
|
||||||
protected function _checkMessageTimeout($task) {
|
protected static function _checkMessageTimeout($task) {
|
||||||
$utc = new DateTimeZone("UTC");
|
$utc = new DateTimeZone("UTC");
|
||||||
$dispatchTime = new DateTime($task->getDbBrokerTaskDispatchTime(), $utc);
|
$dispatchTime = new DateTime($task->getDbBrokerTaskDispatchTime(), $utc);
|
||||||
$now = new DateTime("now", $utc);
|
$now = new DateTime("now", $utc);
|
||||||
|
|
|
@ -49,7 +49,7 @@ class UpgradeManager
|
||||||
*
|
*
|
||||||
* @return boolean whether or not an upgrade was performed
|
* @return boolean whether or not an upgrade was performed
|
||||||
*/
|
*/
|
||||||
public function doUpgrade()
|
public static function doUpgrade()
|
||||||
{
|
{
|
||||||
// Get all upgrades dynamically (in declaration order!) so we don't have to add them explicitly each time
|
// Get all upgrades dynamically (in declaration order!) so we don't have to add them explicitly each time
|
||||||
// TODO: explicitly sort classnames by ascending version suffix for safety
|
// TODO: explicitly sort classnames by ascending version suffix for safety
|
||||||
|
@ -58,7 +58,7 @@ class UpgradeManager
|
||||||
$upgradePerformed = false;
|
$upgradePerformed = false;
|
||||||
|
|
||||||
foreach ($upgraders as $upgrader) {
|
foreach ($upgraders as $upgrader) {
|
||||||
$upgradePerformed = $this->_runUpgrade(new $upgrader($dir)) ? true : $upgradePerformed;
|
$upgradePerformed = self::_runUpgrade(new $upgrader($dir)) ? true : $upgradePerformed;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $upgradePerformed;
|
return $upgradePerformed;
|
||||||
|
@ -71,7 +71,7 @@ class UpgradeManager
|
||||||
*
|
*
|
||||||
* @return bool true if the upgrade was successful, otherwise false
|
* @return bool true if the upgrade was successful, otherwise false
|
||||||
*/
|
*/
|
||||||
private function _runUpgrade(AirtimeUpgrader $upgrader) {
|
private static function _runUpgrade(AirtimeUpgrader $upgrader) {
|
||||||
return $upgrader->checkIfUpgradeSupported() && $upgrader->upgrade();
|
return $upgrader->checkIfUpgradeSupported() && $upgrader->upgrade();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,6 +327,22 @@ class AirtimeUpgrader2512 extends AirtimeUpgrader
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class AirtimeUpgrader2513 - Celery and SoundCloud upgrade
|
||||||
|
*
|
||||||
|
* Adds third_party_track_references table for third party service
|
||||||
|
* authentication and task architecture.
|
||||||
|
*
|
||||||
|
* Schema:
|
||||||
|
* id -> int PK
|
||||||
|
* service -> string internal service name
|
||||||
|
* foreign_id -> int external unique service id
|
||||||
|
* broker_task_id -> int external unique amqp results identifier
|
||||||
|
* broker_task_name -> string external Celery task name
|
||||||
|
* broker_task_dispatch_time -> timestamp internal message dispatch time
|
||||||
|
* file_id -> int internal FK->cc_files track id
|
||||||
|
* status -> string external Celery task status - PENDING, SUCCESS, or FAILED
|
||||||
|
*/
|
||||||
class AirtimeUpgrader2513 extends AirtimeUpgrader
|
class AirtimeUpgrader2513 extends AirtimeUpgrader
|
||||||
{
|
{
|
||||||
protected function getSupportedSchemaVersions() {
|
protected function getSupportedSchemaVersions() {
|
||||||
|
|
|
@ -9,7 +9,6 @@ $(document).ready(function() {
|
||||||
|
|
||||||
//this statement tells the browser to fade out any success message after 5 seconds
|
//this statement tells the browser to fade out any success message after 5 seconds
|
||||||
setTimeout(function(){$(".success").fadeOut("slow", function(){$(this).empty()});}, 5000);
|
setTimeout(function(){$(".success").fadeOut("slow", function(){$(this).empty()});}, 5000);
|
||||||
pollTaskQueues();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -156,9 +155,4 @@ function removeSuccessMsg() {
|
||||||
var $status = $('.success');
|
var $status = $('.success');
|
||||||
|
|
||||||
$status.fadeOut("slow", function(){$status.empty()});
|
$status.fadeOut("slow", function(){$status.empty()});
|
||||||
}
|
|
||||||
|
|
||||||
function pollTaskQueues() {
|
|
||||||
console.log("Polling broker queues...");
|
|
||||||
$.get(baseUrl + 'soundcloud/poll-broker-task-queue');
|
|
||||||
}
|
}
|
|
@ -23,23 +23,15 @@ def parse_rmq_config(rmq_config):
|
||||||
|
|
||||||
# Celery amqp settings
|
# Celery amqp settings
|
||||||
BROKER_URL = get_rmq_broker()
|
BROKER_URL = get_rmq_broker()
|
||||||
CELERY_RESULT_BACKEND = 'amqp' # Use RabbitMQ as the celery backend
|
CELERY_RESULT_BACKEND = 'amqp' # Use RabbitMQ as the celery backend
|
||||||
CELERY_RESULT_PERSISTENT = True # Persist through a broker restart
|
CELERY_RESULT_PERSISTENT = True # Persist through a broker restart
|
||||||
CELERY_TASK_RESULT_EXPIRES = 300 # Expire task results after 5 minutes
|
CELERY_TASK_RESULT_EXPIRES = 600 # Expire task results after 10 minutes
|
||||||
CELERY_TRACK_STARTED = False
|
CELERY_RESULT_EXCHANGE = 'celeryresults' # Default exchange - needed due to php-celery
|
||||||
CELERY_RESULT_EXCHANGE = 'airtime-results'
|
|
||||||
CELERY_QUEUES = (
|
CELERY_QUEUES = (
|
||||||
Queue('soundcloud-uploads', exchange=Exchange('soundcloud-uploads'), routing_key='soundcloud-uploads'),
|
Queue('soundcloud', exchange=Exchange('soundcloud'), routing_key='soundcloud'),
|
||||||
Queue('airtime-results.soundcloud-uploads', exchange=Exchange('airtime-results')),
|
Queue(exchange=Exchange('celeryresults'), auto_delete=True),
|
||||||
)
|
|
||||||
CELERY_ROUTES = (
|
|
||||||
{
|
|
||||||
'soundcloud_uploads.tasks.upload_to_soundcloud': {
|
|
||||||
'exchange': 'airtime-results',
|
|
||||||
'queue': 'airtime-results.soundcloud-uploads',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
CELERY_EVENT_QUEUE_EXPIRES = 600 # RabbitMQ x-expire after 10 minutes
|
||||||
|
|
||||||
# Celery task settings
|
# Celery task settings
|
||||||
CELERY_TASK_SERIALIZER = 'json'
|
CELERY_TASK_SERIALIZER = 'json'
|
||||||
|
|
|
@ -9,8 +9,8 @@ celery = Celery()
|
||||||
logger = get_task_logger(__name__)
|
logger = get_task_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@celery.task(name='upload-to-soundcloud')
|
@celery.task(name='soundcloud-upload')
|
||||||
def upload_to_soundcloud(data, token, file_path):
|
def soundcloud_upload(data, token, file_path):
|
||||||
"""
|
"""
|
||||||
Upload a file to SoundCloud
|
Upload a file to SoundCloud
|
||||||
|
|
||||||
|
@ -32,3 +32,22 @@ def upload_to_soundcloud(data, token, file_path):
|
||||||
raise e
|
raise e
|
||||||
data['asset_data'].close()
|
data['asset_data'].close()
|
||||||
return json.dumps(track.fields())
|
return json.dumps(track.fields())
|
||||||
|
|
||||||
|
@celery.task(name='soundcloud-delete')
|
||||||
|
def soundcloud_delete(token, track_id):
|
||||||
|
"""
|
||||||
|
Delete a file from SoundCloud
|
||||||
|
|
||||||
|
:param token: OAuth2 client access token
|
||||||
|
|
||||||
|
:return: the SoundCloud response object
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
|
client = soundcloud.Client(access_token=token)
|
||||||
|
try:
|
||||||
|
logger.info('Deleting track with ID {0}'.format(track_id))
|
||||||
|
track = client.delete('/tracks/%s' % track_id)
|
||||||
|
except Exception as e:
|
||||||
|
logger.info('Error deleting track!')
|
||||||
|
raise e
|
||||||
|
return json.dumps(track.fields())
|
||||||
|
|
Loading…
Reference in New Issue