|
@@ -10422,15 +10422,21 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
|
|
|
$validation_failed = true;
|
|
$validation_failed = true;
|
|
|
$validation_error_msg = '未生成分镜剧本相关的内容';
|
|
$validation_error_msg = '未生成分镜剧本相关的内容';
|
|
|
} else {
|
|
} else {
|
|
|
|
|
+ // 校验主体列表:不能为空主体(旁白不算)
|
|
|
|
|
+ $generated_roles = array_column($episode_arr['roles'] ?? [], 'role');
|
|
|
|
|
+ // 过滤掉"旁白"角色
|
|
|
|
|
+ $generated_roles = array_filter($generated_roles, function($role) {
|
|
|
|
|
+ return $role !== '旁白';
|
|
|
|
|
+ });
|
|
|
|
|
+ if (empty($generated_roles)) {
|
|
|
|
|
+ $validation_failed = true;
|
|
|
|
|
+ $validation_error_msg = '生成的主体列表不能为空(旁白不算)';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 仅当存在强制主体设定时校验主体列表
|
|
// 仅当存在强制主体设定时校验主体列表
|
|
|
- if (!empty($extra_roles)) {
|
|
|
|
|
- $generated_roles = array_column($episode_arr['roles'], 'role');
|
|
|
|
|
- // 过滤掉"旁白"角色
|
|
|
|
|
- $generated_roles = array_filter($generated_roles, function($role) {
|
|
|
|
|
- return $role !== '旁白';
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ if (!$validation_failed && !empty($extra_roles)) {
|
|
|
$expected_roles = array_keys($extra_roles);
|
|
$expected_roles = array_keys($extra_roles);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 找出生成的主体中不在预期列表中的主体
|
|
// 找出生成的主体中不在预期列表中的主体
|
|
|
$invalid_roles = array_diff($generated_roles, $expected_roles);
|
|
$invalid_roles = array_diff($generated_roles, $expected_roles);
|
|
|
if (!empty($invalid_roles)) {
|
|
if (!empty($invalid_roles)) {
|
|
@@ -10470,6 +10476,17 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
|
|
|
if (empty($episode_arr['acts'])) {
|
|
if (empty($episode_arr['acts'])) {
|
|
|
$validation_failed = true;
|
|
$validation_failed = true;
|
|
|
$validation_error_msg = '未生成分镜剧本相关的内容';
|
|
$validation_error_msg = '未生成分镜剧本相关的内容';
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 校验主体列表:不能为空主体(旁白不算)
|
|
|
|
|
+ $generated_roles = array_column($episode_arr['roles'] ?? [], 'role');
|
|
|
|
|
+ // 过滤掉"旁白"角色
|
|
|
|
|
+ $generated_roles = array_filter($generated_roles, function($role) {
|
|
|
|
|
+ return $role !== '旁白';
|
|
|
|
|
+ });
|
|
|
|
|
+ if (empty($generated_roles)) {
|
|
|
|
|
+ $validation_failed = true;
|
|
|
|
|
+ $validation_error_msg = '生成的主体列表不能为空(旁白不算)';
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -10497,10 +10514,12 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
|
|
|
$retry_prompt = "生成的剧本存在问题:{$validation_error_msg}。\n\n请严格按照要求重新生成,确保:\n";
|
|
$retry_prompt = "生成的剧本存在问题:{$validation_error_msg}。\n\n请严格按照要求重新生成,确保:\n";
|
|
|
if ($anime_script_id) {
|
|
if ($anime_script_id) {
|
|
|
$retry_prompt .= "1. 主体列表中的所有主体名称必须从【强制主体设定】中选择,不得使用其他主体名称\n";
|
|
$retry_prompt .= "1. 主体列表中的所有主体名称必须从【强制主体设定】中选择,不得使用其他主体名称\n";
|
|
|
- $retry_prompt .= "2. 场景列表中的所有场景名称必须从【强制场景设定】中选择,不得使用其他场景名称\n";
|
|
|
|
|
- $retry_prompt .= "3. 必须包含完整的分镜剧本内容\n";
|
|
|
|
|
|
|
+ $retry_prompt .= "2. 主体列表不能为空,必须至少包含一个非“旁白”的主体\n";
|
|
|
|
|
+ $retry_prompt .= "3. 场景列表中的所有场景名称必须从【强制场景设定】中选择,不得使用其他场景名称\n";
|
|
|
|
|
+ $retry_prompt .= "4. 必须包含完整的分镜剧本内容\n";
|
|
|
} else {
|
|
} else {
|
|
|
- $retry_prompt .= "1. 必须包含完整的分镜剧本内容\n";
|
|
|
|
|
|
|
+ $retry_prompt .= "1. 主体列表不能为空,必须至少包含一个非“旁白”的主体\n";
|
|
|
|
|
+ $retry_prompt .= "2. 必须包含完整的分镜剧本内容\n";
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 添加重试消息到messages
|
|
// 添加重试消息到messages
|
|
@@ -10566,28 +10585,34 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
|
|
|
$validation_failed = true;
|
|
$validation_failed = true;
|
|
|
$validation_error_msg = '未生成分镜剧本相关的内容';
|
|
$validation_error_msg = '未生成分镜剧本相关的内容';
|
|
|
} else {
|
|
} else {
|
|
|
|
|
+ // 校验主体列表:不能为空主体(旁白不算)
|
|
|
|
|
+ $generated_roles = array_column($episode_arr['roles'] ?? [], 'role');
|
|
|
|
|
+ // 过滤掉"旁白"角色
|
|
|
|
|
+ $generated_roles = array_filter($generated_roles, function($role) {
|
|
|
|
|
+ return $role !== '旁白';
|
|
|
|
|
+ });
|
|
|
|
|
+ if (empty($generated_roles)) {
|
|
|
|
|
+ $validation_failed = true;
|
|
|
|
|
+ $validation_error_msg = '生成的主体列表不能为空(旁白不算)';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 仅当存在强制主体设定时校验主体列表
|
|
// 仅当存在强制主体设定时校验主体列表
|
|
|
- if (!empty($extra_roles)) {
|
|
|
|
|
- $generated_roles = array_column($episode_arr['roles'], 'role');
|
|
|
|
|
- // 过滤掉"旁白"角色
|
|
|
|
|
- $generated_roles = array_filter($generated_roles, function($role) {
|
|
|
|
|
- return $role !== '旁白';
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ if (!$validation_failed && !empty($extra_roles)) {
|
|
|
$expected_roles = array_keys($extra_roles);
|
|
$expected_roles = array_keys($extra_roles);
|
|
|
$invalid_roles = array_diff($generated_roles, $expected_roles);
|
|
$invalid_roles = array_diff($generated_roles, $expected_roles);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if (!empty($invalid_roles)) {
|
|
if (!empty($invalid_roles)) {
|
|
|
$validation_failed = true;
|
|
$validation_failed = true;
|
|
|
$validation_error_msg = '生成的主体列表包含不在【强制主体设定】中的主体:' . implode('、', $invalid_roles);
|
|
$validation_error_msg = '生成的主体列表包含不在【强制主体设定】中的主体:' . implode('、', $invalid_roles);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 仅当存在强制场景设定时校验场景列表
|
|
// 仅当存在强制场景设定时校验场景列表
|
|
|
if (!$validation_failed && !empty($extra_scenes)) {
|
|
if (!$validation_failed && !empty($extra_scenes)) {
|
|
|
$generated_scenes = array_column($episode_arr['scenes'], 'scene');
|
|
$generated_scenes = array_column($episode_arr['scenes'], 'scene');
|
|
|
$expected_scenes = array_keys($extra_scenes);
|
|
$expected_scenes = array_keys($extra_scenes);
|
|
|
$invalid_scenes = array_diff($generated_scenes, $expected_scenes);
|
|
$invalid_scenes = array_diff($generated_scenes, $expected_scenes);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if (!empty($invalid_scenes)) {
|
|
if (!empty($invalid_scenes)) {
|
|
|
$validation_failed = true;
|
|
$validation_failed = true;
|
|
|
$validation_error_msg = '生成的场景列表包含不在【强制场景设定】中的场景:' . implode('、', $invalid_scenes);
|
|
$validation_error_msg = '生成的场景列表包含不在【强制场景设定】中的场景:' . implode('、', $invalid_scenes);
|
|
@@ -10610,6 +10635,17 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
|
|
|
if (empty($episode_arr['acts'])) {
|
|
if (empty($episode_arr['acts'])) {
|
|
|
$validation_failed = true;
|
|
$validation_failed = true;
|
|
|
$validation_error_msg = '未生成分镜剧本相关的内容';
|
|
$validation_error_msg = '未生成分镜剧本相关的内容';
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 校验主体列表:不能为空主体(旁白不算)
|
|
|
|
|
+ $generated_roles = array_column($episode_arr['roles'] ?? [], 'role');
|
|
|
|
|
+ // 过滤掉"旁白"角色
|
|
|
|
|
+ $generated_roles = array_filter($generated_roles, function($role) {
|
|
|
|
|
+ return $role !== '旁白';
|
|
|
|
|
+ });
|
|
|
|
|
+ if (empty($generated_roles)) {
|
|
|
|
|
+ $validation_failed = true;
|
|
|
|
|
+ $validation_error_msg = '生成的主体列表不能为空(旁白不算)';
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -10855,26 +10891,32 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
|
|
|
// 验证必要字段
|
|
// 验证必要字段
|
|
|
$episode = $finalResult['episode'] ?? [];
|
|
$episode = $finalResult['episode'] ?? [];
|
|
|
|
|
|
|
|
- // 验证 roles
|
|
|
|
|
|
|
+ // 验证 roles(旁白不算主体,过滤后不能为空)
|
|
|
if (empty($episode['roles']) || !is_array($episode['roles'])) {
|
|
if (empty($episode['roles']) || !is_array($episode['roles'])) {
|
|
|
return ['error' => '生成失败:主体列表(roles)为空或格式错误'];
|
|
return ['error' => '生成失败:主体列表(roles)为空或格式错误'];
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+ $nonNarratorRoles = array_filter(array_column($episode['roles'], 'role'), function($role) {
|
|
|
|
|
+ return $role !== '旁白';
|
|
|
|
|
+ });
|
|
|
|
|
+ if (empty($nonNarratorRoles)) {
|
|
|
|
|
+ return ['error' => '生成失败:主体列表(roles)为空或格式错误(旁白不算)'];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 验证 scenes
|
|
// 验证 scenes
|
|
|
if (empty($episode['scenes']) || !is_array($episode['scenes'])) {
|
|
if (empty($episode['scenes']) || !is_array($episode['scenes'])) {
|
|
|
return ['error' => '生成失败:场景列表(scenes)为空或格式错误'];
|
|
return ['error' => '生成失败:场景列表(scenes)为空或格式错误'];
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 验证 props(道具可以为空,但必须存在且为数组)
|
|
// 验证 props(道具可以为空,但必须存在且为数组)
|
|
|
if (!isset($episode['props']) || !is_array($episode['props'])) {
|
|
if (!isset($episode['props']) || !is_array($episode['props'])) {
|
|
|
$episode['props'] = [];
|
|
$episode['props'] = [];
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 验证 acts(分镜剧本)
|
|
// 验证 acts(分镜剧本)
|
|
|
if (empty($episode['acts']) || !is_array($episode['acts'])) {
|
|
if (empty($episode['acts']) || !is_array($episode['acts'])) {
|
|
|
return ['error' => '生成失败:分镜剧本(acts)为空或格式错误'];
|
|
return ['error' => '生成失败:分镜剧本(acts)为空或格式错误'];
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 更新验证后的 episode 数据
|
|
// 更新验证后的 episode 数据
|
|
|
$finalResult['episode'] = $episode;
|
|
$finalResult['episode'] = $episode;
|
|
|
|
|
|
|
@@ -11077,7 +11119,8 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
|
|
|
$processingCount = 0;
|
|
$processingCount = 0;
|
|
|
$completedCount = 0;
|
|
$completedCount = 0;
|
|
|
$failedCount = 0;
|
|
$failedCount = 0;
|
|
|
-
|
|
|
|
|
|
|
+ $failed_episodes = [];
|
|
|
|
|
+
|
|
|
foreach ($tasks as $task) {
|
|
foreach ($tasks as $task) {
|
|
|
switch ($task['status']) {
|
|
switch ($task['status']) {
|
|
|
case 'pending':
|
|
case 'pending':
|
|
@@ -11091,10 +11134,15 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
|
|
|
break;
|
|
break;
|
|
|
case 'failed':
|
|
case 'failed':
|
|
|
$failedCount++;
|
|
$failedCount++;
|
|
|
|
|
+ // 组装失败分集对象(新对象):分集序号、错误原因
|
|
|
|
|
+ $failed_episodes[] = [
|
|
|
|
|
+ 'episode_number' => $task['episode_number'],
|
|
|
|
|
+ 'error_message' => $task['error_message'] ?? ''
|
|
|
|
|
+ ];
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 计算进度百分比
|
|
// 计算进度百分比
|
|
|
$progress = 0;
|
|
$progress = 0;
|
|
|
if ($totalCount > 0) {
|
|
if ($totalCount > 0) {
|
|
@@ -11120,6 +11168,7 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
|
|
|
'failed_count' => $failedCount,
|
|
'failed_count' => $failedCount,
|
|
|
'progress' => $progress,
|
|
'progress' => $progress,
|
|
|
'overall_status' => $overallStatus,
|
|
'overall_status' => $overallStatus,
|
|
|
|
|
+ 'failed_episodes' => $failed_episodes,
|
|
|
'tasks' => $tasks
|
|
'tasks' => $tasks
|
|
|
];
|
|
];
|
|
|
}
|
|
}
|