Просмотр исходного кода

新增sse接口监控分镜图片生成状态

lh 1 день назад
Родитель
Сommit
6fae944b25
3 измененных файлов с 406 добавлено и 45 удалено
  1. 337 10
      app/Http/Controllers/Anime/AnimeController.php
  2. 33 2
      app/Services/Anime/AnimeService.php
  3. 36 33
      routes/api.php

+ 337 - 10
app/Http/Controllers/Anime/AnimeController.php

@@ -321,7 +321,7 @@ class AnimeController extends BaseController
         return response()->stream(function () use ($taskId, $result) {
             // 设置 SSE 响应头
             echo "data: " . json_encode([
-                'type' => 'task_created',
+                'type' => 'init',
                 'data' => $result
             ]) . "\n\n";
             ob_flush();
@@ -423,7 +423,7 @@ class AnimeController extends BaseController
                     
                     // 发送当前状态
                     echo "data: " . json_encode([
-                        'type' => 'status_update',
+                        'type' => 'update',
                         'data' => [
                             'task_id' => $task->id,
                             'status' => $task->status,
@@ -471,7 +471,7 @@ class AnimeController extends BaseController
             // 超时处理
             if (time() - $startTime >= $maxDuration) {
                 echo "data: " . json_encode([
-                    'type' => 'timeout',
+                    'type' => 'time_out',
                     'message' => '任务执行超时,请稍后查询任务状态',
                     'data' => [
                         'task_id' => $taskId
@@ -679,7 +679,7 @@ class AnimeController extends BaseController
         return response()->stream(function () use ($taskIds, $segmentTasks, $animeId, $episodeNumber, $episodeId, $startTime, $maxDuration) {
             // 发送初始任务创建信息
             echo "data: " . json_encode([
-                'type' => 'tasks_created',
+                'type' => 'init',
                 'data' => [
                     'anime_id' => $animeId,
                     'episode_id' => $episodeId,
@@ -770,7 +770,7 @@ class AnimeController extends BaseController
                                         
                                         // 发送单个任务完成通知
                                         echo "data: " . json_encode([
-                                            'type' => 'task_completed',
+                                            'type' => 'update',
                                             'data' => [
                                                 'segment_id' => $segmentId,
                                                 'task_id' => $taskId,
@@ -796,7 +796,7 @@ class AnimeController extends BaseController
                                         
                                         // 发送任务处理失败通知
                                         echo "data: " . json_encode([
-                                            'type' => 'task_process_error',
+                                            'type' => 'error',
                                             'data' => [
                                                 'segment_id' => $segmentId,
                                                 'task_id' => $taskId,
@@ -820,7 +820,7 @@ class AnimeController extends BaseController
                                     
                                     // 发送任务失败通知
                                     echo "data: " . json_encode([
-                                        'type' => 'task_failed',
+                                        'type' => 'update',
                                         'data' => [
                                             'segment_id' => $segmentId,
                                             'task_id' => $taskId,
@@ -855,7 +855,7 @@ class AnimeController extends BaseController
                     
                     // 发送整体进度更新
                     echo "data: " . json_encode([
-                        'type' => 'progress_update',
+                        'type' => 'update',
                         'data' => [
                             'anime_id' => $animeId,
                             'episode_id' => $episodeId,
@@ -882,7 +882,7 @@ class AnimeController extends BaseController
                             ->get();
                         
                         echo "data: " . json_encode([
-                            'type' => 'all_completed',
+                            'type' => 'completed',
                             'data' => [
                                 'anime_id' => $animeId,
                                 'episode_id' => $episodeId,
@@ -944,7 +944,7 @@ class AnimeController extends BaseController
                     ->get();
                 
                 echo "data: " . json_encode([
-                    'type' => 'timeout',
+                    'type' => 'time_out',
                     'message' => '批量视频生成已超时30分钟,停止状态查询',
                     'data' => [
                         'anime_id' => $animeId,
@@ -1049,4 +1049,331 @@ class AnimeController extends BaseController
         $result = $this->AnimeService->generateImg($data);
         return $this->success($result);
     }
+
+    /**
+     * 监控分镜图片和音频生成任务进度(SSE)
+     */
+    public function monitorSegmentTasks(Request $request) {
+        // 忽略所有超时限制
+        set_time_limit(0);
+        ini_set('max_execution_time', '0');
+
+        $data = $request->all();
+        
+        // 验证参数
+        $validator = Validator::make($data, [
+            'anime_id' => 'required|string',
+            'episode_id' => 'required|string',
+        ], [
+            'anime_id.required' => '动漫ID不能为空',
+            'episode_id.required' => '剧集ID不能为空',
+        ]);
+        
+        if ($validator->fails()) {
+            Utils::throwError('1002:' . $validator->errors()->first());
+        }
+        
+        $animeId = $data['anime_id'];
+        $episodeId = $data['episode_id'];
+        
+        // 获取所有分镜信息
+        $segments = DB::table('mp_episode_segments')
+            ->where('anime_id', $animeId)
+            ->where('episode_id', $episodeId)
+            // ->whereNotNull('pic_task_id')
+            ->orderBy('segment_number')
+            ->select('segment_id', 'segment_number', 'pic_task_id', 'pic_task_status', 'audio_task_id', 'audio_task_status', 'img_url', 'audio_url')
+            ->get();
+            
+        if ($segments->isEmpty()) {
+            Utils::throwError('20003:未找到分镜任务数据');
+        }
+        
+        $startTime = time();
+        $maxDuration = 900; // 15分钟超时
+        $checkInterval = 5; // 每5秒检查一次
+        
+        // 设置 SSE 响应头并开始长连接
+        return response()->stream(function () use ($segments, $animeId, $episodeId, $startTime, $maxDuration, $checkInterval) {
+            // 发送初始任务信息
+            echo "data: " . json_encode([
+                'type' => 'init',
+                'data' => [
+                    'anime_id' => $animeId,
+                    'episode_id' => $episodeId,
+                    'total_segments' => count($segments),
+                    'start_time' => $startTime,
+                    'max_duration' => $maxDuration
+                ]
+            ]) . "\n\n";
+            ob_flush();
+            flush();
+            
+            $completedSegments = [];
+            $processedSegments = []; // 记录已处理完成的分镜(成功或失败)
+            $notifiedSegments = []; // 记录已通知过的分镜,避免重复通知
+            
+            while (time() - $startTime < $maxDuration) {
+                try {
+                    $updatedImages = []; // 记录本次循环中更新的图片信息
+                    
+                    foreach ($segments as $segment) {
+                        $segmentId = $segment->segment_id;
+                        $picTaskId = $segment->pic_task_id;
+                        
+                        // 跳过已处理完成的分镜
+                        if (in_array($segmentId, $processedSegments)) {
+                            continue;
+                        }
+                        
+                        $picCompleted = false;
+                        $audioCompleted = false;
+                        $picProcessed = false;
+                        $audioProcessed = false;
+                        
+                        // 检查图片任务状态
+                        if ($picTaskId) {
+                            $picTask = DB::table('mp_generate_pic_tasks')
+                                ->where('id', $picTaskId)
+                                ->select('status', 'result_url', 'error_message')
+                                ->first();
+                            
+                            if ($picTask && in_array($picTask->status, ['success', 'failed'])) {
+                                $updateData = [
+                                    'pic_task_status' => $picTask->status === 'success' ? '已完成' : '失败',
+                                    'updated_at' => date('Y-m-d H:i:s')
+                                ];
+                                
+                                $imgUrl = '';
+                                if ($picTask->status === 'success' && !empty($picTask->result_url)) {
+                                    // 更新分镜表的图片URL
+                                    $resultUrls = json_decode($picTask->result_url, true);
+                                    $imgUrl = is_array($resultUrls) ? $resultUrls[0] : $picTask->result_url;
+                                    $updateData['img_url'] = $imgUrl;
+                                    $picCompleted = true;
+                                } else if ($picTask->status === 'failed') {
+                                    // 失败时清空图片URL
+                                    $updateData['img_url'] = '';
+                                }
+                                
+                                DB::table('mp_episode_segments')
+                                    ->where('segment_id', $segmentId)
+                                    ->update($updateData);
+                                
+                                // 记录更新的图片信息(只有未通知过的才记录)
+                                if (!in_array($segmentId, $notifiedSegments)) {
+                                    $updatedImages[] = [
+                                        'segment_id' => $segmentId,
+                                        'segment_number' => $segment->segment_number,
+                                        'img_url' => $imgUrl,
+                                        'status' => $picTask->status === 'success' ? 'success' : 'failed'
+                                    ];
+                                    
+                                    // 标记为已通知
+                                    $notifiedSegments[] = $segmentId;
+                                }
+                                
+                                $picProcessed = true;
+                            }
+                        }
+                        
+                        // 检查音频任务状态
+                        $audioTask = DB::table('mp_dub_video_tasks')
+                            ->where('alias_segment_id', $segmentId)
+                            ->orderBy('id', 'desc')
+                            ->select('generate_status', 'audio_url')
+                            ->first();
+                        
+                        if ($audioTask && in_array($audioTask->generate_status, ['执行成功', '执行失败'])) {
+                            $updateData = [
+                                'audio_task_status' => $audioTask->generate_status === '执行成功' ? '已完成' : '失败',
+                                'updated_at' => date('Y-m-d H:i:s')
+                            ];
+                            
+                            if ($audioTask->generate_status === '执行成功' && !empty($audioTask->audio_url)) {
+                                $updateData['audio_url'] = $audioTask->audio_url;
+                                $audioCompleted = true;
+                            } else if ($audioTask->generate_status === '执行失败') {
+                                // 失败时清空音频URL
+                                $updateData['audio_url'] = '';
+                            }
+                            
+                            // 静默更新分镜表的音频URL(不输出通知)
+                            DB::table('mp_episode_segments')
+                                ->where('segment_id', $segmentId)
+                                ->update($updateData);
+                            
+                            $audioProcessed = true;
+                        }
+                        
+                        // 如果图片和音频都成功完成,标记为已完成
+                        if ($picCompleted && $audioCompleted) {
+                            $completedSegments[] = $segmentId;
+                        }
+                        
+                        // 如果图片和音频都已处理完成(成功或失败),标记为已处理
+                        if ($picProcessed && $audioProcessed) {
+                            $processedSegments[] = $segmentId;
+                        }
+                        
+                    }
+                    
+                    // 如果有图片更新,发送具体的图片更新信息
+                    if (!empty($updatedImages)) {
+                        echo "data: " . json_encode([
+                            'type' => 'update',
+                            'data' => [
+                                'anime_id' => $animeId,
+                                'episode_id' => $episodeId,
+                                'updated_images' => $updatedImages,
+                                'completed_count' => count($completedSegments), // 成功完成的数量
+                                'processed_count' => count($processedSegments), // 已处理完成的数量(成功+失败)
+                                'total_count' => count($segments),
+                                'elapsed_time' => time() - $startTime,
+                                'remaining_time' => max(0, $maxDuration - (time() - $startTime))
+                            ]
+                        ]) . "\n\n";
+                        ob_flush();
+                        flush();
+                    }
+                    
+                    // 如果所有分镜都已处理完成(成功或失败),获取最终结果并结束连接
+                    if (count($processedSegments) === count($segments)) {
+                        // 获取剧集信息
+                        $episode = DB::table('mp_anime_episodes')
+                            ->where('anime_id', $animeId)
+                            ->where('id', $episodeId)
+                            ->select('id as episode_id', 'anime_id', 'episode_number', 'title', 'intro', 'art_style', 'roles', 'scenes')
+                            ->first();
+                        
+                        if ($episode) {
+                            $episode = (array)$episode;
+                            $episode['roles'] = json_decode($episode['roles'], true);
+                            $episode['scenes'] = json_decode($episode['scenes'], true);
+                            
+                            // 获取所有分镜信息
+                            $allSegments = DB::table('mp_episode_segments')
+                                ->where('anime_id', $animeId)
+                                ->where('episode_id', $episodeId)
+                                ->orderBy('segment_number')
+                                ->select('*')
+                                ->get()
+                                ->map(function ($value) {
+                                    return (array)$value;
+                                })->toArray();
+                            
+                            // 按照episodeInfo的格式组织数据
+                            $acts = [];
+                            foreach($allSegments as $segment) {
+                                $segmentData = [
+                                    'segment_id' => getProp($segment, 'segment_id'),
+                                    'segment_number' => getProp($segment, 'segment_number'),
+                                    'segment_content' => getProp($segment, 'segment_content'),
+                                    'description' => getProp($segment, 'description'),
+                                    'composition' => getProp($segment, 'composition'),
+                                    'camera_movement' => getProp($segment, 'camera_movement'),
+                                    'voice_actor' => getProp($segment, 'voice_actor'),
+                                    'dialogue' => getProp($segment, 'dialogue'),
+                                    'frame_type' => getProp($segment, 'frame_type'),
+                                    'scene' => getProp($segment, 'scene'),
+                                    'characters' => getProp($segment, 'characters'),
+                                    'tail_frame' => getProp($segment, 'tail_frame'),
+                                    'emotion' => getProp($segment, 'emotion'),
+                                    'emotion_type' => getProp($segment, 'emotion_type'),
+                                    'gender' => getProp($segment, 'gender'),
+                                    'speed_ratio' => getProp($segment, 'speed_ratio'),
+                                    'loudness_ratio' => getProp($segment, 'loudness_ratio'),
+                                    'emotion_scale' => getProp($segment, 'emotion_scale'),
+                                    'pitch' => getProp($segment, 'pitch'),
+                                    'voice_name' => getProp($segment, 'voice_name'),
+                                    'voice_type' => getProp($segment, 'voice_type'),
+                                    'voice_audio_url' => getProp($segment, 'voice_audio_url'),
+                                    'img_url' => getProp($segment, 'img_url'),
+                                    'audio_url' => getProp($segment, 'audio_url'),
+                                    'video_url' => getProp($segment, 'video_url'),
+                                    'last_frame_url' => getProp($segment, 'last_frame_url'),
+                                ];
+                                
+                                $actNumber = getProp($segment, 'act_number');
+                                if (isset($acts[$actNumber])) {
+                                    $acts[$actNumber]['segments'][] = $segmentData;
+                                } else {
+                                    $acts[$actNumber] = [
+                                        'act_number' => $actNumber,
+                                        'act_title' => getProp($segment, 'act_title'),
+                                        'segments' => [$segmentData]
+                                    ];
+                                }
+                            }
+                            $episode['acts'] = array_values($acts);
+                        }
+                        
+                        echo "data: " . json_encode([
+                            'type' => 'completed',
+                            'data' => [
+                                'anime_id' => $animeId,
+                                'episode_id' => $episodeId,
+                                'completed_count' => count($completedSegments), // 成功完成的数量
+                                'processed_count' => count($processedSegments), // 已处理完成的数量(成功+失败)
+                                'total_count' => count($segments),
+                                'total_elapsed_time' => time() - $startTime,
+                                'episode_info' => $episode ?? null
+                            ]
+                        ]) . "\n\n";
+                        ob_flush();
+                        flush();
+                        break;
+                    }
+                    
+                    // 检查是否超时
+                    if (time() - $startTime >= $maxDuration) {
+                        break;
+                    }
+                    
+                    sleep($checkInterval);
+                    
+                } catch (\Exception $e) {
+                    echo "data: " . json_encode([
+                        'type' => 'error',
+                        'message' => '查询任务状态失败: ' . $e->getMessage(),
+                        'elapsed_time' => time() - $startTime,
+                        'remaining_time' => max(0, $maxDuration - (time() - $startTime))
+                    ]) . "\n\n";
+                    ob_flush();
+                    flush();
+                    
+                    if (time() - $startTime >= $maxDuration) {
+                        break;
+                    }
+                    
+                    sleep($checkInterval);
+                }
+            }
+            
+            // 超时处理
+            if (time() - $startTime >= $maxDuration) {
+                echo "data: " . json_encode([
+                    'type' => 'time_out',
+                    'message' => '分镜任务监控已超时15分钟,停止状态查询',
+                    'data' => [
+                        'anime_id' => $animeId,
+                        'episode_id' => $episodeId,
+                        'completed_count' => count($completedSegments), // 成功完成的数量
+                        'processed_count' => count($processedSegments), // 已处理完成的数量(成功+失败)
+                        'total_count' => count($segments),
+                        'total_elapsed_time' => time() - $startTime,
+                        'timeout_duration' => $maxDuration
+                    ]
+                ]) . "\n\n";
+                ob_flush();
+                flush();
+            }
+            
+        }, 200, [
+            'Content-Type' => 'text/event-stream',
+            'Cache-Control' => 'no-cache',
+            'Connection' => 'keep-alive',
+            'X-Accel-Buffering' => 'no', // 禁用 Nginx 缓冲
+        ]);
+    }
 }

+ 33 - 2
app/Services/Anime/AnimeService.php

@@ -912,8 +912,6 @@ class AnimeService
             ->where('episode_id', $episode->episode_id)
             ->where('pic_task_status', '')
             ->orderBy('segment_number')
-            ->select('episode_number', 'segment_id', 'segment_number', 'segment_content', 'scene', 'characters')
-            ->limit(3)
             ->get()
             ->toArray();
 
@@ -995,6 +993,38 @@ class AnimeService
                     ];
                     continue;
                 }
+
+                // 如果有对话内容,创建视频配音合成任务
+                if (getProp($segment, 'dialogue')) {
+                    $now = date('Y-m-d H:i:s');
+                    $generate_json = [
+                        'text' => getProp($segment, 'dialogue'),
+                        'role' => getProp($segment, 'voice_actor'),
+                        'voice_type' => getProp($segment, 'voice_type'),
+                        'voice_name' => getProp($segment, 'voice_name'),
+                        'emotion' => getProp($segment, 'emotion'),
+                        'emotion_type' => getProp($segment, 'emotion_type'),
+                        'gender' => getProp($segment, 'gender'),
+                        'speed_ratio' => getProp($segment, 'speed_ratio', 0),
+                        'loudness_ratio' => getProp($segment, 'loudness_ratio', 0),
+                        'emotion_scale' => getProp($segment, 'emotion_scale', 0),
+                        'pitch' => getProp($segment, 'pitch', 0),
+                    ];
+                    
+                    // 插入视频配音合成任务
+                    $dubTaskId = DB::table('mp_dub_video_tasks')->insertGetId([
+                        'alias_segment_id' => $segment_id,
+                        'generate_status' => '执行中',
+                        'audio_url' => '',
+                        'generate_json' => json_encode($generate_json, 256),
+                        'created_at' => $now,
+                        'updated_at' => $now,
+                    ]);
+                    
+                    if (!$dubTaskId) {
+                        Utils::throwError('20003:创建配音任务失败!');
+                    }
+                }
                 
                 // 更新分镜的任务信息
                 $update_result = DB::table('mp_episode_segments')
@@ -1002,6 +1032,7 @@ class AnimeService
                     ->update([
                         'pic_task_id' => $task_id,
                         'pic_task_status' => '生成中',
+                        'audio_url' => '',
                         'updated_at' => date('Y-m-d H:i:s')
                     ]);
 

+ 36 - 33
routes/api.php

@@ -128,45 +128,48 @@ Route::group(['middleware' => ['bindToken', 'bindExportToken', 'checkLogin']], f
 
     // 动漫管理
     Route::group(['prefix' => 'anime'], function () {
-        Route::get('textModel', [AnimeController::class, 'textModel']);
-        Route::get('artStyleList', [AnimeController::class, 'artStyleList']);
-        Route::post('create', [AnimeController::class, 'createAnime']);
-        Route::post('addChat', [DeepSeekController::class, 'addChat']);
-        Route::post('reGenerateAnime', [DeepSeekController::class, 'reGenerateAnime']);
-        Route::get('chatList', [AnimeController::class, 'chatList']);
-        Route::get('chatHistory', [AnimeController::class, 'chatHistory']);
-        Route::post('chat', [DeepSeekController::class, 'chat']);
-        Route::get('detail', [AnimeController::class, 'animeDetail']);
-        Route::get('episodeInfo', [AnimeController::class, 'episodeInfo']);
-        Route::get('segmentInfo', [AnimeController::class, 'segmentInfo']);
-        Route::get('copyEpisodeVersion', [AnimeController::class, 'copyEpisodeVersion']);
-        Route::get('episodeVersions', [AnimeController::class, 'episodeVersions']);
-        Route::get('bindEpisodeVersion', [AnimeController::class, 'bindEpisodeVersion']);
-        Route::post('edit', [AnimeController::class, 'editAnime']);
-        Route::post('changeEpisodeRoles', [AnimeController::class, 'changeEpisodeRoles']);
-        Route::post('changeEpisodeScenes', [AnimeController::class, 'changeEpisodeScenes']);
-        Route::post('editSegment', [AnimeController::class, 'editSegment']);
-        Route::post('batchUploadImg', [ImageGenerationController::class, 'batchUploadImg']);
-        Route::post('batchSetRoleImg', [AnimeController::class, 'batchSetRoleImg']);
-        Route::post('batchSetSceneImg', [AnimeController::class, 'batchSetSceneImg']);
-        Route::post('chatChangeImg', [AnimeController::class, 'chatChangeImg']);
-        Route::post('batchSetSegmentPics', [AnimeController::class, 'batchSetSegmentPics']);
-        Route::post('reGenerateSegment', [AnimeController::class, 'reGenerateSegment']);
-        Route::post('addSegment', [AnimeController::class, 'addSegment']);
-        Route::post('copySegment', [AnimeController::class, 'copySegment']);
-        Route::post('moveSegment', [AnimeController::class, 'moveSegment']);
-        Route::post('delSegment', [AnimeController::class, 'delSegment']);
-        Route::post('segmentHistory', [AnimeController::class, 'segmentHistory']);
-        Route::post('createSegmentVideoTask', [AnimeController::class, 'createSegmentVideoTask']);
-        Route::post('batchSetSegmentVideos', [AnimeController::class, 'batchSetSegmentVideos']);
-        Route::post('applyAudioData', [AnimeController::class, 'applyAudioData']);
-        Route::get('episodePicsInfo', [AnimeController::class, 'episodePicsInfo']);
+        Route::get('textModel', [AnimeController::class, 'textModel']);                         // 文生文模型
+        Route::get('artStyleList', [AnimeController::class, 'artStyleList']);                   // 艺术风格列表
+        Route::post('create', [AnimeController::class, 'createAnime']);                         // 创建动漫
+        Route::post('addChat', [DeepSeekController::class, 'addChat']);                         // 新增动漫对话
+        Route::post('reGenerateAnime', [DeepSeekController::class, 'reGenerateAnime']);         // 调整动漫大纲
+        Route::get('chatList', [AnimeController::class, 'chatList']);                           // 动漫列表
+        Route::get('chatHistory', [AnimeController::class, 'chatHistory']);                     // 动漫对话历史
+        Route::post('chat', [DeepSeekController::class, 'chat']);                               // 动漫对话
+        Route::get('detail', [AnimeController::class, 'animeDetail']);                          // 动漫大纲
+        Route::get('episodeInfo', [AnimeController::class, 'episodeInfo']);                     // 动漫剧集
+        Route::get('segmentInfo', [AnimeController::class, 'segmentInfo']);                     // 分镜信息
+        Route::get('copyEpisodeVersion', [AnimeController::class, 'copyEpisodeVersion']);       // 复制动漫剧集
+        Route::get('episodeVersions', [AnimeController::class, 'episodeVersions']);             // 剧集版本
+        Route::get('bindEpisodeVersion', [AnimeController::class, 'bindEpisodeVersion']);       // 绑定剧集版本
+        Route::post('edit', [AnimeController::class, 'editAnime']);                             // 编辑动漫(弃用)
+        Route::post('changeEpisodeRoles', [AnimeController::class, 'changeEpisodeRoles']);      // 更改分集主体列表
+        Route::post('changeEpisodeScenes', [AnimeController::class, 'changeEpisodeScenes']);    // 更改分集场景列表
+        Route::post('editSegment', [AnimeController::class, 'editSegment']);                    // 编辑分镜
+        Route::post('batchUploadImg', [ImageGenerationController::class, 'batchUploadImg']);    // 批量上传图片
+        Route::post('batchSetRoleImg', [AnimeController::class, 'batchSetRoleImg']);            // 批量生成角色形象
+        Route::post('batchSetSceneImg', [AnimeController::class, 'batchSetSceneImg']);          // 批量生成场景图片
+        Route::post('chatChangeImg', [AnimeController::class, 'chatChangeImg']);                // 对话改图
+        Route::post('batchSetSegmentPics', [AnimeController::class, 'batchSetSegmentPics']);    // 一键生成分镜图片
+        Route::post('reGenerateSegment', [AnimeController::class, 'reGenerateSegment']);        // 重新生成分镜
+        Route::post('addSegment', [AnimeController::class, 'addSegment']);                      // 新增分镜
+        Route::post('copySegment', [AnimeController::class, 'copySegment']);                    // 复制分镜
+        Route::post('moveSegment', [AnimeController::class, 'moveSegment']);                    // 移动分镜
+        Route::post('delSegment', [AnimeController::class, 'delSegment']);                      // 删除分镜
+        Route::post('segmentHistory', [AnimeController::class, 'segmentHistory']);              // 分镜历史图片/视频
+        Route::post('createSegmentVideoTask', [AnimeController::class, 'createSegmentVideoTask']);  // 分镜图片转视频
+        Route::post('batchSetSegmentVideos', [AnimeController::class, 'batchSetSegmentVideos']);    // 一键转视频
+        Route::post('applyAudioData', [AnimeController::class, 'applyAudioData']);                  // 修改分镜音频参数
+        Route::get('episodePicsInfo', [AnimeController::class, 'episodePicsInfo']);                 // 获取主体和场景图片生成信息
         
         // 完整视频合成任务
         Route::post('createCompleteVideoTask', [AnimeController::class, 'createCompleteVideoTask']);
         
         Route::get('generateImg', [AnimeController::class, 'generateImg']);     // 文生图(通用)
         Route::post('generateText', [DeepSeekController::class, 'generateText']);    // 文生文(通用)
+        
+        // 监控分镜图片和音频生成任务进度(SSE)
+        Route::get('segmentPicsInfo', [AnimeController::class, 'monitorSegmentTasks']);
     });
     
 });