فهرست منبع

优化重新生成分镜及添加分镜方法

lh 1 روز پیش
والد
کامیت
97602847b4
2فایلهای تغییر یافته به همراه192 افزوده شده و 16 حذف شده
  1. 119 16
      app/Services/Anime/AnimeService.php
  2. 73 0
      app/Services/DeepSeek/DeepSeekService.php

+ 119 - 16
app/Services/Anime/AnimeService.php

@@ -661,6 +661,8 @@ class AnimeService
             Utils::throwError("20003:" . $e->getMessage());
         }
 
+        // 创建任务之后先暂停10s等待异步任务完成
+        sleep(10);
         $img_url = $this->loopGetPicTaskResult($task_id);
 
         if (!$img_url) Utils::throwError('20003:图片生成中,请稍后刷新');
@@ -1100,6 +1102,11 @@ class AnimeService
             Utils::throwError('1002:请选择分镜');
         }
 
+        // 使用文生文模型重新解析segment_content
+        if (!empty($segment_content)) {
+            $segment_content = $this->parseSegmentContentWithAI($segment_content);
+        }
+
         // 根据画面比例给宽高赋值
         switch ($ratio) {
             case '1:1':
@@ -1166,22 +1173,25 @@ class AnimeService
         }
 
         $segment_id = $segment->segment_id;
-        $segment_content = $segment->segment_content;
+        $original_segment_content = $segment->segment_content;
+        
+        // 如果传入了新的segment_content,使用新的内容,否则使用原有内容
+        $final_segment_content = !empty($segment_content) ? $segment_content : $original_segment_content;
         
-        if (empty($segment_content)) {
+        if (empty($final_segment_content)) {
             Utils::throwError('20003:分镜内容不存在,请填写内容后重试');
         }
 
         try {
             // 分镜剧本数组
             $update_data = [
-                'segment_content' => $segment_content,
+                'segment_content' => $final_segment_content,
                 'pic_task_status' => '生成中',
                 'updated_at' => date('Y-m-d H:i:s')
             ];
 
             // 解析分镜内容,提取画面描述作为主要提示词
-            $prompt = $segment_content;
+            $prompt = $final_segment_content;
             $ref_img_urls = [];
             // 分镜场景
             $scene = getProp($data, 'scene');
@@ -1204,7 +1214,7 @@ class AnimeService
                 $characters = explode(',', $characters);
                 
                 // 从分镜内容中提取涉及的角色
-                $segment_characters = $this->extractCharactersFromSegment($segment_content, array_keys($role_map), $characters);
+                $segment_characters = $this->extractCharactersFromSegment($final_segment_content, array_keys($role_map), $characters);
                 
                 // 获取匹配角色的参考图
                 foreach ($segment_characters as $character) {
@@ -1244,6 +1254,40 @@ class AnimeService
 
             // 更新分镜剧本信息
             $this->handleSegmentContent($update_data);
+
+            // 如果有对话内容,创建视频配音合成任务(有更改分镜内容时创建)
+            if ($segment_content && getProp($update_data, 'dialogue')) {
+                $now = date('Y-m-d H:i:s');
+                $generate_json = [
+                    'text' => getProp($update_data, 'dialogue'),
+                    'role' => getProp($update_data, 'voice_actor'),
+                    'voice_type' => getProp($update_data, 'voice_type'),
+                    'voice_name' => getProp($update_data, 'voice_name'),
+                    'emotion' => getProp($update_data, 'emotion'),
+                    'emotion_type' => getProp($update_data, 'emotion_type'),
+                    'gender' => getProp($update_data, 'gender'),
+                    'speed_ratio' => getProp($update_data, 'speed_ratio', 0),
+                    'loudness_ratio' => getProp($update_data, 'loudness_ratio', 0),
+                    'emotion_scale' => getProp($update_data, 'emotion_scale', 0),
+                    'pitch' => getProp($update_data, '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' => date('Y-m-d H:i:s'),
+                    'updated_at' => date('Y-m-d H:i:s'),
+                ]);
+                
+                if (!$dubTaskId) {
+                    Utils::throwError('20003:创建配音任务失败!');
+                }
+                $update_data['audio_url'] = '';
+            }
+
             $boolen = DB::table('mp_episode_segments')
                 ->where('segment_id', $segment_id)
                 ->update($update_data);
@@ -1260,6 +1304,8 @@ class AnimeService
             ];
         }
 
+        // 创建任务之后先暂停10s等待异步任务完成
+        sleep(10);
         $img_url = $this->loopGetPicTaskResult($task_id);
         if (!$img_url) Utils::throwError('20003:图片生成中,请稍后刷新');
         return $img_url;
@@ -1284,6 +1330,11 @@ class AnimeService
             Utils::throwError('1002:请选择分镜');
         }
 
+        // 使用文生文模型重新解析segment_content
+        if (!empty($segment_content)) {
+            $segment_content = $this->parseSegmentContentWithAI($segment_content);
+        }
+
         // 根据画面比例给宽高赋值
         switch ($ratio) {
             case '1:1':
@@ -1350,9 +1401,12 @@ class AnimeService
         }
 
         $segment_id = $segment->segment_id;
-        $segment_content = $segment->segment_content;
+        $original_segment_content = $segment->segment_content;
         
