config->getFfprobeBinary(), '-v', 'error', '-select_streams', 'v:0', '-show_entries', 'format=format_name,duration,size:stream=width,height,codec_name', '-of', 'json', $absoluteFile, ]); $process->setTimeout(60); try { $process->run(); } catch (\Throwable $e) { throw new LocalizedException(__('Unable to run ffprobe: %1', $e->getMessage())); } if (!$process->isSuccessful()) { throw new LocalizedException( __('ffprobe failed: %1', trim($process->getErrorOutput()) ?: __('unknown error')) ); } $data = json_decode($process->getOutput(), true); $stream = $data['streams'][0] ?? []; $format = $data['format'] ?? []; $formatName = strtolower((string)($format['format_name'] ?? '')); if (!str_contains($formatName, 'mp4') && !str_contains($formatName, 'mov')) { throw new LocalizedException(__('Only MP4 container files are supported.')); } $width = (int)($stream['width'] ?? 0); $height = (int)($stream['height'] ?? 0); if ($width <= 0 || $height <= 0) { throw new LocalizedException(__('The MP4 file does not contain a readable video stream.')); } return [ 'duration' => round((float)($format['duration'] ?? 0), 3), 'size' => (int)($format['size'] ?? filesize($absoluteFile)), 'width' => $width, 'height' => $height, 'resolution' => $this->getResolution($width, $height), ]; } private function getResolution(int $width, int $height): string { $longEdge = max($width, $height); $shortEdge = min($width, $height); if ($longEdge >= 3840 || $shortEdge >= 2160) { return '4K'; } if ($longEdge >= 1920 || $shortEdge >= 1080) { return '1080p'; } return $shortEdge . 'p'; } }