Bladeren bron

优化分段剧本格式化逻辑

lh 1 dag geleden
bovenliggende
commit
c6dbeaaa1c
2 gewijzigde bestanden met toevoegingen van 66 en 299 verwijderingen
  1. 32 150
      app/Services/Anime/AnimeService.php
  2. 34 149
      app/Services/DeepSeek/DeepSeekService.php

+ 32 - 150
app/Services/Anime/AnimeService.php

@@ -8462,40 +8462,36 @@ class AnimeService
         // 获取所有片段数据
         $acts = DB::table('mp_episode_segments')->where('episode_id', $episode_id)->orderBy('act_number')->get();
         
-        // 构建角色、场景和道具名称数组
-        $role_names = [];
-        $scene_names = [];
-        $prop_names = [];
+        // 收集所有资产名称(角色、场景、道具)
+        $product_names = [];
         
+        // 收集角色名称
         foreach ($roles as $role) {
             $role_name = getProp($role, 'role');
             if ($role_name && $role_name != '旁白') {
-                $role_names[] = $role_name;
+                $product_names[] = $role_name;
             }
         }
         
+        // 收集场景名称
         foreach ($scenes as $scene) {
             $scene_name = getProp($scene, 'scene');
             if ($scene_name) {
-                $scene_names[] = $scene_name;
+                $product_names[] = $scene_name;
             }
         }
         
+        // 收集道具名称
         foreach ($props as $prop) {
             $prop_name = getProp($prop, 'prop');
             if ($prop_name) {
-                $prop_names[] = $prop_name;
+                $product_names[] = $prop_name;
             }
         }
         
