Files
easystream-main/f_jobs/VideoProcessingJob.php
SamiAhmed7777 0b7e2d0a5b 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
2025-10-21 00:39:45 -07:00

269 lines
9.5 KiB
PHP

<?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}'.";
}
}
}