Pārlūkot izejas kodu

1.新增接口重新生成分镜剧本2.调整AI模型的请求超时时间为1800秒

lh 3 dienas atpakaļ
vecāks
revīzija
91709d4d08

+ 15 - 0
app/Http/Controllers/DeepSeek/DeepSeekController.php

@@ -1097,4 +1097,19 @@ class DeepSeekController extends BaseController
             'Access-Control-Allow-Credentials' => 'true'
         ]);
     }
+
+    /**
+     * 重新生成分段剧本
+     * @param Request $request
+     * @return mixed
+     */
+    public function regenerateSegmentScript(Request $request) {
+        // 忽略所有超时限制
+        set_time_limit(0);
+        ini_set('max_execution_time', '0');
+
+        $data = $request->all();
+        $result = $this->deepseekService->regenerateSegmentScript($data);
+        return $this->success($result);
+    }
 }

+ 338 - 109
app/Services/DeepSeek/DeepSeekService.php

@@ -1190,7 +1190,7 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
         // 设置最大输出tokens
         $post_data['max_tokens'] = 300000;
 
-        $client = new Client(['timeout' => 1200, 'verify' => false]);
+        $client = new Client(['timeout' => 1800, 'verify' => false]);
         $response = $client->post($this->url, [
             'json' => $post_data, 
             'headers' => $this->headers,
@@ -1306,7 +1306,7 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
             Utils::throwError('20003:GPT API Key未配置');
         }
 
-        $client = new Client(['timeout' => 1200, 'verify' => false]);
+        $client = new Client(['timeout' => 1800, 'verify' => false]);
         $headers = [
             'Authorization' => 'Bearer ' . $apiKey,
             'Content-Type'  => 'application/json'
@@ -1587,7 +1587,7 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
 
         if ($boolen && !$continue) {
             $boolen = false;
-            $client = new Client(['timeout' => 300, 'verify' => false]);
+            $client = new Client(['timeout' => 1800, 'verify' => false]);
             // 根据ID通过API通知合成音频
             // $result = $client->get("http://47.240.171.155:5000/api/previewTask?taskId={$id}");
             $result = $client->get("http://122.9.129.83:5000/api/previewTask?taskId={$id}");
@@ -1659,7 +1659,7 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
 
         DB::commit();
         
-        $client = new Client(['timeout' => 300, 'verify' => false]);
+        $client = new Client(['timeout' => 1800, 'verify' => false]);
         // 根据ID通过API通知合成音频
         // $result = $client->get("http://47.240.171.155:5000/api/postTask?taskId={$id}");
         $result = $client->get("http://122.9.129.83:5000/api/postTask?taskId={$id}");
@@ -1733,7 +1733,7 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
 
         DB::commit();
         
-        $client = new Client(['timeout' => 300, 'verify' => false]);
+        $client = new Client(['timeout' => 1800, 'verify' => false]);
         // 根据ID通过API通知合成音频
         // $result = $client->get("http://47.240.171.155:5000/api/postTask?taskId={$id}");
         $result = $client->get("http://122.9.129.83:5000/api/postTask?taskId={$id}");
@@ -1964,7 +1964,7 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
         DB::commit();
 
         // 通知火山生成音频
-        $client = new Client(['timeout' => 300, 'verify' => false]);
+        $client = new Client(['timeout' => 1800, 'verify' => false]);
         // 根据ID通过API通知合成音频
         // $result = $client->get("http://47.240.171.155:5000/api/chapterTask?taskId={$id}");
         $result = $client->get("http://122.9.129.83:5000/api/chapterTask?taskId={$id}");
@@ -8559,7 +8559,7 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
         }
         
         // 构建完整的AI提示词
-        $system_prompt = "你是一个专业的剧本编剧和影视导演,擅长根据已有的分段剧本进行优化和调整。你需要严格按照用户的要求重新生成分段剧本内容,保持原有的格式和结构。";
+        // $system_prompt = "你是一个专业的剧本编剧和影视导演,擅长根据已有的分段剧本进行优化和调整。你需要严格按照用户的要求重新生成分段剧本内容,保持原有的格式和结构。";
         
         $user_prompt = "请根据以下要求重新生成分段剧本:\n\n【用户要求】\n{$final_prompt}";
         $user_prompt .= "\n\n【原分段剧本内容】\n{$reference_content}";
@@ -8568,39 +8568,300 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
         $user_prompt .= $roles_ref;
         $user_prompt .= $scenes_ref;
         
-        $user_prompt .= "\n\n【格式要求】\n";
-        $user_prompt .= "1. 必须保持原有的分段格式(##片段X格式)\n";
-        $user_prompt .= "2. 每个片段包含:时长、多个分镜\n";
-        $user_prompt .= "3. 每个分镜包含:分镜序号、场景、画面、运镜、配音台词、背景音效\n";
-        $user_prompt .= "4. 场景必须从【场景列表参考】中选择(如果提供了场景列表)\n";
-        $user_prompt .= "5. 出场角色必须从【主体列表参考】中选择(如果提供了主体列表)\n";
-        $user_prompt .= "6. 保持美术风格的一致性(如果提供了美术风格参考)\n";
-        $user_prompt .= "7. 只需输出分段剧本部分,不要输出其他内容(不要包含###分段剧本标题)\n";
+        $system_prompt = "\n\n【强制要求】\n";
+        $system_prompt .= "- 请仅生成<分段剧本>部分,格式强制设置如下,不要生成其他任何内容:";
+        $system_prompt .= "###分段剧本
+        片段数量:8
+        旁白音色:成年男性(约35-50岁),音色厚重且富有磁性,带有老电影叙事般的沧桑感,语调低沉平稳,富有江湖气息。
+
+        ##片段1
+        时长:12s
+        分镜1
+        场景:边境小镇街道
+        画面:[大远景,冷色调,高对比度] 漫天黄沙在狂风中如怒龙翻滚,遮蔽了微弱的星光。悦来酒馆残破的木门在风中剧烈摇晃,铁链撞击木板发出沉重的闷响,四周建筑尽显荒凉。
+        运镜:大远景固定镜头,随后风沙席卷向镜头形成遮挡转场。
+        配音台词:
+        背景音效:
+
+        分镜2
+        场景:悦来酒馆
+        画面:[全景,暖色调阴影构图] 酒馆内部昏暗,几盏油灯在风中疯狂摇曳,投射出扭曲的长影。角落里的食客低头缩颈,双手紧紧攥着粗瓷碗,空气中漂浮着细小的尘埃。
+        运镜:缓慢横移,掠过阴影中瑟缩的食客。
+        配音台词:
+        背景音效:
+
+        分镜3
+        场景:悦来酒馆
+        画面:[中景,三分法构图] 三名大汉围坐在正中央的圆桌旁,腰间挂着的“武林盟”金色令牌在灯火下闪烁。桌上残羹冷炙横陈,一名大汉正张狂地大笑,脚踩在长凳上。
+        运镜:从中景推向令牌特写。
+        配音台词:
+        背景音效:
+
+        分镜4
+        场景:悦来酒馆
+        画面:[特写,低角度仰拍] 刀疤脸横肉颤动,嘴角挂着轻蔑的笑。他随手倾覆手中的酒碗,琥珀色的液体在空中划过弧线,重重溅在老李打着补丁的布鞋和裤脚上。
+        运镜:极速推向酒水溅起的瞬间。
+        配音台词:
+        背景音效:
+
+        分镜5
+        镜头描述
+        场景:悦来酒馆
+        画面:[近景,荷兰角倾斜构图] 刀疤脸斜睨着眼,昏暗的灯光勾勒出他脸上狰狞的伤疤。他身体前倾,阴影完全笼罩了对面的老李,眼神如毒蛇般阴冷。
+        运镜:固定机位,利用倾斜构图制造压迫感。
+        配音台词:中文配音:[刀疤脸]老李头,这酒里掺水了吧?
+        背景音效:";
+        $system_prompt .= "- 每个分镜包含:分镜序号、场景、画面、运镜、配音台词、背景音效\n";
+        $system_prompt .= "- 场景必须从【场景列表参考】中选择(如果提供了场景列表)\n";
+        $system_prompt .= "- 出场角色必须从【主体列表参考】中选择(如果提供了主体列表)\n";
+        $system_prompt .= "- 保持美术风格的一致性(如果提供了美术风格参考)\n";
+        
+        // 获取模型配置
+        $anime = DB::table('mp_animes')->where('id', $target_anime_id)->first();
+        $model = $anime ? ($anime->model ?? 'doubao-seed-2-0-mini-260215') : 'doubao-seed-2-0-mini-260215';
+        
+        // 验证模型是否在可用模型表中
+        if (!DB::table('mp_text_models')->where('model', $model)->where('is_enabled', 1)->exists()) {
+            $model = 'doubao-seed-2-0-mini-260215';
+        }
+        
+        // 模型兼容性处理
+        $thinkingMode = 'disabled';
+        $reasoningEffort = 'high';
+        
+        if ($model === 'deepseek-chat') {
+            $model = 'deepseek-v4-flash';
+            $thinkingMode = 'disabled';
+        } elseif ($model === 'deepseek-reasoner') {
+            $model = 'deepseek-v4-flash';
+            $thinkingMode = 'enabled';
+        }
+        
+        // 构建消息
+        $messages = [
+            [
+                'role' => 'system',
+                'content' => $system_prompt
+            ],
+            [
+                'role' => 'user',
+                'content' => $user_prompt
+            ]
+        ];
+        
+        // 构建请求参数
+        $post_data = [
+            'model' => $model,
+            'messages' => $messages,
+            'temperature' => 0.7,
+            'frequency_penalty' => 0,
+            'presence_penalty' => 0,
+            'response_format' => ['type' => 'text'],
+            'thinking' => ['type' => $thinkingMode],
+            'stream' => false  // 非流式输出
+        ];
+        if ($thinkingMode == 'enabled') $post_data['reasoning_effort'] = $reasoningEffort;
         
         // 调用AI生成新的分段剧本
         try {
-            $ai_result = $this->newGenerateText([
-                'model' => 'deepseek-v4-pro',
-                'system_prompt' => $system_prompt,
-                'prompt' => $user_prompt,
-                'temperature' => 0.7,
-            ]);
+            $fullContent = '';
+            $usage = [];
+            
+            // 根据模型类型选择调用方法
+            if (in_array($model, ['deepseek-reasoner', 'deepseek-chat', 'deepseek-v4-flash', 'deepseek-v4-pro'])) {
+                // DeepSeek 官方模型使用 DeepSeek API
+                $chatResult = $this->chatOnly($post_data);
+            } else if (in_array($model, $this->gpt_text_models)) {
+                // GPT 模型使用 TokenRouter API
+                $chatResult = $this->gpt54ChatOnly($post_data);
+            } else {
+                // 其他模型使用火山引擎API
+                $chatResult = $this->volcEngineChatCompletion($post_data);
+            }
             
-            $generated_content = $ai_result['content'] ?? '';
+            if (is_array($chatResult)) {
+                $fullContent = $chatResult['fullContent'] ?? '';
+                $usage = $chatResult['usage'] ?? [];
+            }
+            
+            $generated_content = $fullContent;
             
             if (empty($generated_content)) {
                 Utils::throwError('20003:AI生成内容为空,请重试');
             }
             
-            // 解析生成的内容
-            $parsed_segments = $this->parseSegmentContent($generated_content);
+            // 解析生成的内容 - 按片段分割
+            $parsed_segments = [];
+            
+            // 先在开头添加换行符,确保第1片段也能被正确分割
+            $normalizedText = "\n" . $generated_content;
+            $parts = preg_split('/\n\s*##/', $normalizedText);
+            
+            foreach ($parts as $part) {
+                $part = trim($part);
+                if (empty($part)) continue;
+                
+                // 匹配"片段X"格式
+                if (!preg_match('/^片段\d+/', $part)) {
+                    continue;
+                }
+                
+                // 分离标题和内容
+                $lines = explode("\n", $part, 2);
+                $actTitle = trim($lines[0]);
+                $actContent = isset($lines[1]) ? trim($lines[1]) : '';
+                
+                // 解析标题,提取序号
+                $actNumber = 0;
+                if (preg_match('/^片段(\d+)\s*$/u', $actTitle, $segmentTitleMatch)) {
+                    $actNumber = intval($segmentTitleMatch[1]);
+                } else {
+                    $actNumber = count($parsed_segments) + 1;
+                }
+                
+                // 提取时长(仅保留数字)
+                $actDuration = 0;
+                if (preg_match('/时长[::]\s*([^\n]+)/u', $actContent, $actDurationMatch)) {
+                    $actDurationStr = trim($actDurationMatch[1]);
+                    // 提取数字部分(支持整数和小数)
+                    if (preg_match('/([-+]?[0-9]*\.?[0-9]+)/', $actDurationStr, $numMatch)) {
+                        $actDuration = (float)$numMatch[1];
+                    }
+                }
+                
+                // 清理act_content:去除时长、旁白音色等非分镜内容行
+                // 只保留分镜内容,并确保分镜标记使用【】格式
+                $cleaned_content = '';
+                $content_lines = explode("\n", $actContent);
+                $is_capturing = false; // 标记是否开始捕获分镜内容
+                
+                foreach ($content_lines as $line) {
+                    $trimmed_line = trim($line);
+                    
+                    // 跳过时长和旁白音色行
+                    if (preg_match('/^(时长|旁白音色)[::]/u', $trimmed_line)) {
+                        continue;
+                    }
+                    
+                    // 检测是否是分镜开始
+                    // 支持格式:【分镜X】、【镜头X】、分镜X、镜头X
+                    if (preg_match('/^(?:【)?(分镜|镜头)(\d+)(?:】)?/u', $trimmed_line, $shot_match)) {
+                        $is_capturing = true;
+                        
+                        // 如果没有【】,则添加
+                        if (!preg_match('/^【/u', $trimmed_line)) {
+                            $trimmed_line = preg_replace('/^(分镜|镜头)(\d+)/u', '【$1$2】', $trimmed_line);
+                        }
+                    }
+                    
+                    // 如果已经开始捕获,则添加该行
+                    if ($is_capturing && !empty($trimmed_line)) {
+                        $cleaned_content .= $trimmed_line;
+                    }
+                }
+                
+                $parsed_segments[] = [
+                    'act_number' => $actNumber,
+                    'act_title' => $actTitle,
+                    'act_duration' => $actDuration,
+                    'act_content' => $cleaned_content,
+                ];
+            }
             
             if (empty($parsed_segments)) {
                 Utils::throwError('20003:无法解析生成的分段剧本,请检查格式');
             }
             
+            // 获取角色和场景名称用于标记
+            $role_names = [];
+            $scene_names = [];
+            
+            if (!empty($roles) && is_array($roles)) {
+                foreach ($roles as $role) {
+                    $role_name = getProp($role, 'role', '');
+                    if (!empty($role_name)) {
+                        $role_names[] = $role_name;
+                    }
+                }
+            }
+            
+            if (!empty($scenes) && is_array($scenes)) {
+                foreach ($scenes as $scene) {
+                    $scene_name = getProp($scene, 'scene', '');
+                    if (!empty($scene_name)) {
+                        $scene_names[] = $scene_name;
+                    }
+                }
+            }
+            
+            // 处理每个片段的 act_show_content
+            foreach ($parsed_segments as &$segment) {
+                $act_content = getProp($segment, 'act_content', '');
+                if (empty($act_content)) {
+                    $segment['act_show_content'] = '';
+                    continue;
+                }
+                
+                $processed_content = $act_content;
+                
+                // 先匹配场景(在"场景:"后面)
+                foreach ($scene_names as $scene_name) {
+                    // 转义特殊字符
+                    $escaped_scene = preg_quote($scene_name, '/');
+                    // 匹配场景:后面的场景名称(可能换行,也可能不换行)
+                    $pattern = '/(场景:[\s\r\n]*)(' . $escaped_scene . ')/u';
+                    
+                    $processed_content = preg_replace_callback($pattern, function($matches) {
+                        $prefix = $matches[1];
+                        $scene_name = $matches[2];
+                        
+                        // 检查是否已经被标记
+                        if (strpos($scene_name, '{') === false && strpos($scene_name, '}') === false) {
+                            return $prefix . '{' . $scene_name . '}';
+                        }
+                        return $matches[0];
+                    }, $processed_content);
+                }
+                
+                // 再匹配角色名称(只在"画面:"部分)
+                foreach ($role_names as $role_name) {
+                    // 转义特殊字符
+                    $escaped_role = preg_quote($role_name, '/');
+                    
+                    // 只匹配"画面:"开始到换行之前的内容
+                    $pattern = '/(画面:[^\n]*)/u';
+                    $processed_content = preg_replace_callback($pattern, function($matches) use ($escaped_role) {
+                        $line = $matches[1]; // 整行"画面:xxxxx"
+                        
+                        // 在这一行中匹配所有角色名(避免重复标记)
+                        $new_line = preg_replace(
+                            '/(?<![{])(' . $escaped_role . ')(?![}])/u',
+                            '{$1}',
+                            $line
+                        );
+                        
+                        return $new_line;
+                    }, $processed_content);
+                }
+                
+                // 处理【镜头X】或【分镜X】重新编号为从1开始的连续整数【镜头X】
+                $shot_counter = 0;
+                $processed_content = preg_replace_callback(
+                    '/【(?:镜头|分镜)(\d+)】/u',
+                    function($matches) use (&$shot_counter) {
+                        $shot_counter++;
+                        return '【镜头' . $shot_counter . '】';
+                    },
+                    $processed_content
+                );
+                
+                $segment['act_show_content'] = $processed_content;
+            }
+            unset($segment); // 解除引用
+            
             // 保存到数据库
             $now = date('Y-m-d H:i:s');
+            $saved_acts = []; // 用于记录保存后的片段信息
             
             if ($act_id > 0) {
                 // 单个片段模式:更新单条记录
@@ -8608,7 +8869,8 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
                     $new_segment = $parsed_segments[0];
                     
                     $update_data = [
-                        'act_content' => $generated_content,
+                        'act_content' => getProp($new_segment, 'act_content', ''),
+                        'act_show_content' => getProp($new_segment, 'act_show_content', ''),
                         'act_title' => getProp($new_segment, 'act_title', ''),
                         'act_duration' => getProp($new_segment, 'act_duration', 0),
                         'updated_at' => $now,
@@ -8618,6 +8880,25 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
                         ->where('id', $act_id)
                         ->update($update_data);
                     
+                    // 获取更新后的记录
+                    $updated_act = DB::table('mp_episode_segments')
+                        ->where('id', $act_id)
+                        ->first();
+                    
+                    if ($updated_act) {
+                        $saved_acts[] = [
+                            'act_id' => $updated_act->id,
+                            'act_number' => $updated_act->act_number,
+                            'act_duration' => $updated_act->video_duration ? $updated_act->video_duration : $updated_act->act_duration,
+                            'act_content' => $updated_act->act_content,
+                            'act_show_content' => $updated_act->act_show_content,
+                            'video_url' => $updated_act->video_url ?? '',
+                            'video_duration' => $updated_act->video_duration ?? 0,
+                            'video_time_point_start' => $updated_act->video_time_point_start ?? 0,
+                            'video_time_point_end' => $updated_act->video_time_point_end ?? 0,
+                        ];
+                    }
+                    
                     dLog('deepseek')->info('单个片段重新生成成功', ['act_id' => $act_id]);
                 }
                 
@@ -8644,6 +8925,7 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
                             'act_title' => getProp($segment, 'act_title', ''),
                             'act_duration' => getProp($segment, 'act_duration', 0),
                             'act_content' => getProp($segment, 'act_content', ''),
+                            'act_show_content' => getProp($segment, 'act_show_content', ''),
                             'created_at' => $now,
                             'updated_at' => $now,
                         ];
@@ -8655,6 +8937,26 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
                     
                     DB::commit();
                     
+                    // 查询保存后的所有片段记录
+                    $saved_segments = DB::table('mp_episode_segments')
+                        ->where('episode_id', $target_episode_id)
+                        ->orderBy('act_number')
+                        ->get();
+                    
+                    foreach ($saved_segments as $saved_segment) {
+                        $saved_acts[] = [
+                            'act_id' => $saved_segment->id,
+                            'act_number' => $saved_segment->act_number,
+                            'act_duration' => $saved_segment->video_duration ? $saved_segment->video_duration : $saved_segment->act_duration,
+                            'act_content' => $saved_segment->act_content,
+                            'act_show_content' => $saved_segment->act_show_content,
+                            'video_url' => $saved_segment->video_url ?? '',
+                            'video_duration' => $saved_segment->video_duration ?? 0,
+                            'video_time_point_start' => $saved_segment->video_time_point_start ?? 0,
+                            'video_time_point_end' => $saved_segment->video_time_point_end ?? 0,
+                        ];
+                    }
+                    
                     dLog('deepseek')->info('整个剧集片段重新生成成功', [
                         'episode_id' => $target_episode_id,
                         'segments_count' => count($segments_to_insert)
@@ -8667,12 +8969,14 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
                 }
             }
             
+            // 构造返回数据结构(参考AnimeService返回格式,但不包含products)
             return [
-                'success' => true,
-                'message' => $act_id > 0 ? '单个片段重新生成成功' : '整个剧集片段重新生成成功',
-                'generated_content' => $generated_content,
-                'parsed_segments' => $parsed_segments,
-                'usage' => $ai_result['usage'] ?? [],
+                'anime_id' => $target_anime_id,
+                'episode_id' => $target_episode_id,
+                'title' => getProp($episode, 'title', ''),
+                'episode_number' => $target_episode_number,
+                'is_generated' => getProp($episode, 'is_generated', 0),
+                'acts' => $saved_acts,
             ];
             
         } catch (\Exception $e) {
@@ -10202,7 +10506,7 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
     // 仅调用deepseek对话接口
     private function chatOnly($post_data) {
         $post_data['max_tokens'] = 300000;
-        $client = new Client(['timeout' => 1200, 'verify' => false]);
+        $client = new Client(['timeout' => 1800, 'verify' => false]);
         $result = $client->post($this->url, ['json' => $post_data, 'headers' => $this->headers]);
         $response = $result->getBody()->getContents();
         $response_arr = json_decode($response, true);
@@ -10230,7 +10534,7 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
             Utils::throwError('20003:GPT-5.4 API Key未配置');
         }
 
-        $client = new Client(['timeout' => 1200, 'verify' => false]);
+        $client = new Client(['timeout' => 1800, 'verify' => false]);
         $headers = [
             'Authorization' => 'Bearer ' . $apiKey,
             'Content-Type'  => 'application/json'
@@ -10527,7 +10831,7 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
             $access_key, $secret_key, $body
         );
 
-        $client = new Client(['timeout' => 60, 'verify' => false]);
+        $client = new Client(['timeout' => 1800, 'verify' => false]);
         $response = $client->post("https://{$host}/?Action={$action}&Version={$version}", [
             'headers' => $headers,
             'body'    => $body
@@ -10862,7 +11166,7 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
 
         dLog('deepseek')->info('火山引擎对话API请求参数: ', $requestData);
 
-        $client = new Client(['verify' => false, 'timeout' => 1200]);
+        $client = new Client(['verify' => false, 'timeout' => 1800]);
 
         try {
             // 判断是否为流式输出
@@ -11376,79 +11680,4 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
             }
         }
     }
-
-    /**
-     * 解析分镜内容的公开方法
-     */
-    public function parseSegmentContent($segment_content, $model = null) {
-        if (!$model) {
-            $model = DB::table('mp_text_models')->where('is_enabled', 1)->orderBy('id')->value('model');
-            if (!$model) {
-                return $segment_content;
-            }
-        }
-
-        // 构建系统提示词
-        $systemPrompt = "你是一个专业的分镜内容解析助手。请将用户提供的分镜内容解析成标准格式,包含以下字段:
-画面描述:详细描述画面内容
-构图设计:镜头构图方式
-运镜调度:摄影机运动方式
-配音角色:配音的角色名称
-台词内容:角色说的话
-画面类型:普通画面或对口型
-场景:拍摄场景
-出镜角色:画面中出现的角色
-尾帧描述:镜头结束时的画面描述
-情感:配音情感(开心、悲伤、生气、惊讶、恐惧、厌恶、激动、冷漠、中性、沮丧、撒娇、害羞、安慰鼓励、咆哮、温柔、自然讲述、情感电台、磁性、广告营销、气泡音、新闻播报、娱乐八卦)
-性别:1(男)、2(女)、0(中性)
-语速:-50到100之间的整数
-音量:-50到100之间的整数
-情感强度:1到5之间的整数
-音调:-12到12之间的整数
-
-请严格按照上述格式输出,如果某个字段无法确定,请设置合理的默认值。";
-
-        // 构建消息
-        $messages = [
-            [
-                'role' => 'system',
-                'content' => $systemPrompt
-            ],
-            [
-                'role' => 'user',
-                'content' => $segment_content
-            ]
-        ];
-
-        $post_data = [
-            'model' => $model,
-            'messages' => $messages,
-            // 'max_tokens' => 2048,
-            'temperature' => 0.7,
-            'frequency_penalty' => 0,
-            'presence_penalty' => 0,
-            'response_format' => ['type' => 'text'],
-            'stream' => false
-        ];
-
-        try {
-            if (in_array($model, ['deepseek-reasoner', 'deepseek-chat', 'deepseek-v4-flash', 'deepseek-v4-pro'])) {
-                // 使用DeepSeek API
-                $chatResult = $this->chatOnly($post_data);
-            } else {
-                // 使用火山引擎API
-                $chatResult = $this->volcEngineChatCompletion($post_data);
-            }
-            
-            if (is_array($chatResult) && !empty($chatResult['fullContent'])) {
-                return $chatResult['fullContent'];
-            }
-        } catch (\Exception $e) {
-            dLog('deepseek')->error('解析分镜内容失败: ' . $e->getMessage());
-            logDB('deepseek', 'error', '解析分镜内容失败', ['error' => $e->getMessage()]);
-        }
-
-        return $segment_content;
-    }
-
 }

+ 1 - 0
routes/api.php

@@ -197,6 +197,7 @@ Route::group(['middleware' => ['bindToken', 'bindExportToken', 'checkLogin']], f
             Route::post('addChatForAce', [DeepSeekController::class, 'addChatForAce']);                 // 新增动漫对话
             Route::post('reGenerateAnimeForAce', [DeepSeekController::class, 'reGenerateAnimeForAce']); // 调整动漫大纲
             Route::post('chatForAce', [DeepSeekController::class, 'chatForAce']);                       // 动漫对话(全能模式)
+            Route::post('regenerateSegmentScript', [DeepSeekController::class, 'regenerateSegmentScript']); // 重新生成分段剧本
             Route::get('episodeInfoForAce', [AnimeController::class, 'episodeInfoForAce']);             // 动漫剧集(全能模式)
             Route::get('actChatHistory', [AnimeController::class, 'actChatHistory']);                   // 片段对话历史记录
             Route::get('confirmActs', [AnimeController::class, 'confirmActs']);                         // 确认片段分镜