"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.extractVideoMetadata = extractVideoMetadata; exports.validateVideoFile = validateVideoFile; const child_process_1 = require("child_process"); const promises_1 = require("fs/promises"); const logger_1 = require("../../../utils/logger"); /** * Run a command with a timeout */ function runCommand(command, args, timeoutMs = 30000) { return new Promise((resolve, reject) => { const process = (0, child_process_1.spawn)(command, args); let stdout = ''; let stderr = ''; const timeout = setTimeout(() => { process.kill(); reject(new Error(`Command timed out after ${timeoutMs}ms`)); }, timeoutMs); process.stdout.on('data', (data) => { stdout += data.toString(); }); process.stderr.on('data', (data) => { stderr += data.toString(); }); process.on('close', (code) => { clearTimeout(timeout); if (code === 0) { resolve(stdout); } else { reject(new Error(`Command failed with code ${code}: ${stderr}`)); } }); process.on('error', (err) => { clearTimeout(timeout); reject(err); }); }); } /** * Extract video metadata using ffprobe */ async function extractVideoMetadata(filePath) { try { // Run ffprobe to get video metadata const output = await runCommand('ffprobe', [ '-v', 'quiet', '-print_format', 'json', '-show_streams', '-show_format', filePath, ]); const data = JSON.parse(output); // Find video stream const videoStream = data.streams.find((s) => s.codec_type === 'video'); if (!videoStream) { throw new Error('No video stream found in file'); } // Find audio stream const hasAudio = data.streams.some((s) => s.codec_type === 'audio'); // Extract dimensions const width = videoStream.width || 0; const height = videoStream.height || 0; if (width === 0 || height === 0) { throw new Error('Could not determine video dimensions'); } // Calculate orientation const orientation = width >= height ? 'H' : 'V'; // Calculate quality based on height let quality; if (height >= 2160) { quality = '4K'; } else if (height >= 1440) { quality = '1440p'; } else if (height >= 1080) { quality = '1080p'; } else if (height >= 720) { quality = '720p'; } else if (height >= 480) { quality = '480p'; } else { quality = 'SD'; } // Extract duration (prefer stream duration, fallback to format duration) let durationSeconds = 0; const streamDuration = videoStream.duration; const formatDuration = data.format.duration; if (streamDuration) { durationSeconds = parseFloat(streamDuration); } else if (formatDuration) { durationSeconds = parseFloat(formatDuration); } if (isNaN(durationSeconds) || durationSeconds <= 0) { throw new Error('Could not determine video duration'); } // Get file size const stats = await (0, promises_1.stat)(filePath); const fileSize = stats.size; logger_1.logger.info(`Extracted metadata for ${filePath}:`, { width, height, orientation, quality, duration: durationSeconds, hasAudio, fileSize, }); return { durationSeconds, width, height, orientation, quality, hasAudio, fileSize, }; } catch (error) { logger_1.logger.error(`Failed to extract metadata from ${filePath}:`, error); throw new Error(`Metadata extraction failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Validate video file by attempting to decode frames */ async function validateVideoFile(filePath) { try { // Try to decode 5 frames await runCommand('ffmpeg', [ '-v', 'error', '-i', filePath, '-vframes', '5', '-f', 'null', '-', ], 60000); // 60s timeout for validation logger_1.logger.info(`Video validation successful: ${filePath}`); return true; } catch (error) { logger_1.logger.warn(`Video validation failed for ${filePath}:`, error); return false; } } //# sourceMappingURL=ffprobe.service.js.map