btg-sandbox-video-upscaler/estimate.php
DJP d49ecf1527 Add automatic file cleanup for uploads directory
- 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>
2025-09-17 14:43:03 -04:00

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()]);
}
?>