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

新增画风列表接口,新增音色可选项,优化解析分镜剧本逻辑

lh 5 месяцев назад
Родитель
Сommit
9298e79959

+ 59 - 0
app/Http/Controllers/Anime/AnimeController.php

@@ -50,6 +50,65 @@ class AnimeController extends BaseController
     }
 
 
+    public function artStyleList(): array
+    {
+        return [
+            [
+                'art_style' => '日系动漫风格',
+                'pic_url' => "https://cdn-zwai.ycsd.cn/mp_audio/art_styles/日系动漫风格.jpg"
+            ],
+            [
+                'art_style' => '国漫风格',
+                'pic_url' => "https://cdn-zwai.ycsd.cn/mp_audio/art_styles/国漫风格.jpg"
+            ],
+            [
+                'art_style' => 'Q版卡通风格',
+                'pic_url' => "https://cdn-zwai.ycsd.cn/mp_audio/art_styles/Q版卡通风格.jpg"
+            ],
+            [
+                'art_style' => '简约扁平风格',
+                'pic_url' => "https://cdn-zwai.ycsd.cn/mp_audio/art_styles/简约扁平风格.jpg"
+            ],
+            [
+                'art_style' => '古风仙侠风格',
+                'pic_url' => "https://cdn-zwai.ycsd.cn/mp_audio/art_styles/古风仙侠风格.jpg"
+            ],
+            [
+                'art_style' => '武侠风格',
+                'pic_url' => "https://cdn-zwai.ycsd.cn/mp_audio/art_styles/武侠风格.jpg"
+            ],
+            [
+                'art_style' => '新中式水墨风格',
+                'pic_url' => "https://cdn-zwai.ycsd.cn/mp_audio/art_styles/新中式水墨风格.jpg"
+            ],
+            [
+                'art_style' => '写实插画风格',
+                'pic_url' => "https://cdn-zwai.ycsd.cn/mp_audio/art_styles/写实插画风格.jpg"
+            ],
+            [
+                'art_style' => '3D卡通风格',
+                'pic_url' => "https://cdn-zwai.ycsd.cn/mp_audio/art_styles/3D卡通风格.jpg"
+            ],
+            [
+                'art_style' => '条漫风格',
+                'pic_url' => "https://cdn-zwai.ycsd.cn/mp_audio/art_styles/条漫风格.jpg"
+            ],
+            [
+                'art_style' => '赛博朋克风格',
+                'pic_url' => "https://cdn-zwai.ycsd.cn/mp_audio/art_styles/赛博朋克风格.jpg"
+            ],
+            [
+                'art_style' => '暗黑悬疑风格',
+                'pic_url' => "https://cdn-zwai.ycsd.cn/mp_audio/art_styles/暗黑悬疑风格.jpg"
+            ],
+            [
+                'art_style' => '治愈清新风格',
+                'pic_url' => "https://cdn-zwai.ycsd.cn/mp_audio/art_styles/治愈清新风格.jpg"
+            ],
+        ];
+    }
+
+
     // 批量生成主体图片
     public function batchSetRoleImg(Request $request) {
         // 忽略所有超时限制

+ 12 - 3
app/Libs/Helpers.php

@@ -2607,16 +2607,25 @@ function handleEpisodeContent($originalContent) {
                 
                 // 如果有音色信息,添加到数组中
                 if ($timbreName) {
-                    $timbreType = DB::table('mp_timbres')->where('is_enabled', 1)->where('timbre_name', 'like', "%{$timbreName}%")->orderBy('id')->value('timbre_type');
-                    if ($timbreType) {
+                    $timbre = DB::table('mp_timbres')->where('is_enabled', 1)->where('timbre_name', 'like', "%{$timbreName}%")->orderBy('id')->select('timbre_type', 'audio_url')->first();
+                    if ($timbre) {
                         $roleData['timbre_name'] = $timbreName;
-                        $roleData['timbre_type'] = $timbreType;
+                        $roleData['timbre_type'] = getProp($timbre, 'timbre_type');
+                        $roleData['audio_url'] = getProp($timbre, 'audio_url');;
                     }
                 }
                 
                 $result['roles'][] = $roleData;
             }
         }
+        // 加入旁白角色
+        $result['roles'][] = [
+            'role' => '旁白',
+            'description' => '负责叙述剧情、补充说明和情感渲染的非视觉角色。',
+            'timbre_name' => '旁白',
+            'timbre_type' => 'zh_male_linjiananhai_moon_bigtts',
+            'audio_url' => 'https://zw-audiobook.tos-cn-beijing.volces.com/demonstrate/zh_male_linjiananhai_moon_bigtts.wav'
+        ];
     }
     
     // 提取场景列表 - 兼容多种格式