-        if (empty($segment_content)) {
+        // 如果传入了新的segment_content,使用新的内容,否则使用原有内容
+        $final_segment_content = !empty($segment_content) ? $segment_content : $original_segment_content;
+        
+        if (empty($final_segment_content)) {
             Utils::throwError('20003:分镜内容不存在,请填写内容后重试');
         }
 
@@ -1370,7 +1424,7 @@ class AnimeService
                 'act_title'     => getProp($segment, 'act_title'),
                 'segment_id'    => $segment_id,
                 'segment_number' => $new_segment_number,
-                'segment_content' => $segment_content,
+                'segment_content' => $final_segment_content,
                 'img_url'       => $img_url,
                 'video_url'     => $video_url,
                 'created_at' => date('Y-m-d H:i:s'),
@@ -1380,10 +1434,42 @@ class AnimeService
             // 更新分镜剧本信息
             $this->handleSegmentContent($insert_data);
 
+            // 如果有对话内容,创建视频配音合成任务
+            if (getProp($insert_data, 'dialogue')) {
+                $now = date('Y-m-d H:i:s');
+                $generate_json = [
+                    'text' => getProp($insert_data, 'dialogue'),
+                    'role' => getProp($insert_data, 'voice_actor'),
+                    'voice_type' => getProp($insert_data, 'voice_type'),
+                    'voice_name' => getProp($insert_data, 'voice_name'),
+                    'emotion' => getProp($insert_data, 'emotion'),
+                    'emotion_type' => getProp($insert_data, 'emotion_type'),
+                    'gender' => getProp($insert_data, 'gender'),
+                    'speed_ratio' => getProp($insert_data, 'speed_ratio', 0),
+                    'loudness_ratio' => getProp($insert_data, 'loudness_ratio', 0),
+                    'emotion_scale' => getProp($insert_data, 'emotion_scale', 0),
+                    'pitch' => getProp($insert_data, '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:创建配音任务失败!');
+                }
+            }
+
             // 如果没有上传图片则使用当前描述进行生成
             if (!$img_url) {
                 // 解析分镜内容,提取画面描述作为主要提示词
-                $prompt = $segment_content;
+                $prompt = $final_segment_content;
                 $ref_img_urls = [];
                 // 分镜场景
                 $scene = getProp($insert_data, 'scene');
@@ -1406,7 +1492,7 @@ class AnimeService
                     $characters = explode(',', $characters);
                     
                     // 从分镜内容中提取涉及的角色
-                    $segment_characters = $this->extractCharactersFromSegment($segment_content, array_keys($role_map), $characters);
+                    $segment_characters = $this->extractCharactersFromSegment($final_segment_content, array_keys($role_map), $characters);
                     
                     // 获取匹配角色的参考图
                     foreach ($segment_characters as $character) {
@@ -1466,6 +1552,8 @@ class AnimeService
 
         DB::commit();
 
+        // 创建任务之后先暂停10s等待异步任务完成
+        sleep(10);
         if (isset($task_id)) $img_url = $this->loopGetPicTaskResult($task_id);
         if (!$img_url) Utils::throwError('20003:图片生成中,请稍后刷新');
         $segment = DB::table('mp_episode_segments')->where('segment_id', $segment_id)->first();
@@ -2048,7 +2136,7 @@ class AnimeService
     private function loopGetPicTaskResult($task_id) {
         $start_count = 0;
         $img_url = '';
-        while ($start_count <= 30) {
+        while ($start_count <= 20) {    // 循环20次请求,每3s一次
             sleep(3);
 
             $task = MpGeneratePicTask::where('id', $task_id)->first();
@@ -2082,7 +2170,7 @@ class AnimeService
                 }
                 break;
             }
-            $start_count+=3;
+            $start_count++;
         }
 
         return $img_url;
@@ -2624,16 +2712,16 @@ class AnimeService
             ->where('anime_id', $animeId)
             ->where('episode_id', $episodeId)
             ->orderBy('segment_number')
-            ->select('segment_id', 'segment_number', 'dub_video_url')
+            ->select('segment_id', 'segment_number', 'video_url', 'audio_url')
             ->get();
 
         if ($segments->isEmpty()) {
             Utils::throwError('20003:未找到该剧集的分镜数据');
         }
 
-        // 检查是否所有分镜都有配音视频
+        // 检查是否所有分镜都有配音
         $missingVideos = $segments->filter(function($segment) {
-            return empty($segment->dub_video_url);
+            return empty($segment->audio_url);
         });
 
         if ($missingVideos->isNotEmpty()) {
@@ -2646,7 +2734,8 @@ class AnimeService
         foreach ($segments as $segment) {
             $generateJson[] = [
                 'sequence' => $sequence++,
-                'dub_video_url' => $segment->dub_video_url,
+                'video_url' => $segment->video_url,
+                'audio_url' => $segment->audio_url,
             ];
         }
 
@@ -2745,4 +2834,18 @@ class AnimeService
             Utils::throwError('20003:' . $e->getMessage());
         }
     }
+
+    /**
+     * 使用文生文模型解析分镜内容
+     */
+    private function parseSegmentContentWithAI($segment_content) {
+        try {
+            // 使用DeepSeek服务的公开方法解析分镜内容
+            return $this->DeepSeekService->parseSegmentContent($segment_content);
+        } catch (\Exception $e) {
+            // 如果AI解析失败,记录日志并返回原内容
+            dLog('anime')->error('AI解析分镜内容失败: ' . $e->getMessage());
+            return $segment_content;
+        }
+    }
 }

+ 73 - 0
app/Services/DeepSeek/DeepSeekService.php

@@ -4774,4 +4774,77 @@ class DeepSeekService
         return $merged_items;
     }
 
+    /**
+     * 解析分镜内容的公开方法
+     */
+    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 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());
+        }
+
+        return $segment_content;
+    }
+
 }