mediainfo = new MediaInfo(); $this->mediainfo->setConfig('use_oldxml_mediainfo_output_format', true); $config = array( 'ffmpeg.binarie' => '/Users/shixuesen/Downloads/ffmpeg', 'ffprobe.binaries' => '/opt/homebrew/bin/ffprobe', 'timeout' => 3600, 'ffmpeg.threads' => 16, ); $this->ffprobe = FFProbe::create($config); } /** * @param null $redis */ public function setRedis($redisConnection): void { $this->redis = Redis::connection($redisConnection); } public function getRedis(): ?\Illuminate\Redis\Connections\Connection { if ($this->redis != null) { return $this->redis; } else { return Redis::connection(); } } /** * @return string */ public function getPreset(): string { return $this->preset; } /** * @param string $preset */ public function setPreset(string $preset): void { $this->preset = $preset; } public function handleVideos($dir = "/Users/shixuesen/Documents/tmp/柚木/2017/泡泡条纹袜/") { $files = scandir($dir); foreach ($files as $file) { if ($file == "." || $file == "..") { continue; } $subDir = implode("/", [$dir, $file]); $isDir = is_dir($subDir); if ($isDir) { $subFiles = scandir($subDir); foreach ($subFiles as $subFile) { $subPathFile = implode("/", [$subDir, $subFile]); if (is_dir($subPathFile) || $subFile == ".DS_Store") { continue; } $mime = mime_content_type($subPathFile); // dump("file type", [$mime, $subPathFile]); // continue; if (strstr($mime, "video/")) { if (is_file($subPathFile)) { $fileInfo = pathinfo($subPathFile); dump("fileInfo", $fileInfo); if (ends_with($fileInfo["filename"], "-1")) { continue; } if (is_file($fileInfo["dirname"] . '/' . $fileInfo["filename"] . '-1' . '.' . $fileInfo["extension"])) { unlink($subPathFile); continue; } $targetFile = $fileInfo["dirname"] . '/' . $fileInfo["filename"] . '-1' . '.' . $fileInfo["extension"]; dump("targetFile", [$targetFile]); // $result = shell_exec("handBrakeCli -Z 'Very Fast 720p30' -i '". $subPathFile ."' -o '". $targetFile . " && echo 'success'"); $result = shell_exec("handBrakeCli -Z 'Very Fast 720p30' -i '" . $subPathFile . "' -o '" . $targetFile . "'"); dump($result); } } } } } print_r($files); } // public function processDir($baseDir = "/Volumes/WD/Video/HuaVid/") public function processDir($baseDir = "/Volumes/Backup/HuaVid/大忽悠") { $files = scandir($baseDir); foreach ($files as $file) { if ($file == "." || $file == ".." || $file == ".DS_Store" || $file == "1762" || str_contains($file, "flv") || starts_with($file, ".")) { continue; } $subDir = implode("/", [$baseDir, $file]); $isDir = is_dir($subDir); if ($isDir) { $this->processDir($subDir); } else { $this->processVideo($subDir); } } } public function processDirWithQueue($baseDir = "/Volumes/Backup/HuaVid/大忽悠", $queue) { $files = scandir($baseDir); foreach ($files as $file) { if ($file == "." || $file == ".." || $file == ".DS_Store" || $file == "1762" || str_contains($file, "flv")) { continue; } $subDir = implode("/", [$baseDir, $file]); $isDir = is_dir($subDir); if ($isDir) { $this->processDir($subDir); } else { $item = $this->getRedis()->rpop($queue); while ($item != null) { $this->processVideo($item); echo $item . "\n"; $item = $this->getRedis()->rpop($queue); } $this->processVideo($subDir); } } } public function processVideo($pathFile) { $slowFlag = false; // if (date("H") >= 5 && date("i") > 30 && date("H") < 9) { // Log::info("H > 6 stopFlag is set"); // dump("H > 6 stopFlag is set"); // exit; // } // while () { // Log::info("now is " . date("Y-m-d H:i:s") . " sleep 5 minutes"); // sleep(5 * 60); // } Log::info("current process pathFile " . $pathFile); try { $mime = mime_content_type($pathFile); } catch (Throwable $e) { Log::error("mime_content_type has exception " . $e->getMessage()); } $mediaInfo = new MediaInfo(); $mediaInfo->setConfig('use_oldxml_mediainfo_output_format', true); if (strstr($mime, "video/") || strstr($mime, "application/octet-stream")) { if (is_file($pathFile)) { if ($this->getRedis()->get("stopFlag") != null) { Log::info("stopFlag is set"); dump("stopFlag is set"); exit; } $fileInfo = pathinfo($pathFile); if ($this->getRedis()->get("encode:lock:" . $fileInfo["filename"]) == 1) { Log::info("file is encoding filename: " . $fileInfo["filename"]); return; } if (!$this->getRedis()->set("encode:lock:" . $fileInfo["filename"], 1, "nx", "ex", 36000)) { Log::info("lock failed filename: " . $fileInfo["filename"]); return; } if ($this->getRedis()->sismember("unneed", $fileInfo["filename"])) { Log::info("in uneed: " . $fileInfo["filename"]); return; } if ($this->getRedis()->sismember("sizeSmall", $fileInfo["filename"]) || !$this->checkFileSize($pathFile)) { $this->getRedis()->sadd("sizeSmall", $fileInfo["filename"]); Log::info("filesize: " . $fileInfo["filename"]); return; } if ($this->getRedis()->sismember("hasEncode", $fileInfo["filename"]) || $this->checkFileEncodeType($pathFile)) { $this->getRedis()->sadd("hasEncode", $fileInfo["filename"]); Log::info("$pathFile has already encode by h265 return"); return; } if (filemtime($pathFile) > strtotime("2021-07-26 00:00:00")) { $mtime = date("Y-m-d H:i:s", filemtime($pathFile)); // dump("$pathFile modify at $mtime is after 2021-07-26 00:00:00 skip"); // return; } else { $mtime = date("Y-m-d H:i:s", filemtime($pathFile)); // dump("$pathFile modify at $mtime is before 2021-07-19 00:00:00"); } if (ends_with($fileInfo["filename"], "-x265")) { return; } $targetFile = $fileInfo["dirname"] . DIRECTORY_SEPARATOR . $fileInfo["filename"] . '-x265' . '.' . self::DEFAULT_EXTENSION; if (is_file($targetFile) && $this->isNeedRemoveExistFiles()) { Log::info("$targetFile is exists"); unlink($pathFile); rename($targetFile, $pathFile); return; } dump("targetFile", [$targetFile]); Log::info("process target file : $targetFile"); $preset = ""; if ($this->getPreset() != null && $this->getPreset() != "") { $preset = " -preset " . $this->getPreset(); } else { $preset = " -preset ultrafast"; } if ($this->getDimension($pathFile)->getWidth() > 3840) { // $result = shell_exec("/Users/shixuesen/Downloads/ffmpeg -threads 16 -i ". escapeshellarg($pathFile) ." -preset ultrafast -crf 25 -c:v libx265 -x265-params pools=8 -vtag hvc1 " . escapeshellarg($targetFile) . " && echo 'ok'"); $result = shell_exec("/Users/shixuesen/Downloads/software/ffmpeg -i " . escapeshellarg($pathFile) . " {$preset} -c:v libx265 -x265-params pools=8 -vtag hvc1 -vf \"scale=4096:-1\" " . escapeshellarg($targetFile) . " && echo 'ok'"); } else { if ($slowFlag) { $result = shell_exec("/Users/shixuesen/Downloads/software/ffmpeg -i " . escapeshellarg($pathFile) . " {$preset} -c:v libx265 -x265-params pools=8 -vtag hvc1 " . escapeshellarg($targetFile) . " && echo 'ok'"); } else { $result = shell_exec("/Users/shixuesen/Downloads/software/ffmpeg -i " . escapeshellarg($pathFile) . " {$preset} -c:v libx265 -x265-params pools=8 -vtag hvc1 " . escapeshellarg($targetFile) . " && echo 'ok'"); } } // echo $result; // return; if (trim($result) == "ok" && $this->isNeedRemoveAfterEncode()) { echo "compress work done remove the file \n"; Log::info("compress work done remove the file"); $oldFileSize = filesize($pathFile); $newFileSize = filesize($targetFile); if ($newFileSize >= $oldFileSize) { $this->getRedis()->sadd("unneed", $fileInfo["filename"]); echo "old file size is smaller than new one, old is " . file_size($oldFileSize) . " and new is " . file_size($newFileSize) . ", now remove new one"; Log::info("old file size is smaller than new one, old is " . file_size($oldFileSize) . " and new is " . file_size($newFileSize) . ", now remove new one"); unlink($targetFile); } else { $this->getRedis()->sadd("unneed", $fileInfo["filename"]); echo "new file size is smaller than old one, new is " . file_size($newFileSize) . " and old is " . file_size($oldFileSize) . ", now remove old one"; Log::info("new file size is smaller than old one, new is " . file_size($newFileSize) . " and old is " . file_size($oldFileSize) . ", now remove old one"); unlink($pathFile); rename($targetFile, $pathFile); } } $this->getRedis()->del("encode:lock:" . $fileInfo["filename"]); } } } public function processUnCompleteDir($baseDir = "/Volumes/WD/tmp/探花系列【AI高清2K修复】大合集") // public function processDir($baseDir = "/Volumes/Backup/iPhone nPlayer/") { $files = scandir($baseDir); foreach ($files as $file) { if ($file == "." || $file == "..") { continue; } $subDir = implode("/", [$baseDir, $file]); $isDir = is_dir($subDir); if ($isDir) { $this->processUnCompleteDir($subDir); } else { $this->processUnCompleteVideo($subDir); } } } public function processUnCompleteVideo($pathFile) { //... $mediaInfo = new MediaInfo(); $mediaInfo->setConfig('use_oldxml_mediainfo_output_format', true); $mime = mime_content_type($pathFile); // dump("file type", [$mime, $subPathFile]); // continue; if (strstr($mime, "video/")) { if (is_file($pathFile)) { $fileInfo = pathinfo($pathFile); // dump("fileInfo", $fileInfo); if (ends_with($fileInfo["filename"], "-1")) { return; } if (is_file($fileInfo["dirname"] . '/' . $fileInfo["filename"] . '-1' . '.' . $fileInfo["extension"])) { $mediaInfoContainer1 = $mediaInfo->getInfo($fileInfo["dirname"] . '/' . $fileInfo["filename"] . '-1' . '.' . $fileInfo["extension"]); $millSecond1 = $mediaInfoContainer1->getGeneral()->get("duration")->getMilliseconds(); echo gettype($millSecond1) . "\n"; // ["duration"] . "\n"; $mediaInfoContainer = $mediaInfo->getInfo($pathFile); $millSecond = $mediaInfoContainer->getGeneral()->get("duration")->getMilliseconds(); echo gettype($millSecond) . "\n"; if (abs(intval($millSecond) - intval($millSecond1)) > 100) { echo $pathFile . "\n"; echo abs(intval($millSecond) - intval($millSecond1)) . "\n"; } // unlink($pathFile); return; } // $targetFile = $fileInfo["dirname"] . '/' .$fileInfo["filename"] . '-1'. '.' . $fileInfo["extension"]; // dump("targetFile", [$targetFile]); //// $result = shell_exec("handBrakeCli -Z 'Very Fast 720p30' -i '". $subPathFile ."' -o '". $targetFile . " && echo 'success'"); // $result = shell_exec("handBrakeCli -Z 'Very Fast 720p30' -i '". $pathFile ."' -o '". $targetFile . "'"); // dump($result); } } } public function checkFileDimension($file): bool { $mediaContainer = $this->mediainfo->getInfo($file); foreach ($mediaContainer->getVideos() as $video) { $height = $video->get('height')->getAbsoluteValue(); $width = $video->get('width')->getAbsoluteValue(); if ($height > $width && $width <= 720) { echo "$file 分辨率小于 720p 跳过\n"; return false; } if ($height <= $width && $height <= 720) { echo "$file 分辨率小于 720p 跳过\n"; return false; } } return true; } public function checkFileSize($file, $size = 1): bool { if (is_file($file) && filesize($file) > 4 * 1024 * 1024 * 1024) { return true; } $fileSize = FileUtils::humanFilesize(filesize($file)); echo "$file size < 4GB filesize is $fileSize skip \n"; return true; } public function checkFileEncodeType($file): bool { try { $codecName = $this->ffprobe ->streams($file) // extracts streams informations ->videos() // filters video streams ->first() // returns the first video stream ->get('codec_name'); } catch (Throwable $e) { echo "error $file \n"; Log::error("ffprobe has error just return false for test, exception: " . $e->getMessage()); return false; } return false; return trim($codecName) == "hevc"; } public function getDimension($file): Dimension { $dimensions = new Dimension(1920, 1080); try { $dimensions = $this->ffprobe ->streams($file) ->videos() ->first() ->getDimensions(); } catch (Throwable $e) { Log::error("ffprobe getDimension has error just return default dimension, exception: " . $e->getMessage(), ["file" => $file]); return $dimensions; } return $dimensions; } /** * @param bool $needRemoveAfterEncode */ public function setNeedRemoveAfterEncode(bool $needRemoveAfterEncode): void { $this->needRemoveAfterEncode = $needRemoveAfterEncode; } /** * @param bool $needRemoveExistFiles */ public function setNeedRemoveExistFiles(bool $needRemoveExistFiles): void { $this->needRemoveExistFiles = $needRemoveExistFiles; } /** * @return bool */ public function isNeedRemoveAfterEncode(): bool { return $this->needRemoveAfterEncode; } /** * @return bool */ public function isNeedRemoveExistFiles(): bool { return $this->needRemoveExistFiles; } }