+ 1 - 0
app/Services/Anime/AnimeService.php

@@ -76,6 +76,7 @@ class AnimeService
         
         foreach ($roles as &$role) {
             $role_name = getProp($role, 'role');
+            if ($role_name == '旁白') continue;
             $description = getProp($role, 'description');
             if ($art_style) $description = "美术风格:\n$art_style\n主体描述:$description";
             // 参考图地址

+ 76 - 22
app/Services/DeepSeek/DeepSeekService.php

@@ -140,21 +140,57 @@ class DeepSeekService
     private function getArtStylePromptMappings(): array
     {
         return [
-            '厚涂古风' => [
-                'aliases' => ['厚涂古风', '古风厚涂', '国风厚涂', '古风'],
-                'prompt' => "基础画风风格词:厚涂古风\n整体采用精致细腻的厚涂古风,色彩浓郁而富有层次感,注重光影对比,营造出古典而富有戏剧张力的氛围。色调以深沉的蓝绿、灰调为主,可辅以温暖的金色、琥珀色光晕强化画面情绪。人物造型飘逸,服饰细节考究,背景虚实结合,既有古韵,又不失奇幻色彩。"
+            '日系动漫风格' => [
+                'aliases' => ['日系动漫风格', '日系动漫', '日系', '动漫'],
+                'prompt' => "日系动漫风格,线条干净流畅,色彩明亮柔和,人物精致,光影细腻,画面统一稳定"
             ],
-            '现代都市情感' => [
-                'aliases' => ['现代都市', '都市情感', '现代情感', '都市写实', '现代写实'],
-                'prompt' => "基础画风风格词:写实细腻的现代都市情感风格,带有轻微悬疑色彩。\n整体采用电影质感的写实风格,色彩随情绪和剧情变化。日常场景偏自然光感与生活化细节,压抑或冲突场景可适度加强蓝灰、暗绿色调,营造情感张力与轻悬疑氛围。人物造型追求真实可信,环境道具需体现都市生活气息。"
+            '国漫风格' => [
+                'aliases' => ['国漫风格', '国漫'],
+                'prompt' => "国漫风格,线条硬朗,人物成熟大气,色彩浓郁,光影对比强烈,整体画风统一,适合漫剧叙事"
             ],
-            '现实主义超现实' => [
-                'aliases' => ['现实主义', '超现实', '现实主义超现实', '现实主义奇幻', '现代奇幻写实'],
-                'prompt' => "基础画风风格词:现实主义的细腻光影与超现实元素结合\n整体以现实主义基调塑造人物与场景,色彩饱和度适中偏低,通过细腻光影与局部超现实视觉元素制造情绪冲击。画面在真实生活质感之上融入象征性表达,让现实压迫感与奇异氛围并存。"
+            'Q版卡通风格' => [
+                'aliases' => ['Q版卡通风格', 'Q版卡通', 'Q版', '卡通'],
+                'prompt' => "Q版卡通风格,头大身小造型可爱,线条简洁,色彩明快,无复杂阴影,整体萌系干净统一"
             ],
-            '韩漫二次元' => [
-                'aliases' => ['韩漫', '韩漫二次元', '二次元', '漫画风', '韩系漫画'],
-                'prompt' => "基础画风风格词:韩漫二次元\n整体采用精致清晰的韩漫二次元风格,人物五官立体、轮廓干净,画面注重高颜值塑造与情绪表达。色彩通透,光影偏柔和,兼顾浪漫氛围与剧情张力,适合强化角色关系和戏剧冲突。"
+            '简约扁平风格' => [
+                'aliases' => ['简约扁平风格', '简约扁平', '扁平', '简约'],
+                'prompt' => "简约扁平风格,大块纯色无渐变,线条简洁现代,画面干净清爽,不出现复杂纹理与光影"
+            ],
+            '古风仙侠风格' => [
+                'aliases' => ['古风仙侠', '古风', '仙侠'],
+                'prompt' => "古风仙侠风格,汉服飘逸,云雾缭绕仙气空灵,色调清雅,光影柔和,国风氛围统一"
+            ],
+            '武侠风格' => [
+                'aliases' => ['武侠风格', '武侠'],
+                'prompt' => "武侠风格,中式古装,线条干练硬朗,江湖氛围浓厚,动态自然,色调沉稳大气"
+            ],
+            '新中式水墨风格' => [
+                'aliases' => ['新中式水墨风格', '新中式', '水墨'],
+                'prompt' => "新中式水墨风格,淡墨渲染笔触自然,留白有意境,色调清雅,国风质感统一不杂乱"
+            ],
+            '写实插画风格' => [
+                'aliases' => ['写实插画风格', '写实插画', '写实', '插画'],
+                'prompt' => "写实插画风格,真实人体比例,光影自然细腻,画面质感真实,不卡通不崩坏"
+            ],
+            '3D卡通风格' => [
+                'aliases' => ['3D卡通风格', '3D卡通'],
+                'prompt' => "3D卡通风格,立体建模质感柔和,造型圆润光滑,光影干净,整体风格统一不穿模"
+            ],
+            '条漫风格' => [
+                'aliases' => ['条漫风格', '条漫'],
+                'prompt' => "条漫风格,纵向分镜简洁,色彩清淡,适合上下滑动阅读,画面轻量化且整体统一"
+            ],
+            '赛博朋克风格' => [
+                'aliases' => ['赛博朋克风格', '赛博朋克'],
+                'prompt' => "赛博朋克风格,霓虹灯光未来都市,暗色调科技感强,高对比光影,风格统一强烈"
+            ],
+            '暗黑悬疑风格' => [
+                'aliases' => ['暗黑悬疑风格', '暗黑悬疑'],
+                'prompt' => "暗黑悬疑风格,低饱和冷色调,强阴影光线昏暗,氛围神秘压抑,整体色调统一"
+            ],
+            '治愈清新风格' => [
+                'aliases' => ['治愈清新风格', '治愈清新'],
+                'prompt' => "治愈清新风格,马卡龙柔和色系,柔光暖调,画面干净舒适,氛围温暖不杂乱"
             ],
         ];
     }
@@ -2536,12 +2572,19 @@ class DeepSeekService
         $scenes_content = $this->buildEpisodeItemContent($scenes, 'scene');
 
         // 获取音色列表
-        $timbres = DB::table('mp_timbres')->select('timbre_name')->where('is_enabled', 1)->where('id', '<=', 15)->get()->map(function ($value) {
-            $value = (array)$value;
-            $value['timbre_name'] = str_replace('(多情感)', '', $value['timbre_name']);
-            return $value;
-        })->pluck('timbre_name')->toArray();
-        $timbres_content = implode('、', $timbres);
+        // $timbres = DB::table('mp_timbres')->select('timbre_name')->where('is_enabled', 1)->where('id', '<=', 15)->get()->map(function ($value) {
+        //     $value = (array)$value;
+        //     $value['timbre_name'] = str_replace('(多情感)', '', $value['timbre_name']);
+        //     return $value;
+        // })->pluck('timbre_name')->toArray();
+        // $timbres_content = implode('、', $timbres);
+        $timbres_content = "青年男:北京小爷、阳光青年、傲娇霸总、优柔公子、解说小明、青涩小生、低音沉郁、亲切青年
+青年女:柔美女友、魅力女友、爽快思思、高冷御姐、知性温婉、甜心小美、清新女声、甜美桃子、温柔文雅、甜美小源、贴心闺蜜
+中年男:胡子叔叔、油腻大叔、神秘法师、优柔帮主
+中年女:邻居阿姨
+老年男:幽默大爷
+老年女:婆婆
+萌娃:奶气萌娃";
 
         $existing_roles = $base_episode ? json_decode((string)getProp($base_episode, 'roles'), true) : [];
         $existing_scenes = $base_episode ? json_decode((string)getProp($base_episode, 'scenes'), true) : [];
@@ -2575,7 +2618,7 @@ class DeepSeekService
                     1.剧集标题必须与文档内容高度相关,剧集序号是{$episode_number}
                     2.<主体列表>必须在以下主体中选择多个(根据拆分出的分镜剧本来确认具体几个),不得自定义(需带上冒号后的具体描述):\n{$roles_content}
                     2.1如果分集内容中出现了<主体列表>中不存在的新主体,则不用在上述主体中选择,而是按照相同的格式新增一个主体显示在主题列表中
-                    2.2主体列表的音色必须在以下音色中($timbres_content)选择,选择的音色放在描述的最后用{{}}框起来
+                    2.2主体列表的音色必须在以下音色中($timbres_content)选择,需要根据:符号前面的年纪和性别选择,比如角色是青年男性就只能在北京小爷、阳光青年、傲娇霸总、优柔公子、解说小明、青涩小生、低音沉郁、亲切青年中选择,选好的音色放在描述的最后用{{}}框起来
                     3.<场景列表>必须在以下场景中选择多个(根据拆分出的分镜剧本来确认具体几个),不得自定义(需带上冒号后的具体描述):\n{$scenes_content}
                     4.<分镜剧本>要求包含“分幕标题”以及“分镜信息”: 
                     4.1“分幕标题”是根据章节中的场景进行拆分,类似于“第1幕 场景、时间及室内或室外”的信息,其中场景只能在所给出的场景列表中选(不带场景详细描述)。
@@ -2857,8 +2900,8 @@ class DeepSeekService
         $generated_scenes = isset($episode_arr['scenes']) && is_array($episode_arr['scenes']) ? $episode_arr['scenes'] : [];
         $merged_roles = $this->mergeEpisodeResourceItems($existing_roles, $generated_roles, 'role');
         $merged_scenes = $this->mergeEpisodeResourceItems($existing_scenes, $generated_scenes, 'scene');
-        // dLog('deepseek')->info('修改后的数据: ', [$existing_roles, $generated_roles, $merged_roles]);
-        // exit();
+        dLog('deepseek')->info('修改后的数据: ', $merged_roles);
+        exit();
         $now = date('Y-m-d H:i:s');
 
         $del_flag = false;
@@ -3132,6 +3175,11 @@ class DeepSeekService
                     if (!empty($timbreType)) {
                         $mergedItem['timbre_type'] = $timbreType;
                     }
+
+                    $audioUrl = getProp($item, 'audio_url');
+                    if (!empty($audioUrl)) {
+                        $mergedItem['audio_url'] = $audioUrl;
+                    }
                     
                     $merged[] = $mergedItem;
                 } else {
@@ -3152,12 +3200,17 @@ class DeepSeekService
                         $mergedItem['timbre_type'] = $timbreType;
                     }
                     
+                    $audioUrl = getProp($item, 'audio_url');
+                    if (!empty($audioUrl)) {
+                        $mergedItem['audio_url'] = $audioUrl;
+                    }
+                    
                     // 不继承 URL(重置状态)
                     $merged[] = $mergedItem;
                 }
             } else {
                 // 新增项,保留生成项的所有字段
-                $merged[] = $this->resetEpisodeResourceItem($item, $nameKey, ['timbre_name', 'timbre_type']);
+                $merged[] = $this->resetEpisodeResourceItem($item, $nameKey, ['timbre_name', 'timbre_type', 'audio_url']);
             }
         }
 
@@ -3220,6 +3273,7 @@ class DeepSeekService
         $update_flag = false;
         foreach ($roles as &$role) {
             $role_name = getProp($role, 'role');
+            if ($role_name == '旁白') continue;
             $description = getProp($role, 'description');
             if ($art_style) $description = "美术风格:\n$art_style\n主体描述:$description";
             // 参考图地址

+ 1 - 0
routes/api.php

@@ -128,6 +128,7 @@ 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('addChat', [DeepSeekController::class, 'addChat']);
         Route::post('reGenerateAnime', [DeepSeekController::class, 'reGenerateAnime']);
         Route::get('chatList', [AnimeController::class, 'chatList']);