diff --git a/airtime_mvc/application/configs/airtime-conf.php b/airtime_mvc/application/configs/airtime-conf.php index aa69b6156..e767e18b8 100644 --- a/airtime_mvc/application/configs/airtime-conf.php +++ b/airtime_mvc/application/configs/airtime-conf.php @@ -1,6 +1,6 @@ array ( @@ -12,6 +12,14 @@ $conf = array ( 'dsn' => 'pgsql:host=localhost;port=5432;dbname=airtime;user=airtime;password=airtime', ), ), + 'airtime_test' => + array ( + 'adapter' => 'pgsql', + 'connection' => + array ( + 'dsn' => 'pgsql:host=localhost;port=5432;dbname=airtime_test;user=airtime;password=airtime', + ), + ), 'default' => 'airtime', ), 'generator_version' => '1.5.2', diff --git a/airtime_mvc/application/configs/application.ini b/airtime_mvc/application/configs/application.ini index c342ebda7..71bcd5c46 100644 --- a/airtime_mvc/application/configs/application.ini +++ b/airtime_mvc/application/configs/application.ini @@ -6,6 +6,10 @@ bootstrap.class = "Bootstrap" appnamespace = "Application" resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers" resources.frontController.params.displayExceptions = 0 +resources.frontController.moduleDirectory = APPLICATION_PATH "/modules" +resources.frontController.plugins.putHandler = "Zend_Controller_Plugin_PutHandler" +;load everything in the modules directory including models +resources.modules[] = "" resources.layout.layoutPath = APPLICATION_PATH "/layouts/scripts/" resources.view[] = resources.db.adapter = "Pdo_Pgsql" diff --git a/airtime_mvc/application/controllers/LoginController.php b/airtime_mvc/application/controllers/LoginController.php index 4c58a6b57..ee4ced5e4 100644 --- a/airtime_mvc/application/controllers/LoginController.php +++ b/airtime_mvc/application/controllers/LoginController.php @@ -65,7 +65,7 @@ class LoginController extends Zend_Controller_Action Application_Model_LoginAttempts::resetAttempts($_SERVER['REMOTE_ADDR']); Application_Model_Subjects::resetLoginAttempts($username); - + $tempSess = new Zend_Session_Namespace("referrer"); $tempSess->referrer = 'login'; diff --git a/airtime_mvc/application/controllers/PluploadController.php b/airtime_mvc/application/controllers/PluploadController.php index 9698b163a..c919207b4 100644 --- a/airtime_mvc/application/controllers/PluploadController.php +++ b/airtime_mvc/application/controllers/PluploadController.php @@ -2,12 +2,11 @@ class PluploadController extends Zend_Controller_Action { - public function init() { $ajaxContext = $this->_helper->getHelper('AjaxContext'); - $ajaxContext->addActionContext('upload', 'json') - ->addActionContext('copyfile', 'json') + $ajaxContext->addActionContext('upload', 'json') + ->addActionContext('recent-uploads', 'json') ->initContext(); } @@ -18,12 +17,14 @@ class PluploadController extends Zend_Controller_Action $baseUrl = Application_Common_OsPath::getBaseDir(); $locale = Application_Model_Preference::GetLocale(); + $this->view->headScript()->appendFile($baseUrl.'js/datatables/js/jquery.dataTables.js?'.$CC_CONFIG['airtime_version'], 'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/plupload/plupload.full.min.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/plupload/jquery.plupload.queue.min.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/airtime/library/plupload.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/plupload/i18n/'.$locale.'.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headLink()->appendStylesheet($baseUrl.'css/plupload.queue.css?'.$CC_CONFIG['airtime_version']); + $this->view->headLink()->appendStylesheet($baseUrl.'css/addmedia.css?'.$CC_CONFIG['airtime_version']); } public function uploadAction() @@ -34,17 +35,53 @@ class PluploadController extends Zend_Controller_Action $this->_helper->json->sendJson(array("jsonrpc" => "2.0", "tempfilepath" => $tempFileName)); } - - public function copyfileAction() + + public function recentUploadsAction() { - $upload_dir = ini_get("upload_tmp_dir") . DIRECTORY_SEPARATOR . "plupload"; - $filename = $this->_getParam('name'); - $tempname = $this->_getParam('tempname'); - $result = Application_Model_StoredFile::copyFileToStor($upload_dir, - $filename, $tempname); - if (!is_null($result)) - $this->_helper->json->sendJson(array("jsonrpc" => "2.0", "error" => $result)); + if (isset($_GET['uploadFilter'])) { + $filter = $_GET['uploadFilter']; + } else { + $filter = "all"; + } + + $limit = isset($_GET['iDisplayLength']) ? $_GET['iDisplayLength'] : 10; + $rowStart = isset($_GET['iDisplayStart']) ? $_GET['iDisplayStart'] : 0; + + $recentUploadsQuery = CcFilesQuery::create()->filterByDbUtime(array('min' => time() - 30 * 24 * 60 * 60)) + ->orderByDbUtime(Criteria::DESC); + + $numTotalRecentUploads = $recentUploadsQuery->find()->count(); + + if ($filter == "pending") { + $recentUploadsQuery->filterByDbImportStatus("1"); + } else if ($filter == "failed") { + $recentUploadsQuery->filterByDbImportStatus(array('min' => 100)); + } + + $recentUploads = $recentUploadsQuery->offset($rowStart)->limit($limit)->find(); + + $numRecentUploads = $limit; + //CcFilesQuery::create()->filterByDbUtime(array('min' => time() - 30 * 24 * 60 * 60)) + + //$this->_helper->json->sendJson(array("jsonrpc" => "2.0", "tempfilepath" => $tempFileName)); + + $uploadsArray = array(); + + foreach ($recentUploads as $upload) + { + $upload->toArray(BasePeer::TYPE_FIELDNAME); + //array_push($uploadsArray, $upload); //TODO: $this->sanitizeResponse($upload)); + + //$this->_helper->json->sendJson($upload->asJson()); + //TODO: Invoke sanitization here + array_push($uploadsArray, $upload->toArray(BasePeer::TYPE_FIELDNAME)); + } + - $this->_helper->json->sendJson(array("jsonrpc" => "2.0")); + $this->view->sEcho = intval($this->getRequest()->getParam('sEcho')); + $this->view->iTotalDisplayRecords = $numTotalRecentUploads; + //$this->view->iTotalDisplayRecords = $numRecentUploads; //$r["iTotalDisplayRecords"]; + $this->view->iTotalRecords = $numTotalRecentUploads; //$r["iTotalRecords"]; + $this->view->files = $uploadsArray; //$r["aaData"]; } } diff --git a/airtime_mvc/application/controllers/plugins/Acl_plugin.php b/airtime_mvc/application/controllers/plugins/Acl_plugin.php index 44555e533..4cf9f97b5 100644 --- a/airtime_mvc/application/controllers/plugins/Acl_plugin.php +++ b/airtime_mvc/application/controllers/plugins/Acl_plugin.php @@ -110,6 +110,13 @@ class Zend_Controller_Plugin_Acl extends Zend_Controller_Plugin_Abstract { $controller = strtolower($request->getControllerName()); + //Ignore authentication for all access to the rest API. We do auth via API keys for this + //and/or by OAuth. + if (strtolower($request->getModuleName()) == "rest") + { + return; + } + if (in_array($controller, array("api", "auth", "locale"))) { $this->setRoleName("G"); diff --git a/airtime_mvc/application/models/MusicDir.php b/airtime_mvc/application/models/MusicDir.php index 08b2b2649..80f6cbe25 100644 --- a/airtime_mvc/application/models/MusicDir.php +++ b/airtime_mvc/application/models/MusicDir.php @@ -301,7 +301,9 @@ SQL; public static function getDirByPK($pk) { $dir = CcMusicDirsQuery::create()->findPK($pk); - + if (!$dir) { + return null; + } $mus_dir = new Application_Model_MusicDir($dir); return $mus_dir; diff --git a/airtime_mvc/application/models/Preference.php b/airtime_mvc/application/models/Preference.php index 87c85ae01..3288d2c8c 100644 --- a/airtime_mvc/application/models/Preference.php +++ b/airtime_mvc/application/models/Preference.php @@ -1409,4 +1409,24 @@ class Application_Model_Preference public static function GetHistoryFileTemplate() { return self::getValue("history_file_template"); } + + public static function getDiskUsage() + { + return self::getValue("disk_usage"); + } + + public static function setDiskUsage($value) + { + self::setValue("disk_usage", $value); + } + + public static function updateDiskUsage($filesize) + { + $currentDiskUsage = self::getDiskUsage(); + if (empty($currentDiskUsage)) { + $currentDiskUsage = 0; + } + + self::setDiskUsage($currentDiskUsage + $filesize); + } } diff --git a/airtime_mvc/application/models/RabbitMq.php b/airtime_mvc/application/models/RabbitMq.php index 7a5448feb..786fd2e0a 100644 --- a/airtime_mvc/application/models/RabbitMq.php +++ b/airtime_mvc/application/models/RabbitMq.php @@ -13,7 +13,7 @@ class Application_Model_RabbitMq self::$doPush = true; } - private static function sendMessage($exchange, $data) + private static function sendMessage($exchange, $exchangeType, $autoDeleteExchange, $data, $queue="") { $CC_CONFIG = Config::getConfig(); @@ -31,7 +31,9 @@ class Application_Model_RabbitMq $channel->access_request($CC_CONFIG["rabbitmq"]["vhost"], false, false, true, true); - $channel->exchange_declare($exchange, 'direct', false, true); + //I'm pretty sure we DON'T want to autodelete ANY exchanges but I'm keeping the code + //the way it is just so I don't accidentally break anything when I add the Analyzer code in. -- Albert, March 13, 2014 + $channel->exchange_declare($exchange, $exchangeType, false, true, $autoDeleteExchange); $msg = new AMQPMessage($data, array('content_type' => 'text/plain')); @@ -46,7 +48,7 @@ class Application_Model_RabbitMq $exchange = 'airtime-pypo'; $data = json_encode($md, JSON_FORCE_OBJECT); - self::sendMessage($exchange, $data); + self::sendMessage($exchange, 'direct', true, $data); } public static function SendMessageToMediaMonitor($event_type, $md) @@ -55,7 +57,7 @@ class Application_Model_RabbitMq $exchange = 'airtime-media-monitor'; $data = json_encode($md); - self::sendMessage($exchange, $data); + self::sendMessage($exchange, 'direct', true, $data); } public static function SendMessageToShowRecorder($event_type) @@ -74,7 +76,21 @@ class Application_Model_RabbitMq } $data = json_encode($temp); - self::sendMessage($exchange, $data); + self::sendMessage($exchange, 'direct', true, $data); + } + + public static function SendMessageToAnalyzer($tmpFilePath, $importedStorageDirectory, $originalFilename, + $callbackUrl, $apiKey) + { + $exchange = 'airtime-uploads'; + $data['tmp_file_path'] = $tmpFilePath; + $data['import_directory'] = $importedStorageDirectory; + $data['original_filename'] = $originalFilename; + $data['callback_url'] = $callbackUrl; + $data['api_key'] = $apiKey; + + $jsonData = json_encode($data); + self::sendMessage($exchange, 'topic', false, $jsonData, 'airtime-uploads'); } public static function SendMessageToHaproxyConfigDaemon($md){ diff --git a/airtime_mvc/application/models/StoredFile.php b/airtime_mvc/application/models/StoredFile.php index 009572647..23247522c 100644 --- a/airtime_mvc/application/models/StoredFile.php +++ b/airtime_mvc/application/models/StoredFile.php @@ -346,6 +346,21 @@ SQL; return array(); } } + + /** + * Check if the file (on disk) corresponding to this class exists or not. + * @return boolean true if the file exists, false otherwise. + */ + public function existsOnDisk() + { + $exists = false; + try { + $exists = file_exists($this->getFilePath()); + } catch (Exception $e) { + return false; + } + return $exists; + } /** * Delete stored virtual file @@ -355,8 +370,11 @@ SQL; */ public function delete() { - $filepath = $this->getFilePath(); + + //Update the user's disk usage + Application_Model_Preference::updateDiskUsage(-1 * abs(filesize($filepath))); + // Check if the file is scheduled to be played in the future if (Application_Model_Schedule::IsFileScheduledInTheFuture($this->getId())) { throw new DeleteScheduledFileException(); @@ -370,8 +388,10 @@ SQL; } $music_dir = Application_Model_MusicDir::getDirByPK($this->_file->getDbDirectory()); + assert($music_dir); $type = $music_dir->getType(); - + + if (file_exists($filepath) && $type == "stor") { $data = array("filepath" => $filepath, "delete" => 1); try { @@ -473,8 +493,13 @@ SQL; */ public function getFilePath() { + assert($this->_file); + $music_dir = Application_Model_MusicDir::getDirByPK($this-> _file->getDbDirectory()); + if (!$music_dir) { + throw new Exception("Invalid music_dir for file in database."); + } $directory = $music_dir->getDirectory(); $filepath = $this->_file->getDbFilepath(); @@ -988,48 +1013,69 @@ SQL; return $freeSpace >= $fileSize; } - public static function copyFileToStor($p_targetDir, $fileName, $tempname) + /** + * Copy a newly uploaded audio file from its temporary upload directory + * on the local disk (like /tmp) over to Airtime's "stor" directory, + * which is where all ingested music/media live. + * + * This is done in PHP here on the web server rather than in airtime_analyzer because + * the airtime_analyzer might be running on a different physical computer than the web server, + * and it probably won't have access to the web server's /tmp folder. The stor/organize directory + * is, however, both accessible to the machines running airtime_analyzer and the web server + * on Airtime Pro. + * + * The file is actually copied to "stor/organize", which is a staging directory where files go + * before they're processed by airtime_analyzer, which then moves them to "stor/imported" in the final + * step. + * + * TODO: Implement better error handling here... + * + * @param string $tempFilePath + * @param string $originalFilename + * @throws Exception + * @return Ambigous + */ + public static function copyFileToStor($tempFilePath, $originalFilename) { - $audio_file = $p_targetDir . DIRECTORY_SEPARATOR . $tempname; + $audio_file = $tempFilePath; Logging::info('copyFileToStor: moving file '.$audio_file); - + $storDir = Application_Model_MusicDir::getStorDir(); $stor = $storDir->getDirectory(); // check if "organize" dir exists and if not create one if (!file_exists($stor."/organize")) { if (!mkdir($stor."/organize", 0777)) { - return array( - "code" => 109, - "message" => _("Failed to create 'organize' directory.")); + throw new Exception("Failed to create organize directory."); } } - + if (chmod($audio_file, 0644) === false) { Logging::info("Warning: couldn't change permissions of $audio_file to 0644"); } - + // Check if we have enough space before copying if (!self::isEnoughDiskSpaceToCopy($stor, $audio_file)) { $freeSpace = disk_free_space($stor); $fileSize = filesize($audio_file); - - return array("code" => 107, - "message" => sprintf(_("The file was not uploaded, there is " - ."%s MB of disk space left and the file you are " - ."uploading has a size of %s MB."), $freeSpace, $fileSize)); + + throw new Exception(sprintf(_("The file was not uploaded, there is " + ."%s MB of disk space left and the file you are " + ."uploading has a size of %s MB."), $freeSpace, $fileSize)); } - + // Check if liquidsoap can play this file + // TODO: Move this to airtime_analyzer if (!self::liquidsoapFilePlayabilityTest($audio_file)) { return array( - "code" => 110, - "message" => _("This file appears to be corrupted and will not " - ."be added to media library.")); + "code" => 110, + "message" => _("This file appears to be corrupted and will not " + ."be added to media library.")); } + // Did all the checks for real, now trying to copy $audio_stor = Application_Common_OsPath::join($stor, "organize", - $fileName); + $originalFilename); $user = Application_Model_User::getCurrentUser(); if (is_null($user)) { $uid = Application_Model_User::getFirstAdminId(); @@ -1044,7 +1090,7 @@ SQL; written)"); } else { Logging::info("Successfully written identification file for - uploaded '$audio_stor'"); + uploaded '$audio_stor'"); } //if the uploaded file is not UTF-8 encoded, let's encode it. Assuming source //encoding is ISO-8859-1 @@ -1059,18 +1105,14 @@ SQL; //is enough disk space . unlink($audio_file); //remove the file after failed rename unlink($id_file); // Also remove the identifier file - - return array( - "code" => 108, - "message" => _("The file was not uploaded, this error can occur if the computer " - ."hard drive does not have enough disk space or the stor " - ."directory does not have correct write permissions.")); + + throw new Exception("The file was not uploaded, this error can occur if the computer " + ."hard drive does not have enough disk space or the stor " + ."directory does not have correct write permissions."); } - // Now that we successfully added this file, we will add another tag - // file that will identify the user that owns it - return null; + return $audio_stor; } - + /* * Pass the file through Liquidsoap and test if it is readable. Return True if readable, and False otherwise. */ diff --git a/airtime_mvc/application/models/airtime/map/CcFilesTableMap.php b/airtime_mvc/application/models/airtime/map/CcFilesTableMap.php index 6f9f414dd..348d2482e 100644 --- a/airtime_mvc/application/models/airtime/map/CcFilesTableMap.php +++ b/airtime_mvc/application/models/airtime/map/CcFilesTableMap.php @@ -44,7 +44,7 @@ class CcFilesTableMap extends TableMap { $this->addColumn('FTYPE', 'DbFtype', 'VARCHAR', true, 128, ''); $this->addForeignKey('DIRECTORY', 'DbDirectory', 'INTEGER', 'cc_music_dirs', 'ID', false, null, null); $this->addColumn('FILEPATH', 'DbFilepath', 'LONGVARCHAR', false, null, ''); - $this->addColumn('STATE', 'DbState', 'VARCHAR', true, 128, 'empty'); + $this->addColumn('IMPORT_STATUS', 'DbImportStatus', 'INTEGER', true, null, 0); $this->addColumn('CURRENTLYACCESSING', 'DbCurrentlyaccessing', 'INTEGER', true, null, 0); $this->addForeignKey('EDITEDBY', 'DbEditedby', 'INTEGER', 'cc_subjs', 'ID', false, null, null); $this->addColumn('MTIME', 'DbMtime', 'TIMESTAMP', false, 6, null); diff --git a/airtime_mvc/application/models/airtime/om/BaseCcFiles.php b/airtime_mvc/application/models/airtime/om/BaseCcFiles.php index 38d147c07..59ea2b6c8 100644 --- a/airtime_mvc/application/models/airtime/om/BaseCcFiles.php +++ b/airtime_mvc/application/models/airtime/om/BaseCcFiles.php @@ -65,11 +65,11 @@ abstract class BaseCcFiles extends BaseObject implements Persistent protected $filepath; /** - * The value for the state field. - * Note: this column has a database default value of: 'empty' - * @var string + * The value for the import_status field. + * Note: this column has a database default value of: 0 + * @var int */ - protected $state; + protected $import_status; /** * The value for the currentlyaccessing field. @@ -524,7 +524,7 @@ abstract class BaseCcFiles extends BaseObject implements Persistent $this->mime = ''; $this->ftype = ''; $this->filepath = ''; - $this->state = 'empty'; + $this->import_status = 0; $this->currentlyaccessing = 0; $this->length = '00:00:00'; $this->file_exists = true; @@ -607,13 +607,13 @@ abstract class BaseCcFiles extends BaseObject implements Persistent } /** - * Get the [state] column value. + * Get the [import_status] column value. * - * @return string + * @return int */ - public function getDbState() + public function getDbImportStatus() { - return $this->state; + return $this->import_status; } /** @@ -1463,24 +1463,24 @@ abstract class BaseCcFiles extends BaseObject implements Persistent } // setDbFilepath() /** - * Set the value of [state] column. + * Set the value of [import_status] column. * - * @param string $v new value + * @param int $v new value * @return CcFiles The current object (for fluent API support) */ - public function setDbState($v) + public function setDbImportStatus($v) { if ($v !== null) { - $v = (string) $v; + $v = (int) $v; } - if ($this->state !== $v || $this->isNew()) { - $this->state = $v; - $this->modifiedColumns[] = CcFilesPeer::STATE; + if ($this->import_status !== $v || $this->isNew()) { + $this->import_status = $v; + $this->modifiedColumns[] = CcFilesPeer::IMPORT_STATUS; } return $this; - } // setDbState() + } // setDbImportStatus() /** * Set the value of [currentlyaccessing] column. @@ -2892,7 +2892,7 @@ abstract class BaseCcFiles extends BaseObject implements Persistent return false; } - if ($this->state !== 'empty') { + if ($this->import_status !== 0) { return false; } @@ -2960,7 +2960,7 @@ abstract class BaseCcFiles extends BaseObject implements Persistent $this->ftype = ($row[$startcol + 3] !== null) ? (string) $row[$startcol + 3] : null; $this->directory = ($row[$startcol + 4] !== null) ? (int) $row[$startcol + 4] : null; $this->filepath = ($row[$startcol + 5] !== null) ? (string) $row[$startcol + 5] : null; - $this->state = ($row[$startcol + 6] !== null) ? (string) $row[$startcol + 6] : null; + $this->import_status = ($row[$startcol + 6] !== null) ? (int) $row[$startcol + 6] : null; $this->currentlyaccessing = ($row[$startcol + 7] !== null) ? (int) $row[$startcol + 7] : null; $this->editedby = ($row[$startcol + 8] !== null) ? (int) $row[$startcol + 8] : null; $this->mtime = ($row[$startcol + 9] !== null) ? (string) $row[$startcol + 9] : null; @@ -3502,7 +3502,7 @@ abstract class BaseCcFiles extends BaseObject implements Persistent return $this->getDbFilepath(); break; case 6: - return $this->getDbState(); + return $this->getDbImportStatus(); break; case 7: return $this->getDbCurrentlyaccessing(); @@ -3723,7 +3723,7 @@ abstract class BaseCcFiles extends BaseObject implements Persistent $keys[3] => $this->getDbFtype(), $keys[4] => $this->getDbDirectory(), $keys[5] => $this->getDbFilepath(), - $keys[6] => $this->getDbState(), + $keys[6] => $this->getDbImportStatus(), $keys[7] => $this->getDbCurrentlyaccessing(), $keys[8] => $this->getDbEditedby(), $keys[9] => $this->getDbMtime(), @@ -3848,7 +3848,7 @@ abstract class BaseCcFiles extends BaseObject implements Persistent $this->setDbFilepath($value); break; case 6: - $this->setDbState($value); + $this->setDbImportStatus($value); break; case 7: $this->setDbCurrentlyaccessing($value); @@ -4069,7 +4069,7 @@ abstract class BaseCcFiles extends BaseObject implements Persistent if (array_key_exists($keys[3], $arr)) $this->setDbFtype($arr[$keys[3]]); if (array_key_exists($keys[4], $arr)) $this->setDbDirectory($arr[$keys[4]]); if (array_key_exists($keys[5], $arr)) $this->setDbFilepath($arr[$keys[5]]); - if (array_key_exists($keys[6], $arr)) $this->setDbState($arr[$keys[6]]); + if (array_key_exists($keys[6], $arr)) $this->setDbImportStatus($arr[$keys[6]]); if (array_key_exists($keys[7], $arr)) $this->setDbCurrentlyaccessing($arr[$keys[7]]); if (array_key_exists($keys[8], $arr)) $this->setDbEditedby($arr[$keys[8]]); if (array_key_exists($keys[9], $arr)) $this->setDbMtime($arr[$keys[9]]); @@ -4150,7 +4150,7 @@ abstract class BaseCcFiles extends BaseObject implements Persistent if ($this->isColumnModified(CcFilesPeer::FTYPE)) $criteria->add(CcFilesPeer::FTYPE, $this->ftype); if ($this->isColumnModified(CcFilesPeer::DIRECTORY)) $criteria->add(CcFilesPeer::DIRECTORY, $this->directory); if ($this->isColumnModified(CcFilesPeer::FILEPATH)) $criteria->add(CcFilesPeer::FILEPATH, $this->filepath); - if ($this->isColumnModified(CcFilesPeer::STATE)) $criteria->add(CcFilesPeer::STATE, $this->state); + if ($this->isColumnModified(CcFilesPeer::IMPORT_STATUS)) $criteria->add(CcFilesPeer::IMPORT_STATUS, $this->import_status); if ($this->isColumnModified(CcFilesPeer::CURRENTLYACCESSING)) $criteria->add(CcFilesPeer::CURRENTLYACCESSING, $this->currentlyaccessing); if ($this->isColumnModified(CcFilesPeer::EDITEDBY)) $criteria->add(CcFilesPeer::EDITEDBY, $this->editedby); if ($this->isColumnModified(CcFilesPeer::MTIME)) $criteria->add(CcFilesPeer::MTIME, $this->mtime); @@ -4280,7 +4280,7 @@ abstract class BaseCcFiles extends BaseObject implements Persistent $copyObj->setDbFtype($this->ftype); $copyObj->setDbDirectory($this->directory); $copyObj->setDbFilepath($this->filepath); - $copyObj->setDbState($this->state); + $copyObj->setDbImportStatus($this->import_status); $copyObj->setDbCurrentlyaccessing($this->currentlyaccessing); $copyObj->setDbEditedby($this->editedby); $copyObj->setDbMtime($this->mtime); @@ -5328,7 +5328,7 @@ abstract class BaseCcFiles extends BaseObject implements Persistent $this->ftype = null; $this->directory = null; $this->filepath = null; - $this->state = null; + $this->import_status = null; $this->currentlyaccessing = null; $this->editedby = null; $this->mtime = null; diff --git a/airtime_mvc/application/models/airtime/om/BaseCcFilesPeer.php b/airtime_mvc/application/models/airtime/om/BaseCcFilesPeer.php index dbfbeaf06..dd2e9bec5 100644 --- a/airtime_mvc/application/models/airtime/om/BaseCcFilesPeer.php +++ b/airtime_mvc/application/models/airtime/om/BaseCcFilesPeer.php @@ -49,8 +49,8 @@ abstract class BaseCcFilesPeer { /** the column name for the FILEPATH field */ const FILEPATH = 'cc_files.FILEPATH'; - /** the column name for the STATE field */ - const STATE = 'cc_files.STATE'; + /** the column name for the IMPORT_STATUS field */ + const IMPORT_STATUS = 'cc_files.IMPORT_STATUS'; /** the column name for the CURRENTLYACCESSING field */ const CURRENTLYACCESSING = 'cc_files.CURRENTLYACCESSING'; @@ -257,11 +257,11 @@ abstract class BaseCcFilesPeer { * e.g. self::$fieldNames[self::TYPE_PHPNAME][0] = 'Id' */ private static $fieldNames = array ( - BasePeer::TYPE_PHPNAME => array ('DbId', 'DbName', 'DbMime', 'DbFtype', 'DbDirectory', 'DbFilepath', 'DbState', 'DbCurrentlyaccessing', 'DbEditedby', 'DbMtime', 'DbUtime', 'DbLPtime', 'DbMd5', 'DbTrackTitle', 'DbArtistName', 'DbBitRate', 'DbSampleRate', 'DbFormat', 'DbLength', 'DbAlbumTitle', 'DbGenre', 'DbComments', 'DbYear', 'DbTrackNumber', 'DbChannels', 'DbUrl', 'DbBpm', 'DbRating', 'DbEncodedBy', 'DbDiscNumber', 'DbMood', 'DbLabel', 'DbComposer', 'DbEncoder', 'DbChecksum', 'DbLyrics', 'DbOrchestra', 'DbConductor', 'DbLyricist', 'DbOriginalLyricist', 'DbRadioStationName', 'DbInfoUrl', 'DbArtistUrl', 'DbAudioSourceUrl', 'DbRadioStationUrl', 'DbBuyThisUrl', 'DbIsrcNumber', 'DbCatalogNumber', 'DbOriginalArtist', 'DbCopyright', 'DbReportDatetime', 'DbReportLocation', 'DbReportOrganization', 'DbSubject', 'DbContributor', 'DbLanguage', 'DbFileExists', 'DbSoundcloudId', 'DbSoundcloudErrorCode', 'DbSoundcloudErrorMsg', 'DbSoundcloudLinkToFile', 'DbSoundCloundUploadTime', 'DbReplayGain', 'DbOwnerId', 'DbCuein', 'DbCueout', 'DbSilanCheck', 'DbHidden', 'DbIsScheduled', 'DbIsPlaylist', ), - BasePeer::TYPE_STUDLYPHPNAME => array ('dbId', 'dbName', 'dbMime', 'dbFtype', 'dbDirectory', 'dbFilepath', 'dbState', 'dbCurrentlyaccessing', 'dbEditedby', 'dbMtime', 'dbUtime', 'dbLPtime', 'dbMd5', 'dbTrackTitle', 'dbArtistName', 'dbBitRate', 'dbSampleRate', 'dbFormat', 'dbLength', 'dbAlbumTitle', 'dbGenre', 'dbComments', 'dbYear', 'dbTrackNumber', 'dbChannels', 'dbUrl', 'dbBpm', 'dbRating', 'dbEncodedBy', 'dbDiscNumber', 'dbMood', 'dbLabel', 'dbComposer', 'dbEncoder', 'dbChecksum', 'dbLyrics', 'dbOrchestra', 'dbConductor', 'dbLyricist', 'dbOriginalLyricist', 'dbRadioStationName', 'dbInfoUrl', 'dbArtistUrl', 'dbAudioSourceUrl', 'dbRadioStationUrl', 'dbBuyThisUrl', 'dbIsrcNumber', 'dbCatalogNumber', 'dbOriginalArtist', 'dbCopyright', 'dbReportDatetime', 'dbReportLocation', 'dbReportOrganization', 'dbSubject', 'dbContributor', 'dbLanguage', 'dbFileExists', 'dbSoundcloudId', 'dbSoundcloudErrorCode', 'dbSoundcloudErrorMsg', 'dbSoundcloudLinkToFile', 'dbSoundCloundUploadTime', 'dbReplayGain', 'dbOwnerId', 'dbCuein', 'dbCueout', 'dbSilanCheck', 'dbHidden', 'dbIsScheduled', 'dbIsPlaylist', ), - BasePeer::TYPE_COLNAME => array (self::ID, self::NAME, self::MIME, self::FTYPE, self::DIRECTORY, self::FILEPATH, self::STATE, self::CURRENTLYACCESSING, self::EDITEDBY, self::MTIME, self::UTIME, self::LPTIME, self::MD5, self::TRACK_TITLE, self::ARTIST_NAME, self::BIT_RATE, self::SAMPLE_RATE, self::FORMAT, self::LENGTH, self::ALBUM_TITLE, self::GENRE, self::COMMENTS, self::YEAR, self::TRACK_NUMBER, self::CHANNELS, self::URL, self::BPM, self::RATING, self::ENCODED_BY, self::DISC_NUMBER, self::MOOD, self::LABEL, self::COMPOSER, self::ENCODER, self::CHECKSUM, self::LYRICS, self::ORCHESTRA, self::CONDUCTOR, self::LYRICIST, self::ORIGINAL_LYRICIST, self::RADIO_STATION_NAME, self::INFO_URL, self::ARTIST_URL, self::AUDIO_SOURCE_URL, self::RADIO_STATION_URL, self::BUY_THIS_URL, self::ISRC_NUMBER, self::CATALOG_NUMBER, self::ORIGINAL_ARTIST, self::COPYRIGHT, self::REPORT_DATETIME, self::REPORT_LOCATION, self::REPORT_ORGANIZATION, self::SUBJECT, self::CONTRIBUTOR, self::LANGUAGE, self::FILE_EXISTS, self::SOUNDCLOUD_ID, self::SOUNDCLOUD_ERROR_CODE, self::SOUNDCLOUD_ERROR_MSG, self::SOUNDCLOUD_LINK_TO_FILE, self::SOUNDCLOUD_UPLOAD_TIME, self::REPLAY_GAIN, self::OWNER_ID, self::CUEIN, self::CUEOUT, self::SILAN_CHECK, self::HIDDEN, self::IS_SCHEDULED, self::IS_PLAYLIST, ), - BasePeer::TYPE_RAW_COLNAME => array ('ID', 'NAME', 'MIME', 'FTYPE', 'DIRECTORY', 'FILEPATH', 'STATE', 'CURRENTLYACCESSING', 'EDITEDBY', 'MTIME', 'UTIME', 'LPTIME', 'MD5', 'TRACK_TITLE', 'ARTIST_NAME', 'BIT_RATE', 'SAMPLE_RATE', 'FORMAT', 'LENGTH', 'ALBUM_TITLE', 'GENRE', 'COMMENTS', 'YEAR', 'TRACK_NUMBER', 'CHANNELS', 'URL', 'BPM', 'RATING', 'ENCODED_BY', 'DISC_NUMBER', 'MOOD', 'LABEL', 'COMPOSER', 'ENCODER', 'CHECKSUM', 'LYRICS', 'ORCHESTRA', 'CONDUCTOR', 'LYRICIST', 'ORIGINAL_LYRICIST', 'RADIO_STATION_NAME', 'INFO_URL', 'ARTIST_URL', 'AUDIO_SOURCE_URL', 'RADIO_STATION_URL', 'BUY_THIS_URL', 'ISRC_NUMBER', 'CATALOG_NUMBER', 'ORIGINAL_ARTIST', 'COPYRIGHT', 'REPORT_DATETIME', 'REPORT_LOCATION', 'REPORT_ORGANIZATION', 'SUBJECT', 'CONTRIBUTOR', 'LANGUAGE', 'FILE_EXISTS', 'SOUNDCLOUD_ID', 'SOUNDCLOUD_ERROR_CODE', 'SOUNDCLOUD_ERROR_MSG', 'SOUNDCLOUD_LINK_TO_FILE', 'SOUNDCLOUD_UPLOAD_TIME', 'REPLAY_GAIN', 'OWNER_ID', 'CUEIN', 'CUEOUT', 'SILAN_CHECK', 'HIDDEN', 'IS_SCHEDULED', 'IS_PLAYLIST', ), - BasePeer::TYPE_FIELDNAME => array ('id', 'name', 'mime', 'ftype', 'directory', 'filepath', 'state', 'currentlyaccessing', 'editedby', 'mtime', 'utime', 'lptime', 'md5', 'track_title', 'artist_name', 'bit_rate', 'sample_rate', 'format', 'length', 'album_title', 'genre', 'comments', 'year', 'track_number', 'channels', 'url', 'bpm', 'rating', 'encoded_by', 'disc_number', 'mood', 'label', 'composer', 'encoder', 'checksum', 'lyrics', 'orchestra', 'conductor', 'lyricist', 'original_lyricist', 'radio_station_name', 'info_url', 'artist_url', 'audio_source_url', 'radio_station_url', 'buy_this_url', 'isrc_number', 'catalog_number', 'original_artist', 'copyright', 'report_datetime', 'report_location', 'report_organization', 'subject', 'contributor', 'language', 'file_exists', 'soundcloud_id', 'soundcloud_error_code', 'soundcloud_error_msg', 'soundcloud_link_to_file', 'soundcloud_upload_time', 'replay_gain', 'owner_id', 'cuein', 'cueout', 'silan_check', 'hidden', 'is_scheduled', 'is_playlist', ), + BasePeer::TYPE_PHPNAME => array ('DbId', 'DbName', 'DbMime', 'DbFtype', 'DbDirectory', 'DbFilepath', 'DbImportStatus', 'DbCurrentlyaccessing', 'DbEditedby', 'DbMtime', 'DbUtime', 'DbLPtime', 'DbMd5', 'DbTrackTitle', 'DbArtistName', 'DbBitRate', 'DbSampleRate', 'DbFormat', 'DbLength', 'DbAlbumTitle', 'DbGenre', 'DbComments', 'DbYear', 'DbTrackNumber', 'DbChannels', 'DbUrl', 'DbBpm', 'DbRating', 'DbEncodedBy', 'DbDiscNumber', 'DbMood', 'DbLabel', 'DbComposer', 'DbEncoder', 'DbChecksum', 'DbLyrics', 'DbOrchestra', 'DbConductor', 'DbLyricist', 'DbOriginalLyricist', 'DbRadioStationName', 'DbInfoUrl', 'DbArtistUrl', 'DbAudioSourceUrl', 'DbRadioStationUrl', 'DbBuyThisUrl', 'DbIsrcNumber', 'DbCatalogNumber', 'DbOriginalArtist', 'DbCopyright', 'DbReportDatetime', 'DbReportLocation', 'DbReportOrganization', 'DbSubject', 'DbContributor', 'DbLanguage', 'DbFileExists', 'DbSoundcloudId', 'DbSoundcloudErrorCode', 'DbSoundcloudErrorMsg', 'DbSoundcloudLinkToFile', 'DbSoundCloundUploadTime', 'DbReplayGain', 'DbOwnerId', 'DbCuein', 'DbCueout', 'DbSilanCheck', 'DbHidden', 'DbIsScheduled', 'DbIsPlaylist', ), + BasePeer::TYPE_STUDLYPHPNAME => array ('dbId', 'dbName', 'dbMime', 'dbFtype', 'dbDirectory', 'dbFilepath', 'dbImportStatus', 'dbCurrentlyaccessing', 'dbEditedby', 'dbMtime', 'dbUtime', 'dbLPtime', 'dbMd5', 'dbTrackTitle', 'dbArtistName', 'dbBitRate', 'dbSampleRate', 'dbFormat', 'dbLength', 'dbAlbumTitle', 'dbGenre', 'dbComments', 'dbYear', 'dbTrackNumber', 'dbChannels', 'dbUrl', 'dbBpm', 'dbRating', 'dbEncodedBy', 'dbDiscNumber', 'dbMood', 'dbLabel', 'dbComposer', 'dbEncoder', 'dbChecksum', 'dbLyrics', 'dbOrchestra', 'dbConductor', 'dbLyricist', 'dbOriginalLyricist', 'dbRadioStationName', 'dbInfoUrl', 'dbArtistUrl', 'dbAudioSourceUrl', 'dbRadioStationUrl', 'dbBuyThisUrl', 'dbIsrcNumber', 'dbCatalogNumber', 'dbOriginalArtist', 'dbCopyright', 'dbReportDatetime', 'dbReportLocation', 'dbReportOrganization', 'dbSubject', 'dbContributor', 'dbLanguage', 'dbFileExists', 'dbSoundcloudId', 'dbSoundcloudErrorCode', 'dbSoundcloudErrorMsg', 'dbSoundcloudLinkToFile', 'dbSoundCloundUploadTime', 'dbReplayGain', 'dbOwnerId', 'dbCuein', 'dbCueout', 'dbSilanCheck', 'dbHidden', 'dbIsScheduled', 'dbIsPlaylist', ), + BasePeer::TYPE_COLNAME => array (self::ID, self::NAME, self::MIME, self::FTYPE, self::DIRECTORY, self::FILEPATH, self::IMPORT_STATUS, self::CURRENTLYACCESSING, self::EDITEDBY, self::MTIME, self::UTIME, self::LPTIME, self::MD5, self::TRACK_TITLE, self::ARTIST_NAME, self::BIT_RATE, self::SAMPLE_RATE, self::FORMAT, self::LENGTH, self::ALBUM_TITLE, self::GENRE, self::COMMENTS, self::YEAR, self::TRACK_NUMBER, self::CHANNELS, self::URL, self::BPM, self::RATING, self::ENCODED_BY, self::DISC_NUMBER, self::MOOD, self::LABEL, self::COMPOSER, self::ENCODER, self::CHECKSUM, self::LYRICS, self::ORCHESTRA, self::CONDUCTOR, self::LYRICIST, self::ORIGINAL_LYRICIST, self::RADIO_STATION_NAME, self::INFO_URL, self::ARTIST_URL, self::AUDIO_SOURCE_URL, self::RADIO_STATION_URL, self::BUY_THIS_URL, self::ISRC_NUMBER, self::CATALOG_NUMBER, self::ORIGINAL_ARTIST, self::COPYRIGHT, self::REPORT_DATETIME, self::REPORT_LOCATION, self::REPORT_ORGANIZATION, self::SUBJECT, self::CONTRIBUTOR, self::LANGUAGE, self::FILE_EXISTS, self::SOUNDCLOUD_ID, self::SOUNDCLOUD_ERROR_CODE, self::SOUNDCLOUD_ERROR_MSG, self::SOUNDCLOUD_LINK_TO_FILE, self::SOUNDCLOUD_UPLOAD_TIME, self::REPLAY_GAIN, self::OWNER_ID, self::CUEIN, self::CUEOUT, self::SILAN_CHECK, self::HIDDEN, self::IS_SCHEDULED, self::IS_PLAYLIST, ), + BasePeer::TYPE_RAW_COLNAME => array ('ID', 'NAME', 'MIME', 'FTYPE', 'DIRECTORY', 'FILEPATH', 'IMPORT_STATUS', 'CURRENTLYACCESSING', 'EDITEDBY', 'MTIME', 'UTIME', 'LPTIME', 'MD5', 'TRACK_TITLE', 'ARTIST_NAME', 'BIT_RATE', 'SAMPLE_RATE', 'FORMAT', 'LENGTH', 'ALBUM_TITLE', 'GENRE', 'COMMENTS', 'YEAR', 'TRACK_NUMBER', 'CHANNELS', 'URL', 'BPM', 'RATING', 'ENCODED_BY', 'DISC_NUMBER', 'MOOD', 'LABEL', 'COMPOSER', 'ENCODER', 'CHECKSUM', 'LYRICS', 'ORCHESTRA', 'CONDUCTOR', 'LYRICIST', 'ORIGINAL_LYRICIST', 'RADIO_STATION_NAME', 'INFO_URL', 'ARTIST_URL', 'AUDIO_SOURCE_URL', 'RADIO_STATION_URL', 'BUY_THIS_URL', 'ISRC_NUMBER', 'CATALOG_NUMBER', 'ORIGINAL_ARTIST', 'COPYRIGHT', 'REPORT_DATETIME', 'REPORT_LOCATION', 'REPORT_ORGANIZATION', 'SUBJECT', 'CONTRIBUTOR', 'LANGUAGE', 'FILE_EXISTS', 'SOUNDCLOUD_ID', 'SOUNDCLOUD_ERROR_CODE', 'SOUNDCLOUD_ERROR_MSG', 'SOUNDCLOUD_LINK_TO_FILE', 'SOUNDCLOUD_UPLOAD_TIME', 'REPLAY_GAIN', 'OWNER_ID', 'CUEIN', 'CUEOUT', 'SILAN_CHECK', 'HIDDEN', 'IS_SCHEDULED', 'IS_PLAYLIST', ), + BasePeer::TYPE_FIELDNAME => array ('id', 'name', 'mime', 'ftype', 'directory', 'filepath', 'import_status', 'currentlyaccessing', 'editedby', 'mtime', 'utime', 'lptime', 'md5', 'track_title', 'artist_name', 'bit_rate', 'sample_rate', 'format', 'length', 'album_title', 'genre', 'comments', 'year', 'track_number', 'channels', 'url', 'bpm', 'rating', 'encoded_by', 'disc_number', 'mood', 'label', 'composer', 'encoder', 'checksum', 'lyrics', 'orchestra', 'conductor', 'lyricist', 'original_lyricist', 'radio_station_name', 'info_url', 'artist_url', 'audio_source_url', 'radio_station_url', 'buy_this_url', 'isrc_number', 'catalog_number', 'original_artist', 'copyright', 'report_datetime', 'report_location', 'report_organization', 'subject', 'contributor', 'language', 'file_exists', 'soundcloud_id', 'soundcloud_error_code', 'soundcloud_error_msg', 'soundcloud_link_to_file', 'soundcloud_upload_time', 'replay_gain', 'owner_id', 'cuein', 'cueout', 'silan_check', 'hidden', 'is_scheduled', 'is_playlist', ), BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, ) ); @@ -272,11 +272,11 @@ abstract class BaseCcFilesPeer { * e.g. self::$fieldNames[BasePeer::TYPE_PHPNAME]['Id'] = 0 */ private static $fieldKeys = array ( - BasePeer::TYPE_PHPNAME => array ('DbId' => 0, 'DbName' => 1, 'DbMime' => 2, 'DbFtype' => 3, 'DbDirectory' => 4, 'DbFilepath' => 5, 'DbState' => 6, 'DbCurrentlyaccessing' => 7, 'DbEditedby' => 8, 'DbMtime' => 9, 'DbUtime' => 10, 'DbLPtime' => 11, 'DbMd5' => 12, 'DbTrackTitle' => 13, 'DbArtistName' => 14, 'DbBitRate' => 15, 'DbSampleRate' => 16, 'DbFormat' => 17, 'DbLength' => 18, 'DbAlbumTitle' => 19, 'DbGenre' => 20, 'DbComments' => 21, 'DbYear' => 22, 'DbTrackNumber' => 23, 'DbChannels' => 24, 'DbUrl' => 25, 'DbBpm' => 26, 'DbRating' => 27, 'DbEncodedBy' => 28, 'DbDiscNumber' => 29, 'DbMood' => 30, 'DbLabel' => 31, 'DbComposer' => 32, 'DbEncoder' => 33, 'DbChecksum' => 34, 'DbLyrics' => 35, 'DbOrchestra' => 36, 'DbConductor' => 37, 'DbLyricist' => 38, 'DbOriginalLyricist' => 39, 'DbRadioStationName' => 40, 'DbInfoUrl' => 41, 'DbArtistUrl' => 42, 'DbAudioSourceUrl' => 43, 'DbRadioStationUrl' => 44, 'DbBuyThisUrl' => 45, 'DbIsrcNumber' => 46, 'DbCatalogNumber' => 47, 'DbOriginalArtist' => 48, 'DbCopyright' => 49, 'DbReportDatetime' => 50, 'DbReportLocation' => 51, 'DbReportOrganization' => 52, 'DbSubject' => 53, 'DbContributor' => 54, 'DbLanguage' => 55, 'DbFileExists' => 56, 'DbSoundcloudId' => 57, 'DbSoundcloudErrorCode' => 58, 'DbSoundcloudErrorMsg' => 59, 'DbSoundcloudLinkToFile' => 60, 'DbSoundCloundUploadTime' => 61, 'DbReplayGain' => 62, 'DbOwnerId' => 63, 'DbCuein' => 64, 'DbCueout' => 65, 'DbSilanCheck' => 66, 'DbHidden' => 67, 'DbIsScheduled' => 68, 'DbIsPlaylist' => 69, ), - BasePeer::TYPE_STUDLYPHPNAME => array ('dbId' => 0, 'dbName' => 1, 'dbMime' => 2, 'dbFtype' => 3, 'dbDirectory' => 4, 'dbFilepath' => 5, 'dbState' => 6, 'dbCurrentlyaccessing' => 7, 'dbEditedby' => 8, 'dbMtime' => 9, 'dbUtime' => 10, 'dbLPtime' => 11, 'dbMd5' => 12, 'dbTrackTitle' => 13, 'dbArtistName' => 14, 'dbBitRate' => 15, 'dbSampleRate' => 16, 'dbFormat' => 17, 'dbLength' => 18, 'dbAlbumTitle' => 19, 'dbGenre' => 20, 'dbComments' => 21, 'dbYear' => 22, 'dbTrackNumber' => 23, 'dbChannels' => 24, 'dbUrl' => 25, 'dbBpm' => 26, 'dbRating' => 27, 'dbEncodedBy' => 28, 'dbDiscNumber' => 29, 'dbMood' => 30, 'dbLabel' => 31, 'dbComposer' => 32, 'dbEncoder' => 33, 'dbChecksum' => 34, 'dbLyrics' => 35, 'dbOrchestra' => 36, 'dbConductor' => 37, 'dbLyricist' => 38, 'dbOriginalLyricist' => 39, 'dbRadioStationName' => 40, 'dbInfoUrl' => 41, 'dbArtistUrl' => 42, 'dbAudioSourceUrl' => 43, 'dbRadioStationUrl' => 44, 'dbBuyThisUrl' => 45, 'dbIsrcNumber' => 46, 'dbCatalogNumber' => 47, 'dbOriginalArtist' => 48, 'dbCopyright' => 49, 'dbReportDatetime' => 50, 'dbReportLocation' => 51, 'dbReportOrganization' => 52, 'dbSubject' => 53, 'dbContributor' => 54, 'dbLanguage' => 55, 'dbFileExists' => 56, 'dbSoundcloudId' => 57, 'dbSoundcloudErrorCode' => 58, 'dbSoundcloudErrorMsg' => 59, 'dbSoundcloudLinkToFile' => 60, 'dbSoundCloundUploadTime' => 61, 'dbReplayGain' => 62, 'dbOwnerId' => 63, 'dbCuein' => 64, 'dbCueout' => 65, 'dbSilanCheck' => 66, 'dbHidden' => 67, 'dbIsScheduled' => 68, 'dbIsPlaylist' => 69, ), - BasePeer::TYPE_COLNAME => array (self::ID => 0, self::NAME => 1, self::MIME => 2, self::FTYPE => 3, self::DIRECTORY => 4, self::FILEPATH => 5, self::STATE => 6, self::CURRENTLYACCESSING => 7, self::EDITEDBY => 8, self::MTIME => 9, self::UTIME => 10, self::LPTIME => 11, self::MD5 => 12, self::TRACK_TITLE => 13, self::ARTIST_NAME => 14, self::BIT_RATE => 15, self::SAMPLE_RATE => 16, self::FORMAT => 17, self::LENGTH => 18, self::ALBUM_TITLE => 19, self::GENRE => 20, self::COMMENTS => 21, self::YEAR => 22, self::TRACK_NUMBER => 23, self::CHANNELS => 24, self::URL => 25, self::BPM => 26, self::RATING => 27, self::ENCODED_BY => 28, self::DISC_NUMBER => 29, self::MOOD => 30, self::LABEL => 31, self::COMPOSER => 32, self::ENCODER => 33, self::CHECKSUM => 34, self::LYRICS => 35, self::ORCHESTRA => 36, self::CONDUCTOR => 37, self::LYRICIST => 38, self::ORIGINAL_LYRICIST => 39, self::RADIO_STATION_NAME => 40, self::INFO_URL => 41, self::ARTIST_URL => 42, self::AUDIO_SOURCE_URL => 43, self::RADIO_STATION_URL => 44, self::BUY_THIS_URL => 45, self::ISRC_NUMBER => 46, self::CATALOG_NUMBER => 47, self::ORIGINAL_ARTIST => 48, self::COPYRIGHT => 49, self::REPORT_DATETIME => 50, self::REPORT_LOCATION => 51, self::REPORT_ORGANIZATION => 52, self::SUBJECT => 53, self::CONTRIBUTOR => 54, self::LANGUAGE => 55, self::FILE_EXISTS => 56, self::SOUNDCLOUD_ID => 57, self::SOUNDCLOUD_ERROR_CODE => 58, self::SOUNDCLOUD_ERROR_MSG => 59, self::SOUNDCLOUD_LINK_TO_FILE => 60, self::SOUNDCLOUD_UPLOAD_TIME => 61, self::REPLAY_GAIN => 62, self::OWNER_ID => 63, self::CUEIN => 64, self::CUEOUT => 65, self::SILAN_CHECK => 66, self::HIDDEN => 67, self::IS_SCHEDULED => 68, self::IS_PLAYLIST => 69, ), - BasePeer::TYPE_RAW_COLNAME => array ('ID' => 0, 'NAME' => 1, 'MIME' => 2, 'FTYPE' => 3, 'DIRECTORY' => 4, 'FILEPATH' => 5, 'STATE' => 6, 'CURRENTLYACCESSING' => 7, 'EDITEDBY' => 8, 'MTIME' => 9, 'UTIME' => 10, 'LPTIME' => 11, 'MD5' => 12, 'TRACK_TITLE' => 13, 'ARTIST_NAME' => 14, 'BIT_RATE' => 15, 'SAMPLE_RATE' => 16, 'FORMAT' => 17, 'LENGTH' => 18, 'ALBUM_TITLE' => 19, 'GENRE' => 20, 'COMMENTS' => 21, 'YEAR' => 22, 'TRACK_NUMBER' => 23, 'CHANNELS' => 24, 'URL' => 25, 'BPM' => 26, 'RATING' => 27, 'ENCODED_BY' => 28, 'DISC_NUMBER' => 29, 'MOOD' => 30, 'LABEL' => 31, 'COMPOSER' => 32, 'ENCODER' => 33, 'CHECKSUM' => 34, 'LYRICS' => 35, 'ORCHESTRA' => 36, 'CONDUCTOR' => 37, 'LYRICIST' => 38, 'ORIGINAL_LYRICIST' => 39, 'RADIO_STATION_NAME' => 40, 'INFO_URL' => 41, 'ARTIST_URL' => 42, 'AUDIO_SOURCE_URL' => 43, 'RADIO_STATION_URL' => 44, 'BUY_THIS_URL' => 45, 'ISRC_NUMBER' => 46, 'CATALOG_NUMBER' => 47, 'ORIGINAL_ARTIST' => 48, 'COPYRIGHT' => 49, 'REPORT_DATETIME' => 50, 'REPORT_LOCATION' => 51, 'REPORT_ORGANIZATION' => 52, 'SUBJECT' => 53, 'CONTRIBUTOR' => 54, 'LANGUAGE' => 55, 'FILE_EXISTS' => 56, 'SOUNDCLOUD_ID' => 57, 'SOUNDCLOUD_ERROR_CODE' => 58, 'SOUNDCLOUD_ERROR_MSG' => 59, 'SOUNDCLOUD_LINK_TO_FILE' => 60, 'SOUNDCLOUD_UPLOAD_TIME' => 61, 'REPLAY_GAIN' => 62, 'OWNER_ID' => 63, 'CUEIN' => 64, 'CUEOUT' => 65, 'SILAN_CHECK' => 66, 'HIDDEN' => 67, 'IS_SCHEDULED' => 68, 'IS_PLAYLIST' => 69, ), - BasePeer::TYPE_FIELDNAME => array ('id' => 0, 'name' => 1, 'mime' => 2, 'ftype' => 3, 'directory' => 4, 'filepath' => 5, 'state' => 6, 'currentlyaccessing' => 7, 'editedby' => 8, 'mtime' => 9, 'utime' => 10, 'lptime' => 11, 'md5' => 12, 'track_title' => 13, 'artist_name' => 14, 'bit_rate' => 15, 'sample_rate' => 16, 'format' => 17, 'length' => 18, 'album_title' => 19, 'genre' => 20, 'comments' => 21, 'year' => 22, 'track_number' => 23, 'channels' => 24, 'url' => 25, 'bpm' => 26, 'rating' => 27, 'encoded_by' => 28, 'disc_number' => 29, 'mood' => 30, 'label' => 31, 'composer' => 32, 'encoder' => 33, 'checksum' => 34, 'lyrics' => 35, 'orchestra' => 36, 'conductor' => 37, 'lyricist' => 38, 'original_lyricist' => 39, 'radio_station_name' => 40, 'info_url' => 41, 'artist_url' => 42, 'audio_source_url' => 43, 'radio_station_url' => 44, 'buy_this_url' => 45, 'isrc_number' => 46, 'catalog_number' => 47, 'original_artist' => 48, 'copyright' => 49, 'report_datetime' => 50, 'report_location' => 51, 'report_organization' => 52, 'subject' => 53, 'contributor' => 54, 'language' => 55, 'file_exists' => 56, 'soundcloud_id' => 57, 'soundcloud_error_code' => 58, 'soundcloud_error_msg' => 59, 'soundcloud_link_to_file' => 60, 'soundcloud_upload_time' => 61, 'replay_gain' => 62, 'owner_id' => 63, 'cuein' => 64, 'cueout' => 65, 'silan_check' => 66, 'hidden' => 67, 'is_scheduled' => 68, 'is_playlist' => 69, ), + BasePeer::TYPE_PHPNAME => array ('DbId' => 0, 'DbName' => 1, 'DbMime' => 2, 'DbFtype' => 3, 'DbDirectory' => 4, 'DbFilepath' => 5, 'DbImportStatus' => 6, 'DbCurrentlyaccessing' => 7, 'DbEditedby' => 8, 'DbMtime' => 9, 'DbUtime' => 10, 'DbLPtime' => 11, 'DbMd5' => 12, 'DbTrackTitle' => 13, 'DbArtistName' => 14, 'DbBitRate' => 15, 'DbSampleRate' => 16, 'DbFormat' => 17, 'DbLength' => 18, 'DbAlbumTitle' => 19, 'DbGenre' => 20, 'DbComments' => 21, 'DbYear' => 22, 'DbTrackNumber' => 23, 'DbChannels' => 24, 'DbUrl' => 25, 'DbBpm' => 26, 'DbRating' => 27, 'DbEncodedBy' => 28, 'DbDiscNumber' => 29, 'DbMood' => 30, 'DbLabel' => 31, 'DbComposer' => 32, 'DbEncoder' => 33, 'DbChecksum' => 34, 'DbLyrics' => 35, 'DbOrchestra' => 36, 'DbConductor' => 37, 'DbLyricist' => 38, 'DbOriginalLyricist' => 39, 'DbRadioStationName' => 40, 'DbInfoUrl' => 41, 'DbArtistUrl' => 42, 'DbAudioSourceUrl' => 43, 'DbRadioStationUrl' => 44, 'DbBuyThisUrl' => 45, 'DbIsrcNumber' => 46, 'DbCatalogNumber' => 47, 'DbOriginalArtist' => 48, 'DbCopyright' => 49, 'DbReportDatetime' => 50, 'DbReportLocation' => 51, 'DbReportOrganization' => 52, 'DbSubject' => 53, 'DbContributor' => 54, 'DbLanguage' => 55, 'DbFileExists' => 56, 'DbSoundcloudId' => 57, 'DbSoundcloudErrorCode' => 58, 'DbSoundcloudErrorMsg' => 59, 'DbSoundcloudLinkToFile' => 60, 'DbSoundCloundUploadTime' => 61, 'DbReplayGain' => 62, 'DbOwnerId' => 63, 'DbCuein' => 64, 'DbCueout' => 65, 'DbSilanCheck' => 66, 'DbHidden' => 67, 'DbIsScheduled' => 68, 'DbIsPlaylist' => 69, ), + BasePeer::TYPE_STUDLYPHPNAME => array ('dbId' => 0, 'dbName' => 1, 'dbMime' => 2, 'dbFtype' => 3, 'dbDirectory' => 4, 'dbFilepath' => 5, 'dbImportStatus' => 6, 'dbCurrentlyaccessing' => 7, 'dbEditedby' => 8, 'dbMtime' => 9, 'dbUtime' => 10, 'dbLPtime' => 11, 'dbMd5' => 12, 'dbTrackTitle' => 13, 'dbArtistName' => 14, 'dbBitRate' => 15, 'dbSampleRate' => 16, 'dbFormat' => 17, 'dbLength' => 18, 'dbAlbumTitle' => 19, 'dbGenre' => 20, 'dbComments' => 21, 'dbYear' => 22, 'dbTrackNumber' => 23, 'dbChannels' => 24, 'dbUrl' => 25, 'dbBpm' => 26, 'dbRating' => 27, 'dbEncodedBy' => 28, 'dbDiscNumber' => 29, 'dbMood' => 30, 'dbLabel' => 31, 'dbComposer' => 32, 'dbEncoder' => 33, 'dbChecksum' => 34, 'dbLyrics' => 35, 'dbOrchestra' => 36, 'dbConductor' => 37, 'dbLyricist' => 38, 'dbOriginalLyricist' => 39, 'dbRadioStationName' => 40, 'dbInfoUrl' => 41, 'dbArtistUrl' => 42, 'dbAudioSourceUrl' => 43, 'dbRadioStationUrl' => 44, 'dbBuyThisUrl' => 45, 'dbIsrcNumber' => 46, 'dbCatalogNumber' => 47, 'dbOriginalArtist' => 48, 'dbCopyright' => 49, 'dbReportDatetime' => 50, 'dbReportLocation' => 51, 'dbReportOrganization' => 52, 'dbSubject' => 53, 'dbContributor' => 54, 'dbLanguage' => 55, 'dbFileExists' => 56, 'dbSoundcloudId' => 57, 'dbSoundcloudErrorCode' => 58, 'dbSoundcloudErrorMsg' => 59, 'dbSoundcloudLinkToFile' => 60, 'dbSoundCloundUploadTime' => 61, 'dbReplayGain' => 62, 'dbOwnerId' => 63, 'dbCuein' => 64, 'dbCueout' => 65, 'dbSilanCheck' => 66, 'dbHidden' => 67, 'dbIsScheduled' => 68, 'dbIsPlaylist' => 69, ), + BasePeer::TYPE_COLNAME => array (self::ID => 0, self::NAME => 1, self::MIME => 2, self::FTYPE => 3, self::DIRECTORY => 4, self::FILEPATH => 5, self::IMPORT_STATUS => 6, self::CURRENTLYACCESSING => 7, self::EDITEDBY => 8, self::MTIME => 9, self::UTIME => 10, self::LPTIME => 11, self::MD5 => 12, self::TRACK_TITLE => 13, self::ARTIST_NAME => 14, self::BIT_RATE => 15, self::SAMPLE_RATE => 16, self::FORMAT => 17, self::LENGTH => 18, self::ALBUM_TITLE => 19, self::GENRE => 20, self::COMMENTS => 21, self::YEAR => 22, self::TRACK_NUMBER => 23, self::CHANNELS => 24, self::URL => 25, self::BPM => 26, self::RATING => 27, self::ENCODED_BY => 28, self::DISC_NUMBER => 29, self::MOOD => 30, self::LABEL => 31, self::COMPOSER => 32, self::ENCODER => 33, self::CHECKSUM => 34, self::LYRICS => 35, self::ORCHESTRA => 36, self::CONDUCTOR => 37, self::LYRICIST => 38, self::ORIGINAL_LYRICIST => 39, self::RADIO_STATION_NAME => 40, self::INFO_URL => 41, self::ARTIST_URL => 42, self::AUDIO_SOURCE_URL => 43, self::RADIO_STATION_URL => 44, self::BUY_THIS_URL => 45, self::ISRC_NUMBER => 46, self::CATALOG_NUMBER => 47, self::ORIGINAL_ARTIST => 48, self::COPYRIGHT => 49, self::REPORT_DATETIME => 50, self::REPORT_LOCATION => 51, self::REPORT_ORGANIZATION => 52, self::SUBJECT => 53, self::CONTRIBUTOR => 54, self::LANGUAGE => 55, self::FILE_EXISTS => 56, self::SOUNDCLOUD_ID => 57, self::SOUNDCLOUD_ERROR_CODE => 58, self::SOUNDCLOUD_ERROR_MSG => 59, self::SOUNDCLOUD_LINK_TO_FILE => 60, self::SOUNDCLOUD_UPLOAD_TIME => 61, self::REPLAY_GAIN => 62, self::OWNER_ID => 63, self::CUEIN => 64, self::CUEOUT => 65, self::SILAN_CHECK => 66, self::HIDDEN => 67, self::IS_SCHEDULED => 68, self::IS_PLAYLIST => 69, ), + BasePeer::TYPE_RAW_COLNAME => array ('ID' => 0, 'NAME' => 1, 'MIME' => 2, 'FTYPE' => 3, 'DIRECTORY' => 4, 'FILEPATH' => 5, 'IMPORT_STATUS' => 6, 'CURRENTLYACCESSING' => 7, 'EDITEDBY' => 8, 'MTIME' => 9, 'UTIME' => 10, 'LPTIME' => 11, 'MD5' => 12, 'TRACK_TITLE' => 13, 'ARTIST_NAME' => 14, 'BIT_RATE' => 15, 'SAMPLE_RATE' => 16, 'FORMAT' => 17, 'LENGTH' => 18, 'ALBUM_TITLE' => 19, 'GENRE' => 20, 'COMMENTS' => 21, 'YEAR' => 22, 'TRACK_NUMBER' => 23, 'CHANNELS' => 24, 'URL' => 25, 'BPM' => 26, 'RATING' => 27, 'ENCODED_BY' => 28, 'DISC_NUMBER' => 29, 'MOOD' => 30, 'LABEL' => 31, 'COMPOSER' => 32, 'ENCODER' => 33, 'CHECKSUM' => 34, 'LYRICS' => 35, 'ORCHESTRA' => 36, 'CONDUCTOR' => 37, 'LYRICIST' => 38, 'ORIGINAL_LYRICIST' => 39, 'RADIO_STATION_NAME' => 40, 'INFO_URL' => 41, 'ARTIST_URL' => 42, 'AUDIO_SOURCE_URL' => 43, 'RADIO_STATION_URL' => 44, 'BUY_THIS_URL' => 45, 'ISRC_NUMBER' => 46, 'CATALOG_NUMBER' => 47, 'ORIGINAL_ARTIST' => 48, 'COPYRIGHT' => 49, 'REPORT_DATETIME' => 50, 'REPORT_LOCATION' => 51, 'REPORT_ORGANIZATION' => 52, 'SUBJECT' => 53, 'CONTRIBUTOR' => 54, 'LANGUAGE' => 55, 'FILE_EXISTS' => 56, 'SOUNDCLOUD_ID' => 57, 'SOUNDCLOUD_ERROR_CODE' => 58, 'SOUNDCLOUD_ERROR_MSG' => 59, 'SOUNDCLOUD_LINK_TO_FILE' => 60, 'SOUNDCLOUD_UPLOAD_TIME' => 61, 'REPLAY_GAIN' => 62, 'OWNER_ID' => 63, 'CUEIN' => 64, 'CUEOUT' => 65, 'SILAN_CHECK' => 66, 'HIDDEN' => 67, 'IS_SCHEDULED' => 68, 'IS_PLAYLIST' => 69, ), + BasePeer::TYPE_FIELDNAME => array ('id' => 0, 'name' => 1, 'mime' => 2, 'ftype' => 3, 'directory' => 4, 'filepath' => 5, 'import_status' => 6, 'currentlyaccessing' => 7, 'editedby' => 8, 'mtime' => 9, 'utime' => 10, 'lptime' => 11, 'md5' => 12, 'track_title' => 13, 'artist_name' => 14, 'bit_rate' => 15, 'sample_rate' => 16, 'format' => 17, 'length' => 18, 'album_title' => 19, 'genre' => 20, 'comments' => 21, 'year' => 22, 'track_number' => 23, 'channels' => 24, 'url' => 25, 'bpm' => 26, 'rating' => 27, 'encoded_by' => 28, 'disc_number' => 29, 'mood' => 30, 'label' => 31, 'composer' => 32, 'encoder' => 33, 'checksum' => 34, 'lyrics' => 35, 'orchestra' => 36, 'conductor' => 37, 'lyricist' => 38, 'original_lyricist' => 39, 'radio_station_name' => 40, 'info_url' => 41, 'artist_url' => 42, 'audio_source_url' => 43, 'radio_station_url' => 44, 'buy_this_url' => 45, 'isrc_number' => 46, 'catalog_number' => 47, 'original_artist' => 48, 'copyright' => 49, 'report_datetime' => 50, 'report_location' => 51, 'report_organization' => 52, 'subject' => 53, 'contributor' => 54, 'language' => 55, 'file_exists' => 56, 'soundcloud_id' => 57, 'soundcloud_error_code' => 58, 'soundcloud_error_msg' => 59, 'soundcloud_link_to_file' => 60, 'soundcloud_upload_time' => 61, 'replay_gain' => 62, 'owner_id' => 63, 'cuein' => 64, 'cueout' => 65, 'silan_check' => 66, 'hidden' => 67, 'is_scheduled' => 68, 'is_playlist' => 69, ), BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, ) ); @@ -355,7 +355,7 @@ abstract class BaseCcFilesPeer { $criteria->addSelectColumn(CcFilesPeer::FTYPE); $criteria->addSelectColumn(CcFilesPeer::DIRECTORY); $criteria->addSelectColumn(CcFilesPeer::FILEPATH); - $criteria->addSelectColumn(CcFilesPeer::STATE); + $criteria->addSelectColumn(CcFilesPeer::IMPORT_STATUS); $criteria->addSelectColumn(CcFilesPeer::CURRENTLYACCESSING); $criteria->addSelectColumn(CcFilesPeer::EDITEDBY); $criteria->addSelectColumn(CcFilesPeer::MTIME); @@ -426,7 +426,7 @@ abstract class BaseCcFilesPeer { $criteria->addSelectColumn($alias . '.FTYPE'); $criteria->addSelectColumn($alias . '.DIRECTORY'); $criteria->addSelectColumn($alias . '.FILEPATH'); - $criteria->addSelectColumn($alias . '.STATE'); + $criteria->addSelectColumn($alias . '.IMPORT_STATUS'); $criteria->addSelectColumn($alias . '.CURRENTLYACCESSING'); $criteria->addSelectColumn($alias . '.EDITEDBY'); $criteria->addSelectColumn($alias . '.MTIME'); diff --git a/airtime_mvc/application/models/airtime/om/BaseCcFilesQuery.php b/airtime_mvc/application/models/airtime/om/BaseCcFilesQuery.php index 50a21a332..e93985922 100644 --- a/airtime_mvc/application/models/airtime/om/BaseCcFilesQuery.php +++ b/airtime_mvc/application/models/airtime/om/BaseCcFilesQuery.php @@ -12,7 +12,7 @@ * @method CcFilesQuery orderByDbFtype($order = Criteria::ASC) Order by the ftype column * @method CcFilesQuery orderByDbDirectory($order = Criteria::ASC) Order by the directory column * @method CcFilesQuery orderByDbFilepath($order = Criteria::ASC) Order by the filepath column - * @method CcFilesQuery orderByDbState($order = Criteria::ASC) Order by the state column + * @method CcFilesQuery orderByDbImportStatus($order = Criteria::ASC) Order by the import_status column * @method CcFilesQuery orderByDbCurrentlyaccessing($order = Criteria::ASC) Order by the currentlyaccessing column * @method CcFilesQuery orderByDbEditedby($order = Criteria::ASC) Order by the editedby column * @method CcFilesQuery orderByDbMtime($order = Criteria::ASC) Order by the mtime column @@ -83,7 +83,7 @@ * @method CcFilesQuery groupByDbFtype() Group by the ftype column * @method CcFilesQuery groupByDbDirectory() Group by the directory column * @method CcFilesQuery groupByDbFilepath() Group by the filepath column - * @method CcFilesQuery groupByDbState() Group by the state column + * @method CcFilesQuery groupByDbImportStatus() Group by the import_status column * @method CcFilesQuery groupByDbCurrentlyaccessing() Group by the currentlyaccessing column * @method CcFilesQuery groupByDbEditedby() Group by the editedby column * @method CcFilesQuery groupByDbMtime() Group by the mtime column @@ -193,7 +193,7 @@ * @method CcFiles findOneByDbFtype(string $ftype) Return the first CcFiles filtered by the ftype column * @method CcFiles findOneByDbDirectory(int $directory) Return the first CcFiles filtered by the directory column * @method CcFiles findOneByDbFilepath(string $filepath) Return the first CcFiles filtered by the filepath column - * @method CcFiles findOneByDbState(string $state) Return the first CcFiles filtered by the state column + * @method CcFiles findOneByDbImportStatus(int $import_status) Return the first CcFiles filtered by the import_status column * @method CcFiles findOneByDbCurrentlyaccessing(int $currentlyaccessing) Return the first CcFiles filtered by the currentlyaccessing column * @method CcFiles findOneByDbEditedby(int $editedby) Return the first CcFiles filtered by the editedby column * @method CcFiles findOneByDbMtime(string $mtime) Return the first CcFiles filtered by the mtime column @@ -264,7 +264,7 @@ * @method array findByDbFtype(string $ftype) Return CcFiles objects filtered by the ftype column * @method array findByDbDirectory(int $directory) Return CcFiles objects filtered by the directory column * @method array findByDbFilepath(string $filepath) Return CcFiles objects filtered by the filepath column - * @method array findByDbState(string $state) Return CcFiles objects filtered by the state column + * @method array findByDbImportStatus(int $import_status) Return CcFiles objects filtered by the import_status column * @method array findByDbCurrentlyaccessing(int $currentlyaccessing) Return CcFiles objects filtered by the currentlyaccessing column * @method array findByDbEditedby(int $editedby) Return CcFiles objects filtered by the editedby column * @method array findByDbMtime(string $mtime) Return CcFiles objects filtered by the mtime column @@ -574,25 +574,34 @@ abstract class BaseCcFilesQuery extends ModelCriteria } /** - * Filter the query on the state column + * Filter the query on the import_status column * - * @param string $dbState The value to use as filter. - * Accepts wildcards (* and % trigger a LIKE) + * @param int|array $dbImportStatus The value to use as filter. + * Accepts an associative array('min' => $minValue, 'max' => $maxValue) * @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL * * @return CcFilesQuery The current query, for fluid interface */ - public function filterByDbState($dbState = null, $comparison = null) + public function filterByDbImportStatus($dbImportStatus = null, $comparison = null) { - if (null === $comparison) { - if (is_array($dbState)) { + if (is_array($dbImportStatus)) { + $useMinMax = false; + if (isset($dbImportStatus['min'])) { + $this->addUsingAlias(CcFilesPeer::IMPORT_STATUS, $dbImportStatus['min'], Criteria::GREATER_EQUAL); + $useMinMax = true; + } + if (isset($dbImportStatus['max'])) { + $this->addUsingAlias(CcFilesPeer::IMPORT_STATUS, $dbImportStatus['max'], Criteria::LESS_EQUAL); + $useMinMax = true; + } + if ($useMinMax) { + return $this; + } + if (null === $comparison) { $comparison = Criteria::IN; - } elseif (preg_match('/[\%\*]/', $dbState)) { - $dbState = str_replace('*', '%', $dbState); - $comparison = Criteria::LIKE; } } - return $this->addUsingAlias(CcFilesPeer::STATE, $dbState, $comparison); + return $this->addUsingAlias(CcFilesPeer::IMPORT_STATUS, $dbImportStatus, $comparison); } /** diff --git a/airtime_mvc/application/modules/rest/Bootstrap.php b/airtime_mvc/application/modules/rest/Bootstrap.php new file mode 100644 index 000000000..e7017ba16 --- /dev/null +++ b/airtime_mvc/application/modules/rest/Bootstrap.php @@ -0,0 +1,27 @@ +getRouter(); + + $restRoute = new Zend_Rest_Route($front, array(), array( + 'rest'=> array('media'))); + assert($router->addRoute('rest', $restRoute)); + + $downloadRoute = new Zend_Controller_Router_Route( + 'rest/media/:id/download', + array( + 'controller' => 'media', + 'action' => 'download', + 'module' => 'rest' + ), + array( + 'id' => '\d+' + ) + ); + $router->addRoute('download', $downloadRoute); + } +} \ No newline at end of file diff --git a/airtime_mvc/application/modules/rest/controllers/MediaController.php b/airtime_mvc/application/modules/rest/controllers/MediaController.php new file mode 100644 index 000000000..f092574b3 --- /dev/null +++ b/airtime_mvc/application/modules/rest/controllers/MediaController.php @@ -0,0 +1,382 @@ +view->layout()->disableLayout(); + } + + public function indexAction() + { + if (!$this->verifyAuth(true, true)) + { + return; + } + + $files_array = array(); + foreach (CcFilesQuery::create()->find() as $file) + { + array_push($files_array, $this->sanitizeResponse($file)); + } + + $this->getResponse() + ->setHttpResponseCode(200) + ->appendBody(json_encode($files_array)); + + /** TODO: Use this simpler code instead after we upgrade to Propel 1.7 (Airtime 2.6.x branch): + $this->getResponse() + ->setHttpResponseCode(200) + ->appendBody(json_encode(CcFilesQuery::create()->find()->toArray(BasePeer::TYPE_FIELDNAME))); + */ + } + + public function downloadAction() + { + if (!$this->verifyAuth(true, true)) + { + return; + } + + $id = $this->getId(); + if (!$id) { + return; + } + + $file = CcFilesQuery::create()->findPk($id); + if ($file) { + $con = Propel::getConnection(); + $storedFile = new Application_Model_StoredFile($file, $con); + $baseUrl = Application_Common_OsPath::getBaseDir(); + + $this->getResponse() + ->setHttpResponseCode(200) + ->appendBody($this->_redirect($storedFile->getRelativeFileUrl($baseUrl).'/download/true')); + } else { + $this->fileNotFoundResponse(); + } + } + + public function getAction() + { + if (!$this->verifyAuth(true, true)) + { + return; + } + + $id = $this->getId(); + if (!$id) { + return; + } + + $file = CcFilesQuery::create()->findPk($id); + if ($file) { + + $this->getResponse() + ->setHttpResponseCode(200) + ->appendBody(json_encode($this->sanitizeResponse($file))); + } else { + $this->fileNotFoundResponse(); + } + } + + public function postAction() + { + if (!$this->verifyAuth(true, true)) + { + return; + } + + //If we do get an ID on a POST, then that doesn't make any sense + //since POST is only for creating. + if ($id = $this->_getParam('id', false)) { + $resp = $this->getResponse(); + $resp->setHttpResponseCode(400); + $resp->appendBody("ERROR: ID should not be specified when using POST. POST is only used for file creation, and an ID will be chosen by Airtime"); + return; + } + + $file = new CcFiles(); + $file->fromArray($this->validateRequestData($this->getRequest()->getPost())); + $file->setDbOwnerId($this->getOwnerId()); + $now = new DateTime("now", new DateTimeZone("UTC")); + $file->setDbTrackTitle($_FILES["file"]["name"]); + $file->setDbUtime($now); + $file->setDbMtime($now); + $file->setDbHidden(true); + $file->save(); + + $callbackUrl = $this->getRequest()->getScheme() . '://' . $this->getRequest()->getHttpHost() . $this->getRequest()->getRequestUri() . "/" . $file->getPrimaryKey(); + + $this->processUploadedFile($callbackUrl, $_FILES["file"]["name"], $this->getOwnerId()); + + $this->getResponse() + ->setHttpResponseCode(201) + ->appendBody(json_encode($this->sanitizeResponse($file))); + } + + public function putAction() + { + if (!$this->verifyAuth(true, true)) + { + return; + } + + $id = $this->getId(); + if (!$id) { + return; + } + + $file = CcFilesQuery::create()->findPk($id); + if ($file) + { + $requestData = json_decode($this->getRequest()->getRawBody(), true); + $file->fromArray($this->validateRequestData($requestData), BasePeer::TYPE_FIELDNAME); + + //Our RESTful API takes "full_path" as a field, which we then split and translate to match + //our internal schema. Internally, file path is stored relative to a directory, with the directory + //as a foreign key to cc_music_dirs. + if (isset($requestData["full_path"])) { + Application_Model_Preference::updateDiskUsage(filesize($requestData["full_path"])); + + $fullPath = $requestData["full_path"]; + $storDir = Application_Model_MusicDir::getStorDir()->getDirectory(); + $pos = strpos($fullPath, $storDir); + + if ($pos !== FALSE) + { + assert($pos == 0); //Path must start with the stor directory path + + $filePathRelativeToStor = substr($fullPath, strlen($storDir)); + $file->setDbFilepath($filePathRelativeToStor); + $file->setDbDirectory(1); //1 corresponds to the default stor/imported directory. + } + } + + $now = new DateTime("now", new DateTimeZone("UTC")); + $file->setDbMtime($now); + $file->save(); + $this->getResponse() + ->setHttpResponseCode(200) + ->appendBody(json_encode($this->sanitizeResponse($file))); + } else { + $this->fileNotFoundResponse(); + } + } + + public function deleteAction() + { + if (!$this->verifyAuth(true, true)) + { + return; + } + + $id = $this->getId(); + if (!$id) { + return; + } + $file = CcFilesQuery::create()->findPk($id); + if ($file) { + $con = Propel::getConnection(); + $storedFile = new Application_Model_StoredFile($file, $con); + if ($storedFile->existsOnDisk()) { + $storedFile->delete(); //TODO: This checks your session permissions... Make it work without a session? + } + $file->delete(); + $this->getResponse() + ->setHttpResponseCode(204); + } else { + $this->fileNotFoundResponse(); + } + } + + private function getId() + { + if (!$id = $this->_getParam('id', false)) { + $resp = $this->getResponse(); + $resp->setHttpResponseCode(400); + $resp->appendBody("ERROR: No file ID specified."); + return false; + } + return $id; + } + + private function verifyAuth($checkApiKey, $checkSession) + { + //Session takes precedence over API key for now: + if ($checkSession && $this->verifySession()) + { + return true; + } + + if ($checkApiKey && $this->verifyAPIKey()) + { + return true; + } + + $resp = $this->getResponse(); + $resp->setHttpResponseCode(401); + $resp->appendBody("ERROR: Incorrect API key."); + + return false; + } + + + private function verifyAPIKey() + { + //The API key is passed in via HTTP "basic authentication": + // http://en.wikipedia.org/wiki/Basic_access_authentication + + $CC_CONFIG = Config::getConfig(); + + //Decode the API key that was passed to us in the HTTP request. + $authHeader = $this->getRequest()->getHeader("Authorization"); + $encodedRequestApiKey = substr($authHeader, strlen("Basic ")); + $encodedStoredApiKey = base64_encode($CC_CONFIG["apiKey"][0] . ":"); + + if ($encodedRequestApiKey === $encodedStoredApiKey) + { + return true; + } else { + return false; + } + + return false; + } + + private function verifySession() + { + $auth = Zend_Auth::getInstance(); + if ($auth->hasIdentity()) + { + return true; + } + return false; + + //Token checking stub code. We'd need to change LoginController.php to generate a token too, but + //but luckily all the token code already exists and works. + //$auth = new Application_Model_Auth(); + //$auth->checkToken(Application_Model_Preference::getUserId(), $token); + } + + private function fileNotFoundResponse() + { + $resp = $this->getResponse(); + $resp->setHttpResponseCode(404); + $resp->appendBody("ERROR: Media not found."); + } + + private function processUploadedFile($callbackUrl, $originalFilename, $ownerId) + { + $CC_CONFIG = Config::getConfig(); + $apiKey = $CC_CONFIG["apiKey"][0]; + + $upload_dir = ini_get("upload_tmp_dir") . DIRECTORY_SEPARATOR . "plupload"; + $tempFilePath = Application_Model_StoredFile::uploadFile($upload_dir); + $tempFileName = basename($tempFilePath); + + //TODO: Remove copyFileToStor from StoredFile... + + //TODO: Remove uploadFileAction from ApiController.php **IMPORTANT** - It's used by the recorder daemon? + + $upload_dir = ini_get("upload_tmp_dir") . DIRECTORY_SEPARATOR . "plupload"; + $tempFilePath = $upload_dir . "/" . $tempFileName; + + $storDir = Application_Model_MusicDir::getStorDir(); + //$finalFullFilePath = $storDir->getDirectory() . "/imported/" . $ownerId . "/" . $originalFilename; + $importedStorageDirectory = $storDir->getDirectory() . "/imported/" . $ownerId; + + + try { + //Copy the temporary file over to the "organize" folder so that it's off our webserver + //and accessible by airtime_analyzer which could be running on a different machine. + $newTempFilePath = Application_Model_StoredFile::copyFileToStor($tempFilePath, $originalFilename); + } catch (Exception $e) { + Logging::error($e->getMessage()); + } + + //Logging::info("New temporary file path: " . $newTempFilePath); + //Logging::info("Final file path: " . $finalFullFilePath); + + //Dispatch a message to airtime_analyzer through RabbitMQ, + //notifying it that there's a new upload to process! + Application_Model_RabbitMq::SendMessageToAnalyzer($newTempFilePath, + $importedStorageDirectory, $originalFilename, + $callbackUrl, $apiKey); + } + + private function getOwnerId() + { + try { + if ($this->verifySession()) { + $service_user = new Application_Service_UserService(); + return $service_user->getCurrentUser()->getDbId(); + } else { + $defaultOwner = CcSubjsQuery::create() + ->filterByDbType('A') + ->orderByDbId() + ->findOne(); + if (!$defaultOwner) { + // what to do if there is no admin user? + // should we handle this case? + return null; + } + return $defaultOwner->getDbId(); + } + } catch(Exception $e) { + Logging::info($e->getMessage()); + } + } + + /** + * + * Strips out fields from incoming request data that should never be modified + * from outside of Airtime + * @param array $data + */ + private function validateRequestData($data) + { + foreach ($this->blackList as $key) { + unset($data[$key]); + } + + return $data; + } + + /** + * + * Strips out the private fields we do not want to send back in API responses + */ + //TODO: rename this function? + public function sanitizeResponse($file) + { + $response = $file->toArray(BasePeer::TYPE_FIELDNAME); + + foreach ($this->privateFields as $key) { + unset($response[$key]); + } + + return $response; + } + +} + diff --git a/airtime_mvc/application/modules/rest/views/scripts/media/delete.phtml b/airtime_mvc/application/modules/rest/views/scripts/media/delete.phtml new file mode 100644 index 000000000..e69de29bb diff --git a/airtime_mvc/application/modules/rest/views/scripts/media/download.phtml b/airtime_mvc/application/modules/rest/views/scripts/media/download.phtml new file mode 100644 index 000000000..e69de29bb diff --git a/airtime_mvc/application/modules/rest/views/scripts/media/get.phtml b/airtime_mvc/application/modules/rest/views/scripts/media/get.phtml new file mode 100644 index 000000000..e69de29bb diff --git a/airtime_mvc/application/modules/rest/views/scripts/media/index.phtml b/airtime_mvc/application/modules/rest/views/scripts/media/index.phtml new file mode 100644 index 000000000..e69de29bb diff --git a/airtime_mvc/application/modules/rest/views/scripts/media/post.phtml b/airtime_mvc/application/modules/rest/views/scripts/media/post.phtml new file mode 100644 index 000000000..e69de29bb diff --git a/airtime_mvc/application/modules/rest/views/scripts/media/put.phtml b/airtime_mvc/application/modules/rest/views/scripts/media/put.phtml new file mode 100644 index 000000000..e69de29bb diff --git a/airtime_mvc/application/views/scripts/plupload/index.phtml b/airtime_mvc/application/views/scripts/plupload/index.phtml index cf236b8d6..1c3f42e55 100644 --- a/airtime_mvc/application/views/scripts/plupload/index.phtml +++ b/airtime_mvc/application/views/scripts/plupload/index.phtml @@ -9,3 +9,19 @@
+ +
+
+ +
+
+ + + +
+
+

