AnimeService = $AnimeService; $this->AIImageGenerationService = $AIImageGenerationService; $this->AIVideoGenerationService = $AIVideoGenerationService; } public function textModel() { $models = [ [ 'model' => 'v3', 'name' => 'DeepSeek 推理', ], [ 'model' => 'r1', 'name' => 'DeepSeek 思考' ], ]; return $this->success($models); } // 批量生成主体图片 public function batchSetRoleImg(Request $request) { // 忽略所有超时限制 set_time_limit(0); ini_set('max_execution_time', '0'); $data = $request->all(); $result = $this->AnimeService->batchSetRoleImg($data); return $this->success($result); } // 批量生成场景图片 public function batchSetSceneImg(Request $request) { // 忽略所有超时限制 set_time_limit(0); ini_set('max_execution_time', '0'); $data = $request->all(); $result = $this->AnimeService->batchSetSceneImg($data); return $this->success($result); } // 编辑动漫 public function editAnime(Request $request) { $data = $request->all(); $result = $this->AnimeService->editAnime($data); return $this->success(['success'=>$result ? 1 : 0]); } // 动漫对话列表 public function chatList(Request $request) { $data = $request->all(); $result = $this->AnimeService->chatList($data); return $this->success($result, [new AnimeTransformer(), 'newBuildChatList']); } // 动漫对话历史记录 public function chatHistory(Request $request) { $data = $request->all(); $result = $this->AnimeService->chatHistory($data); return $this->success($result); } // 动漫大纲 public function animeDetail(Request $request) { $data = $request->all(); $result = $this->AnimeService->animeDetail($data); return $this->success($result); } // 动漫剧集 public function episodeInfo(Request $request) { $data = $request->all(); $result = $this->AnimeService->episodeInfo($data); return $this->success($result); } // 复制剧集副本 public function copyEpisodeVersion(Request $request) { $data = $request->all(); $result = $this->AnimeService->copyEpisodeVersion($data); return $this->success(['success' => $result ? 1 : 0]); } // 剧集副本列表 public function episodeVersions(Request $request) { $data = $request->all(); $result = $this->AnimeService->episodeVersions($data); return $this->success($result); } // 绑定剧集副本 public function bindEpisodeVersion(Request $request) { $data = $request->all(); $result = $this->AnimeService->bindEpisodeVersion($data); return $this->success(['success' => $result ? 1 : 0]); } // 对话改图 public function chatChangeImg(Request $request) { $data = $request->all(); $result = $this->AnimeService->chatChangeImg($data); return $this->success(['img_url' => $result]); } // 一键生成分镜 public function batchSetSegmentPics(Request $request) { // 忽略所有超时限制 set_time_limit(0); ini_set('max_execution_time', '0'); $data = $request->all(); $result = $this->AnimeService->batchSetSegmentPics($data); return $this->success($result); } // 重新生成分镜 public function reGenerateSegment(Request $request) { // 忽略所有超时限制 set_time_limit(0); ini_set('max_execution_time', '0'); $data = $request->all(); $result = $this->AnimeService->reGenerateSegment($data); return $this->success(['img_url'=>$result]); } // 添加分镜 public function addSegment(Request $request) { // 忽略所有超时限制 set_time_limit(0); ini_set('max_execution_time', '0'); $data = $request->all(); $result = $this->AnimeService->addSegment($data); return $this->success($result); } // 复制分镜 public function copySegment(Request $request) { $data = $request->all(); $result = $this->AnimeService->copySegment($data); return $this->success(['success'=>$result ? 1: 0]); } // 移动分镜 public function moveSegment(Request $request) { $data = $request->all(); $result = $this->AnimeService->moveSegment($data); return $this->success(['success'=>$result ? 1: 0]); } // 删除分镜 public function delSegment(Request $request) { $data = $request->all(); $result = $this->AnimeService->delSegment($data); return $this->success(['success'=>$result ? 1: 0]); } // 分镜历史图片|视频 public function segmentHistory(Request $request) { $data = $request->all(); $result = $this->AnimeService->segmentHistory($data); return $this->success($result); } public function createSegmentVideoTask(Request $request) { $data = $request->all(); // 验证参数 $validator = Validator::make($data, [ 'segment_id' => 'required|string', 'tail_frame' => 'nullable|string|max:500', ], [ 'segment_id.required' => '分镜ID不能为空', 'tail_frame.max' => '尾帧描述不能超过500个字符', ]); if ($validator->fails()) { Utils::throwError('1002:' . $validator->errors()->first()); } // 创建视频生成任务 $result = $this->AnimeService->createSegmentVideoTask($data); $taskId = $result['task_id']; // 设置 SSE 响应头 return response()->stream(function () use ($taskId, $result) { // 设置 SSE 响应头 echo "data: " . json_encode([ 'type' => 'task_created', 'data' => $result ]) . "\n\n"; ob_flush(); flush(); $startTime = time(); $maxDuration = 300; // 5分钟超时 $checkInterval = 5; // 每5秒检查一次 while (time() - $startTime < $maxDuration) { try { // 查询任务状态 $task = \App\Models\MpGenerateVideoTask::find($taskId); if (!$task) { echo "data: " . json_encode([ 'type' => 'error', 'message' => '任务不存在' ]) . "\n\n"; ob_flush(); flush(); break; } // 如果任务还在处理中,查询最新状态 if ($task->status === 'processing') { $statusResult = $this->AIVideoGenerationService->querySeedanceTaskStatus($task); if (isset($statusResult['status'])) { // 更新任务状态 $task->update([ 'status' => $statusResult['status'], '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'])) { DB::table('mp_episode_segments') ->where('video_task_id', $taskId) ->update([ 'video_url' => $statusResult['result_url'], 'video_task_status' => '已完成', 'last_frame_url' => $statusResult['last_frame_url'] ?? '', 'updated_at' => date('Y-m-d H:i:s') ]); } 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' => 'status_update', 'data' => [ 'task_id' => $task->id, 'status' => $task->status, 'result_url' => $task->result_url, 'error_message' => $task->error_message, 'progress' => $this->getTaskProgress($task->status), 'elapsed_time' => time() - $startTime ] ]) . "\n\n"; ob_flush(); flush(); // 如果任务完成(成功或失败),结束连接 if (in_array($task->status, [ 'success', 'failed' ])) { echo "data: " . json_encode([ 'type' => 'completed', 'data' => [ 'task_id' => $task->id, 'status' => $task->status, 'result_url' => $task->result_url, 'video_url' => $task->result_url, // 兼容字段 'error_message' => $task->error_message ] ]) . "\n\n"; ob_flush(); flush(); break; } sleep($checkInterval); } catch (\Exception $e) { echo "data: " . json_encode([ 'type' => 'error', 'message' => '查询任务状态失败: ' . $e->getMessage() ]) . "\n\n"; ob_flush(); flush(); sleep($checkInterval); } } // 超时处理 if (time() - $startTime >= $maxDuration) { echo "data: " . json_encode([ 'type' => 'timeout', 'message' => '任务执行超时,请稍后查询任务状态', 'data' => [ 'task_id' => $taskId ] ]) . "\n\n"; ob_flush(); flush(); } }, 200, [ 'Content-Type' => 'text/event-stream', 'Cache-Control' => 'no-cache', 'Connection' => 'keep-alive', 'X-Accel-Buffering' => 'no', // 禁用 Nginx 缓冲 ]); } /** * 获取任务进度百分比 */ private function getTaskProgress($status) { switch ($status) { case \App\Models\MpGenerateVideoTask::STATUS_PENDING: return 10; case \App\Models\MpGenerateVideoTask::STATUS_PROCESSING: return 50; case \App\Models\MpGenerateVideoTask::STATUS_SUCCESS: return 100; case \App\Models\MpGenerateVideoTask::STATUS_FAILED: return 0; default: return 0; } } // public function scriptList(Request $request) { // $data = $request->all(); // $result = $this->AnimeService->scriptList($data); // return $this->success($result, [new AnimeTransformer(), 'newBuildScriptList']); // } }