|
|
@@ -445,7 +445,6 @@ class AnimeController extends BaseController
|
|
|
}
|
|
|
|
|
|
public function createSegmentVideoTask(Request $request) {
|
|
|
- $uid = Site::getUid();
|
|
|
// 忽略所有超时限制
|
|
|
set_time_limit(0);
|
|
|
ini_set('max_execution_time', '0');
|
|
|
@@ -470,7 +469,7 @@ class AnimeController extends BaseController
|
|
|
$taskId = $result['task_id'];
|
|
|
|
|
|
// 设置 SSE 响应头
|
|
|
- return response()->stream(function () use ($taskId, $result, $uid) {
|
|
|
+ return response()->stream(function () use ($taskId, $result) {
|
|
|
// 设置 SSE 响应头
|
|
|
echo "data: " . json_encode([
|
|
|
'type' => 'init',
|
|
|
@@ -484,6 +483,9 @@ class AnimeController extends BaseController
|
|
|
$startTime = time();
|
|
|
$maxDuration = 1800; // 5分钟超时
|
|
|
$checkInterval = 5; // 每5秒检查一次
|
|
|
+ $lastTaskData = null; // 记录上一次推送的任务数据,仅在有变化时发送update
|
|
|
+ $lastQueueMessageTime = time(); // 记录上次发送队列消息的时间
|
|
|
+ $queueMessageInterval = 60; // 队列消息发送间隔(秒)
|
|
|
|
|
|
while (time() - $startTime < $maxDuration) {
|
|
|
try {
|
|
|
@@ -501,147 +503,37 @@ class AnimeController extends BaseController
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- // 如果任务还在处理中,查询最新状态
|
|
|
- if ($task->status === 'processing') {
|
|
|
- // 根据任务类型查询不同的状态
|
|
|
- if ($task->api_type === 'jimeng') {
|
|
|
- $statusResult = $this->AIVideoGenerationService->queryJimengTaskStatus($task);
|
|
|
- } elseif ($task->api_type === 'keling') {
|
|
|
- $statusResult = $this->AIVideoGenerationService->queryKelingOmniTaskStatus($task);
|
|
|
- } elseif ($task->api_type === 'zzengine') {
|
|
|
- $statusResult = $this->AIVideoGenerationService->queryUnifiedApiTaskStatus($task);
|
|
|
- } else {
|
|
|
- $statusResult = $this->AIVideoGenerationService->querySeedanceTaskStatus($task);
|
|
|
- }
|
|
|
- if (isset($statusResult['status'])) {
|
|
|
- // 更新任务状态
|
|
|
- $task->update([
|
|
|
- 'status' => $statusResult['status'],
|
|
|
- 'result_url' => $statusResult['result_url'] ?? null,
|
|
|
- 'compressed_url' => $statusResult['compressed_url'] ?? null,
|
|
|
- 'last_frame_url' => $statusResult['last_frame_url'] ?? '',
|
|
|
- 'error_message' => $statusResult['error_message'] ?? null,
|
|
|
- 'completed_at' => in_array($statusResult['status'], [
|
|
|
- 'success',
|
|
|
- 'failed'
|
|
|
- ]) ? now() : null
|
|
|
- ]);
|
|
|
-
|
|
|
- // 如果任务成功,更新分镜表
|
|
|
- if ($statusResult['status'] === 'success' && isset($statusResult['result_url'])) {
|
|
|
- try {
|
|
|
- DB::beginTransaction();
|
|
|
-
|
|
|
- $now = date('Y-m-d H:i:s');
|
|
|
-
|
|
|
- // 获取分镜ID
|
|
|
- $segment = DB::table('mp_episode_segments')
|
|
|
- ->where('video_task_id', $taskId)
|
|
|
- ->first();
|
|
|
-
|
|
|
- if (!$segment) {
|
|
|
- throw new \Exception('未找到对应的分镜记录');
|
|
|
- }
|
|
|
-
|
|
|
- $segmentId = $segment->segment_id;
|
|
|
- $anime_id = $segment->anime_id;
|
|
|
- $episode_number = $segment->episode_number;
|
|
|
- $tail_frame = $segment->tail_frame;
|
|
|
-
|
|
|
- // 更新分镜表
|
|
|
- $segmentUpdateData = [
|
|
|
- 'origin_video_url' => $statusResult['result_url'],
|
|
|
- 'video_task_status' => '已完成',
|
|
|
- 'last_frame_url' => $statusResult['last_frame_url'] ?? '',
|
|
|
- 'current_type' => 2,
|
|
|
- 'updated_at' => $now
|
|
|
- ];
|
|
|
- $compressed_video_url = compressVideo($statusResult['result_url']);
|
|
|
- $segmentUpdateData['video_url'] = $compressed_video_url ?: $statusResult['result_url'];
|
|
|
-
|
|
|
- // 只有当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'];
|
|
|
- $segmentUpdateData['video_time_point_end'] = $segment->audio_duration ?? 0;
|
|
|
- }
|
|
|
-
|
|
|
- $updateResult = DB::table('mp_episode_segments')
|
|
|
- ->where('segment_id', $segmentId)
|
|
|
- ->update($segmentUpdateData);
|
|
|
-
|
|
|
- if (!$updateResult) {
|
|
|
- Utils::throwError('20003:更新分镜表失败');
|
|
|
- }
|
|
|
-
|
|
|
- // 更新分镜视频成功后新增对话记录
|
|
|
- // 保存对话记录
|
|
|
- $records = [
|
|
|
- [
|
|
|
- 'uid' => $uid,
|
|
|
- 'anime_id' => $anime_id,
|
|
|
- 'sequence' => $episode_number,
|
|
|
- 'role' => 'user',
|
|
|
- 'content' => '图片转视频',
|
|
|
- 'segment_id' => $segmentId,
|
|
|
- 'video_url' => '',
|
|
|
- 'created_at' => $now,
|
|
|
- 'updated_at' => $now
|
|
|
- ],
|
|
|
- [
|
|
|
- 'uid' => $uid,
|
|
|
- 'anime_id' => $anime_id,
|
|
|
- 'sequence' => $episode_number,
|
|
|
- '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();
|
|
|
-
|
|
|
- } catch (\Exception $e) {
|
|
|
- DB::rollBack();
|
|
|
-
|
|
|
- $logData = [
|
|
|
- 'task_id' => $taskId,
|
|
|
- 'error' => $e->getMessage()
|
|
|
- ];
|
|
|
- dLog('anime')->error('视频任务处理失败', $logData);
|
|
|
- logDB('anime', 'error', '视频任务处理失败', $logData);
|
|
|
- }
|
|
|
- } elseif ($statusResult['status'] === 'failed') {
|
|
|
- DB::table('mp_episode_segments')
|
|
|
- ->where('video_task_id', $taskId)
|
|
|
- ->update([
|
|
|
- 'video_task_status' => '失败',
|
|
|
- 'updated_at' => date('Y-m-d H:i:s')
|
|
|
- ]);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ // 视频任务结果由定时任务实时更新,此处直接读取任务表状态
|
|
|
|
|
|
- // 发送当前状态
|
|
|
- echo "data: " . json_encode([
|
|
|
- 'type' => 'update',
|
|
|
- 'data' => [
|
|
|
- 'task_id' => $task->id,
|
|
|
- 'status' => $task->status,
|
|
|
- 'result_url' => $task->result_url,
|
|
|
- 'error_message' => $task->error_message ? mapErrorMessage($task->error_message) : '',
|
|
|
- 'elapsed_time' => time() - $startTime
|
|
|
- ]
|
|
|
- ]) . "\n\n";
|
|
|
- if (ob_get_level() > 0) {
|
|
|
- ob_flush();
|
|
|
+ // 构建当前任务数据
|
|
|
+ $taskData = [
|
|
|
+ 'status' => $task->status,
|
|
|
+ 'result_url' => $task->result_url,
|
|
|
+ 'error_message' => $task->error_message ? mapErrorMessage($task->error_message) : ''
|
|
|
+ ];
|
|
|
+
|
|
|
+ // 首次查询只记录状态,不发送update(init已推送任务创建信息)
|
|
|
+ if ($lastTaskData === null) {
|
|
|
+ $lastTaskData = $taskData;
|
|
|
+ } elseif ($taskData != $lastTaskData) {
|
|
|
+ $lastTaskData = $taskData;
|
|
|
+
|
|
|
+ // 有数据更新才发送update
|
|
|
+ echo "data: " . json_encode([
|
|
|
+ 'type' => 'update',
|
|
|
+ 'data' => [
|
|
|
+ 'task_id' => $task->id,
|
|
|
+ 'status' => $task->status,
|
|
|
+ 'result_url' => $task->result_url,
|
|
|
+ 'error_message' => $task->error_message ? mapErrorMessage($task->error_message) : '',
|
|
|
+ 'elapsed_time' => time() - $startTime
|
|
|
+ ]
|
|
|
+ ]) . "\n\n";
|
|
|
+ if (ob_get_level() > 0) {
|
|
|
+ ob_flush();
|
|
|
+ }
|
|
|
+ flush();
|
|
|
}
|
|
|
- flush();
|
|
|
|
|
|
// 如果任务完成(成功或失败),结束连接
|
|
|
if (in_array($task->status, [
|
|
|
@@ -666,6 +558,25 @@ class AnimeController extends BaseController
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
+ // 每分钟发送一次队列状态消息
|
|
|
+ $currentTime = time();
|
|
|
+ if ($currentTime - $lastQueueMessageTime >= $queueMessageInterval) {
|
|
|
+ echo "data: " . json_encode([
|
|
|
+ 'type' => 'queue',
|
|
|
+ 'data' => [
|
|
|
+ 'task_id' => $task->id,
|
|
|
+ 'status' => $task->status,
|
|
|
+ 'elapsed_time' => $currentTime - $startTime,
|
|
|
+ 'message' => '任务正在队列中执行...'
|
|
|
+ ]
|
|
|
+ ]) . "\n\n";
|
|
|
+ if (ob_get_level() > 0) {
|
|
|
+ ob_flush();
|
|
|
+ }
|
|
|
+ flush();
|
|
|
+ $lastQueueMessageTime = $currentTime;
|
|
|
+ }
|
|
|
+
|
|
|
sleep($checkInterval);
|
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
@@ -1031,10 +942,11 @@ class AnimeController extends BaseController
|
|
|
$startTime = time();
|
|
|
$maxDuration = 1800; // 30分钟超时
|
|
|
|
|
|
- $uid = Site::getUid();
|
|
|
// 设置 SSE 响应头并开始长连接
|
|
|
- return response()->stream(function () use ($taskIds, $segmentTasks, $animeId, $episodeNumber, $episodeId, $startTime, $maxDuration, $uid) {
|
|
|
+ return response()->stream(function () use ($taskIds, $segmentTasks, $episodeId, $startTime, $maxDuration) {
|
|
|
$checkInterval = 10; // 每10秒检查一次
|
|
|
+ $lastQueueMessageTime = time(); // 记录上次发送队列消息的时间
|
|
|
+ $queueMessageInterval = 60; // 队列消息发送间隔(秒)
|
|
|
|
|
|
// 初始化任务数组
|
|
|
$totalTasks = $taskIds; // 所有任务ID
|
|
|
@@ -1069,135 +981,7 @@ class AnimeController extends BaseController
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
- // 如果任务还在处理中,查询最新状态
|
|
|
- if ($task->status === 'processing') {
|
|
|
- // 根据任务类型查询不同的状态
|
|
|
- if ($task->api_type === 'jimeng') {
|
|
|
- $statusResult = $this->AIVideoGenerationService->queryJimengTaskStatus($task);
|
|
|
- } elseif ($task->api_type === 'keling') {
|
|
|
- $statusResult = $this->AIVideoGenerationService->queryKelingOmniTaskStatus($task);
|
|
|
- } elseif ($task->api_type === 'zzengine') {
|
|
|
- $statusResult = $this->AIVideoGenerationService->queryUnifiedApiTaskStatus($task);
|
|
|
- } else {
|
|
|
- $statusResult = $this->AIVideoGenerationService->querySeedanceTaskStatus($task);
|
|
|
- }
|
|
|
-
|
|
|
- if (isset($statusResult['status'])) {
|
|
|
- // 更新任务状态
|
|
|
- $task->update([
|
|
|
- 'status' => $statusResult['status'],
|
|
|
- 'result_json' => $statusResult['result_json'] ?? [],
|
|
|
- 'result_url' => $statusResult['result_url'] ?? null,
|
|
|
- 'last_frame_url' => $statusResult['last_frame_url'] ?? '',
|
|
|
- 'error_message' => $statusResult['error_message'] ?? null,
|
|
|
- 'completed_at' => in_array($statusResult['status'], [
|
|
|
- 'success',
|
|
|
- 'failed'
|
|
|
- ]) ? now() : null
|
|
|
- ]);
|
|
|
-
|
|
|
- // 如果任务成功,更新分镜表
|
|
|
- if ($statusResult['status'] === 'success' && isset($statusResult['result_url'])) {
|
|
|
- try {
|
|
|
- DB::beginTransaction();
|
|
|
-
|
|
|
- $now = date('Y-m-d H:i:s');
|
|
|
-
|
|
|
- // 获取分镜ID
|
|
|
- $segmentId = null;
|
|
|
- foreach ($segmentTasks as $segmentTask) {
|
|
|
- if ($segmentTask['task_id'] == $taskId) {
|
|
|
- $segmentId = $segmentTask['segment_id'];
|
|
|
- $tail_frame = $segmentTask['tail_frame'];
|
|
|
- $audio_duration = $segmentTask['audio_duration'];
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if ($segmentId) {
|
|
|
- // 更新分镜表
|
|
|
- $segmentUpdateData = [
|
|
|
- 'origin_video_url' => $statusResult['result_url'],
|
|
|
- 'video_task_status' => '已完成',
|
|
|
- 'last_frame_url' => $statusResult['last_frame_url'] ?? '',
|
|
|
- 'current_type' => 2,
|
|
|
- 'updated_at' => $now
|
|
|
- ];
|
|
|
- $compressed_video_url = compressVideo($statusResult['result_url']);
|
|
|
- $segmentUpdateData['video_url'] = $compressed_video_url ?: $statusResult['result_url'];
|
|
|
-
|
|
|
- // 只有当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'];
|
|
|
- $segmentUpdateData['video_time_point_end'] = $audio_duration ?? 0;
|
|
|
- }
|
|
|
-
|
|
|
- $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);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- DB::commit();
|
|
|
-
|
|
|
- } catch (\Exception $e) {
|
|
|
- DB::rollBack();
|
|
|
-
|
|
|
- $logData = [
|
|
|
- 'task_id' => $taskId,
|
|
|
- 'error' => $e->getMessage()
|
|
|
- ];
|
|
|
- dLog('anime')->error('视频任务处理失败', $logData);
|
|
|
- logDB('anime', 'error', '视频任务处理失败', $logData);
|
|
|
- }
|
|
|
-
|
|
|
- } elseif ($statusResult['status'] === 'failed') {
|
|
|
- // 获取分镜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;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ // 视频任务结果由定时任务实时更新,此处直接读取任务表状态
|
|
|
|
|
|
// 检查任务是否完成(成功或失败)
|
|
|
if (in_array($task->status, ['success', 'failed'])) {
|
|
|
@@ -1262,6 +1046,26 @@ class AnimeController extends BaseController
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
+ // 每分钟发送一次队列状态消息
|
|
|
+ $currentTime = time();
|
|
|
+ if ($currentTime - $lastQueueMessageTime >= $queueMessageInterval) {
|
|
|
+ echo "data: " . json_encode([
|
|
|
+ 'type' => 'queue',
|
|
|
+ 'data' => [
|
|
|
+ 'total_tasks_count' => count($totalTasks),
|
|
|
+ 'completed_tasks_count' => count($completeTasks),
|
|
|
+ 'success_tasks_count' => count($successTasks),
|
|
|
+ 'elapsed_time' => $currentTime - $startTime,
|
|
|
+ 'message' => '任务正在队列中执行...'
|
|
|
+ ]
|
|
|
+ ]) . "\n\n";
|
|
|
+ if (ob_get_level() > 0) {
|
|
|
+ ob_flush();
|
|
|
+ }
|
|
|
+ flush();
|
|
|
+ $lastQueueMessageTime = $currentTime;
|
|
|
+ }
|
|
|
+
|
|
|
// 检查是否超时
|
|
|
if (time() - $startTime >= $maxDuration) {
|
|
|
break;
|