Recent Uploads

+
+
+
+
diff --git a/airtime_mvc/build/build.properties b/airtime_mvc/build/build.properties index de8c64cb5..da4a76877 100644 --- a/airtime_mvc/build/build.properties +++ b/airtime_mvc/build/build.properties @@ -1,6 +1,6 @@ #Note: project.home is automatically generated by the propel-install script. #Any manual changes to this value will be overwritten. -project.home = /home/asantoni/airtime/airtime_mvc +project.home = /home/ubuntu/airtime/airtime_mvc project.build = ${project.home}/build #Database driver diff --git a/airtime_mvc/build/schema.xml b/airtime_mvc/build/schema.xml index d5ba76516..e614748bf 100644 --- a/airtime_mvc/build/schema.xml +++ b/airtime_mvc/build/schema.xml @@ -18,7 +18,7 @@ - + diff --git a/airtime_mvc/build/sql/schema.sql b/airtime_mvc/build/sql/schema.sql index 13d1654a5..8eb638878 100644 --- a/airtime_mvc/build/sql/schema.sql +++ b/airtime_mvc/build/sql/schema.sql @@ -36,7 +36,7 @@ CREATE TABLE "cc_files" "ftype" VARCHAR(128) default '' NOT NULL, "directory" INTEGER, "filepath" TEXT default '', - "state" VARCHAR(128) default 'empty' NOT NULL, + "import_status" INTEGER default 0 NOT NULL, "currentlyaccessing" INTEGER default 0 NOT NULL, "editedby" INTEGER, "mtime" TIMESTAMP(6), diff --git a/airtime_mvc/public/index.php b/airtime_mvc/public/index.php index ddbf84738..b4fd27b33 100644 --- a/airtime_mvc/public/index.php +++ b/airtime_mvc/public/index.php @@ -49,6 +49,29 @@ $application = new Zend_Application( APPLICATION_ENV, $_SERVER["AIRTIME_APPINI"] ); -$application->bootstrap() - ->run(); +require_once (APPLICATION_PATH."/logging/Logging.php"); +Logging::setLogPath('/var/log/airtime/zendphp.log'); + +// Create application, bootstrap, and run +try { + $sapi_type = php_sapi_name(); + if (substr($sapi_type, 0, 3) == 'cli') { + set_include_path(APPLICATION_PATH . PATH_SEPARATOR . get_include_path()); + require_once("Bootstrap.php"); + } else { + $application->bootstrap()->run(); + } +} catch (Exception $e) { + echo $e->getMessage(); + echo "
";
+    echo $e->getTraceAsString();
+    echo "
"; + Logging::info($e->getMessage()); + if (VERBOSE_STACK_TRACE) { + Logging::info($e->getTraceAsString()); + } else { + Logging::info($e->getTrace()); + } + throw $e; +} diff --git a/airtime_mvc/public/js/airtime/library/plupload.js b/airtime_mvc/public/js/airtime/library/plupload.js index 2d76b3383..71aff7336 100644 --- a/airtime_mvc/public/js/airtime/library/plupload.js +++ b/airtime_mvc/public/js/airtime/library/plupload.js @@ -1,12 +1,24 @@ $(document).ready(function() { var uploader; + var self = this; + self.uploadFilter = "all"; + + self.IMPORT_STATUS_CODES = { + 0 : { message: $.i18n._("Successfully imported")}, + 1 : { message: $.i18n._("Pending import")}, + 2 : { message: $.i18n._("Import failed.")}, + UNKNOWN : { message: $.i18n._("Unknown")} + }; + if (Object.freeze) { + Object.freeze(self.IMPORT_STATUS_CODES); + } $("#plupload_files").pluploadQueue({ // General settings runtimes : 'gears, html5, html4', - url : baseUrl+'Plupload/upload/format/json', - chunk_size : '5mb', + 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 : [ @@ -16,37 +28,17 @@ $(document).ready(function() { uploader = $("#plupload_files").pluploadQueue(); - uploader.bind('FileUploaded', function(up, file, json) { - var j = jQuery.parseJSON(json.response); - - if(j.error !== undefined) { - var row = $("") - .append('' + file.name +'') - .append('' + j.error.message + ''); - - $("#plupload_error").find("table").append(row); - $("#plupload_error table").css("display", "inline-table"); - }else{ - var tempFileName = j.tempfilepath; - $.get(baseUrl+'Plupload/copyfile/format/json/name/'+ - encodeURIComponent(file.name)+'/tempname/' + - encodeURIComponent(tempFileName), function(jr){ - if(jr.error !== undefined) { - var row = $("") - .append('' + file.name +'') - .append('' + jr.error.message + ''); - - $("#plupload_error").find("table").append(row); - $("#plupload_error table").css("display", "inline-table"); - } - }); - } + 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) + uploadProgress = (uploader.files.length > 0); }); uploader.bind('UploadComplete', function(){ @@ -59,5 +51,140 @@ $(document).ready(function() { "\n", "\n"); } }); + + self.renderImportStatus = function ( data, type, full ) { + if (typeof data !== "number") { + console.log("Invalid data type for the import_status."); + return; + } + var statusStr = self.IMPORT_STATUS_CODES.UNKNOWN.message; + var importStatusCode = data; + if (self.IMPORT_STATUS_CODES[importStatusCode]) { + statusStr = self.IMPORT_STATUS_CODES[importStatusCode].message; + }; + + return statusStr; + }; + + self.renderFileActions = function ( data, type, full ) { + if (full.import_status == 0) { + return '' + $.i18n._('Delete from Library') + ''; + } else if (full.import_status == 1) { + //No actions for pending files + return $.i18n._('N/A'); + } else { //Failed downloads + return '' + $.i18n._('Clear') + ''; + } + }; + + $("#recent_uploads_table").on("click", "a.deleteFileAction", function () { + //Grab the file object for the row that was clicked. + // Some tips from the DataTables forums: + // fnGetData is used to get the object behind the row - you can also use + // fnGetPosition if you need to get the index instead + file = $("#recent_uploads_table").dataTable().fnGetData($(this).closest("tr")[0]); + + $.ajax({ + type: 'DELETE', + url: '/rest/media/' + file.id, + success: function(resp) { + self.recentUploadsTable.fnDraw(); + }, + error: function() { + alert($.i18n._("Error: The file could not be deleted. Please try again later.")); + } + }); + }); + + self.setupRecentUploadsTable = function() { + recentUploadsTable = $("#recent_uploads_table").dataTable({ + "bJQueryUI": true, + "bProcessing": false, + "bServerSide": true, + "sAjaxSource": '/Plupload/recent-uploads/format/json', + "sAjaxDataProp": 'files', + "bSearchable": false, + "bInfo": true, + //"sScrollY": "200px", + "bFilter": false, + "bSort": false, + "sDom": '<"H"l>frtip', + "bPaginate" : true, + "sPaginationType": "full_numbers", + "aoColumns": [ + { "mData" : "artist_name", "sTitle" : $.i18n._("Creator") }, + { "mData" : "track_title", "sTitle" : $.i18n._("Title") }, + { "mData" : "import_status", "sTitle" : $.i18n._("Import Status"), + "mRender": self.renderImportStatus + }, + { "mData" : "utime", "sTitle" : $.i18n._("Uploaded") }, + { "mData" : "id", "sTitle" : $.i18n._("Actions"), + "mRender": self.renderFileActions + } + ], + "fnServerData": function ( sSource, aoData, fnCallback ) { + /* Add some extra data to the sender */ + aoData.push( { "name": "uploadFilter", "value": self.uploadFilter } ); + $.getJSON( sSource, aoData, function (json) { + fnCallback(json); + if (json.files) { + var areAnyFileImportsPending = false; + for (var i = 0; i < json.files.length; i++) { + //console.log(file); + var file = json.files[i]; + if (file.import_status == 1) + { + areAnyFileImportsPending = true; + } + } + if (areAnyFileImportsPending) { + //alert("pending uploads, starting refresh on timer"); + self.startRefreshingRecentUploads(); + } else { + self.stopRefreshingRecentUploads(); + } + } + } ); + } + }); + + return recentUploadsTable; + }; + + self.startRefreshingRecentUploads = function() + { + if (self.isRecentUploadsRefreshTimerActive()) { //Prevent multiple timers from running + return; + } + self.recentUploadsRefreshTimer = setInterval("self.recentUploadsTable.fnDraw()", 3000); + }; + + self.isRecentUploadsRefreshTimerActive = function() + { + return (self.recentUploadsRefreshTimer != null); + }; + + self.stopRefreshingRecentUploads = function() + { + clearInterval(self.recentUploadsRefreshTimer); + self.recentUploadsRefreshTimer = null; + }; + + $("#upload_status_all").click(function() { + self.uploadFilter = "all"; + self.recentUploadsTable.fnDraw(); + }); + $("#upload_status_pending").click(function() { + self.uploadFilter = "pending"; + self.recentUploadsTable.fnDraw(); + }); + $("#upload_status_failed").click(function() { + self.uploadFilter = "failed"; + self.recentUploadsTable.fnDraw(); + }); + //Create the recent uploads table. + self.recentUploadsTable = self.setupRecentUploadsTable(); + + //$("#recent_uploads_table.div.fg-toolbar").prepend('Custom tool bar! Text/images etc.'); }); diff --git a/install_minimal/upgrades/airtime-2.5.3/DbUpgrade.php b/install_minimal/upgrades/airtime-2.5.3/DbUpgrade.php new file mode 100644 index 000000000..dbfef98c5 --- /dev/null +++ b/install_minimal/upgrades/airtime-2.5.3/DbUpgrade.php @@ -0,0 +1,24 @@ +&1 | grep -v \"will create implicit index\""); + } +} diff --git a/install_minimal/upgrades/airtime-2.5.3/airtime-upgrade.php b/install_minimal/upgrades/airtime-2.5.3/airtime-upgrade.php new file mode 100644 index 000000000..042b92d05 --- /dev/null +++ b/install_minimal/upgrades/airtime-2.5.3/airtime-upgrade.php @@ -0,0 +1,8 @@ +2.5 + if exc.errno == errno.EEXIST and os.path.isdir(path): + pass + else: raise + diff --git a/python_apps/airtime_analyzer/airtime_analyzer/message_listener.py b/python_apps/airtime_analyzer/airtime_analyzer/message_listener.py new file mode 100644 index 000000000..c5e4da373 --- /dev/null +++ b/python_apps/airtime_analyzer/airtime_analyzer/message_listener.py @@ -0,0 +1,144 @@ +import sys +import pika +import json +import time +import logging +import multiprocessing +from analyzer_pipeline import AnalyzerPipeline +from status_reporter import StatusReporter + +EXCHANGE = "airtime-uploads" +EXCHANGE_TYPE = "topic" +ROUTING_KEY = "" #"airtime.analyzer.tasks" +QUEUE = "airtime-uploads" + + +''' TODO: Document me + - round robin messaging + - acking + - why we use the multiprocess architecture + - in general, how it works and why it works this way +''' +class MessageListener: + + def __init__(self, config): + + # Read the RabbitMQ connection settings from the config file + # The exceptions throw here by default give good error messages. + RMQ_CONFIG_SECTION = "rabbitmq" + self._host = config.get(RMQ_CONFIG_SECTION, 'host') + self._port = config.getint(RMQ_CONFIG_SECTION, 'port') + self._username = config.get(RMQ_CONFIG_SECTION, 'user') + self._password = config.get(RMQ_CONFIG_SECTION, 'password') + self._vhost = config.get(RMQ_CONFIG_SECTION, 'vhost') + + while True: + try: + self.connect_to_messaging_server() + self.wait_for_messages() + except KeyboardInterrupt: + self.disconnect_from_messaging_server() + break + except pika.exceptions.AMQPError as e: + logging.error("Connection to message queue failed. ") + logging.error(e) + logging.info("Retrying in 5 seconds...") + time.sleep(5) + + self._connection.close() + + + def connect_to_messaging_server(self): + + self._connection = pika.BlockingConnection(pika.ConnectionParameters(host=self._host, + port=self._port, virtual_host=self._vhost, + credentials=pika.credentials.PlainCredentials(self._username, self._password))) + self._channel = self._connection.channel() + self._channel.exchange_declare(exchange=EXCHANGE, type=EXCHANGE_TYPE, durable=True) + result = self._channel.queue_declare(queue=QUEUE, durable=True) + + self._channel.queue_bind(exchange=EXCHANGE, queue=QUEUE, routing_key=ROUTING_KEY) + + logging.info(" Listening for messages...") + self._channel.basic_consume(MessageListener.msg_received_callback, + queue=QUEUE, no_ack=False) + + def wait_for_messages(self): + self._channel.start_consuming() + + def disconnect_from_messaging_server(self): + self._channel.stop_consuming() + + + # consume callback function + @staticmethod + def msg_received_callback(channel, method_frame, header_frame, body): + logging.info(" - Received '%s' on routing_key '%s'" % (body, method_frame.routing_key)) + + # Spin up a worker process. We use the multiprocessing module and multiprocessing.Queue + # to pass objects between the processes so that if the analyzer process crashes, it does not + # take down the rest of the daemon and we NACK that message so that it doesn't get + # propagated to other airtime_analyzer daemons (eg. running on other servers). + # We avoid cascading failure this way. + try: + msg_dict = json.loads(body) + audio_file_path = msg_dict["tmp_file_path"] + #final_file_path = msg_dict["final_file_path"] + import_directory = msg_dict["import_directory"] + original_filename = msg_dict["original_filename"] + callback_url = msg_dict["callback_url"] + api_key = msg_dict["api_key"] + + audio_metadata = MessageListener.spawn_analyzer_process(audio_file_path, import_directory, original_filename) + StatusReporter.report_success_to_callback_url(callback_url, api_key, audio_metadata) + + except KeyError as e: + # A field in msg_dict that we needed was missing (eg. audio_file_path) + logging.exception("A mandatory airtime_analyzer message field was missing from the message.") + # See the huge comment about NACK below. + channel.basic_nack(delivery_tag=method_frame.delivery_tag, multiple=False, + requeue=False) #Important that it doesn't requeue the message + + except Exception as e: + logging.exception(e) + #If ANY exception happens while processing a file, we're going to NACK to the + #messaging server and tell it to remove the message from the queue. + #(NACK is a negative acknowledgement. We could use ACK instead, but this might come + # in handy in the future.) + #Exceptions in this context are unexpected, unhandled errors. We try to recover + #from as many errors as possble in AnalyzerPipeline, but we're safeguarding ourselves + #here from any catastrophic or genuinely unexpected errors: + channel.basic_nack(delivery_tag=method_frame.delivery_tag, multiple=False, + requeue=False) #Important that it doesn't requeue the message + + # TODO: Report this as a failed upload to the File Upload REST API. + # + # TODO: If the JSON was invalid or the web server is down, + # then don't report that failure to the REST API + #TODO: Catch exceptions from this HTTP request too: + StatusReporter.report_failure_to_callback_url(callback_url, api_key, import_status=2, + reason=u'An error occurred while importing this file') + + + else: + # ACK at the very end, after the message has been successfully processed. + # If we don't ack, then RabbitMQ will redeliver the message in the future. + channel.basic_ack(delivery_tag=method_frame.delivery_tag) + + @staticmethod + def spawn_analyzer_process(audio_file_path, import_directory, original_filename): + + q = multiprocessing.Queue() + p = multiprocessing.Process(target=AnalyzerPipeline.run_analysis, + args=(q, audio_file_path, import_directory, original_filename)) + p.start() + p.join() + if p.exitcode == 0: + results = q.get() + logging.info("Main process received results from child: ") + logging.info(results) + else: + raise Exception("Analyzer process terminated unexpectedly.") + + return results + diff --git a/python_apps/airtime_analyzer/airtime_analyzer/metadata_analyzer.py b/python_apps/airtime_analyzer/airtime_analyzer/metadata_analyzer.py new file mode 100644 index 000000000..fa0a88807 --- /dev/null +++ b/python_apps/airtime_analyzer/airtime_analyzer/metadata_analyzer.py @@ -0,0 +1,142 @@ +import time +import datetime +import mutagen +import magic # For MIME type detection +from analyzer import Analyzer + +class MetadataAnalyzer(Analyzer): + + @staticmethod + def analyze(filename, metadata): + if not isinstance(filename, unicode): + raise TypeError("filename must be unicode. Was of type " + type(filename).__name__) + if not isinstance(metadata, dict): + raise TypeError("metadata must be a dict. Was of type " + type(metadata).__name__) + + #Extract metadata from an audio file using mutagen + audio_file = mutagen.File(filename, easy=True) + + #Grab other file information that isn't encoded in a tag, but instead usually + #in the file header. Mutagen breaks that out into a separate "info" object: + info = audio_file.info + metadata["sample_rate"] = info.sample_rate + metadata["length_seconds"] = info.length + #Converting the length in seconds (float) to a formatted time string + track_length = datetime.timedelta(seconds=info.length) + metadata["length"] = str(track_length) #time.strftime("%H:%M:%S.%f", track_length) + metadata["bit_rate"] = info.bitrate + #metadata["channels"] = info.channels + + #Use the python-magic module to get the MIME type. + mime_magic = magic.Magic(mime=True) + metadata["mime"] = mime_magic.from_file(filename) + + if isinstance(info, mutagen.mp3.MPEGInfo): + print "mode is: " + str(info.mode) + + #Try to get the number of channels if mutagen can... + try: + #Special handling for getting the # of channels from MP3s. It's in the "mode" field + #which is 0=Stereo, 1=Joint Stereo, 2=Dual Channel, 3=Mono. Part of the ID3 spec... + if metadata["mime"] == "audio/mpeg": + if info.mode == 3: + metadata["channels"] = 1 + else: + metadata["channels"] = 2 + else: + metadata["channels"] = info.channels + except (AttributeError, KeyError): + #If mutagen can't figure out the number of channels, we'll just leave it out... + pass + + #Try to extract the number of tracks on the album if we can (the "track total") + try: + track_number = audio_file["tracknumber"] + if isinstance(track_number, list): # Sometimes tracknumber is a list, ugh + track_number = track_number[0] + track_number_tokens = track_number.split(u'/') + track_number = track_number_tokens[0] + metadata["track_number"] = track_number + track_total = track_number_tokens[1] + metadata["track_total"] = track_total + except (AttributeError, KeyError, IndexError): + #If we couldn't figure out the track_number or track_total, just ignore it... + pass + + #We normalize the mutagen tags slightly here, so in case mutagen changes, + #we find the + mutagen_to_airtime_mapping = { + 'title': 'track_title', + 'artist': 'artist_name', + 'album': 'album_title', + 'bpm': 'bpm', + 'composer': 'composer', + 'conductor': 'conductor', + 'copyright': 'copyright', + 'comment': 'comment', + 'encoded_by': 'encoder', + 'genre': 'genre', + 'isrc': 'isrc', + 'label': 'label', + 'length': 'length', + 'language': 'language', + 'last_modified':'last_modified', + 'mood': 'mood', + 'replay_gain': 'replaygain', + #'tracknumber': 'track_number', + #'track_total': 'track_total', + 'website': 'website', + 'date': 'year', + #'mime_type': 'mime', + } + + for mutagen_tag, airtime_tag in mutagen_to_airtime_mapping.iteritems(): + try: + metadata[airtime_tag] = audio_file[mutagen_tag] + + # Some tags are returned as lists because there could be multiple values. + # This is unusual so we're going to always just take the first item in the list. + if isinstance(metadata[airtime_tag], list): + metadata[airtime_tag] = metadata[airtime_tag][0] + + except KeyError: + continue + + #Airtime <= 2.5.x nonsense: + metadata["ftype"] = "audioclip" + #Other fields we'll want to set for Airtime: + metadata["cueout"] = metadata["length"] + metadata["hidden"] = False + + return metadata + + + +''' +For reference, the Airtime metadata fields are: + title + artist ("Creator" in Airtime) + album + bit rate + BPM + composer + conductor + copyright + cue in + cue out + encoded by + genre + ISRC + label + language + last modified + length + mime + mood + owner + replay gain + sample rate + track number + website + year +''' diff --git a/python_apps/airtime_analyzer/airtime_analyzer/replaygain_analyzer.py b/python_apps/airtime_analyzer/airtime_analyzer/replaygain_analyzer.py new file mode 100644 index 000000000..cf10c0a44 --- /dev/null +++ b/python_apps/airtime_analyzer/airtime_analyzer/replaygain_analyzer.py @@ -0,0 +1,12 @@ +from analyzer import Analyzer + +''' TODO: everything ''' +class ReplayGainAnalyzer(Analyzer): + + def __init__(self): + pass + + @staticmethod + def analyze(filename): + pass + diff --git a/python_apps/airtime_analyzer/airtime_analyzer/status_reporter.py b/python_apps/airtime_analyzer/airtime_analyzer/status_reporter.py new file mode 100644 index 000000000..2a0963dd9 --- /dev/null +++ b/python_apps/airtime_analyzer/airtime_analyzer/status_reporter.py @@ -0,0 +1,44 @@ +import requests +import json +import logging + +class StatusReporter(): + + _HTTP_REQUEST_TIMEOUT = 30 + + # Report the extracted metadata and status of the successfully imported file + # to the callback URL (which should be the Airtime File Upload API) + @classmethod + def report_success_to_callback_url(self, callback_url, api_key, audio_metadata): + + # encode the audio metadata as json and post it back to the callback_url + put_payload = json.dumps(audio_metadata) + logging.debug("sending http put with payload: " + put_payload) + r = requests.put(callback_url, data=put_payload, + auth=requests.auth.HTTPBasicAuth(api_key, ''), + timeout=StatusReporter._HTTP_REQUEST_TIMEOUT) + logging.debug("HTTP request returned status: " + str(r.status_code)) + logging.debug(r.text) # log the response body + + #todo: queue up failed requests and try them again later. + r.raise_for_status() # raise an exception if there was an http error code returned + + @classmethod + def report_failure_to_callback_url(self, callback_url, api_key, import_status, reason): + # TODO: Make import_status is an int? + + logging.debug("Reporting import failure to Airtime REST API...") + audio_metadata = dict() + audio_metadata["import_status"] = import_status + audio_metadata["comment"] = reason # hack attack + put_payload = json.dumps(audio_metadata) + logging.debug("sending http put with payload: " + put_payload) + r = requests.put(callback_url, data=put_payload, + auth=requests.auth.HTTPBasicAuth(api_key, ''), + timeout=StatusReporter._HTTP_REQUEST_TIMEOUT) + logging.debug("HTTP request returned status: " + str(r.status_code)) + logging.debug(r.text) # log the response body + + #TODO: queue up failed requests and try them again later. + r.raise_for_status() # raise an exception if there was an http error code returned + diff --git a/python_apps/airtime_analyzer/bin/airtime_analyzer b/python_apps/airtime_analyzer/bin/airtime_analyzer new file mode 100755 index 000000000..23865fdc6 --- /dev/null +++ b/python_apps/airtime_analyzer/bin/airtime_analyzer @@ -0,0 +1,42 @@ +#!/usr/bin/env python + +import daemon +import argparse +import os +import airtime_analyzer.airtime_analyzer as aa + +VERSION = "1.0" + +print "Airtime Analyzer " + VERSION + +parser = argparse.ArgumentParser() +parser.add_argument("-d", "--daemon", help="run as a daemon", action="store_true") +parser.add_argument("--debug", help="log full debugging output", action="store_true") +args = parser.parse_args() + +'''Ensure media_monitor isn't running before we start, because it'll move newly uploaded + files into the library on us and screw up the operation of airtime_analyzer. + media_monitor is deprecated. +''' +def check_if_media_monitor_is_running(): + pids = [pid for pid in os.listdir('/proc') if pid.isdigit()] + + for pid in pids: + try: + process_name = open(os.path.join('/proc', pid, 'cmdline'), 'rb').read() + if 'media_monitor.py' in process_name: + print "Error: This process conflicts with media_monitor, and media_monitor is running." + print " Please terminate the running media_monitor.py process and try again." + exit(1) + except IOError: # proc has already terminated + continue + +check_if_media_monitor_is_running() + +if args.daemon: + with daemon.DaemonContext(): + analyzer = aa.AirtimeAnalyzerServer(debug=args.debug) +else: + # Run without daemonizing + analyzer = aa.AirtimeAnalyzerServer(debug=args.debug) + diff --git a/python_apps/airtime_analyzer/setup.py b/python_apps/airtime_analyzer/setup.py new file mode 100644 index 000000000..5691676f3 --- /dev/null +++ b/python_apps/airtime_analyzer/setup.py @@ -0,0 +1,22 @@ +from setuptools import setup + +setup(name='airtime_analyzer', + version='0.1', + description='Airtime Analyzer Worker and File Importer', + url='http://github.com/sourcefabric/Airtime', + author='Albert Santoni', + author_email='albert.santoni@sourcefabric.org', + license='MIT', + packages=['airtime_analyzer'], + scripts=['bin/airtime_analyzer'], + install_requires=[ + 'mutagen', + 'python-magic', + 'pika', + 'nose', + 'coverage', + 'mock', + 'python-daemon', + 'requests', + ], + zip_safe=False) diff --git a/python_apps/airtime_analyzer/tests/__init__.py b/python_apps/airtime_analyzer/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/python_apps/airtime_analyzer/tests/airtime_analyzer_tests.py b/python_apps/airtime_analyzer/tests/airtime_analyzer_tests.py new file mode 100644 index 000000000..928e02b31 --- /dev/null +++ b/python_apps/airtime_analyzer/tests/airtime_analyzer_tests.py @@ -0,0 +1,12 @@ +from nose.tools import * +import airtime_analyzer + +def setup(): + pass + +def teardown(): + pass + +def test_basic(): + pass + diff --git a/python_apps/airtime_analyzer/tests/analyzer_pipeline_tests.py b/python_apps/airtime_analyzer/tests/analyzer_pipeline_tests.py new file mode 100644 index 000000000..ba7b6f202 --- /dev/null +++ b/python_apps/airtime_analyzer/tests/analyzer_pipeline_tests.py @@ -0,0 +1,51 @@ +from nose.tools import * +import os +import shutil +import multiprocessing +import Queue +import datetime +from airtime_analyzer.analyzer_pipeline import AnalyzerPipeline + +DEFAULT_AUDIO_FILE = u'tests/test_data/44100Hz-16bit-mono.mp3' +DEFAULT_IMPORT_DEST = u'Test Artist/Test Album/44100Hz-16bit-mono.mp3' + +def setup(): + pass + +def teardown(): + #Move the file back + shutil.move(DEFAULT_IMPORT_DEST, DEFAULT_AUDIO_FILE) + assert os.path.exists(DEFAULT_AUDIO_FILE) + +def test_basic(): + filename = os.path.basename(DEFAULT_AUDIO_FILE) + q = multiprocessing.Queue() + #This actually imports the file into the "./Test Artist" directory. + AnalyzerPipeline.run_analysis(q, DEFAULT_AUDIO_FILE, u'.', filename) + results = q.get() + assert results['track_title'] == u'Test Title' + assert results['artist_name'] == u'Test Artist' + assert results['album_title'] == u'Test Album' + assert results['year'] == u'1999' + assert results['genre'] == u'Test Genre' + assert results['mime'] == 'audio/mpeg' # Not unicode because MIMEs aren't. + assert results['length_seconds'] == 3.90925 + assert results["length"] == str(datetime.timedelta(seconds=results["length_seconds"])) + assert os.path.exists(DEFAULT_IMPORT_DEST) + +@raises(TypeError) +def test_wrong_type_queue_param(): + AnalyzerPipeline.run_analysis(Queue.Queue(), u'', u'', u'') + +@raises(TypeError) +def test_wrong_type_string_param2(): + AnalyzerPipeline.run_analysis(multiprocessing.queues.Queue(), '', u'', u'') + +@raises(TypeError) +def test_wrong_type_string_param3(): + AnalyzerPipeline.run_analysis(multiprocessing.queues.Queue(), u'', '', u'') + +@raises(TypeError) +def test_wrong_type_string_param4(): + AnalyzerPipeline.run_analysis(multiprocessing.queues.Queue(), u'', u'', '') + diff --git a/python_apps/airtime_analyzer/tests/metadata_analyzer_tests.py b/python_apps/airtime_analyzer/tests/metadata_analyzer_tests.py new file mode 100644 index 000000000..8baad0c54 --- /dev/null +++ b/python_apps/airtime_analyzer/tests/metadata_analyzer_tests.py @@ -0,0 +1,149 @@ +# -*- coding: utf-8 -*- +import datetime +import mutagen +import mock +from nose.tools import * +from airtime_analyzer.metadata_analyzer import MetadataAnalyzer + +def setup(): + pass + +def teardown(): + pass + +def check_default_metadata(metadata): + assert metadata['track_title'] == u'Test Title' + assert metadata['artist_name'] == u'Test Artist' + assert metadata['album_title'] == u'Test Album' + assert metadata['year'] == u'1999' + assert metadata['genre'] == u'Test Genre' + assert metadata['track_number'] == u'1' + assert metadata["length"] == str(datetime.timedelta(seconds=metadata["length_seconds"])) + +def test_mp3_mono(): + metadata = MetadataAnalyzer.analyze(u'tests/test_data/44100Hz-16bit-mono.mp3', dict()) + check_default_metadata(metadata) + assert metadata['channels'] == 1 + assert metadata['bit_rate'] == 64000 + assert metadata['length_seconds'] == 3.90925 + assert metadata['mime'] == 'audio/mpeg' # Not unicode because MIMEs aren't. + assert metadata['track_total'] == u'10' # MP3s can have a track_total + #Mutagen doesn't extract comments from mp3s it seems + +def test_mp3_jointstereo(): + metadata = MetadataAnalyzer.analyze(u'tests/test_data/44100Hz-16bit-jointstereo.mp3', dict()) + check_default_metadata(metadata) + assert metadata['channels'] == 2 + assert metadata['bit_rate'] == 128000 + assert metadata['length_seconds'] == 3.90075 + assert metadata['mime'] == 'audio/mpeg' + assert metadata['track_total'] == u'10' # MP3s can have a track_total + +def test_mp3_simplestereo(): + metadata = MetadataAnalyzer.analyze(u'tests/test_data/44100Hz-16bit-simplestereo.mp3', dict()) + check_default_metadata(metadata) + assert metadata['channels'] == 2 + assert metadata['bit_rate'] == 128000 + assert metadata['length_seconds'] == 3.90075 + assert metadata['mime'] == 'audio/mpeg' + assert metadata['track_total'] == u'10' # MP3s can have a track_total + +def test_mp3_dualmono(): + metadata = MetadataAnalyzer.analyze(u'tests/test_data/44100Hz-16bit-dualmono.mp3', dict()) + check_default_metadata(metadata) + assert metadata['channels'] == 2 + assert metadata['bit_rate'] == 128000 + assert metadata['length_seconds'] == 3.90075 + assert metadata['mime'] == 'audio/mpeg' + assert metadata['track_total'] == u'10' # MP3s can have a track_total + + +def test_ogg_mono(): + metadata = MetadataAnalyzer.analyze(u'tests/test_data/44100Hz-16bit-mono.ogg', dict()) + check_default_metadata(metadata) + assert metadata['channels'] == 1 + assert metadata['bit_rate'] == 80000 + assert metadata['length_seconds'] == 3.8394104308390022 + assert metadata['mime'] == 'application/ogg' + assert metadata['comment'] == u'Test Comment' + +def test_ogg_stereo(): + metadata = MetadataAnalyzer.analyze(u'tests/test_data/44100Hz-16bit-stereo.ogg', dict()) + check_default_metadata(metadata) + assert metadata['channels'] == 2 + assert metadata['bit_rate'] == 112000 + assert metadata['length_seconds'] == 3.8394104308390022 + assert metadata['mime'] == 'application/ogg' + assert metadata['comment'] == u'Test Comment' + +''' faac and avconv can't seem to create a proper mono AAC file... ugh +def test_aac_mono(): + metadata = MetadataAnalyzer.analyze(u'tests/test_data/44100Hz-16bit-mono.m4a') + print "Mono AAC metadata:" + print metadata + check_default_metadata(metadata) + assert metadata['channels'] == 1 + assert metadata['bit_rate'] == 80000 + assert metadata['length_seconds'] == 3.8394104308390022 + assert metadata['mime'] == 'video/mp4' + assert metadata['comment'] == u'Test Comment' +''' + +def test_aac_stereo(): + metadata = MetadataAnalyzer.analyze(u'tests/test_data/44100Hz-16bit-stereo.m4a', dict()) + check_default_metadata(metadata) + assert metadata['channels'] == 2 + assert metadata['bit_rate'] == 102619 + assert metadata['length_seconds'] == 3.8626303854875284 + assert metadata['mime'] == 'video/mp4' + assert metadata['comment'] == u'Test Comment' + +def test_mp3_utf8(): + metadata = MetadataAnalyzer.analyze(u'tests/test_data/44100Hz-16bit-stereo-utf8.mp3', dict()) + # Using a bunch of different UTF-8 codepages here. Test data is from: + # http://winrus.com/utf8-jap.htm + assert metadata['track_title'] == u'アイウエオカキクケコサシスセソタチツテ' + assert metadata['artist_name'] == u'てすと' + assert metadata['album_title'] == u'Ä ä Ü ü ß' + assert metadata['year'] == u'1999' + assert metadata['genre'] == u'Я Б Г Д Ж Й' + assert metadata['track_number'] == u'1' + assert metadata['channels'] == 2 + assert metadata['bit_rate'] == 128000 + assert metadata['length_seconds'] == 3.90075 + assert metadata['mime'] == 'audio/mpeg' + assert metadata['track_total'] == u'10' # MP3s can have a track_total + +# Make sure the parameter checking works +@raises(TypeError) +def test_move_wrong_string_param1(): + not_unicode = 'asdfasdf' + MetadataAnalyzer.analyze(not_unicode, dict()) + +@raises(TypeError) +def test_move_wrong_metadata_dict(): + not_a_dict = list() + MetadataAnalyzer.analyze(u'asdfasdf', not_a_dict) + +# Test an mp3 file where the number of channels is invalid or missing: +def test_mp3_bad_channels(): + filename = u'tests/test_data/44100Hz-16bit-mono.mp3' + ''' + It'd be a pain in the ass to construct a real MP3 with an invalid number + of channels by hand because that value is stored in every MP3 frame in the file + ''' + print "testing bad channels..." + audio_file = mutagen.File(filename, easy=True) + audio_file.info.mode = 1777 + with mock.patch('airtime_analyzer.metadata_analyzer.mutagen') as mock_mutagen: + mock_mutagen.File.return_value = audio_file + #mock_mutagen.side_effect = lambda *args, **kw: audio_file #File(*args, **kw) + + metadata = MetadataAnalyzer.analyze(filename, dict()) + check_default_metadata(metadata) + assert metadata['channels'] == 1 + assert metadata['bit_rate'] == 64000 + assert metadata['length_seconds'] == 3.90925 + assert metadata['mime'] == 'audio/mpeg' # Not unicode because MIMEs aren't. + assert metadata['track_total'] == u'10' # MP3s can have a track_total + #Mutagen doesn't extract comments from mp3s it seems diff --git a/python_apps/airtime_analyzer/tests/test_data/44100Hz-16bit-dualmono.mp3 b/python_apps/airtime_analyzer/tests/test_data/44100Hz-16bit-dualmono.mp3 new file mode 100644 index 000000000..1e98b616e Binary files /dev/null and b/python_apps/airtime_analyzer/tests/test_data/44100Hz-16bit-dualmono.mp3 differ diff --git a/python_apps/airtime_analyzer/tests/test_data/44100Hz-16bit-jointstereo.mp3 b/python_apps/airtime_analyzer/tests/test_data/44100Hz-16bit-jointstereo.mp3 new file mode 100644 index 000000000..649dc371d Binary files /dev/null and b/python_apps/airtime_analyzer/tests/test_data/44100Hz-16bit-jointstereo.mp3 differ diff --git a/python_apps/airtime_analyzer/tests/test_data/44100Hz-16bit-mono.mp3 b/python_apps/airtime_analyzer/tests/test_data/44100Hz-16bit-mono.mp3 new file mode 100644 index 000000000..0c12b1b39 Binary files /dev/null and b/python_apps/airtime_analyzer/tests/test_data/44100Hz-16bit-mono.mp3 differ diff --git a/python_apps/airtime_analyzer/tests/test_data/44100Hz-16bit-mono.ogg b/python_apps/airtime_analyzer/tests/test_data/44100Hz-16bit-mono.ogg new file mode 100644 index 000000000..097ccf1f5 Binary files /dev/null and b/python_apps/airtime_analyzer/tests/test_data/44100Hz-16bit-mono.ogg differ diff --git a/python_apps/airtime_analyzer/tests/test_data/44100Hz-16bit-simplestereo.mp3 b/python_apps/airtime_analyzer/tests/test_data/44100Hz-16bit-simplestereo.mp3 new file mode 100644 index 000000000..1ae7f078c Binary files /dev/null and b/python_apps/airtime_analyzer/tests/test_data/44100Hz-16bit-simplestereo.mp3 differ diff --git a/python_apps/airtime_analyzer/tests/test_data/44100Hz-16bit-stereo-utf8.mp3 b/python_apps/airtime_analyzer/tests/test_data/44100Hz-16bit-stereo-utf8.mp3 new file mode 100644 index 000000000..2e29e3f2a Binary files /dev/null and b/python_apps/airtime_analyzer/tests/test_data/44100Hz-16bit-stereo-utf8.mp3 differ diff --git a/python_apps/airtime_analyzer/tests/test_data/44100Hz-16bit-stereo.m4a b/python_apps/airtime_analyzer/tests/test_data/44100Hz-16bit-stereo.m4a new file mode 100644 index 000000000..ee6ef7b3d Binary files /dev/null and b/python_apps/airtime_analyzer/tests/test_data/44100Hz-16bit-stereo.m4a differ diff --git a/python_apps/airtime_analyzer/tests/test_data/44100Hz-16bit-stereo.mp3 b/python_apps/airtime_analyzer/tests/test_data/44100Hz-16bit-stereo.mp3 new file mode 100644 index 000000000..649dc371d Binary files /dev/null and b/python_apps/airtime_analyzer/tests/test_data/44100Hz-16bit-stereo.mp3 differ diff --git a/python_apps/airtime_analyzer/tests/test_data/44100Hz-16bit-stereo.ogg b/python_apps/airtime_analyzer/tests/test_data/44100Hz-16bit-stereo.ogg new file mode 100644 index 000000000..83bd04f77 Binary files /dev/null and b/python_apps/airtime_analyzer/tests/test_data/44100Hz-16bit-stereo.ogg differ diff --git a/python_apps/airtime_analyzer/tools/ftp-upload-hook.sh b/python_apps/airtime_analyzer/tools/ftp-upload-hook.sh new file mode 100755 index 000000000..4d8dc789a --- /dev/null +++ b/python_apps/airtime_analyzer/tools/ftp-upload-hook.sh @@ -0,0 +1,5 @@ +#! /bin/bash + +path=$1 +filename="${path##*/}" +curl http://localhost/rest/media -u 3188BDIMPJROQP89Z0OX: -X POST -F "file=@$path" -F "name=$filename" diff --git a/python_apps/airtime_analyzer/tools/message_sender.php b/python_apps/airtime_analyzer/tools/message_sender.php new file mode 100644 index 000000000..59677673d --- /dev/null +++ b/python_apps/airtime_analyzer/tools/message_sender.php @@ -0,0 +1,47 @@ +channel(); + +// declare/create the queue +$channel->queue_declare($queue, false, true, false, false); + +// declare/create the exchange as a topic exchange. +$channel->exchange_declare($exchange, $exchangeType, false, true, false); + +$msg = new AMQPMessage($message, array("content_type" => "text/plain")); + +$channel->basic_publish($msg, $exchange, $routingKey); +print "Sent $message ($routingKey)\n"; +$channel->close(); +$connection->close(); + diff --git a/python_apps/airtime_analyzer/tools/php-amqplib b/python_apps/airtime_analyzer/tools/php-amqplib new file mode 120000 index 000000000..68ef0b223 --- /dev/null +++ b/python_apps/airtime_analyzer/tools/php-amqplib @@ -0,0 +1 @@ +../../../airtime_mvc/library/php-amqplib \ No newline at end of file