More refactoring: Allow files to be imported from disk directly
This commit is contained in:
parent
2a89e4d5a0
commit
84f0f135ef
|
@ -940,19 +940,17 @@ SQL;
|
|||
* 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
|
||||
* @param bool $copyFile Copy the file instead of moving it.
|
||||
* @throws Exception
|
||||
* @return Ambigous <unknown, string>
|
||||
*/
|
||||
public static function copyFileToStor($tempFilePath, $originalFilename)
|
||||
public static function moveFileToStor($tempFilePath, $originalFilename, $copyFile=false)
|
||||
{
|
||||
$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
|
||||
|
@ -966,55 +964,35 @@ SQL;
|
|||
Logging::info("Warning: couldn't change permissions of $audio_file to 0644");
|
||||
}
|
||||
|
||||
// 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."));
|
||||
}*/
|
||||
|
||||
|
||||
// Did all the checks for real, now trying to copy
|
||||
$audio_stor = Application_Common_OsPath::join($stor, "organize",
|
||||
$originalFilename);
|
||||
$user = Application_Model_User::getCurrentUser();
|
||||
if (is_null($user)) {
|
||||
$uid = Application_Model_User::getFirstAdminId();
|
||||
} else {
|
||||
$uid = $user->getId();
|
||||
}
|
||||
/*
|
||||
$id_file = "$audio_stor.identifier";
|
||||
if (file_put_contents($id_file, $uid) === false) {
|
||||
Logging::info("Could not write file to identify user: '$uid'");
|
||||
Logging::info("Id file path: '$id_file'");
|
||||
Logging::info("Defaulting to admin (no identification file was
|
||||
written)");
|
||||
} else {
|
||||
Logging::info("Successfully written identification file for
|
||||
uploaded '$audio_stor'");
|
||||
}*/
|
||||
|
||||
|
||||
//if the uploaded file is not UTF-8 encoded, let's encode it. Assuming source
|
||||
//encoding is ISO-8859-1
|
||||
$audio_stor = mb_detect_encoding($audio_stor, "UTF-8") == "UTF-8" ? $audio_stor : utf8_encode($audio_stor);
|
||||
Logging::info("copyFileToStor: moving file $audio_file to $audio_stor");
|
||||
// Martin K.: changed to rename: Much less load + quicker since this is
|
||||
// an atomic operation
|
||||
if (@rename($audio_file, $audio_stor) === false) {
|
||||
//something went wrong likely there wasn't enough space in .
|
||||
//the audio_stor to move the file too warn the user that .
|
||||
//the file wasn't uploaded and they should check if there .
|
||||
//is enough disk space .
|
||||
unlink($audio_file); //remove the file after failed rename
|
||||
//unlink($id_file); // Also remove the identifier file
|
||||
|
||||
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.");
|
||||
if ($copyFile) {
|
||||
Logging::info("Copying file $audio_file to $audio_stor");
|
||||
if (@copy($audio_file, $audio_stor) === false) {
|
||||
throw new Exception("Failed to copy $audio_file to $audio_stor");
|
||||
}
|
||||
} else {
|
||||
Logging::info("Moving file $audio_file to $audio_stor");
|
||||
|
||||
// Martin K.: changed to rename: Much less load + quicker since this is
|
||||
// an atomic operation
|
||||
if (@rename($audio_file, $audio_stor) === false) {
|
||||
//something went wrong likely there wasn't enough space in .
|
||||
//the audio_stor to move the file too warn the user that .
|
||||
//the file wasn't uploaded and they should check if there .
|
||||
//is enough disk space .
|
||||
unlink($audio_file); //remove the file after failed rename
|
||||
//unlink($id_file); // Also remove the identifier file
|
||||
|
||||
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.");
|
||||
}
|
||||
}
|
||||
return $audio_stor;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,10 @@ class CcFiles extends BaseCcFiles {
|
|||
|
||||
const MUSIC_DIRS_STOR_PK = 1;
|
||||
|
||||
const IMPORT_STATUS_SUCCESS = 0;
|
||||
const IMPORT_STATUS_PENDING = 1;
|
||||
const IMPORT_STATUS_FAILED = 2;
|
||||
|
||||
|
||||
//fields that are not modifiable via our RESTful API
|
||||
private static $blackList = array(
|
||||
|
@ -78,57 +82,104 @@ class CcFiles extends BaseCcFiles {
|
|||
throw new OverDiskQuotaException();
|
||||
}
|
||||
|
||||
$file = new CcFiles();
|
||||
/* If full_path is set, the post request came from ftp.
|
||||
* Users are allowed to upload folders via ftp. If this is the case
|
||||
* we need to include the folder name with the file name, otherwise
|
||||
* files won't get removed from the organize folder.
|
||||
*/
|
||||
|
||||
try{
|
||||
$fileArray = self::removeBlacklistedFields($fileArray);
|
||||
//Extract the relative path to the temporary uploaded file on disk.
|
||||
if (isset($fileArray["full_path"])) {
|
||||
$fullPath = $fileArray["full_path"];
|
||||
$basePath = isset($_SERVER['AIRTIME_BASE']) ? $_SERVER['AIRTIME_BASE']."/srv/airtime/stor/organize/" : "/srv/airtime/stor/organize/";
|
||||
//$relativePath is the folder name(if one) + track name, that was uploaded via ftp
|
||||
$filePathRelativeToOrganize = substr($fullPath, strlen($basePath)-1);
|
||||
$originalFilename = $filePathRelativeToOrganize;
|
||||
} else {
|
||||
//Extract the original filename, which we set as the temporary title for the track
|
||||
//until it's finished being processed by the analyzer.
|
||||
$originalFilename = $_FILES["file"]["name"];
|
||||
}
|
||||
|
||||
/*if (!self::validateFileArray($fileArray))
|
||||
{
|
||||
$file->setDbTrackTitle($_FILES["file"]["name"]);
|
||||
$file->setDbUtime(new DateTime("now", new DateTimeZone("UTC")));
|
||||
$file->save();
|
||||
return CcFiles::sanitizeResponse($file);*/
|
||||
self::validateFileArray($fileArray);
|
||||
$tempFilePath = $_FILES['file']['tmp_name'];
|
||||
|
||||
/* If full_path is set, the post request came from ftp.
|
||||
* Users are allowed to upload folders via ftp. If this is the case
|
||||
* we need to include the folder name with the file name, otherwise
|
||||
* files won't get removed from the organize folder.
|
||||
*/
|
||||
if (isset($fileArray["full_path"])) {
|
||||
$fullPath = $fileArray["full_path"];
|
||||
$basePath = isset($_SERVER['AIRTIME_BASE']) ? $_SERVER['AIRTIME_BASE']."/srv/airtime/stor/organize/" : "/srv/airtime/stor/organize/";
|
||||
//$relativePath is the folder name(if one) + track name, that was uploaded via ftp
|
||||
$relativePath = substr($fullPath, strlen($basePath)-1);
|
||||
} else {
|
||||
$relativePath = $_FILES["file"]["name"];
|
||||
}
|
||||
|
||||
|
||||
$file->fromArray($fileArray);
|
||||
$file->setDbOwnerId(self::getOwnerId());
|
||||
$now = new DateTime("now", new DateTimeZone("UTC"));
|
||||
$file->setDbTrackTitle($_FILES["file"]["name"]);
|
||||
$file->setDbUtime($now);
|
||||
$file->setDbHidden(true);
|
||||
$file->save();
|
||||
|
||||
$callbackUrl = Application_Common_HTTPHelper::getStationUrl() . "rest/media/" . $file->getPrimaryKey();
|
||||
|
||||
Application_Service_MediaService::processUploadedFile($callbackUrl, $relativePath, self::getOwnerId());
|
||||
return CcFiles::sanitizeResponse($file);
|
||||
|
||||
} catch (Exception $e) {
|
||||
$file->setDbImportStatus(2);
|
||||
$file->setDbHidden(true);
|
||||
try {
|
||||
self::createAndImport($fileArray, $tempFilePath, $originalFilename);
|
||||
} catch (Exception $e)
|
||||
{
|
||||
@unlink($tempFilePath);
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/** Import a music file to the library from a local file on disk (something pre-existing).
|
||||
* This function allows you to copy a file rather than move it, which is useful for importing
|
||||
* static music files (like sample tracks).
|
||||
* @param string $filePath The full path to the audio file to import.
|
||||
* @param bool $copyFile True if you want to just copy the false, false if you want to move it (default false)
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function createFromLocalFile($filePath, $copyFile=false)
|
||||
{
|
||||
$fileArray = array();
|
||||
$info = pathinfo($filePath);
|
||||
$fileName = basename($filePath).'.'.$info['extension'];
|
||||
self::createAndImport($fileArray, $filePath, $fileName, $copyFile);
|
||||
}
|
||||
|
||||
/** Create a new CcFiles object/row and import a file for it.
|
||||
* You shouldn't call this directly. Either use createFromUpload() or createFromLocalFile().
|
||||
* @param array $fileArray Any metadata to pre-fill for the audio file
|
||||
* @param string $filePath The full path to the audio file to import
|
||||
* @param string $originalFilename
|
||||
* @param bool $copyFile
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
* @throws PropelException
|
||||
*/
|
||||
private static function createAndImport($fileArray, $filePath, $originalFilename, $copyFile=false)
|
||||
{
|
||||
$file = new CcFiles();
|
||||
|
||||
try
|
||||
{
|
||||
$fileArray = self::removeBlacklistedFields($fileArray);
|
||||
|
||||
self::validateFileArray($fileArray);
|
||||
|
||||
$file->fromArray($fileArray);
|
||||
$file->setDbOwnerId(self::getOwnerId());
|
||||
$now = new DateTime("now", new DateTimeZone("UTC"));
|
||||
$file->setDbTrackTitle($originalFilename);
|
||||
$file->setDbUtime($now);
|
||||
$file->setDbHidden(true);
|
||||
$file->save();
|
||||
|
||||
//Only accept files with a file extension that we support.
|
||||
$fileExtension = pathinfo($originalFilename, PATHINFO_EXTENSION);
|
||||
if (!in_array(strtolower($fileExtension), explode(",", "ogg,mp3,oga,flac,wav,m4a,mp4,opus"))) {
|
||||
throw new Exception("Bad file extension.");
|
||||
}
|
||||
|
||||
$callbackUrl = Application_Common_HTTPHelper::getStationUrl() . "rest/media/" . $file->getPrimaryKey();
|
||||
|
||||
Application_Service_MediaService::importFileToLibrary($callbackUrl, $filePath,
|
||||
$originalFilename, self::getOwnerId(), $copyFile);
|
||||
|
||||
return CcFiles::sanitizeResponse($file);
|
||||
|
||||
} catch (Exception $e) {
|
||||
$file->setDbImportStatus(self::IMPORT_STATUS_FAILED);
|
||||
$file->setDbHidden(true);
|
||||
$file->save();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Update a file with metadata specified in an array.
|
||||
* @param $fileId The ID of the file to update in the DB.
|
||||
* @param $fileArray An associative array containing metadata. Replaces those fields if they exist.
|
||||
* @param $fileId string The ID of the file to update in the DB.
|
||||
* @param $fileArray array An associative array containing metadata. Replaces those fields if they exist.
|
||||
* @return array A sanitized version of the file metadata array.
|
||||
* @throws Exception
|
||||
* @throws FileNotFoundException
|
||||
|
@ -238,22 +289,6 @@ class CcFiles extends BaseCcFiles {
|
|||
|
||||
}
|
||||
|
||||
public static function getDownloadUrl($id)
|
||||
{
|
||||
$file = CcFilesQuery::create()->findPk($id);
|
||||
if ($file) {
|
||||
$con = Propel::getConnection();
|
||||
$storedFile = new Application_Model_StoredFile($file, $con);
|
||||
$baseDir = Application_Common_OsPath::getBaseDir();
|
||||
|
||||
return $storedFile->getRelativeFileUrl($baseDir) . '/download/true';
|
||||
}
|
||||
else {
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static function validateFileArray(&$fileArray)
|
||||
{
|
||||
// Sanitize any wildly incorrect metadata before it goes to be validated
|
||||
|
@ -330,7 +365,7 @@ class CcFiles extends BaseCcFiles {
|
|||
/**
|
||||
*
|
||||
* Strips out the private fields we do not want to send back in API responses
|
||||
* @param $file a CcFiles object
|
||||
* @param $file string a CcFiles object
|
||||
*/
|
||||
//TODO: rename this function?
|
||||
public static function sanitizeResponse($file)
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
|
||||
class Rest_MediaController extends Zend_Rest_Controller
|
||||
{
|
||||
const IMPORT_STATUS_SUCCESS = 0;
|
||||
const IMPORT_STATUS_PENDING = 1;
|
||||
const IMPORT_STATUS_FAILED = 2;
|
||||
|
||||
|
||||
public function init()
|
||||
{
|
||||
|
|
|
@ -4,38 +4,30 @@ require_once('ProxyStorageBackend.php');
|
|||
|
||||
class Application_Service_MediaService
|
||||
{
|
||||
public static function processUploadedFile($callbackUrl, $originalFilename, $ownerId)
|
||||
/** Move (or copy) a file to the stor/organize directory and send it off to the
|
||||
analyzer to be processed.
|
||||
* @param $callbackUrl
|
||||
* @param $filePath string Path to the local file to import to the library
|
||||
* @param $originalFilename string The original filename, if you want it to be preserved after import.
|
||||
* @param $ownerId string The ID of the user that will own the file inside Airtime.
|
||||
* @param $copyFile bool True if you want to copy the file to the "organize" directory, false if you want to move it (default)
|
||||
* @return Ambigous
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function importFileToLibrary($callbackUrl, $filePath, $originalFilename, $ownerId, $copyFile)
|
||||
{
|
||||
$CC_CONFIG = Config::getConfig();
|
||||
$apiKey = $CC_CONFIG["apiKey"][0];
|
||||
|
||||
$tempFilePath = $_FILES['file']['tmp_name'];
|
||||
$tempFileName = basename($tempFilePath);
|
||||
|
||||
//Only accept files with a file extension that we support.
|
||||
$fileExtension = pathinfo($originalFilename, PATHINFO_EXTENSION);
|
||||
if (!in_array(strtolower($fileExtension), explode(",", "ogg,mp3,oga,flac,wav,m4a,mp4,opus"))) {
|
||||
@unlink($tempFilePath);
|
||||
throw new Exception("Bad file extension.");
|
||||
}
|
||||
|
||||
//TODO: Remove uploadFileAction from ApiController.php **IMPORTANT** - It's used by the recorder daemon...
|
||||
|
||||
$importedStorageDirectory = "";
|
||||
if ($CC_CONFIG["current_backend"] == "file") {
|
||||
$storDir = Application_Model_MusicDir::getStorDir();
|
||||
$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) {
|
||||
@unlink($tempFilePath);
|
||||
Logging::error($e->getMessage());
|
||||
return;
|
||||
}
|
||||
//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::moveFileToStor($filePath, $originalFilename, $copyFile);
|
||||
|
||||
//Dispatch a message to airtime_analyzer through RabbitMQ,
|
||||
//notifying it that there's a new upload to process!
|
||||
|
@ -45,6 +37,8 @@ class Application_Service_MediaService
|
|||
$callbackUrl, $apiKey,
|
||||
$CC_CONFIG["current_backend"],
|
||||
$storageBackend->getFilePrefix());
|
||||
|
||||
return $newTempFilePath;
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue