|
|
@@ -639,6 +639,7 @@ class AnimeController extends BaseController
|
|
|
$episode = DB::table('mp_anime_episodes')->where('id', $episodeId)->where('anime_id', $animeId)->first();
|
|
|
if (!$episode) Utils::throwError('20003:分集不存在');
|
|
|
$episodeNumber = $episode->episode_number;
|
|
|
+ $ratio = !empty($episode->ratio) ? $episode->ratio : '9:16';
|
|
|
// 获取所有分镜信息
|
|
|
$segments = DB::table('mp_episode_segments')
|
|
|
->where('anime_id', $animeId)
|
|
|
@@ -702,7 +703,7 @@ class AnimeController extends BaseController
|
|
|
'video_duration' => $videoDuration,
|
|
|
'video_resolution' => '720P',
|
|
|
'seed' => -1,
|
|
|
- 'ratio' => '9:16',
|
|
|
+ 'ratio' => $ratio,
|
|
|
'generate_audio' => false,
|
|
|
'draft' => false,
|
|
|
'watermark' => false,
|
|
|
@@ -841,47 +842,36 @@ class AnimeController extends BaseController
|
|
|
$uid = Site::getUid();
|
|
|
// 设置 SSE 响应头并开始长连接
|
|
|
return response()->stream(function () use ($taskIds, $segmentTasks, $animeId, $episodeNumber, $episodeId, $startTime, $maxDuration, $uid) {
|
|
|
+ $checkInterval = 10; // 每10秒检查一次
|
|
|
+
|
|
|
+ // 初始化任务数组
|
|
|
+ $totalTasks = $taskIds; // 所有任务ID
|
|
|
+ $completeTasks = []; // 已完成的任务ID(包含失败)
|
|
|
+ $successTasks = []; // 成功的任务ID
|
|
|
+
|
|
|
// 发送初始任务创建信息
|
|
|
echo "data: " . json_encode([
|
|
|
'type' => 'init',
|
|
|
- 'data' => [
|
|
|
- 'anime_id' => $animeId,
|
|
|
- 'episode_id' => $episodeId,
|
|
|
- 'episode_number' => $episodeNumber,
|
|
|
- 'total_tasks' => count($taskIds),
|
|
|
- 'task_ids' => $taskIds,
|
|
|
- 'segments' => $segmentTasks,
|
|
|
- 'start_time' => $startTime,
|
|
|
- 'max_duration' => $maxDuration
|
|
|
- ]
|
|
|
+ 'total_tasks_count' => count($totalTasks),
|
|
|
+ 'completed_tasks_count' => count($completeTasks),
|
|
|
+ 'success_tasks_count' => count($successTasks)
|
|
|
]) . "\n\n";
|
|
|
ob_flush();
|
|
|
flush();
|
|
|
|
|
|
- $checkInterval = 10; // 每10秒检查一次
|
|
|
- $completedTasks = [];
|
|
|
- $failedTasks = [];
|
|
|
-
|
|
|
- // 检查是否已经超时(包含创建任务的时间)
|
|
|
while (time() - $startTime < $maxDuration) {
|
|
|
try {
|
|
|
- $allCompleted = true;
|
|
|
- $currentStatus = [];
|
|
|
+ $hasUpdate = false; // 标记是否有更新
|
|
|
|
|
|
- foreach ($segmentTasks as $segmentTask) {
|
|
|
- $taskId = $segmentTask['task_id'];
|
|
|
- $segmentId = $segmentTask['segment_id'];
|
|
|
- $tail_frame = $segmentTasks['tail_frame'];
|
|
|
-
|
|
|
- // 跳过已完成或失败的任务
|
|
|
- if (in_array($taskId, $completedTasks) || in_array($taskId, $failedTasks)) {
|
|
|
+ // 获取所有任务的最新状态
|
|
|
+ foreach ($totalTasks as $taskId) {
|
|
|
+ // 如果任务已经在完成列表中,跳过
|
|
|
+ if (in_array($taskId, $completeTasks)) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
- // 查询任务状态
|
|
|
$task = \App\Models\MpGenerateVideoTask::find($taskId);
|
|
|
if (!$task) {
|
|
|
- $failedTasks[] = $taskId;
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
@@ -895,6 +885,7 @@ class AnimeController extends BaseController
|
|
|
} else {
|
|
|
$statusResult = $this->AIVideoGenerationService->querySeedanceTaskStatus($task);
|
|
|
}
|
|
|
+
|
|
|
if (isset($statusResult['status'])) {
|
|
|
// 更新任务状态
|
|
|
$task->update([
|
|
|
@@ -915,205 +906,159 @@ class AnimeController extends BaseController
|
|
|
DB::beginTransaction();
|
|
|
|
|
|
$now = date('Y-m-d H:i:s');
|
|
|
- // 更新分镜表
|
|
|
- $segmentUpdateData = [
|
|
|
- 'video_url' => $statusResult['result_url'],
|
|
|
- 'video_task_status' => '已完成',
|
|
|
- 'last_frame_url' => $statusResult['last_frame_url'] ?? '',
|
|
|
- 'current_type' => 2,
|
|
|
- 'updated_at' => $now
|
|
|
- ];
|
|
|
|
|
|
- // 只有当video_duration存在且大于0时才更新
|
|
|
- if (isset($statusResult['video_duration']) && $statusResult['video_duration'] > 0) {
|
|
|
- $segmentUpdateData['video_duration'] = $statusResult['video_duration'];
|
|
|
- $segmentUpdateData['video_time_point_start'] = 0;
|
|
|
- $segmentUpdateData['video_time_point_end'] = $statusResult['video_duration'];
|
|
|
+ // 获取分镜ID
|
|
|
+ $segmentId = null;
|
|
|
+ foreach ($segmentTasks as $segmentTask) {
|
|
|
+ if ($segmentTask['task_id'] == $taskId) {
|
|
|
+ $segmentId = $segmentTask['segment_id'];
|
|
|
+ $tail_frame = $segmentTask['tail_frame'];
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- $updateResult = DB::table('mp_episode_segments')
|
|
|
- ->where('segment_id', $segmentId)
|
|
|
- ->update($segmentUpdateData);
|
|
|
-
|
|
|
- if (!$updateResult) {
|
|
|
- Utils::throwError('20003:更新分镜表失败');
|
|
|
+ if ($segmentId) {
|
|
|
+ // 更新分镜表
|
|
|
+ $segmentUpdateData = [
|
|
|
+ 'video_url' => $statusResult['result_url'],
|
|
|
+ 'video_task_status' => '已完成',
|
|
|
+ 'last_frame_url' => $statusResult['last_frame_url'] ?? '',
|
|
|
+ 'current_type' => 2,
|
|
|
+ 'updated_at' => $now
|
|
|
+ ];
|
|
|
+
|
|
|
+ // 只有当video_duration存在且大于0时才更新
|
|
|
+ if (isset($statusResult['video_duration']) && $statusResult['video_duration'] > 0) {
|
|
|
+ $segmentUpdateData['video_duration'] = $statusResult['video_duration'];
|
|
|
+ $segmentUpdateData['video_time_point_start'] = 0;
|
|
|
+ $segmentUpdateData['video_time_point_end'] = $statusResult['video_duration'];
|
|
|
+ }
|
|
|
+
|
|
|
+ $updateResult = DB::table('mp_episode_segments')
|
|
|
+ ->where('segment_id', $segmentId)
|
|
|
+ ->update($segmentUpdateData);
|
|
|
+
|
|
|
+ if ($updateResult) {
|
|
|
+ // 更新分镜视频成功后新增对话记录
|
|
|
+ $records = [
|
|
|
+ [
|
|
|
+ 'uid' => $uid,
|
|
|
+ 'anime_id' => $animeId,
|
|
|
+ 'sequence' => $episodeNumber,
|
|
|
+ 'role' => 'user',
|
|
|
+ 'content' => '图片转视频',
|
|
|
+ 'segment_id' => $segmentId,
|
|
|
+ 'video_url' => '',
|
|
|
+ 'created_at' => $now,
|
|
|
+ 'updated_at' => $now
|
|
|
+ ],
|
|
|
+ [
|
|
|
+ 'uid' => $uid,
|
|
|
+ 'anime_id' => $animeId,
|
|
|
+ 'sequence' => $episodeNumber,
|
|
|
+ 'role' => 'assistant',
|
|
|
+ 'content' => $tail_frame ?? '',
|
|
|
+ 'segment_id' => $segmentId,
|
|
|
+ 'video_url' => $statusResult['result_url'],
|
|
|
+ 'created_at' => $now,
|
|
|
+ 'updated_at' => $now
|
|
|
+ ]
|
|
|
+ ];
|
|
|
+
|
|
|
+ DB::table('mp_anime_records')->insert($records);
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
- // 更新分镜视频成功后新增对话记录
|
|
|
- // 保存对话记录
|
|
|
- $records = [
|
|
|
- [
|
|
|
- 'uid' => $uid,
|
|
|
- 'anime_id' => $animeId,
|
|
|
- 'sequence' => $episodeNumber,
|
|
|
- 'role' => 'user',
|
|
|
- 'content' => '图片转视频',
|
|
|
- 'segment_id' => $segmentId,
|
|
|
- 'video_url' => '',
|
|
|
- 'created_at' => $now,
|
|
|
- 'updated_at' => $now
|
|
|
- ],
|
|
|
- [
|
|
|
- 'uid' => $uid,
|
|
|
- 'anime_id' => $animeId,
|
|
|
- 'sequence' => $episodeNumber,
|
|
|
- 'role' => 'assistant',
|
|
|
- 'content' => $tail_frame,
|
|
|
- 'segment_id' => $segmentId,
|
|
|
- 'video_url' => $statusResult['result_url'],
|
|
|
- 'created_at' => $now,
|
|
|
- 'updated_at' => $now
|
|
|
- ]
|
|
|
- ];
|
|
|
-
|
|
|
- DB::table('mp_anime_records')->insert($records);
|
|
|
|
|
|
DB::commit();
|
|
|
|
|
|
- $completedTasks[] = $taskId;
|
|
|
-
|
|
|
- // 发送单个任务完成通知
|
|
|
- echo "data: " . json_encode([
|
|
|
- 'type' => 'update',
|
|
|
- 'data' => [
|
|
|
- 'segment_id' => $segmentId,
|
|
|
- 'task_id' => $taskId,
|
|
|
- 'segment_number' => $segmentTask['segment_number'],
|
|
|
- 'status' => 'success',
|
|
|
- 'video_url' => $statusResult['result_url'],
|
|
|
- 'last_frame_url' => $statusResult['last_frame_url'] ?? '',
|
|
|
- 'completed_count' => count($completedTasks),
|
|
|
- 'total_count' => count($segmentTasks)
|
|
|
- ]
|
|
|
- ]) . "\n\n";
|
|
|
- ob_flush();
|
|
|
- flush();
|
|
|
-
|
|
|
} catch (\Exception $e) {
|
|
|
DB::rollBack();
|
|
|
|
|
|
$logData = [
|
|
|
- 'segment_id' => $segmentId,
|
|
|
'task_id' => $taskId,
|
|
|
'error' => $e->getMessage()
|
|
|
];
|
|
|
dLog('anime')->error('视频任务处理失败', $logData);
|
|
|
logDB('anime', 'error', '视频任务处理失败', $logData);
|
|
|
-
|
|
|
- // 发送任务处理失败通知
|
|
|
- echo "data: " . json_encode([
|
|
|
- 'type' => 'error',
|
|
|
- 'data' => [
|
|
|
- 'segment_id' => $segmentId,
|
|
|
- 'task_id' => $taskId,
|
|
|
- 'segment_number' => $segmentTask['segment_number'],
|
|
|
- 'error_message' => '任务处理失败: ' . $e->getMessage()
|
|
|
- ]
|
|
|
- ]) . "\n\n";
|
|
|
- ob_flush();
|
|
|
- flush();
|
|
|
}
|
|
|
|
|
|
} elseif ($statusResult['status'] === 'failed') {
|
|
|
- $updateResult = DB::table('mp_episode_segments')
|
|
|
- ->where('segment_id', $segmentId)
|
|
|
- ->update([
|
|
|
- 'video_task_status' => '失败',
|
|
|
- 'updated_at' => date('Y-m-d H:i:s')
|
|
|
- ]);
|
|
|
-
|
|
|
- $failedTasks[] = $taskId;
|
|
|
-
|
|
|
- // 发送任务失败通知
|
|
|
- echo "data: " . json_encode([
|
|
|
- 'type' => 'update',
|
|
|
- 'data' => [
|
|
|
- 'segment_id' => $segmentId,
|
|
|
- 'task_id' => $taskId,
|
|
|
- 'segment_number' => $segmentTask['segment_number'],
|
|
|
- 'status' => 'failed',
|
|
|
- 'error_message' => $statusResult['error_message'] ?? '视频生成失败',
|
|
|
- 'failed_count' => count($failedTasks),
|
|
|
- 'total_count' => count($segmentTasks)
|
|
|
- ]
|
|
|
- ]) . "\n\n";
|
|
|
- ob_flush();
|
|
|
- flush();
|
|
|
+ // 获取分镜ID并更新失败状态
|
|
|
+ foreach ($segmentTasks as $segmentTask) {
|
|
|
+ if ($segmentTask['task_id'] == $taskId) {
|
|
|
+ DB::table('mp_episode_segments')
|
|
|
+ ->where('segment_id', $segmentTask['segment_id'])
|
|
|
+ ->update([
|
|
|
+ 'video_task_status' => '失败',
|
|
|
+ 'updated_at' => date('Y-m-d H:i:s')
|
|
|
+ ]);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 收集当前状态
|
|
|
- $currentStatus[] = [
|
|
|
- 'segment_id' => $segmentId,
|
|
|
- 'task_id' => $taskId,
|
|
|
- 'segment_number' => $segmentTask['segment_number'],
|
|
|
- 'status' => $task->status,
|
|
|
- 'video_url' => $task->result_url,
|
|
|
- 'error_message' => $task->error_message
|
|
|
- ];
|
|
|
-
|
|
|
- // 检查是否还有未完成的任务
|
|
|
- if (!in_array($task->status, ['success', 'failed'])) {
|
|
|
- $allCompleted = false;
|
|
|
+ // 检查任务是否完成(成功或失败)
|
|
|
+ if (in_array($task->status, ['success', 'failed'])) {
|
|
|
+ $completeTasks[] = $taskId;
|
|
|
+ $hasUpdate = true;
|
|
|
+
|
|
|
+ if ($task->status === 'success') {
|
|
|
+ $successTasks[] = $taskId;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 发送整体进度更新
|
|
|
- echo "data: " . json_encode([
|
|
|
- 'type' => 'update',
|
|
|
- 'data' => [
|
|
|
- 'anime_id' => $animeId,
|
|
|
- 'episode_id' => $episodeId,
|
|
|
- 'episode_number' => $episodeNumber,
|
|
|
- 'completed_count' => count($completedTasks),
|
|
|
- 'failed_count' => count($failedTasks),
|
|
|
- 'total_count' => count($segmentTasks),
|
|
|
- 'elapsed_time' => time() - $startTime,
|
|
|
- 'remaining_time' => max(0, $maxDuration - (time() - $startTime)),
|
|
|
- 'segments_status' => $currentStatus
|
|
|
- ]
|
|
|
- ]) . "\n\n";
|
|
|
- ob_flush();
|
|
|
- flush();
|
|
|
+ // 如果有更新,发送update通知
|
|
|
+ if ($hasUpdate) {
|
|
|
+ echo "data: " . json_encode([
|
|
|
+ 'type' => 'update',
|
|
|
+ 'total_tasks_count' => count($totalTasks),
|
|
|
+ 'completed_tasks_count' => count($completeTasks),
|
|
|
+ 'success_tasks_count' => count($successTasks)
|
|
|
+ ]) . "\n\n";
|
|
|
+ ob_flush();
|
|
|
+ flush();
|
|
|
+ }
|
|
|
|
|
|
// 如果所有任务都完成了,结束连接
|
|
|
- if ($allCompleted) {
|
|
|
- // 获取最终结果
|
|
|
- $finalSegments = DB::table('mp_episode_segments')
|
|
|
- ->where('anime_id', $animeId)
|
|
|
- ->where('episode_id', $episodeId)
|
|
|
- ->orderBy('segment_number')
|
|
|
- ->select('segment_id', 'segment_number', 'video_url', 'video_task_status', 'last_frame_url')
|
|
|
- ->get();
|
|
|
+ if (count($completeTasks) >= count($totalTasks)) {
|
|
|
+ // 获取所有成功任务的分镜信息
|
|
|
+ $successSegments = [];
|
|
|
+ foreach ($successTasks as $taskId) {
|
|
|
+ // 通过taskId找到对应的segmentTask
|
|
|
+ foreach ($segmentTasks as $segmentTask) {
|
|
|
+ if ($segmentTask['task_id'] == $taskId) {
|
|
|
+ $segment = DB::table('mp_episode_segments')
|
|
|
+ ->where('segment_id', $segmentTask['segment_id'])
|
|
|
+ ->select('segment_id', 'video_url')
|
|
|
+ ->first();
|
|
|
+
|
|
|
+ if ($segment && !empty($segment->video_url)) {
|
|
|
+ $successSegments[] = [
|
|
|
+ 'segment_id' => $segment->segment_id,
|
|
|
+ 'video_url' => $segment->video_url
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
echo "data: " . json_encode([
|
|
|
'type' => 'completed',
|
|
|
- 'data' => [
|
|
|
- 'anime_id' => $animeId,
|
|
|
- 'episode_id' => $episodeId,
|
|
|
- 'episode_number' => $episodeNumber,
|
|
|
- 'completed_count' => count($completedTasks),
|
|
|
- 'failed_count' => count($failedTasks),
|
|
|
- 'total_count' => count($segmentTasks),
|
|
|
- 'total_elapsed_time' => time() - $startTime,
|
|
|
- 'segments' => $finalSegments->map(function($segment) {
|
|
|
- return [
|
|
|
- 'segment_id' => $segment->segment_id,
|
|
|
- 'segment_number' => $segment->segment_number,
|
|
|
- 'video_url' => $segment->video_url,
|
|
|
- 'status' => $segment->video_task_status,
|
|
|
- 'last_frame_url' => $segment->last_frame_url
|
|
|
- ];
|
|
|
- })->toArray()
|
|
|
- ]
|
|
|
+ 'total_tasks_count' => count($totalTasks),
|
|
|
+ 'completed_tasks_count' => count($completeTasks),
|
|
|
+ 'success_tasks_count' => count($successTasks),
|
|
|
+ 'success_segments' => $successSegments
|
|
|
]) . "\n\n";
|
|
|
ob_flush();
|
|
|
flush();
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- // 在睡眠前再次检查是否超时,避免不必要的等待
|
|
|
+ // 检查是否超时
|
|
|
if (time() - $startTime >= $maxDuration) {
|
|
|
break;
|
|
|
}
|
|
|
@@ -1124,8 +1069,9 @@ class AnimeController extends BaseController
|
|
|
echo "data: " . json_encode([
|
|
|
'type' => 'error',
|
|
|
'message' => '查询任务状态失败: ' . $e->getMessage(),
|
|
|
- 'elapsed_time' => time() - $startTime,
|
|
|
- 'remaining_time' => max(0, $maxDuration - (time() - $startTime))
|
|
|
+ 'total_tasks_count' => count($totalTasks),
|
|
|
+ 'completed_tasks_count' => count($completeTasks),
|
|
|
+ 'success_tasks_count' => count($successTasks)
|
|
|
]) . "\n\n";
|
|
|
ob_flush();
|
|
|
flush();
|
|
|
@@ -1139,38 +1085,14 @@ class AnimeController extends BaseController
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 超时处理 - 只有在真正超时时才执行
|
|
|
+ // 超时处理
|
|
|
if (time() - $startTime >= $maxDuration) {
|
|
|
- // 获取当前所有任务状态
|
|
|
- $timeoutSegments = DB::table('mp_episode_segments')
|
|
|
- ->where('anime_id', $animeId)
|
|
|
- ->where('episode_id', $episodeId)
|
|
|
- ->orderBy('segment_number')
|
|
|
- ->select('segment_id', 'segment_number', 'video_task_id', 'video_task_status', 'video_url')
|
|
|
- ->get();
|
|
|
-
|
|
|
echo "data: " . json_encode([
|
|
|
'type' => 'time_out',
|
|
|
'message' => '批量视频生成已超时30分钟,停止状态查询',
|
|
|
- 'data' => [
|
|
|
- 'anime_id' => $animeId,
|
|
|
- 'episode_id' => $episodeId,
|
|
|
- 'episode_number' => $episodeNumber,
|
|
|
- 'completed_count' => count($completedTasks),
|
|
|
- 'failed_count' => count($failedTasks),
|
|
|
- 'total_count' => count($segmentTasks),
|
|
|
- 'total_elapsed_time' => time() - $startTime,
|
|
|
- 'timeout_duration' => $maxDuration,
|
|
|
- 'segments' => $timeoutSegments->map(function($segment) {
|
|
|
- return [
|
|
|
- 'segment_id' => $segment->segment_id,
|
|
|
- 'segment_number' => $segment->segment_number,
|
|
|
- 'task_id' => $segment->video_task_id,
|
|
|
- 'status' => $segment->video_task_status,
|
|
|
- 'video_url' => $segment->video_url
|
|
|
- ];
|
|
|
- })->toArray()
|
|
|
- ]
|
|
|
+ 'total_tasks_count' => count($totalTasks),
|
|
|
+ 'completed_tasks_count' => count($completeTasks),
|
|
|
+ 'success_tasks_count' => count($successTasks)
|
|
|
]) . "\n\n";
|
|
|
ob_flush();
|
|
|
flush();
|