feat: Add comprehensive documentation suite and reorganize project structure
- Created complete documentation in docs/ directory - Added PROJECT_OVERVIEW.md with feature highlights and getting started guide - Added ARCHITECTURE.md with system design and technical details - Added SECURITY.md with comprehensive security implementation guide - Added DEVELOPMENT.md with development workflows and best practices - Added DEPLOYMENT.md with production deployment instructions - Added API.md with complete REST API documentation - Added CONTRIBUTING.md with contribution guidelines - Added CHANGELOG.md with version history and migration notes - Reorganized all documentation files into docs/ directory for better organization - Updated README.md with proper documentation links and quick navigation - Enhanced project structure with professional documentation standards
This commit is contained in:
269
f_jobs/VideoProcessingJob.php
Normal file
269
f_jobs/VideoProcessingJob.php
Normal file
@@ -0,0 +1,269 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Enhanced Video Processing Job
|
||||
| Handles video transcoding and processing in the background with VVideoProcessor
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
class VideoProcessingJob extends BaseJob
|
||||
{
|
||||
private $videoProcessor;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->videoProcessor = new VVideoProcessor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle video processing using VVideoProcessor
|
||||
* @param array $data Video processing data
|
||||
* @return array Processing result
|
||||
*/
|
||||
public function handle($data)
|
||||
{
|
||||
$this->validateData($data, ['video_key', 'input_file']);
|
||||
|
||||
$videoKey = $data['video_key'];
|
||||
$inputFile = $data['input_file'];
|
||||
$options = [
|
||||
'formats' => $data['formats'] ?? ['1080p', '720p', '480p', '360p'],
|
||||
'generate_preview' => $data['generate_preview'] ?? true,
|
||||
'generate_hls' => $data['generate_hls'] ?? true
|
||||
];
|
||||
|
||||
$this->logProgress('Starting video processing', [
|
||||
'video_key' => $videoKey,
|
||||
'input_file' => $inputFile,
|
||||
'options' => $options
|
||||
]);
|
||||
|
||||
try {
|
||||
// Process video using enhanced VVideoProcessor
|
||||
$result = $this->videoProcessor->processVideo($inputFile, $videoKey, $options);
|
||||
|
||||
if ($result['success']) {
|
||||
$this->logProgress('Video processing completed successfully', [
|
||||
'video_key' => $videoKey,
|
||||
'formats_processed' => array_keys($result['results']),
|
||||
'video_info' => $result['video_info']
|
||||
]);
|
||||
|
||||
// Send success notification to user
|
||||
$this->sendUserNotification($videoKey, 'processing_completed', $result);
|
||||
|
||||
return $result;
|
||||
} else {
|
||||
throw new Exception($result['error'] ?? 'Video processing failed');
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->logError('Video processing failed', [
|
||||
'video_key' => $videoKey,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
|
||||
// Send failure notification to user
|
||||
$this->sendUserNotification($videoKey, 'processing_failed', ['error' => $e->getMessage()]);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process video to specific format
|
||||
* @param string $inputFile Input file path
|
||||
* @param string $outputDir Output directory
|
||||
* @param string $format Format (720p, 480p, etc.)
|
||||
* @param string $videoId Video ID
|
||||
* @return array Processing result
|
||||
*/
|
||||
private function processVideoFormat($inputFile, $outputDir, $format, $videoId)
|
||||
{
|
||||
$outputFile = $outputDir . '/' . $videoId . '_' . $format . '.mp4';
|
||||
|
||||
// Define format settings
|
||||
$formatSettings = [
|
||||
'1080p' => ['width' => 1920, 'height' => 1080, 'bitrate' => '5000k'],
|
||||
'720p' => ['width' => 1280, 'height' => 720, 'bitrate' => '2500k'],
|
||||
'480p' => ['width' => 854, 'height' => 480, 'bitrate' => '1000k'],
|
||||
'360p' => ['width' => 640, 'height' => 360, 'bitrate' => '750k'],
|
||||
'240p' => ['width' => 426, 'height' => 240, 'bitrate' => '400k']
|
||||
];
|
||||
|
||||
if (!isset($formatSettings[$format])) {
|
||||
return [
|
||||
'success' => false,
|
||||
'error' => "Unknown format: {$format}",
|
||||
'output_file' => null
|
||||
];
|
||||
}
|
||||
|
||||
$settings = $formatSettings[$format];
|
||||
|
||||
// Build FFmpeg command
|
||||
$ffmpegCmd = sprintf(
|
||||
'ffmpeg -i %s -vf "scale=%d:%d:force_original_aspect_ratio=decrease,pad=%d:%d:(ow-iw)/2:(oh-ih)/2" -c:v libx264 -b:v %s -c:a aac -b:a 128k -movflags +faststart %s 2>&1',
|
||||
escapeshellarg($inputFile),
|
||||
$settings['width'],
|
||||
$settings['height'],
|
||||
$settings['width'],
|
||||
$settings['height'],
|
||||
$settings['bitrate'],
|
||||
escapeshellarg($outputFile)
|
||||
);
|
||||
|
||||
$this->logProgress("Executing FFmpeg for {$format}", [
|
||||
'command' => $ffmpegCmd,
|
||||
'output_file' => $outputFile
|
||||
]);
|
||||
|
||||
// Execute FFmpeg
|
||||
$output = [];
|
||||
$returnCode = 0;
|
||||
exec($ffmpegCmd, $output, $returnCode);
|
||||
|
||||
if ($returnCode === 0 && file_exists($outputFile)) {
|
||||
return [
|
||||
'success' => true,
|
||||
'output_file' => $outputFile,
|
||||
'file_size' => filesize($outputFile),
|
||||
'format' => $format
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'success' => false,
|
||||
'error' => 'FFmpeg processing failed',
|
||||
'return_code' => $returnCode,
|
||||
'output' => implode("\n", $output),
|
||||
'output_file' => $outputFile
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate video thumbnail
|
||||
* @param string $inputFile Input file path
|
||||
* @param string $outputDir Output directory
|
||||
* @param string $videoId Video ID
|
||||
* @return array Generation result
|
||||
*/
|
||||
private function generateThumbnail($inputFile, $outputDir, $videoId)
|
||||
{
|
||||
$thumbnailFile = $outputDir . '/' . $videoId . '_thumb.jpg';
|
||||
|
||||
// Generate thumbnail at 10% of video duration
|
||||
$ffmpegCmd = sprintf(
|
||||
'ffmpeg -i %s -ss 00:00:10 -vframes 1 -vf "scale=320:240:force_original_aspect_ratio=decrease,pad=320:240:(ow-iw)/2:(oh-ih)/2" %s 2>&1',
|
||||
escapeshellarg($inputFile),
|
||||
escapeshellarg($thumbnailFile)
|
||||
);
|
||||
|
||||
$output = [];
|
||||
$returnCode = 0;
|
||||
exec($ffmpegCmd, $output, $returnCode);
|
||||
|
||||
if ($returnCode === 0 && file_exists($thumbnailFile)) {
|
||||
return [
|
||||
'success' => true,
|
||||
'thumbnail_file' => $thumbnailFile,
|
||||
'file_size' => filesize($thumbnailFile)
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'success' => false,
|
||||
'error' => 'Thumbnail generation failed',
|
||||
'return_code' => $returnCode,
|
||||
'output' => implode("\n", $output)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send notification to user about processing status
|
||||
* @param string $videoKey Video key
|
||||
* @param string $type Notification type
|
||||
* @param array $data Additional data
|
||||
*/
|
||||
private function sendUserNotification($videoKey, $type, $data = [])
|
||||
{
|
||||
try {
|
||||
// Get video and user information
|
||||
$db = $this->getDatabase();
|
||||
$query = "SELECT vf.*, au.usr_user, au.usr_email
|
||||
FROM db_videofiles vf
|
||||
JOIN db_accountuser au ON vf.usr_id = au.usr_id
|
||||
WHERE vf.file_key = ?";
|
||||
$result = $db->doQuery($query, [$videoKey]);
|
||||
$video = $db->doFetch($result);
|
||||
|
||||
if (!$video) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create notification
|
||||
$notification = [
|
||||
'user_id' => $video['usr_id'],
|
||||
'type' => 'video_' . $type,
|
||||
'title' => $this->getNotificationTitle($type),
|
||||
'message' => $this->getNotificationMessage($type, $video['file_title']),
|
||||
'data' => json_encode([
|
||||
'video_key' => $videoKey,
|
||||
'video_title' => $video['file_title'],
|
||||
'processing_data' => $data
|
||||
]),
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
'read_status' => 0
|
||||
];
|
||||
|
||||
$db->doInsert('db_notifications', $notification);
|
||||
|
||||
$this->logProgress('User notification sent', [
|
||||
'video_key' => $videoKey,
|
||||
'user_id' => $video['usr_id'],
|
||||
'type' => $type
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->logError('Failed to send user notification', [
|
||||
'video_key' => $videoKey,
|
||||
'type' => $type,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get notification title based on type
|
||||
* @param string $type Notification type
|
||||
* @return string Title
|
||||
*/
|
||||
private function getNotificationTitle($type)
|
||||
{
|
||||
switch ($type) {
|
||||
case 'processing_completed':
|
||||
return 'Video Processing Complete';
|
||||
case 'processing_failed':
|
||||
return 'Video Processing Failed';
|
||||
default:
|
||||
return 'Video Update';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get notification message based on type
|
||||
* @param string $type Notification type
|
||||
* @param string $videoTitle Video title
|
||||
* @return string Message
|
||||
*/
|
||||
private function getNotificationMessage($type, $videoTitle)
|
||||
{
|
||||
switch ($type) {
|
||||
case 'processing_completed':
|
||||
return "Your video '{$videoTitle}' has been processed successfully and is now available for viewing.";
|
||||
case 'processing_failed':
|
||||
return "Processing failed for your video '{$videoTitle}'. Please try uploading again or contact support.";
|
||||
default:
|
||||
return "Update for your video '{$videoTitle}'.";
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user