|
|
@@ -3439,8 +3439,12 @@ function handleEpisodeContentForAce($originalContent) {
|
|
|
}
|
|
|
|
|
|
// 提取主体列表 - 兼容多种格式
|
|
|
- // if (preg_match('/###\s*主体列表\s*\n(.*?)(?=\n\s*###[^#]|\z)/s', $originalContent, $charactersMatch)) {
|
|
|
- if (preg_match('/###[ \t]*主体列表[ \t]*\r?\n(.*?)(?=###[^#\n]|\z)/s', $originalContent, $charactersMatch)) {
|
|
|
+ // 旧正则(2026-08-14 前):只允许 ### 与标题同行,初衷是防止板块为空时把后续"###分段剧本"当板块内容;
|
|
|
+ // 但不兼容模型偶发的"###\n主体列表"换行标题格式(如 mp_anime_records id=31915),导致 roles/scenes/props 解析为空。
|
|
|
+ // if (preg_match('/###[ \t]*主体列表[ \t]*\r?\n(.*?)(?=###[^#\n]|\z)/s', $originalContent, $charactersMatch)) {
|
|
|
+ // 新正则:开头 \s*? 非贪婪,兼容 ###主体列表、### 主体列表、###\n主体列表、###\n\n主体列表;
|
|
|
+ // 结束边界 (?=\n?\s*###[^#]|\z) 保证空板块(### 后直接跟下一章节)与换行标题格式都不会吞入后续内容。
|
|
|
+ if (preg_match('/###\s*?主体列表\s*?\r?\n(.*?)(?=\n?\s*###[^#]|\z)/s', $originalContent, $charactersMatch)) {
|
|
|
$charactersText = trim($charactersMatch[1]);
|
|
|
$characterLines = explode("\n", $charactersText);
|
|
|
|
|
|
@@ -3551,8 +3555,9 @@ function handleEpisodeContentForAce($originalContent) {
|
|
|
}
|
|
|
|
|
|
// 提取场景列表 - 兼容多种格式
|
|
|
- // if (preg_match('/###\s*场景列表\s*\n(.*?)(?=\n\s*###[^#]|\z)/s', $originalContent, $scenesMatch)) {
|
|
|
- if (preg_match('/###[ \t]*场景列表[ \t]*\r?\n(.*?)(?=###[^#\n]|\z)/s', $originalContent, $scenesMatch)) {
|
|
|
+ // 旧正则(2026-08-14 前):只允许 ### 与标题同行,原因同上方主体列表,不兼容"###\n场景列表"换行标题格式。
|
|
|
+ // if (preg_match('/###[ \t]*场景列表[ \t]*\r?\n(.*?)(?=###[^#\n]|\z)/s', $originalContent, $scenesMatch)) {
|
|
|
+ if (preg_match('/###\s*?场景列表\s*?\r?\n(.*?)(?=\n?\s*###[^#]|\z)/s', $originalContent, $scenesMatch)) {
|
|
|
$scenesText = trim($scenesMatch[1]);
|
|
|
$sceneLines = explode("\n", $scenesText);
|
|
|
|
|
|
@@ -3566,11 +3571,20 @@ function handleEpisodeContentForAce($originalContent) {
|
|
|
$description = trim($sceneMatch[2]);
|
|
|
$picPrompt = null;
|
|
|
|
|
|
- // 检查描述末尾是否有{场景图片提示词}格式
|
|
|
- if (preg_match('/^(.*?)\{([^}]+)\}\s*$/u', $description, $promptMatch)) {
|
|
|
- // 匹配格式:场景描述{场景图片提示词}
|
|
|
- $description = trim($promptMatch[1]);
|
|
|
- $picPrompt = trim($promptMatch[2]);
|
|
|
+ // 检查描述末尾是否有{场景图片提示词}或{场景图片提示词}{{补充说明}}格式
|
|
|
+ // 格式1: 描述{pic_prompt}{{补充说明}}(如"{...}{{无人物}}")
|
|
|
+ if (preg_match('/^(.*?)\{(.+?)\}\s*\{\{(.+?)\}\}\s*$/u', $description, $fullMatch)) {
|
|
|
+ $description = trim($fullMatch[1]);
|
|
|
+ $picPrompt = trim($fullMatch[2]);
|
|
|
+ }
|
|
|
+ // 格式2: 描述{{补充说明}}(只有补充说明,无图片提示词)
|
|
|
+ elseif (preg_match('/^(.*?)\{\{(.+?)\}\}\s*$/u', $description, $voiceMatch)) {
|
|
|
+ $description = trim($voiceMatch[1]);
|
|
|
+ }
|
|
|
+ // 格式3: 描述{pic_prompt}(只有图片提示词)
|
|
|
+ elseif (preg_match('/^(.*?)\{(.+?)\}\s*$/u', $description, $picMatch)) {
|
|
|
+ $description = trim($picMatch[1]);
|
|
|
+ $picPrompt = trim($picMatch[2]);
|
|
|
}
|
|
|
|
|
|
$sceneData = [
|
|
|
@@ -3589,7 +3603,10 @@ function handleEpisodeContentForAce($originalContent) {
|
|
|
}
|
|
|
|
|
|
// 提取道具列表 - 兼容多种格式
|
|
|
- if (preg_match('/###[ \t]*道具列表[ \t]*\r?\n(.*?)(?=###[^#\n]|\z)/s', $originalContent, $propsMatch)) {
|
|
|
+ // 旧正则(2026-08-14 前):只允许 ### 与标题同行,原因同上方主体列表,不兼容"###\n道具列表"换行标题格式;
|
|
|
+ // 同时旧代码仅在提取到 pic_prompt 时才保存道具,用于兜底防止把分段剧本误当道具,代价是纯描述道具被丢弃。
|
|
|
+ // if (preg_match('/###[ \t]*道具列表[ \t]*\r?\n(.*?)(?=###[^#\n]|\z)/s', $originalContent, $propsMatch)) {
|
|
|
+ if (preg_match('/###\s*?道具列表\s*?\r?\n(.*?)(?=\n?\s*###[^#]|\z)/s', $originalContent, $propsMatch)) {
|
|
|
$propsText = trim($propsMatch[1]);
|
|
|
$propLines = explode("\n", $propsText);
|
|
|
|
|
|
@@ -3603,11 +3620,20 @@ function handleEpisodeContentForAce($originalContent) {
|
|
|
$description = trim($propMatch[2]);
|
|
|
$picPrompt = null;
|
|
|
|
|
|
- // 检查描述末尾是否有{道具图片提示词}格式
|
|
|
- if (preg_match('/^(.*?)\{([^}]+)\}\s*$/u', $description, $promptMatch)) {
|
|
|
- // 匹配格式:道具描述{道具图片提示词}
|
|
|
- $description = trim($promptMatch[1]);
|
|
|
- $picPrompt = trim($promptMatch[2]);
|
|
|
+ // 检查描述末尾是否有{道具图片提示词}或{道具图片提示词}{{音效提示词}}格式(与主体列表保持一致)
|
|
|
+ // 格式1: 描述{pic_prompt}{{voice_prompt}}(图片提示词+音效提示词)
|
|
|
+ if (preg_match('/^(.*?)\{(.+?)\}\s*\{\{(.+?)\}\}\s*$/u', $description, $fullMatch)) {
|
|
|
+ $description = trim($fullMatch[1]);
|
|
|
+ $picPrompt = trim($fullMatch[2]);
|
|
|
+ }
|
|
|
+ // 格式2: 描述{{voice_prompt}}(只有音效提示词)
|
|
|
+ elseif (preg_match('/^(.*?)\{\{(.+?)\}\}\s*$/u', $description, $voiceMatch)) {
|
|
|
+ $description = trim($voiceMatch[1]);
|
|
|
+ }
|
|
|
+ // 格式3: 描述{pic_prompt}(只有图片提示词)
|
|
|
+ elseif (preg_match('/^(.*?)\{(.+?)\}\s*$/u', $description, $picMatch)) {
|
|
|
+ $description = trim($picMatch[1]);
|
|
|
+ $picPrompt = trim($picMatch[2]);
|
|
|
}
|
|
|
|
|
|
$propData = [
|
|
|
@@ -3615,10 +3641,11 @@ function handleEpisodeContentForAce($originalContent) {
|
|
|
'description' => $description
|
|
|
];
|
|
|
|
|
|
+ // 新正则已精确切分道具板块,不再依赖"必须有提示词"兜底,纯描述道具也保留
|
|
|
if ($picPrompt) {
|
|
|
$propData['pic_prompt'] = $picPrompt;
|
|
|
- $result['props'][] = $propData;
|
|
|
}
|
|
|
+ $result['props'][] = $propData;
|
|
|
}
|
|
|
}
|
|
|
}
|