-        // 按照字符长度降序排序,确保先匹配长的名称(避免部分匹配问题)
-        usort($role_names, function($a, $b) {
-            return mb_strlen($b) - mb_strlen($a);
-        });
-        usort($scene_names, function($a, $b) {
-            return mb_strlen($b) - mb_strlen($a);
-        });
-        usort($prop_names, function($a, $b) {
+        // 去重并按长度降序排序(避免短名称先匹配到长名称的一部分)
+        $product_names = array_unique($product_names);
+        usort($product_names, function($a, $b) {
             return mb_strlen($b) - mb_strlen($a);
         });
 
@@ -8508,146 +8504,32 @@ class AnimeService
                 
                 $processed_content = $act_content;
                 
-                // 标记已处理的位置,避免重复标记
-                $marked_positions = [];
+                // 统一替换所有资产名称为 {资产名} 格式
+                // 使用占位符避免嵌套替换问题
+                $placeholder_map = [];
+                $placeholder_index = 0;
                 
-                // 先匹配场景(在"场景:"后面)
-                foreach ($scene_names as $scene_name) {
+                foreach ($product_names as $product_name) {
                     // 转义特殊字符
-                    $escaped_scene = preg_quote($scene_name, '/');
-                    // 匹配场景:后面的场景名称(可能换行,也可能不换行)
-                    $pattern = '/(场景:[\s\r\n]*)(' . $escaped_scene . ')/u';
+                    $escaped_product = preg_quote($product_name, '/');
                     
-                    $processed_content = preg_replace_callback($pattern, function($matches) use (&$marked_positions) {
-                        $full_match = $matches[0];
-                        $prefix = $matches[1];
-                        $scene_name = $matches[2];
-                        
-                        // 检查是否已经被标记
-                        if (strpos($scene_name, '{') === false && strpos($scene_name, '}') === false) {
-                            return $prefix . '{' . $scene_name . '}';
-                        }
-                        return $full_match;
-                    }, $processed_content);
+                    // 检查是否匹配且未被 {} 包裹
+                    $processed_content = preg_replace_callback(
+                        '/(?<![{])(' . $escaped_product . ')(?![}])/u',
+                        function($matches) use (&$placeholder_map, &$placeholder_index) {
+                            // 使用唯一占位符
+                            $placeholder = '<<<PLACEHOLDER_' . $placeholder_index . '>>>';
+                            $placeholder_map[$placeholder] = '{' . $matches[1] . '}';
+                            $placeholder_index++;
+                            return $placeholder;
+                        },
+                        $processed_content
+                    );
                 }
                 
-                // 匹配道具(在"道具:"后面,逻辑与场景一致)
-                foreach ($prop_names as $prop_name) {
-                    // 转义特殊字符
-                    $escaped_prop = preg_quote($prop_name, '/');
-                    // 匹配道具:后面的道具名称(可能换行,也可能不换行)
-                    $pattern = '/(道具:[\s\r\n]*)(' . $escaped_prop . ')/u';
-                    
-                    $processed_content = preg_replace_callback($pattern, function($matches) use (&$marked_positions) {
-                        $full_match = $matches[0];
-                        $prefix = $matches[1];
-                        $prop_name = $matches[2];
-                        
-                        // 检查是否已经被标记
-                        if (strpos($prop_name, '{') === false && strpos($prop_name, '}') === false) {
-                            return $prefix . '{' . $prop_name . '}';
-                        }
-                        return $full_match;
-                    }, $processed_content);
-                }
-                
-                // 再匹配角色名称(在"画面:"部分和"角色:"部分)
-                foreach ($role_names as $role_name) {
-                    // 转义特殊字符
-                    $escaped_role = preg_quote($role_name, '/');
-                    
-                    // 1. 匹配"画面:"开始到换行之前的内容
-                    $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);
-                    
-                    // 2. 匹配"角色:"或"出场角色:"这一整行内的角色名称
-                    $pattern = '/((?:出场)?角色:[^\n]*)/u';
-                    $processed_content = preg_replace_callback($pattern, function($matches) use ($escaped_role) {
-                        $line = $matches[1]; // 整行"角色:xxxxx"或"出场角色:xxxxx"
-                        
-                        // 在这一行中匹配所有角色名(避免重复标记)
-                        $new_line = preg_replace(
-                            '/(?<![{])(' . $escaped_role . ')(?![}])/u',
-                            '{$1}',
-                            $line
-                        );
-                        
-                        return $new_line;
-                    }, $processed_content);
-                }
-                
-                // 匹配道具名称(在"画面:"部分,逻辑与角色一致)
-                foreach ($prop_names as $prop_name) {
-                    // 转义特殊字符
-                    $escaped_prop = preg_quote($prop_name, '/');
-                    
-                    // 匹配"画面:"开始到换行之前的内容
-                    $pattern = '/(画面:[^\n]*)/u';
-                    $processed_content = preg_replace_callback($pattern, function($matches) use ($escaped_prop) {
-                        $line = $matches[1]; // 整行"画面:xxxxx"
-                        
-                        // 在这一行中匹配所有道具名(避免重复标记)
-                        $new_line = preg_replace(
-                            '/(?<![{])(' . $escaped_prop . ')(?![}])/u',
-                            '{$1}',
-                            $line
-                        );
-                        
-                        return $new_line;
-                    }, $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);
-                }
-                
-                // 匹配道具名称(在"运镜:"部分)
-                foreach ($prop_names as $prop_name) {
-                    // 转义特殊字符
-                    $escaped_prop = preg_quote($prop_name, '/');
-                    
-                    // 匹配"运镜:"开始到换行之前的内容
-                    $pattern = '/(运镜:[^\n]*)/u';
-                    $processed_content = preg_replace_callback($pattern, function($matches) use ($escaped_prop) {
-                        $line = $matches[1]; // 整行"运镜:xxxxx"
-                        
-                        // 在这一行中匹配所有道具名(避免重复标记)
-                        $new_line = preg_replace(
-                            '/(?<![{])(' . $escaped_prop . ')(?![}])/u',
-                            '{$1}',
-                            $line
-                        );
-                        
-                        return $new_line;
-                    }, $processed_content);
+                // 将所有占位符替换回实际内容
+                foreach ($placeholder_map as $placeholder => $replacement) {
+                    $processed_content = str_replace($placeholder, $replacement, $processed_content);
                 }
                 
                 // 处理【镜头X】或【分镜X】重新编号为从1开始的连续整数【镜头X】

+ 34 - 149
app/Services/DeepSeek/DeepSeekService.php

@@ -9758,47 +9758,42 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
                 Utils::throwError('20003:无法解析生成的分段剧本,请检查格式');
             }
             
-            // 获取角色和场景名称用于标记
-            $role_names = [];
-            $scene_names = [];
+            // 收集所有资产名称(角色、场景、道具)
+            $product_names = [];
             
+            // 收集角色名称
             if (!empty($roles) && is_array($roles)) {
                 foreach ($roles as $role) {
                     $role_name = getProp($role, 'role', '');
                     if (!empty($role_name)) {
-                        $role_names[] = $role_name;
+                        $product_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;
+                        $product_names[] = $scene_name;
                     }
                 }
             }
             
-            // 获取道具名称用于标记
-            $prop_names = [];
+            // 收集道具名称
             if (!empty($props) && is_array($props)) {
                 foreach ($props as $prop) {
                     $prop_name = getProp($prop, 'prop', '');
                     if (!empty($prop_name)) {
-                        $prop_names[] = $prop_name;
+                        $product_names[] = $prop_name;
                     }
                 }
             }
             
-            // 对角色、场景、道具名称按长度降序排序(避免短名称先匹配到长名称的一部分)
-            usort($role_names, function($a, $b) {
-                return mb_strlen($b) - mb_strlen($a);
-            });
-            usort($scene_names, function($a, $b) {
-                return mb_strlen($b) - mb_strlen($a);
-            });
-            usort($prop_names, function($a, $b) {
+            // 去重并按长度降序排序(避免短名称先匹配到长名称的一部分)
+            $product_names = array_unique($product_names);
+            usort($product_names, function($a, $b) {
                 return mb_strlen($b) - mb_strlen($a);
             });
             
@@ -9812,142 +9807,32 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
                 
                 $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 ($prop_names as $prop_name) {
-                    // 转义特殊字符
-                    $escaped_prop = preg_quote($prop_name, '/');
-                    // 匹配道具:后面的道具名称(可能换行,也可能不换行)
-                    $pattern = '/(道具:[\s\r\n]*)(' . $escaped_prop . ')/u';
-                    
-                    $processed_content = preg_replace_callback($pattern, function($matches) {
-                        $full_match = $matches[0];
-                        $prefix = $matches[1];
-                        $prop_name = $matches[2];
-                        
-                        // 检查是否已经被标记
-                        if (strpos($prop_name, '{') === false && strpos($prop_name, '}') === false) {
-                            return $prefix . '{' . $prop_name . '}';
-                        }
-                        return $full_match;
-                    }, $processed_content);
-                }
-                
-                // 再匹配角色名称(在"画面:"部分和"角色:"部分)
-                foreach ($role_names as $role_name) {
-                    // 转义特殊字符
-                    $escaped_role = preg_quote($role_name, '/');
-                    
-                    // 1. 匹配"画面:"开始到换行之前的内容
-                    $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);
-                    
-                    // 2. 匹配"角色:"或"出场角色:"这一整行内的角色名称
-                    $pattern = '/((?:出场)?角色:[^\n]*)/u';
-                    $processed_content = preg_replace_callback($pattern, function($matches) use ($escaped_role) {
-                        $line = $matches[1]; // 整行"角色:xxxxx"或"出场角色:xxxxx"
-                        
-                        // 在这一行中匹配所有角色名(避免重复标记)
-                        $new_line = preg_replace(
-                            '/(?<![{])(' . $escaped_role . ')(?![}])/u',
-                            '{$1}',
-                            $line
-                        );
-                        
-                        return $new_line;
-                    }, $processed_content);
-                }
-                
-                // 匹配道具名称(在"画面:"部分,逻辑与角色一致)
-                foreach ($prop_names as $prop_name) {
-                    // 转义特殊字符
-                    $escaped_prop = preg_quote($prop_name, '/');
-                    
-                    // 匹配"画面:"开始到换行之前的内容
-                    $pattern = '/(画面:[^\n]*)/u';
-                    $processed_content = preg_replace_callback($pattern, function($matches) use ($escaped_prop) {
-                        $line = $matches[1]; // 整行"画面:xxxxx"
-                        
-                        // 在这一行中匹配所有道具名(避免重复标记)
-                        $new_line = preg_replace(
-                            '/(?<![{])(' . $escaped_prop . ')(?![}])/u',
-                            '{$1}',
-                            $line
-                        );
-                        
-                        return $new_line;
-                    }, $processed_content);
-                }
+                // 统一替换所有资产名称为 {资产名} 格式
+                // 使用占位符避免嵌套替换问题
+                $placeholder_map = [];
+                $placeholder_index = 0;
                 
-                // 匹配角色名称(在"运镜:"部分)
-                foreach ($role_names as $role_name) {
+                foreach ($product_names as $product_name) {
                     // 转义特殊字符
-                    $escaped_role = preg_quote($role_name, '/');
+                    $escaped_product = preg_quote($product_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);
-                }
-                
-                // 匹配道具名称(在"运镜:"部分)
-                foreach ($prop_names as $prop_name) {
-                    // 转义特殊字符
-                    $escaped_prop = preg_quote($prop_name, '/');
-                    
-                    // 匹配"运镜:"开始到换行之前的内容
-                    $pattern = '/(运镜:[^\n]*)/u';
-                    $processed_content = preg_replace_callback($pattern, function($matches) use ($escaped_prop) {
-                        $line = $matches[1]; // 整行"运镜:xxxxx"
-                        
-                        // 在这一行中匹配所有道具名(避免重复标记)
-                        $new_line = preg_replace(
-                            '/(?<![{])(' . $escaped_prop . ')(?![}])/u',
-                            '{$1}',
-                            $line
-                        );
-                        
-                        return $new_line;
-                    }, $processed_content);
+                    // 检查是否匹配且未被 {} 包裹
+                    $processed_content = preg_replace_callback(
+                        '/(?<![{])(' . $escaped_product . ')(?![}])/u',
+                        function($matches) use (&$placeholder_map, &$placeholder_index) {
+                            // 使用唯一占位符
+                            $placeholder = '<<<PLACEHOLDER_' . $placeholder_index . '>>>';
+                            $placeholder_map[$placeholder] = '{' . $matches[1] . '}';
+                            $placeholder_index++;
+                            return $placeholder;
+                        },
+                        $processed_content
+                    );
+                }
+                
+                // 将所有占位符替换回实际内容
+                foreach ($placeholder_map as $placeholder => $replacement) {
+                    $processed_content = str_replace($placeholder, $replacement, $processed_content);
                 }
                 
                 // 处理【镜头X】或【分镜X】重新编号为从1开始的连续整数【镜头X】