- Created FileCleanup utility class to manage old upload files - Automatically removes files older than 24 hours on each request - Added cleanup calls to both estimate.php and uploader.php processes - Includes detailed logging and statistics for cleanup operations - Provides formatBytes utility for human-readable file size reporting - Includes directory statistics functionality for monitoring This prevents the uploads directory from growing indefinitely and ensures proper disk space management for the video processing system. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
263 lines
No EOL
9.9 KiB
PHP
263 lines
No EOL
9.9 KiB
PHP
<?php
|
|
require_once 'config.php';
|
|
require_once 'logger.php';
|
|
require_once 'api_client.php';
|
|
require_once 'cleanup.php';
|
|
|
|
header('Content-Type: application/json');
|
|
|
|
try {
|
|
// Clean up old files first (older than 24 hours)
|
|
Logger::log('Starting automatic cleanup of old upload files');
|
|
$cleanupStats = FileCleanup::cleanupOldFiles(24, 'uploads/');
|
|
if ($cleanupStats['files_deleted'] > 0) {
|
|
Logger::log("Cleanup completed: {$cleanupStats['files_deleted']} old files removed, " .
|
|
FileCleanup::formatBytes($cleanupStats['bytes_freed']) . " freed");
|
|
}
|
|
|
|
Logger::log('Starting estimation process');
|
|
|
|
// Check PHP settings
|
|
Logger::log("PHP upload_max_filesize: " . ini_get('upload_max_filesize'));
|
|
Logger::log("PHP post_max_size: " . ini_get('post_max_size'));
|
|
Logger::log("PHP memory_limit: " . ini_get('memory_limit'));
|
|
|
|
if (!isset($_FILES['video'])) {
|
|
throw new Exception('No video file uploaded');
|
|
}
|
|
|
|
$enhancement = $_POST['enhancement'] ?? '';
|
|
if (empty($enhancement)) {
|
|
throw new Exception('No enhancement type selected');
|
|
}
|
|
|
|
// Create uploads directory if it doesn't exist
|
|
$uploadDir = __DIR__ . '/uploads';
|
|
if (!file_exists($uploadDir)) {
|
|
if (!mkdir($uploadDir, 0755, true)) {
|
|
throw new Exception('Failed to create uploads directory');
|
|
}
|
|
}
|
|
|
|
// Check directory permissions for MAMP
|
|
if (!is_writable($uploadDir)) {
|
|
Logger::log("Directory not writable: $uploadDir", 'ERROR');
|
|
chmod($uploadDir, 0755);
|
|
if (!is_writable($uploadDir)) {
|
|
throw new Exception('Upload directory is not writable');
|
|
}
|
|
}
|
|
|
|
// Generate unique filename
|
|
$tempName = uniqid('video_') . '_' . basename($_FILES['video']['name']);
|
|
$uploadFile = $uploadDir . '/' . $tempName;
|
|
Logger::log("Attempting to move uploaded file to: $uploadFile");
|
|
|
|
// Move uploaded file
|
|
if (!move_uploaded_file($_FILES['video']['tmp_name'], $uploadFile)) {
|
|
Logger::log("Failed to move uploaded file. Error: " . error_get_last()['message'], 'ERROR');
|
|
throw new Exception('Failed to move uploaded file');
|
|
}
|
|
|
|
Logger::log("File successfully moved to: $uploadFile");
|
|
|
|
// Basic video file validation
|
|
$fileInfo = finfo_open(FILEINFO_MIME_TYPE);
|
|
$mimeType = finfo_file($fileInfo, $uploadFile);
|
|
finfo_close($fileInfo);
|
|
|
|
Logger::log("File MIME type: " . $mimeType);
|
|
|
|
if (strpos($mimeType, 'video/') !== 0) {
|
|
unlink($uploadFile);
|
|
throw new Exception('Invalid file type. Please upload a video file.');
|
|
}
|
|
|
|
// Get video dimensions using ffmpeg directly (more reliable on MAMP)
|
|
$ffmpegPath = '/Applications/MAMP/Library/bin/ffmpeg';
|
|
if (!file_exists($ffmpegPath)) {
|
|
// Try alternative locations
|
|
$ffmpegPath = '/usr/local/bin/ffmpeg';
|
|
if (!file_exists($ffmpegPath)) {
|
|
$ffmpegPath = '/opt/homebrew/bin/ffmpeg';
|
|
}
|
|
}
|
|
|
|
// Get dimensions using ffmpeg -i command
|
|
$command = escapeshellcmd("$ffmpegPath -i " . escapeshellarg($uploadFile) . " 2>&1");
|
|
$output = shell_exec($command);
|
|
Logger::log("FFmpeg output: " . $output);
|
|
|
|
// Parse the ffmpeg output to get dimensions
|
|
if (preg_match('/Stream.*Video.* (\d+)x(\d+)/', $output, $matches)) {
|
|
$actualWidth = intval($matches[1]);
|
|
$actualHeight = intval($matches[2]);
|
|
Logger::log("Successfully extracted dimensions: {$actualWidth}x{$actualHeight}");
|
|
} else {
|
|
Logger::log("Failed to extract dimensions from ffmpeg output, checking file directly", 'WARNING');
|
|
|
|
// Try using MediaInfo as a fallback if available
|
|
$mediainfoPath = '/usr/local/bin/mediainfo';
|
|
if (file_exists($mediainfoPath)) {
|
|
$command = escapeshellcmd("$mediainfoPath --Output='Video;%Width%\n%Height%' " . escapeshellarg($uploadFile));
|
|
$dimensions = explode("\n", shell_exec($command));
|
|
if (count($dimensions) === 2 && is_numeric($dimensions[0]) && is_numeric($dimensions[1])) {
|
|
$actualWidth = intval($dimensions[0]);
|
|
$actualHeight = intval($dimensions[1]);
|
|
Logger::log("Got dimensions from MediaInfo: {$actualWidth}x{$actualHeight}");
|
|
} else {
|
|
// Last resort - get first frame and check its dimensions
|
|
Logger::log("MediaInfo failed, trying to extract first frame", 'WARNING');
|
|
$frameFile = $uploadDir . '/temp_frame.jpg';
|
|
$command = escapeshellcmd("$ffmpegPath -i " . escapeshellarg($uploadFile) . " -vframes 1 -f image2 " . escapeshellarg($frameFile));
|
|
shell_exec($command);
|
|
|
|
if (file_exists($frameFile)) {
|
|
$dimensions = getimagesize($frameFile);
|
|
unlink($frameFile); // Clean up
|
|
if ($dimensions) {
|
|
$actualWidth = $dimensions[0];
|
|
$actualHeight = $dimensions[1];
|
|
Logger::log("Got dimensions from first frame: {$actualWidth}x{$actualHeight}");
|
|
} else {
|
|
Logger::log("Could not get dimensions from frame, using original file dimensions", 'WARNING');
|
|
$actualWidth = $_POST['original_width'] ?? 1920;
|
|
$actualHeight = $_POST['original_height'] ?? 1080;
|
|
}
|
|
} else {
|
|
Logger::log("Could not extract frame, using original file dimensions", 'WARNING');
|
|
$actualWidth = $_POST['original_width'] ?? 1920;
|
|
$actualHeight = $_POST['original_height'] ?? 1080;
|
|
}
|
|
}
|
|
} else {
|
|
Logger::log("No MediaInfo available, using original file dimensions", 'WARNING');
|
|
$actualWidth = $_POST['original_width'] ?? 1920;
|
|
$actualHeight = $_POST['original_height'] ?? 1080;
|
|
}
|
|
}
|
|
|
|
Logger::log("Final video dimensions: {$actualWidth}x{$actualHeight}");
|
|
|
|
// Calculate estimated duration based on file size
|
|
$fileSize = filesize($uploadFile);
|
|
$estimatedBitrate = 2000000; // Assume 2Mbps
|
|
$estimatedDuration = ($fileSize * 8) / $estimatedBitrate;
|
|
|
|
// Create metadata structure with actual dimensions
|
|
$metadata = [
|
|
'format' => [
|
|
'duration' => $estimatedDuration,
|
|
],
|
|
'streams' => [
|
|
[
|
|
'codec_type' => 'video',
|
|
'width' => $actualWidth,
|
|
'height' => $actualHeight,
|
|
'r_frame_rate' => '30/1',
|
|
'duration' => $estimatedDuration,
|
|
]
|
|
]
|
|
];
|
|
|
|
Logger::log("Estimated metadata: " . json_encode($metadata));
|
|
Logger::log("Final metadata: " . json_encode($metadata));
|
|
Logger::log("Video metadata retrieved successfully");
|
|
// Estimate frame count
|
|
$frameRate = 24;
|
|
$duration = $metadata['format']['duration'];
|
|
$frameCount = ceil($duration * $frameRate);
|
|
|
|
Logger::log("Calculated frames: $frameCount from duration: $duration at $frameRate fps");
|
|
|
|
// Get the requested resolution scaling
|
|
$resolutionScale = $_POST['resolution'] ?? 'original';
|
|
|
|
// Calculate output resolution based on selected scale
|
|
switch ($resolutionScale) {
|
|
case '2x':
|
|
$outputWidth = $actualWidth * 2;
|
|
$outputHeight = $actualHeight * 2;
|
|
break;
|
|
case '4x':
|
|
$outputWidth = $actualWidth * 4;
|
|
$outputHeight = $actualHeight * 4;
|
|
break;
|
|
case 'original':
|
|
default:
|
|
$outputWidth = $actualWidth;
|
|
$outputHeight = $actualHeight;
|
|
break;
|
|
}
|
|
|
|
Logger::log("Selected resolution scale: $resolutionScale, Output: {$outputWidth}x{$outputHeight}");
|
|
|
|
// Build the video info structure
|
|
$videoInfo = [
|
|
'source' => [
|
|
'container' => strtolower(pathinfo($_FILES['video']['name'], PATHINFO_EXTENSION)),
|
|
'size' => $fileSize,
|
|
'duration' => floatval($duration),
|
|
'frameCount' => $frameCount,
|
|
'frameRate' => $frameRate,
|
|
'resolution' => [
|
|
'width' => $actualWidth,
|
|
'height' => $actualHeight
|
|
]
|
|
],
|
|
'filters' => [
|
|
[
|
|
'model' => $enhancement,
|
|
'videoType' => 'Progressive',
|
|
'auto' => 'Auto',
|
|
'fieldOrder' => 'Auto',
|
|
'focusFixLevel' => 'None',
|
|
'blur' => floatval($_POST['blur'] ?? 0.0),
|
|
'grain' => floatval($_POST['grain'] ?? 0.0),
|
|
'grainSize' => floatval($_POST['grainSize'] ?? 1.5),
|
|
'recoverOriginalDetailValue' => floatval($_POST['recoverDetail'] ?? 0.2)
|
|
]
|
|
],
|
|
'output' => [
|
|
'resolution' => [
|
|
'width' => $outputWidth,
|
|
'height' => $outputHeight
|
|
],
|
|
'frameRate' => $frameRate,
|
|
'audioCodec' => 'AAC',
|
|
'audioTransfer' => 'Copy',
|
|
|
|
'videoEncoder' => 'H265',
|
|
'videoBitrate' => '6000k',
|
|
|
|
'videoProfile' => 'Main',
|
|
'cropToFit' => false,
|
|
'container' => 'mp4'
|
|
]
|
|
];
|
|
|
|
$apiClient = new TopazApiClient(Config::API_KEY);
|
|
$response = $apiClient->createVideoRequest($videoInfo);
|
|
|
|
Logger::log('Estimate received: ' . json_encode($response['estimates']));
|
|
|
|
// Store request details in session
|
|
session_start();
|
|
$_SESSION['video_request'] = [
|
|
'info' => $videoInfo,
|
|
'file' => [
|
|
'name' => $_FILES['video']['name'],
|
|
'tmp_name' => $uploadFile,
|
|
'size' => $fileSize
|
|
],
|
|
'requestId' => $response['requestId']
|
|
];
|
|
|
|
echo json_encode($response);
|
|
|
|
} catch (Exception $e) {
|
|
Logger::log('Error during estimation: ' . $e->getMessage(), 'ERROR');
|
|
http_response_code(400);
|
|
echo json_encode(['error' => $e->getMessage()]);
|
|
}
|
|
?>
|