|
|
@@ -1170,6 +1170,147 @@ function compressVideo($videoUrl, $prefix = 'videos')
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * 获取视频第一帧并上传
|
|
|
+ * 根据远程视频URL获取第一帧图片并保存到TOS或OSS(默认TOS)
|
|
|
+ *
|
|
|
+ * @param string $videoUrl 视频URL地址
|
|
|
+ * @param string $prefix 上传文件夹前缀(如:'videos')
|
|
|
+ * @param string $uploadMethod 上传方式:'tos'(默认) 或 'oss'
|
|
|
+ * @return string 返回图片URL地址,失败返回空字符串
|
|
|
+ */
|
|
|
+function getVideoFirstFrame($videoUrl, $prefix = 'videos', $uploadMethod = 'tos')
|
|
|
+{
|
|
|
+ if (env('APP_ENV') == 'local') return '';
|
|
|
+ try {
|
|
|
+ // 创建临时目录
|
|
|
+ $tempDir = storage_path('app/temp/video_frames');
|
|
|
+ if (!is_dir($tempDir)) {
|
|
|
+ mkdir($tempDir, 0775, true);
|
|
|
+ chmod($tempDir, 0775);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成唯一文件名
|
|
|
+ $uniqueId = uniqid('frame_') . bin2hex(random_bytes(4));
|
|
|
+ $videoExt = getVideoExtFromUrl($videoUrl);
|
|
|
+ $inputFile = $tempDir . '/' . $uniqueId . '_video' . $videoExt;
|
|
|
+ $outputFile = $tempDir . '/' . $uniqueId . '_frame.jpg';
|
|
|
+
|
|
|
+ // 下载视频到本地
|
|
|
+ dLog('video_frame')->info('开始下载视频', ['url' => $videoUrl]);
|
|
|
+ $client = new \GuzzleHttp\Client(['timeout' => 300]);
|
|
|
+ $response = $client->get($videoUrl);
|
|
|
+
|
|
|
+ $fileContent = $response->getBody()->getContents();
|
|
|
+ if (file_put_contents($inputFile, $fileContent) === false) {
|
|
|
+ dLog('video_frame')->error('视频文件写入失败', [
|
|
|
+ 'url' => $videoUrl,
|
|
|
+ 'file' => $inputFile
|
|
|
+ ]);
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+
|
|
|
+ chmod($inputFile, 0664);
|
|
|
+
|
|
|
+ if (!file_exists($inputFile)) {
|
|
|
+ dLog('video_frame')->error('视频下载失败', ['url' => $videoUrl]);
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+
|
|
|
+ dLog('video_frame')->info('视频下载成功', [
|
|
|
+ 'url' => $videoUrl,
|
|
|
+ 'size_mb' => round(filesize($inputFile) / 1024 / 1024, 2) . 'MB'
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // 使用FFmpeg提取第一帧
|
|
|
+ // -i: 输入文件
|
|
|
+ // -ss 00:00:00: 从视频开始位置(第0秒)
|
|
|
+ // -vframes 1: 只提取1帧
|
|
|
+ // -q:v 2: 图片质量(1-31,数字越小质量越高)
|
|
|
+ $ffmpegPath = env('FFMPEG_PATH', 'ffmpeg');
|
|
|
+ $ffmpegCmd = "$ffmpegPath -i \"$inputFile\" -ss 00:00:00 -vframes 1 -q:v 2 -y \"$outputFile\" 2>&1";
|
|
|
+
|
|
|
+ dLog('video_frame')->info('开始提取第一帧', ['command' => $ffmpegCmd]);
|
|
|
+ $output = shell_exec($ffmpegCmd);
|
|
|
+
|
|
|
+ if (!file_exists($outputFile)) {
|
|
|
+ dLog('video_frame')->error('提取第一帧失败', [
|
|
|
+ 'input' => $inputFile,
|
|
|
+ 'output' => $output
|
|
|
+ ]);
|
|
|
+ @unlink($inputFile);
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+
|
|
|
+ chmod($outputFile, 0664);
|
|
|
+
|
|
|
+ dLog('video_frame')->info('第一帧提取成功', [
|
|
|
+ 'size' => round(filesize($outputFile) / 1024, 2) . 'KB'
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // 上传图片到云存储
|
|
|
+ $stream = fopen($outputFile, 'r');
|
|
|
+ if (!$stream) {
|
|
|
+ dLog('video_frame')->error('无法打开图片文件', ['file' => $outputFile]);
|
|
|
+ @unlink($inputFile);
|
|
|
+ @unlink($outputFile);
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+
|
|
|
+ $filename = $uniqueId . '.jpg';
|
|
|
+
|
|
|
+ // 根据参数选择上传方式,默认使用TOS
|
|
|
+ if ($uploadMethod === 'oss') {
|
|
|
+ $frameUrl = uploadStreamToOss($prefix, $stream, $filename);
|
|
|
+ } else {
|
|
|
+ $frameUrl = uploadStreamByTos($prefix, $stream, $filename);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 安全关闭文件流
|
|
|
+ if (is_resource($stream)) {
|
|
|
+ fclose($stream);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 清理临时文件
|
|
|
+ @unlink($inputFile);
|
|
|
+ @unlink($outputFile);
|
|
|
+
|
|
|
+ if (!$frameUrl) {
|
|
|
+ dLog('video_frame')->error('图片上传失败');
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+
|
|
|
+ dLog('video_frame')->info('图片上传成功', [
|
|
|
+ 'video_url' => $videoUrl,
|
|
|
+ 'frame_url' => $frameUrl
|
|
|
+ ]);
|
|
|
+
|
|
|
+ return $frameUrl;
|
|
|
+
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ dLog('video_frame')->error('提取视频第一帧异常', [
|
|
|
+ 'url' => $videoUrl,
|
|
|
+ 'error' => $e->getMessage(),
|
|
|
+ 'trace' => $e->getTraceAsString()
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // 安全关闭文件流(如果存在)
|
|
|
+ if (isset($stream) && is_resource($stream)) {
|
|
|
+ @fclose($stream);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 清理可能存在的临时文件
|
|
|
+ if (isset($inputFile) && file_exists($inputFile)) {
|
|
|
+ @unlink($inputFile);
|
|
|
+ }
|
|
|
+ if (isset($outputFile) && file_exists($outputFile)) {
|
|
|
+ @unlink($outputFile);
|
|
|
+ }
|
|
|
+
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
function sensitiveStr($list, $string)
|
|
|
{
|
|
|
$count = 0; //违规词的个数
|