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