|
|
@@ -37,47 +37,63 @@ class VideoGenerationController extends BaseController
|
|
|
'prompt' => 'required|string|max:1000',
|
|
|
'seed' => 'required|integer',
|
|
|
'video_duration' => 'required|in:5,10',
|
|
|
+ 'video_resolution' => 'required|in:720P,1080P'
|
|
|
];
|
|
|
|
|
|
$baseMessages = [
|
|
|
'prompt.required' => '提示词不能为空',
|
|
|
'prompt.max' => '提示词不能超过1000个字符',
|
|
|
+ 'seed.required' => '随机种子不能为空',
|
|
|
+ 'seed.integer' => '随机种子必须是整数',
|
|
|
'video_duration.required' => '请选择视频时长',
|
|
|
'video_duration.in' => '视频时长只能是5s或10s',
|
|
|
+ 'video_resolution.required' => '请选择视频分辨率',
|
|
|
+ 'video_resolution.in' => '视频分辨率只能是720P或1080P',
|
|
|
];
|
|
|
|
|
|
- // 根据传入参数决定验证规则
|
|
|
- if (isset($data['first_frame_url']) && isset($data['tail_frame_url'])) {
|
|
|
- // URL 方式
|
|
|
- $validationRules = array_merge($baseRules, [
|
|
|
- 'first_frame_url' => 'required|url',
|
|
|
- 'tail_frame_url' => 'required|url',
|
|
|
- ]);
|
|
|
-
|
|
|
- $validationMessages = array_merge($baseMessages, [
|
|
|
- 'first_frame_url.required' => '首帧图片URL不能为空',
|
|
|
- 'first_frame_url.url' => '首帧图片URL格式不正确',
|
|
|
- 'tail_frame_url.required' => '尾帧图片URL不能为空',
|
|
|
- 'tail_frame_url.url' => '尾帧图片URL格式不正确',
|
|
|
- ]);
|
|
|
- } else {
|
|
|
- // 上传文件方式
|
|
|
- $validationRules = array_merge($baseRules, [
|
|
|
- 'first_frame_file' => 'required|image|mimes:jpeg,png|max:4800',
|
|
|
- 'tail_frame_file' => 'required|image|mimes:jpeg,png|max:4800',
|
|
|
- ]);
|
|
|
-
|
|
|
- $validationMessages = array_merge($baseMessages, [
|
|
|
- 'first_frame_file.required' => '首帧图片不能为空',
|
|
|
+ // 动态构建验证规则
|
|
|
+ $validationRules = $baseRules;
|
|
|
+ $validationMessages = $baseMessages;
|
|
|
+
|
|
|
+ // 检查是否有首帧URL
|
|
|
+ if (isset($data['first_frame_url'])) {
|
|
|
+ $validationRules['first_frame_url'] = 'url';
|
|
|
+ $validationMessages['first_frame_url.url'] = '首帧图片URL格式不正确';
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否有尾帧URL
|
|
|
+ if (isset($data['tail_frame_url'])) {
|
|
|
+ $validationRules['tail_frame_url'] = 'url';
|
|
|
+ $validationMessages['tail_frame_url.url'] = '尾帧图片URL格式不正确';
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否有首帧文件
|
|
|
+ if ($request->hasFile('first_frame_file')) {
|
|
|
+ $validationRules['first_frame_file'] = 'image|mimes:jpeg,png|max:4800';
|
|
|
+ $validationMessages = array_merge($validationMessages, [
|
|
|
'first_frame_file.image' => '首帧必须是图片文件',
|
|
|
'first_frame_file.mimes' => '首帧图片必须是jpeg,png格式',
|
|
|
'first_frame_file.max' => '首帧图片大小不能超过4.7MB',
|
|
|
- 'tail_frame_file.required' => '尾帧图片不能为空',
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否有尾帧文件
|
|
|
+ if ($request->hasFile('tail_frame_file')) {
|
|
|
+ $validationRules['tail_frame_file'] = 'image|mimes:jpeg,png|max:4800';
|
|
|
+ $validationMessages = array_merge($validationMessages, [
|
|
|
'tail_frame_file.image' => '尾帧必须是图片文件',
|
|
|
'tail_frame_file.mimes' => '尾帧图片必须是jpeg,png格式',
|
|
|
'tail_frame_file.max' => '尾帧图片大小不能超过4.7MB',
|
|
|
]);
|
|
|
}
|
|
|
+
|
|
|
+ if (isset($data['tail_frame_url']) && !isset($data['first_frame_url'])) {
|
|
|
+ Utils::throwError('1002:提供尾帧URL时必须提供首帧URL');
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($request->hasFile('tail_frame_file') && !$request->has('first_frame_file')) {
|
|
|
+ Utils::throwError('1002:提供尾帧文件时必须提供首帧文件');
|
|
|
+ }
|
|
|
|
|
|
$validator = Validator::make($data, $validationRules, $validationMessages);
|
|
|
|
|
|
@@ -85,78 +101,57 @@ class VideoGenerationController extends BaseController
|
|
|
Utils::throwError('1002:'.$validator->errors()->first());
|
|
|
}
|
|
|
|
|
|
- // 处理上传的图片
|
|
|
- if ($request->hasFile('first_frame_file') && $request->hasFile('tail_frame_file')) {
|
|
|
- $firstFrameFile = $request->file('first_frame_file');
|
|
|
- $tailFrameFile = $request->file('tail_frame_file');
|
|
|
+ $firstFrame = null;
|
|
|
+ $tailFrame = null;
|
|
|
|
|
|
- $firstFrameExtension = $firstFrameFile->getClientOriginalExtension();
|
|
|
- $tailFrameExtension = $tailFrameFile->getClientOriginalExtension();
|
|
|
-
|
|
|
- // 验证图片分辨率和比例
|
|
|
- $firstFrameInfo = @getimagesize($firstFrameFile->getPathname());
|
|
|
- $tailFrameInfo = @getimagesize($tailFrameFile->getPathname());
|
|
|
-
|
|
|
- if (!$firstFrameInfo || !$tailFrameInfo) {
|
|
|
- Utils::throwError('1003:图片格式无效');
|
|
|
+ // 处理首帧图片(文件上传方式)
|
|
|
+ if ($request->hasFile('first_frame_file')) {
|
|
|
+ $firstFrameFile = $request->file('first_frame_file');
|
|
|
+ $firstFrame = $this->processFrameFile($firstFrameFile, 'first');
|
|
|
+ $data['first_frame_url'] = $firstFrame['url'];
|
|
|
+ }
|
|
|
+ // 处理首帧图片(URL方式)
|
|
|
+ elseif (isset($data['first_frame_url']) && !empty($data['first_frame_url'])) {
|
|
|
+ $firstFrame = Utils::getRemoteImageInfo($data['first_frame_url'], true);
|
|
|
+ if (!$firstFrame) {
|
|
|
+ Utils::throwError('1003:首帧图片获取失败');
|
|
|
}
|
|
|
-
|
|
|
- $firstFrame = [
|
|
|
- 'width' => $firstFrameInfo[0],
|
|
|
- 'height' => $firstFrameInfo[1],
|
|
|
- 'size' => $firstFrameFile->getSize(),
|
|
|
- ];
|
|
|
-
|
|
|
- $tailFrame = [
|
|
|
- 'width' => $tailFrameInfo[0],
|
|
|
- 'height' => $tailFrameInfo[1],
|
|
|
- 'size' => $tailFrameFile->getSize(),
|
|
|
- ];
|
|
|
-
|
|
|
- // 本地保存
|
|
|
- $firstFramePath = $firstFrameFile->store('video_frames', 'public');
|
|
|
- $tailFramePath = $tailFrameFile->store('video_frames', 'public');
|
|
|
-
|
|
|
- // 获取图片绝对路径
|
|
|
- $firstFramePath = storage_path('app/public/' . $firstFramePath);
|
|
|
- $tailFramePath = storage_path('app/public/' . $tailFramePath);
|
|
|
-
|
|
|
- // 图片上传的方式需获取图片的图片文件base64编码
|
|
|
- $data['first_frame_base64'] = 'data:image/' . $firstFrameExtension . ';base64,' . base64_encode(file_get_contents($firstFramePath));
|
|
|
- $data['tail_frame_base64'] = 'data:image/' . $tailFrameExtension . ';base64,' . base64_encode(file_get_contents($tailFramePath));
|
|
|
-
|
|
|
- unlink($firstFramePath);
|
|
|
- unlink($tailFramePath);
|
|
|
+ }
|
|
|
|
|
|
- } else {
|
|
|
- // 使用URL方式,验证远程图片
|
|
|
- $firstFrame = Utils::getRemoteImageInfo($data['first_frame_url'], true);
|
|
|
+ // 处理尾帧图片(文件上传方式)
|
|
|
+ if ($request->hasFile('tail_frame_file')) {
|
|
|
+ $tailFrameFile = $request->file('tail_frame_file');
|
|
|
+ $tailFrame = $this->processFrameFile($tailFrameFile, 'tail');
|
|
|
+ $data['tail_frame_url'] = $tailFrame['url'];
|
|
|
+ }
|
|
|
+ // 处理尾帧图片(URL方式)
|
|
|
+ elseif (isset($data['tail_frame_url']) && !empty($data['tail_frame_url'])) {
|
|
|
$tailFrame = Utils::getRemoteImageInfo($data['tail_frame_url'], true);
|
|
|
- if (!$firstFrame || !$tailFrame) {
|
|
|
- Utils::throwError('1003:图片获取失败');
|
|
|
+ if (!$tailFrame) {
|
|
|
+ Utils::throwError('1003:尾帧图片获取失败');
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 判断首尾帧图片是否满足以下条件:
|
|
|
- // 1.图片文件最大4.7MB
|
|
|
- // 2.图片分辨率最大: 4096*4096, 最短边不低于320
|
|
|
- // 3.图片长边与短边比例在3以内
|
|
|
- // 4.尾帧图片与首帧图片比例相同
|
|
|
- if ($firstFrame['size'] > 4.7 * 1024 * 1024) {
|
|
|
- Utils::throwError('1003:图片大小不能超过4.7MB');
|
|
|
- }
|
|
|
- if ($firstFrame['width'] > 4096 || $firstFrame['height'] > 4096 || $firstFrame['width'] < 320 || $firstFrame['height'] < 320) {
|
|
|
- Utils::throwError('1003:图片分辨率不能超过4096*4096,且最短边不能低于320');
|
|
|
+ // 验证首帧图片规格(如果存在)
|
|
|
+ if ($firstFrame) {
|
|
|
+ $this->validateFrameSpecs($firstFrame, '首帧');
|
|
|
}
|
|
|
- if ($firstFrame['width'] / $firstFrame['height'] > 3 || $tailFrame['width'] / $tailFrame['height'] > 3) {
|
|
|
- Utils::throwError('1003:图片长边与短边比例不能超过3');
|
|
|
+
|
|
|
+ // 验证尾帧图片规格(如果存在)
|
|
|
+ if ($tailFrame) {
|
|
|
+ $this->validateFrameSpecs($tailFrame, '尾帧');
|
|
|
}
|
|
|
- if ($firstFrame['width'] / $firstFrame['height'] != $tailFrame['width'] / $tailFrame['height']) {
|
|
|
- Utils::throwError('1003:尾帧图片与首帧图片比例相同');
|
|
|
+
|
|
|
+ // 如果首尾帧都存在,验证比例是否相同
|
|
|
+ if ($firstFrame && $tailFrame) {
|
|
|
+ $firstRatio = round($firstFrame['width'] / $firstFrame['height'], 2);
|
|
|
+ $tailRatio = round($tailFrame['width'] / $tailFrame['height'], 2);
|
|
|
+
|
|
|
+ if ($firstRatio != $tailRatio) {
|
|
|
+ Utils::throwError('1003:尾帧图片与首帧图片比例必须相同');
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- dd($data);
|
|
|
-
|
|
|
try {
|
|
|
$task = $this->aiVideoGenerationService->createVideoGenerationTask($data);
|
|
|
|
|
|
@@ -198,6 +193,66 @@ class VideoGenerationController extends BaseController
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
+ * 处理帧图片文件
|
|
|
+ *
|
|
|
+ * @param \Illuminate\Http\UploadedFile $file
|
|
|
+ * @param string $frameType 帧类型(first/tail)
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ private function processFrameFile($file, string $frameType): array
|
|
|
+ {
|
|
|
+ // 验证图片信息
|
|
|
+ $frameInfo = @getimagesize($file->getPathname());
|
|
|
+
|
|
|
+ if (!$frameInfo) {
|
|
|
+ Utils::throwError('1003:' . ($frameType === 'first' ? '首帧' : '尾帧') . '图片格式无效');
|
|
|
+ }
|
|
|
+
|
|
|
+ $frameData = [
|
|
|
+ 'width' => $frameInfo[0],
|
|
|
+ 'height' => $frameInfo[1],
|
|
|
+ 'size' => $file->getSize(),
|
|
|
+ ];
|
|
|
+
|
|
|
+ // 上传图片到服务器
|
|
|
+ try {
|
|
|
+ $frameData['url'] = uploadFileByTos('image', $file);
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ Utils::throwError('1003:' . ($frameType === 'first' ? '首帧' : '尾帧') . '图片上传失败:' . $e->getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ return $frameData;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 验证帧图片规格
|
|
|
+ *
|
|
|
+ * @param array $frame
|
|
|
+ * @param string $frameName
|
|
|
+ * @return void
|
|
|
+ */
|
|
|
+ private function validateFrameSpecs(array $frame, string $frameName): void
|
|
|
+ {
|
|
|
+ // 1.图片文件最大4.7MB
|
|
|
+ if ($frame['size'] > 4.7 * 1024 * 1024) {
|
|
|
+ Utils::throwError('1003:' . $frameName . '图片大小不能超过4.7MB');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2.图片分辨率最大: 4096*4096, 最短边不低于320
|
|
|
+ $minDimension = min($frame['width'], $frame['height']);
|
|
|
+ $maxDimension = max($frame['width'], $frame['height']);
|
|
|
+
|
|
|
+ if ($maxDimension > 4096 || $minDimension < 320) {
|
|
|
+ Utils::throwError('1003:' . $frameName . '图片分辨率不能超过4096*4096,且最短边不能低于320');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3.图片长边与短边比例在3以内
|
|
|
+ if ($maxDimension / $minDimension > 3) {
|
|
|
+ Utils::throwError('1003:' . $frameName . '图片长边与短边比例不能超过3');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
* 获取视频任务列表
|
|
|
*
|
|
|
* @param Request $request
|