fix: verify and guard video ai modules
This commit is contained in:
parent
eab0f0e0d4
commit
9474086356
@ -3,7 +3,7 @@
|
||||
> HLDP://video-ai-system/CURRENT
|
||||
> 类型: 子系统官方置信入口 · 每次进入视频AI系统先读
|
||||
> 创建: D140 · 2026-06-22 · Codex收口
|
||||
> 更新: D143 · 2026-06-23 · preview-002复盘·制片厂化重启
|
||||
> 更新: D144 · 2026-06-24 · 8模块验收·生产阻塞项确认
|
||||
> 铸渊 ICE-GL-ZY001 · 冰朔 TCS-0002∞
|
||||
> 国作登字-2026-A-00037559
|
||||
|
||||
@ -133,6 +133,7 @@ JZAO外置盘: 产物存放地,不是状态主控。
|
||||
| 苏白TTS配音 | ✅ 工程测试通过 | `video-ai-system/outputs/tests/TEST-TTS-001-SUBAI-RUN-001-REPORT.hdlp` |
|
||||
| 模型/成本路线 | ✅ 已建立 | `video-ai-system/knowledge/D144-MODEL-COST-ROUTE.hdlp` |
|
||||
| 腾讯AI开发交接 | ✅ 已建立 | `video-ai-system/plans/D144-TENCENT-AI-DEV-HANDOFF.hdlp` |
|
||||
| 腾讯AI 8模块代码 | 🟡 部分通过 | `video-ai-system/experience/D144-8-MODULE-VERIFICATION.hdlp` · 框架入仓,但主角资产、多参考图、口型同步、EP01 CLI仍非生产可用 |
|
||||
| 广告牌资产 | ✅ 草案 | `video-ai-system/assets/props/PROP-FREE-AD-BOARD/manifest.hdlp` |
|
||||
| 广告牌文字贴图 | ✅ PNG已生成 | `video-ai-system/assets/props/PROP-FREE-AD-BOARD/texture/free-ad-board-texture.png` |
|
||||
| 百宗会场景资产 | ✅ 草案 | `video-ai-system/assets/envs/ENV-002-Baizonghui/manifest.hdlp` |
|
||||
@ -149,10 +150,10 @@ JZAO外置盘: 产物存放地,不是状态主控。
|
||||
当前正式任务:
|
||||
|
||||
```
|
||||
D143-VIDEO-AI-RESET:
|
||||
从 preview-002 失败复盘和《渔乡守真心》样片对标开始,重新建立影视制作流程知识库、素材库、模型准入、对白口型测试、竖屏剪辑模板和经验闭环。
|
||||
不直接生成 preview-003。
|
||||
第一个突破测试是 2-3 秒“苏白说一句话”,第二个突破测试是“天道宗牌匾跨镜一致”。
|
||||
D144-VIDEO-AI-PRODUCTION-BLOCKERS:
|
||||
腾讯AI 8模块已入仓并完成第一轮验收,但不能按“全部开发完成”验收。
|
||||
当前优先级仍是字幕商业标准工具链、真实口型同步、剧本原文锁定、主角资产包真实生成、多参考/合成一致性路线。
|
||||
任何模块不能把占位输出、复制输出、未验证API能力标记成生产成功。
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@ -19,14 +19,23 @@ import os
|
||||
import sys
|
||||
import json
|
||||
import argparse
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
# 添加项目根目录到路径
|
||||
PROJECT_ROOT = Path(__file__).parent.parent.parent
|
||||
sys.path.insert(0, str(PROJECT_ROOT / "engines"))
|
||||
|
||||
from image_api_adapter import generate_image, save_image
|
||||
from hldp_prompt import expand_prompt
|
||||
try:
|
||||
from image_api_adapter import generate_image
|
||||
except Exception as exc:
|
||||
print(f"⚠️ Python图片生成适配器未接通: {exc}")
|
||||
generate_image = None
|
||||
|
||||
try:
|
||||
from hldp_prompt import expand_prompt
|
||||
except Exception:
|
||||
expand_prompt = None
|
||||
|
||||
|
||||
class CharHeroDesignPacker:
|
||||
@ -115,6 +124,14 @@ class CharHeroDesignPacker:
|
||||
print(f" 参考图: {candidate}")
|
||||
print(f" 提示词: {prompt[:100]}...")
|
||||
|
||||
if generate_image is None:
|
||||
print(" ⚠️ 当前仓库只有 JS 图片适配器 image-api-adapter.js,未提供 Python generate_image。")
|
||||
print(" ⚠️ 暂不能生成新主角资产;仅将候选图复制到 candidates/ 供人工/后续工具处理。")
|
||||
fallback_path = self.candidates_dir / output_name
|
||||
shutil.copy2(candidate, fallback_path)
|
||||
print(f" 🟡 已复制候选图: {fallback_path}")
|
||||
return None
|
||||
|
||||
try:
|
||||
result_path = generate_image(
|
||||
prompt=prompt,
|
||||
@ -203,24 +220,24 @@ class CharHeroDesignPacker:
|
||||
|
||||
prompts = {
|
||||
"front_half_body": f"""
|
||||
{base_desc.get('visual_description', '中国古代修仙少年,16岁,白色长发,蓝色眼睛')}
|
||||
{base_desc.get('visual_description', '中国古代修仙少年,16岁,主角辨识度强')}
|
||||
正面半身像,胸部以上,面部清晰,眼神坚定,
|
||||
3D动画风格,皮克斯风格,统一渲染风格,
|
||||
3D国风漫剧风格,统一渲染风格,
|
||||
高清,8K,最佳质量
|
||||
""".strip(),
|
||||
|
||||
"side_face": f"""
|
||||
{base_desc.get('visual_description', '中国古代修仙少年')}
|
||||
侧脸45度,能看到面部轮廓和发型,
|
||||
3D动画风格,皮克斯风格,统一渲染风格,
|
||||
3D国风漫剧风格,统一渲染风格,
|
||||
高清,8K,最佳质量
|
||||
""".strip(),
|
||||
|
||||
"full_body_costume": f"""
|
||||
{base_desc.get('visual_description', '中国古代修仙少年')}
|
||||
全身像,站立姿势,完整展示服装细节,
|
||||
白色内衬,蓝色外袍,黑色腰带,棕色靴子,
|
||||
3D动画风格,皮克斯风格,统一渲染风格,
|
||||
服装细节必须来自角色资产清单,不得自由改色,
|
||||
3D国风漫剧风格,统一渲染风格,
|
||||
高清,8K,最佳质量
|
||||
""".strip(),
|
||||
|
||||
@ -229,7 +246,7 @@ class CharHeroDesignPacker:
|
||||
表情表,网格布局,包含:
|
||||
平静,微笑,大笑,生气,惊讶,悲伤,
|
||||
每个表情单独一格,统一光照和背景,
|
||||
3D动画风格,皮克斯风格,
|
||||
3D国风漫剧风格,
|
||||
高清,8K,最佳质量
|
||||
""".strip(),
|
||||
}
|
||||
@ -263,7 +280,7 @@ class CharHeroDesignPacker:
|
||||
if path:
|
||||
results["expression_sheet"] = path
|
||||
|
||||
# 更新 manifest
|
||||
# 更新 manifest。只有四项资产都真实生成时才可进入 APPROVED。
|
||||
self._update_manifest(results)
|
||||
|
||||
print(f"\n✅ 资产包生成完成!")
|
||||
@ -276,10 +293,20 @@ class CharHeroDesignPacker:
|
||||
"""更新 manifest.hdlp"""
|
||||
print(f"\n📝 更新 manifest.hdlp...")
|
||||
|
||||
required_assets = {"front_half_body", "side_face", "full_body_costume", "expression_sheet"}
|
||||
is_complete = required_assets.issubset(results.keys())
|
||||
approval_status = "APPROVED" if is_complete else "DRAFT_GENERATED"
|
||||
type_label = "已批准" if is_complete else "待质检"
|
||||
lock_note = (
|
||||
"⊢ 资产已批准,可用于成片镜头。"
|
||||
if is_complete
|
||||
else "⊢ 资产包尚未完整生成/质检,禁止作为成片锁定资产。"
|
||||
)
|
||||
|
||||
manifest_content = f"""# 资产清单 · {self.character_id}
|
||||
|
||||
> HLDP://video-ai-system/assets/characters/{self.character_id}/manifest
|
||||
> 类型: 角色资产 · 已批准
|
||||
> 类型: 角色资产 · {type_label}
|
||||
> 建立: D143 · 2026-06-23
|
||||
> 更新: D144 · 2026-06-24
|
||||
> 项目: 付费才能修仙 · EP01
|
||||
@ -289,7 +316,7 @@ class CharHeroDesignPacker:
|
||||
## 状态
|
||||
|
||||
```
|
||||
approval_status: APPROVED
|
||||
approval_status: {approval_status}
|
||||
asset_type: character
|
||||
canonical_id: {self.character_id}
|
||||
canonical_name: 苏白
|
||||
@ -311,19 +338,19 @@ expression_sheet: {results.get("expression_sheet", "NOT_GENERATED")}
|
||||
## 视觉锁
|
||||
|
||||
```
|
||||
face_shape: 少年脸,柔和轮廓,白色长发
|
||||
hair_style: 白色长发,束发,有发带
|
||||
costume: 白色内衬 + 蓝色外袍 + 黑色腰带 + 棕色靴子
|
||||
face_shape: TODO_FROM_APPROVED_ASSET
|
||||
hair_style: TODO_FROM_APPROVED_ASSET
|
||||
costume: TODO_FROM_APPROVED_ASSET
|
||||
age_band: 16岁
|
||||
render_style: 3D动画,皮克斯风格,明亮色彩
|
||||
color_palette: 白,蓝,黑,棕
|
||||
render_style: 3D国风漫剧
|
||||
color_palette: TODO_FROM_APPROVED_ASSET
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 锁定
|
||||
|
||||
⊢ 资产已批准,可用于成片镜头。
|
||||
{lock_note}
|
||||
⊢ 禁止使用 candidates/ 或 rejected/ 中的图片作为最终资产。
|
||||
"""
|
||||
|
||||
|
||||
@ -270,6 +270,14 @@ class CharacterDistinctivenessQC:
|
||||
|
||||
return results
|
||||
|
||||
def save_report(self, result, output_path):
|
||||
"""保存单张或批量评估报告"""
|
||||
output_path = Path(output_path)
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(output_path, "w", encoding="utf-8") as f:
|
||||
json.dump(result, f, ensure_ascii=False, indent=2)
|
||||
print(f"\n 报告已保存: {output_path}")
|
||||
|
||||
def _generate_batch_report(self, results):
|
||||
"""生成批量评估报告"""
|
||||
print(f"\n📊 批量评估报告")
|
||||
@ -301,6 +309,7 @@ def main():
|
||||
parser.add_argument("--character", type=str, default="CHAR-003-SuBai",
|
||||
help="角色ID (默认: CHAR-003-SuBai)")
|
||||
parser.add_argument("--batch", type=str, help="批量评估目录")
|
||||
parser.add_argument("--output", type=str, help="输出 JSON 报告路径")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
@ -314,9 +323,13 @@ def main():
|
||||
result = qc.evaluate_image(args.image)
|
||||
print(f"\n📋 评估详情:")
|
||||
print(json.dumps(result, ensure_ascii=False, indent=2))
|
||||
if args.output:
|
||||
qc.save_report(result, args.output)
|
||||
|
||||
elif args.batch:
|
||||
results = qc.evaluate_batch(args.batch)
|
||||
if args.output:
|
||||
qc.save_report(results, args.output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@ -33,8 +33,9 @@ sys.path.insert(0, str(PROJECT_ROOT / "engines"))
|
||||
class LipSyncAdapter:
|
||||
"""口型适配器"""
|
||||
|
||||
def __init__(self, wav2lip_path=None):
|
||||
def __init__(self, wav2lip_path=None, allow_fallback=False):
|
||||
self.wav2lip_path = wav2lip_path or PROJECT_ROOT / "tools" / "Wav2Lip"
|
||||
self.allow_fallback = allow_fallback
|
||||
self.available = self._check_wav2lip()
|
||||
|
||||
def _check_wav2lip(self):
|
||||
@ -53,7 +54,7 @@ class LipSyncAdapter:
|
||||
print(f"✅ Wav2Lip 已安装: {self.wav2lip_path}")
|
||||
return True
|
||||
|
||||
def sync_lips(self, video_path, audio_path, output_path=None):
|
||||
def sync_lips(self, video_path, audio_path, output_path=None, allow_fallback=None):
|
||||
"""
|
||||
口型同步
|
||||
|
||||
@ -98,9 +99,20 @@ class LipSyncAdapter:
|
||||
result = self._run_wav2lip(video_path, audio_path, output_path)
|
||||
return result
|
||||
|
||||
# 方法2: 回退 (不处理,只复制视频)
|
||||
print(f" ⚠️ Wav2Lip 不可用,回退到不处理模式")
|
||||
print(f" 💡 提示: 安装 Wav2Lip 以获得口型同步能力")
|
||||
fallback_enabled = self.allow_fallback if allow_fallback is None else allow_fallback
|
||||
if not fallback_enabled:
|
||||
print(f" ❌ Wav2Lip 不可用,口型同步未执行")
|
||||
print(f" 💡 如需调试占位输出,可显式传入 --allow-fallback-copy")
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Wav2Lip 不可用,未执行真实口型同步",
|
||||
"method": "not-run",
|
||||
"requires": "Install Wav2Lip and checkpoint wav2lip_gan.pth"
|
||||
}
|
||||
|
||||
# 调试回退: 不处理,只复制视频。必须显式开启,避免生产线假阳性。
|
||||
print(f" ⚠️ Wav2Lip 不可用,按 --allow-fallback-copy 复制原视频")
|
||||
print(f" 💡 注意: 该输出不是口型同步成品")
|
||||
|
||||
import shutil
|
||||
shutil.copy2(video_path, output_path)
|
||||
@ -109,7 +121,7 @@ class LipSyncAdapter:
|
||||
"success": True,
|
||||
"output_path": str(output_path),
|
||||
"method": "fallback(copy)",
|
||||
"warning": "Wav2Lip 不可用,口型未同步。请安装 Wav2Lip。"
|
||||
"warning": "Wav2Lip 不可用,口型未同步。该文件只能用于调试占位。"
|
||||
}
|
||||
|
||||
def _run_wav2lip(self, video_path, audio_path, output_path):
|
||||
@ -247,6 +259,8 @@ def main():
|
||||
parser.add_argument("--batch", type=str, help="批量处理配置文件 (JSON)")
|
||||
parser.add_argument("--wav2lip-path", type=str, help="Wav2Lip 安装路径")
|
||||
parser.add_argument("--check-sync", action="store_true", help="检查口型同步质量")
|
||||
parser.add_argument("--allow-fallback-copy", action="store_true",
|
||||
help="仅调试用: Wav2Lip 不可用时复制原视频并标记为 fallback")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
@ -276,7 +290,7 @@ def main():
|
||||
|
||||
output_dir = config.get("output_dir", "./outputs/lipsync/")
|
||||
|
||||
adapter = LipSyncAdapter(args.wav2lip_path)
|
||||
adapter = LipSyncAdapter(args.wav2lip_path, allow_fallback=args.allow_fallback_copy)
|
||||
results = adapter.batch_sync(video_audio_pairs, output_dir)
|
||||
|
||||
sys.exit(0)
|
||||
@ -285,7 +299,7 @@ def main():
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
adapter = LipSyncAdapter(args.wav2lip_path)
|
||||
adapter = LipSyncAdapter(args.wav2lip_path, allow_fallback=args.allow_fallback_copy)
|
||||
result = adapter.sync_lips(args.video, args.audio, args.output)
|
||||
|
||||
if result["success"]:
|
||||
|
||||
@ -30,34 +30,43 @@ from datetime import datetime
|
||||
PROJECT_ROOT = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(PROJECT_ROOT / "engines"))
|
||||
|
||||
# 从环境变量加载 API 配置
|
||||
def load_api_config():
|
||||
"""从 video-ai-system/.env 加载配置"""
|
||||
"""从仓库 .env、本机密钥文件和环境变量加载配置"""
|
||||
config = {}
|
||||
env_file = PROJECT_ROOT / ".env"
|
||||
if env_file.exists():
|
||||
with open(env_file, "r") as f:
|
||||
env_files = [
|
||||
PROJECT_ROOT / ".env",
|
||||
Path.home() / "guanghulab-local-secrets" / "video-ai-system.env",
|
||||
]
|
||||
for env_file in env_files:
|
||||
if not env_file.exists():
|
||||
continue
|
||||
with open(env_file, "r", encoding="utf-8") as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if line and not line.startswith("#"):
|
||||
key, _, val = line.partition("=")
|
||||
config[key.strip()] = val.strip()
|
||||
config[key.strip()] = val.strip().strip('"').strip("'")
|
||||
for key in ["JIMENG_API_KEY", "JIMENG_BASE_URL", "JIMENG_MODEL"]:
|
||||
if os.environ.get(key):
|
||||
config[key] = os.environ[key]
|
||||
return config
|
||||
|
||||
|
||||
class MultiReferenceVideoAdapter:
|
||||
"""多参考图视频适配器"""
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, live_capability_check=False, allow_single_reference_fallback=False):
|
||||
self.config = load_api_config()
|
||||
self.api_key = self.config.get("JIMENG_API_KEY", "")
|
||||
self.base_url = self.config.get("JIMENG_BASE_URL", "https://ark.cn-beijing.volces.com/api/v3")
|
||||
self.model = self.config.get("JIMENG_MODEL", "doubao-seedance-2-0-260128")
|
||||
self.live_capability_check = live_capability_check
|
||||
self.allow_single_reference_fallback = allow_single_reference_fallback
|
||||
|
||||
# API 能力探测结果缓存
|
||||
self._api_capabilities = None
|
||||
|
||||
def check_api_capabilities(self):
|
||||
def check_api_capabilities(self, live=None):
|
||||
"""
|
||||
检查 API 是否支持多参考图
|
||||
返回: {
|
||||
@ -70,6 +79,7 @@ class MultiReferenceVideoAdapter:
|
||||
if self._api_capabilities:
|
||||
return self._api_capabilities
|
||||
|
||||
live = self.live_capability_check if live is None else live
|
||||
print("🔍 检查 Seedance API 多参考图支持...")
|
||||
|
||||
# 根据 Volcengine 官方文档 (https://www.volcengine.com/docs/82379/1520757)
|
||||
@ -83,8 +93,17 @@ class MultiReferenceVideoAdapter:
|
||||
# { type: "image_url", image_url: { url: "data:image/png;base64,..." } }, # 第二张参考图
|
||||
# ]
|
||||
|
||||
# 实际测试: 尝试提交一个包含2张参考图的请求,看是否报错
|
||||
test_result = self._test_multi_reference()
|
||||
if not live:
|
||||
test_result = {
|
||||
"multi_reference_supported": False,
|
||||
"max_references": 1,
|
||||
"supported_types": ["image_url"],
|
||||
"status": "unverified",
|
||||
"details": "未执行付费/联网实测;需要 --live-check 才会提交真实 API 探测任务",
|
||||
"requires_live_check": True
|
||||
}
|
||||
else:
|
||||
test_result = self._test_multi_reference()
|
||||
|
||||
self._api_capabilities = test_result
|
||||
return test_result
|
||||
@ -94,6 +113,15 @@ class MultiReferenceVideoAdapter:
|
||||
实际测试 API 是否支持多参考图
|
||||
方法: 提交一个测试请求,包含2张参考图,观察响应
|
||||
"""
|
||||
if not self.api_key:
|
||||
return {
|
||||
"multi_reference_supported": False,
|
||||
"max_references": 1,
|
||||
"supported_types": ["image_url"],
|
||||
"details": "未找到 JIMENG_API_KEY,无法执行 live check",
|
||||
"status": "missing_api_key"
|
||||
}
|
||||
|
||||
# 构造一个最小测试请求
|
||||
test_prompt = "test multi-reference support"
|
||||
|
||||
@ -216,7 +244,16 @@ class MultiReferenceVideoAdapter:
|
||||
print(f" 2. 后期合成牌匾/场景 (平面追踪 + 贴图)")
|
||||
print(f" 3. 或使用可灵生成角色,Seedance 生成场景,后期合成")
|
||||
|
||||
# 回退: 只用第一张参考图
|
||||
if not self.allow_single_reference_fallback:
|
||||
return {
|
||||
"success": False,
|
||||
"method": "not-run",
|
||||
"error": "多参考图能力未确认或不支持,未自动回退单参考图以避免误导一致性测试",
|
||||
"capabilities": capabilities,
|
||||
"next_step": "使用 --live-check 验证多参考图,或显式传入 --allow-single-reference-fallback 做调试占位"
|
||||
}
|
||||
|
||||
# 回退: 只用第一张参考图。必须显式开启,避免误导牌匾/场景一致性判断。
|
||||
warning = "API不支持多参考图,已回退到单参考图模式。牌匾/场景一致性需要后期合成。"
|
||||
print(f"\n 🔄 回退: 使用第一张参考图生成...")
|
||||
|
||||
@ -336,6 +373,10 @@ class MultiReferenceVideoAdapter:
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="MULTI-REFERENCE-VIDEO-ADAPTER")
|
||||
parser.add_argument("--check-api", action="store_true", help="检查API多参考图支持")
|
||||
parser.add_argument("--live-check", action="store_true",
|
||||
help="执行真实 API 探测请求,可能产生费用")
|
||||
parser.add_argument("--allow-single-reference-fallback", action="store_true",
|
||||
help="多参考图不可用时允许单参考图调试占位")
|
||||
parser.add_argument("--prompt", type=str, help="提示词")
|
||||
parser.add_argument("--references", type=str, nargs="+", help="参考图路径列表")
|
||||
parser.add_argument("--output", type=str, help="输出路径")
|
||||
@ -344,10 +385,13 @@ def main():
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
adapter = MultiReferenceVideoAdapter()
|
||||
adapter = MultiReferenceVideoAdapter(
|
||||
live_capability_check=args.live_check,
|
||||
allow_single_reference_fallback=args.allow_single_reference_fallback
|
||||
)
|
||||
|
||||
if args.check_api:
|
||||
capabilities = adapter.check_api_capabilities()
|
||||
capabilities = adapter.check_api_capabilities(live=args.live_check)
|
||||
print(f"\n📊 API 能力报告:")
|
||||
print(f" 多参考图支持: {capabilities['multi_reference_supported']}")
|
||||
print(f" 最大参考图数: {capabilities['max_references']}")
|
||||
|
||||
@ -585,7 +585,12 @@ def main():
|
||||
print(f"\n⚠️ QC 跳过 (依赖不可用)")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print(f"\n❌ QC 失败: {result.get('error', 'Unknown error')}")
|
||||
if result.get("error"):
|
||||
print(f"\n❌ QC 失败: {result['error']}")
|
||||
else:
|
||||
issues = result.get("issues") or []
|
||||
issue_text = ";".join(issues) if issues else "未通过阈值"
|
||||
print(f"\n❌ QC 未通过: {issue_text}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
|
||||
@ -20,17 +20,24 @@ import os
|
||||
import sys
|
||||
import json
|
||||
import argparse
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
PROJECT_ROOT = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(PROJECT_ROOT / "engines"))
|
||||
|
||||
# 导入现有的 TTS 引擎
|
||||
# 导入现有的 TTS 引擎。文件名是 tts-engine.py,不能用普通 import。
|
||||
tts_engine_path = PROJECT_ROOT / "engines" / "tts-engine.py"
|
||||
try:
|
||||
from tts_engine import generate_speech, generate_by_character, load_voice_config
|
||||
except ImportError:
|
||||
print("⚠️ 无法导入 tts-engine,将使用简化模式")
|
||||
spec = importlib.util.spec_from_file_location("tts_engine", tts_engine_path)
|
||||
tts_engine = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(tts_engine)
|
||||
generate_speech = tts_engine.generate_speech
|
||||
generate_by_character = tts_engine.generate_by_character
|
||||
load_voice_config = tts_engine.load_voice_config
|
||||
except Exception as exc:
|
||||
print(f"⚠️ 无法导入 tts-engine,将使用简化模式: {exc}")
|
||||
generate_speech = None
|
||||
generate_by_character = None
|
||||
|
||||
@ -270,7 +277,7 @@ class VoiceEmotionCompiler:
|
||||
|
||||
variants = [
|
||||
{"label": "基准", "params": base_params},
|
||||
{"label": "语速+10%", "params": {**base_params, "rate": f"+{int(base_params['rate'].strip('%+')) + 10}%"},
|
||||
{"label": "语速+10%", "params": {**base_params, "rate": f"+{int(base_params['rate'].strip('%+')) + 10}%"}},
|
||||
{"label": "音调+5Hz", "params": {**base_params, "pitch": f"+{int(base_params['pitch'].strip('Hz+')) + 5}Hz"}},
|
||||
{"label": "音量+10%", "params": {**base_params, "volume": f"+{int(base_params['volume'].strip('%+')) + 10}%"}},
|
||||
]
|
||||
|
||||
263
video-ai-system/experience/D144-8-MODULE-VERIFICATION.hdlp
Normal file
263
video-ai-system/experience/D144-8-MODULE-VERIFICATION.hdlp
Normal file
@ -0,0 +1,263 @@
|
||||
# D144 · 视频AI系统8模块验收记录
|
||||
|
||||
> HLDP://video-ai-system/experience/D144-8-MODULE-VERIFICATION
|
||||
> 日期: 2026-06-24
|
||||
> 入口: 腾讯AI提交的 8 大模块工程验收
|
||||
> 结论: 框架已进入仓库,但不能整体视为生产完成;需要按下方缺口继续补齐。
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
```
|
||||
overall_status: PARTIAL_PASS
|
||||
production_ready: false
|
||||
main_blockers:
|
||||
- 主角资产包生成未真正接通图片生成适配器
|
||||
- 口型同步依赖 Wav2Lip 未安装,不能解决“人物说台词”
|
||||
- 多参考图能力未做真实付费验证,默认不能用于一致性承诺
|
||||
- EP01 一键生产 CLI 仍读取占位台词,不满足“剧本怎么写就怎么拍”
|
||||
- 字幕标准工具链仍需独立开发,不在本次8模块内完成
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 模块验收
|
||||
|
||||
### 1. CHAR-HERO-DESIGN-PACKER
|
||||
|
||||
```
|
||||
status: NOT_READY
|
||||
syntax: PASS_AFTER_FIX
|
||||
production_ready: false
|
||||
```
|
||||
|
||||
发现:
|
||||
- 原代码直接导入不存在的 Python `image_api_adapter`,仓库当前只有 JS 图片适配器,启动即崩。
|
||||
- 原代码会在资产不完整时把 `manifest.hdlp` 写成 `APPROVED`,会污染后续角色锁定。
|
||||
- 原提示词带有未经剧本/资产锁确认的视觉默认值,例如白发、蓝眼、蓝袍、皮克斯风格,容易造成角色漂移。
|
||||
|
||||
已修:
|
||||
- 图片适配器未接通时不崩溃,明确提示未生成。
|
||||
- 只有 4/4 资产完整生成时才允许 `APPROVED`;否则为 `DRAFT_GENERATED`。
|
||||
- 默认风格改为 3D国风漫剧,并要求服装来自角色资产清单,不得自由改色。
|
||||
|
||||
后续:
|
||||
- 需要开发 Python 到现有 JS 图片适配器的桥接,或统一改成 Node CLI 调用。
|
||||
- 需要把“主角辨识度设计稿/正侧全身/表情表/色彩锁”作为独立资产包生成和人工确认流程。
|
||||
|
||||
---
|
||||
|
||||
### 2. CHARACTER-DISTINCTIVENESS-QC
|
||||
|
||||
```
|
||||
status: PARTIAL_PASS
|
||||
syntax: PASS
|
||||
report_output: PASS_AFTER_FIX
|
||||
production_ready: false
|
||||
```
|
||||
|
||||
实测:
|
||||
- 输入 `video-ai-system/assets/references/EP01-P0/ref-char003-subai-plus-tdz-plaque.png`
|
||||
- 输出 `/tmp/guanghulab-module-check/character-qc.json`
|
||||
- 结果: `overall_score: 6.35/10`, `verdict: FAIL`
|
||||
|
||||
说明:
|
||||
- 这个结论与编辑意见“主角太普通、像路人甲”一致。
|
||||
- 当前算法是基础 OpenCV 分数,只能做第一层粗筛,不能替代人眼/多模型视觉评审。
|
||||
|
||||
已修:
|
||||
- 增加 `--output`,允许质检报告落盘进入经验库。
|
||||
|
||||
后续:
|
||||
- 接入图像理解模型做“主角感/路人感/记忆点”语义评分。
|
||||
- 加入同镜头前后角色一致性对比,而不是只看单张图。
|
||||
|
||||
---
|
||||
|
||||
### 3. MULTI-REFERENCE-VIDEO-ADAPTER
|
||||
|
||||
```
|
||||
status: GUARDED_NOT_VERIFIED
|
||||
syntax: PASS
|
||||
production_ready: false
|
||||
```
|
||||
|
||||
发现:
|
||||
- 原 `--check-api` 会直接提交真实视频生成请求,可能产生费用。
|
||||
- 原代码只读仓库 `.env`,不读本机密钥文件。
|
||||
- 多参考图未确认时自动回退单参考图,会误导牌匾/角色/场景一致性判断。
|
||||
|
||||
已修:
|
||||
- 默认 `--check-api` 只做无付费预检。
|
||||
- 只有显式传入 `--live-check` 才会提交真实 API 探测请求。
|
||||
- 读取本机 `~/guanghulab-local-secrets/video-ai-system.env`。
|
||||
- 多参考图未确认时不自动回退;只有显式 `--allow-single-reference-fallback` 才允许调试占位。
|
||||
|
||||
后续:
|
||||
- 需要一次受控 live-check,确认 Seedance/豆包视频 API 是否真支持多参考图。
|
||||
- 如果不支持,必须走“角色视频 + 牌匾/场景后期合成”的工程路线。
|
||||
|
||||
---
|
||||
|
||||
### 4. VOICE-EMOTION-COMPILER
|
||||
|
||||
```
|
||||
status: PASS_FOR_TTS_PARAM_COMPILE
|
||||
syntax: PASS_AFTER_FIX
|
||||
production_ready: partial
|
||||
```
|
||||
|
||||
发现:
|
||||
- 原代码有语法错误。
|
||||
- 原代码用普通 import 导入 `tts-engine.py`,文件名含连字符,无法导入。
|
||||
|
||||
已修并实测:
|
||||
- 修复语法错误。
|
||||
- 用动态导入接上 `tts-engine.py`。
|
||||
- 实测生成苏白情绪配音 MP3 成功: `/tmp/guanghulab-module-check/subai-emotion.mp3`
|
||||
|
||||
后续:
|
||||
- Edge-TTS 可做低成本测试,但商业成片建议继续 A/B 测豆包语音、火山语音或更适合角色对白的声音模型。
|
||||
- 需要按角色建立“台词情绪标签 -> 音色/语速/音高/停顿”表。
|
||||
|
||||
---
|
||||
|
||||
### 5. LIPSYNC-ADAPTER
|
||||
|
||||
```
|
||||
status: FAIL_DEPENDENCY_MISSING
|
||||
syntax: PASS_AFTER_FIX
|
||||
production_ready: false
|
||||
```
|
||||
|
||||
发现:
|
||||
- Wav2Lip 未安装。
|
||||
- 原代码在 Wav2Lip 不可用时复制原视频并返回成功,会把“没做口型”伪装为“口型完成”。
|
||||
|
||||
已修:
|
||||
- 默认无 Wav2Lip 时返回失败。
|
||||
- 只有显式传入 `--allow-fallback-copy` 才允许复制原视频,并标记为调试占位。
|
||||
|
||||
实测:
|
||||
- 当前返回失败: `Wav2Lip 不可用,未执行真实口型同步`
|
||||
|
||||
后续:
|
||||
- 安装 Wav2Lip 或改接可商用口型/视频驱动 API。
|
||||
- 需要口型 QC:音频峰值与嘴部开合是否同步,不能只看生成成功。
|
||||
|
||||
---
|
||||
|
||||
### 6. AUDIO-MIXER
|
||||
|
||||
```
|
||||
status: BASIC_PASS
|
||||
production_ready: partial
|
||||
```
|
||||
|
||||
实测:
|
||||
- 单音轨输出成功: `/tmp/guanghulab-module-check/mix-single.mp3`
|
||||
|
||||
缺口:
|
||||
- 当前对白压低 BGM 是简化处理,不是真正精细侧链压缩。
|
||||
- 需要对白优先、BGM 自动 ducking、响度标准、峰值保护、短视频平台响度目标。
|
||||
|
||||
后续:
|
||||
- 开发基于 FFmpeg sidechaincompress / loudnorm 的商业混音预设。
|
||||
- 输出混音 QC 报告:对白响度、BGM 响度、峰值、电平余量。
|
||||
|
||||
---
|
||||
|
||||
### 7. SHOT-QC-AUTOMATION
|
||||
|
||||
```
|
||||
status: PARTIAL_PASS
|
||||
syntax: PASS
|
||||
production_ready: false
|
||||
```
|
||||
|
||||
实测:
|
||||
- 输入移动硬盘样片: `TEST-SUBTITLE-007-RUN002-REFERENCE-DRAMA.mp4`
|
||||
- 输出 `/tmp/guanghulab-module-check/qc-after-fix.json`
|
||||
- 结果: 检测到横屏 `1112x834`,失败原因 `竖屏比例错误`
|
||||
|
||||
已修:
|
||||
- 原 CLI 失败时只显示 `Unknown error`;已改成输出真实失败项。
|
||||
- 依赖清单补充 `numpy`, `opencv-python`, `requests`。
|
||||
|
||||
缺口:
|
||||
- OCR 未安装,字幕/牌匾文字检查被跳过。
|
||||
- 现代物品检测仍是 TODO。
|
||||
- 换脸检查只是粗糙直方图,不足以判定角色一致性。
|
||||
|
||||
后续:
|
||||
- 安装/配置 OCR。
|
||||
- 接 YOLO 或视觉模型做现代物品、遮挡、文字、角色漂移检查。
|
||||
|
||||
---
|
||||
|
||||
### 8. EP01-SHOT03-PRODUCTION-CLI
|
||||
|
||||
```
|
||||
status: DRY_RUN_ONLY
|
||||
syntax: PASS
|
||||
production_ready: false
|
||||
```
|
||||
|
||||
实测:
|
||||
- `--dry-run` 可执行完整步骤列表。
|
||||
|
||||
重大问题:
|
||||
- 配置读取仍显示 `台词: 苏白原文台词`,不是剧本原文台词。
|
||||
- 这不满足用户要求:剧本怎么写,就怎么拍;不能用占位台词进入生产。
|
||||
|
||||
后续:
|
||||
- 必须接入剧本/分镜头源文件,逐字锁定台词。
|
||||
- 每个镜头需要输出“剧本文本 -> 提示词 -> 画面 -> 配音 -> 字幕”的可追溯链路。
|
||||
|
||||
---
|
||||
|
||||
## 本次工程修复清单
|
||||
|
||||
```
|
||||
fixed_files:
|
||||
- engines/voice-emotion-compiler.py
|
||||
- engines/char-hero-design-packer/char-hero-design-packer.py
|
||||
- engines/character-distinctiveness-qc/character-distinctiveness-qc.py
|
||||
- engines/lipsync-adapter.py
|
||||
- engines/multi-reference-video-adapter.py
|
||||
- engines/shot-qc-automation.py
|
||||
- requirements-video-ai.txt
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 下一步优先级
|
||||
|
||||
```
|
||||
P0:
|
||||
- 字幕商业标准渲染工具链
|
||||
- 真实口型同步方案
|
||||
- 剧本原文锁定与 EP01 CLI 台词读取修复
|
||||
- 主角资产包真实生成与人工确认
|
||||
|
||||
P1:
|
||||
- 多参考图 live-check
|
||||
- 角色一致性跨镜头 QC
|
||||
- BGM/对白混音 loudness + ducking
|
||||
|
||||
P2:
|
||||
- OCR/牌匾文字 QC
|
||||
- 现代物品检测
|
||||
- 成本账本与模型可用性检查
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 验收结论
|
||||
|
||||
```
|
||||
腾讯AI这次提交的8个模块,不能按“全部开发完成”验收。
|
||||
准确结论是: 工程骨架已建立,其中语音参数编译、基础混音、基础QC可继续用;
|
||||
主角资产生成、多参考视频、口型同步、EP01一键生产仍是生产阻塞项。
|
||||
```
|
||||
@ -1,7 +1,7 @@
|
||||
# 付费才能修仙 · 3D漫剧主线 · 项目状态
|
||||
|
||||
> HLDP://video-ai-system/memory/zai-fu-fei-xiu-xian/STATUS
|
||||
> 更新: D143 · 2026-06-23 · preview-002复盘·制片厂化重启
|
||||
> 更新: D144 · 2026-06-24 · 8模块验收·生产阻塞项确认
|
||||
> 铸渊 ICE-GL-ZY001
|
||||
> 国作登字-2026-A-00037559
|
||||
|
||||
@ -119,6 +119,7 @@ preview-003: 暂不生成。
|
||||
| 苏白TTS配音 | ✅ 工程测试通过 | `outputs/tests/TEST-TTS-001-SUBAI-RUN-001-REPORT.hdlp` |
|
||||
| 模型/成本路线 | ✅ 已建立 | `knowledge/D144-MODEL-COST-ROUTE.hdlp` |
|
||||
| 腾讯AI开发交接 | ✅ 已建立 | `plans/D144-TENCENT-AI-DEV-HANDOFF.hdlp` |
|
||||
| 腾讯AI 8模块代码 | 🟡 部分通过 | `experience/D144-8-MODULE-VERIFICATION.hdlp` · 框架已入仓,生产阻塞项仍在 |
|
||||
| 广告牌资产 | ✅ 草案 | `assets/props/PROP-FREE-AD-BOARD/manifest.hdlp` |
|
||||
| 广告牌文字贴图 | ✅ PNG已生成 | `assets/props/PROP-FREE-AD-BOARD/texture/free-ad-board-texture.png` |
|
||||
| 百宗会场景资产 | ✅ 草案 | `assets/envs/ENV-002-Baizonghui/manifest.hdlp` |
|
||||
@ -218,6 +219,11 @@ preview-003: 暂不生成。
|
||||
- [ ] 开发 reference-subtitle-analyzer / ASS渲染 / 字体管理 / 安全区QC 字幕工具链
|
||||
- [x] 重定性 RUN-002: 牌匾一致性失败,但百宗会人群氛围可用
|
||||
- [x] 测试苏白Edge-TTS工程配音 → `outputs/tests/TEST-TTS-001-SUBAI-RUN-001-REPORT.hdlp`
|
||||
- [x] 验收腾讯AI 8模块代码 → `experience/D144-8-MODULE-VERIFICATION.hdlp`
|
||||
- [ ] 接通主角资产包真实图片生成适配器,禁止未完整资产写入 APPROVED
|
||||
- [ ] 安装或替换真实口型同步方案,禁止复制视频冒充口型完成
|
||||
- [ ] 修复 EP01-SHOT03 CLI 台词读取,占位台词不得进入生产
|
||||
- [ ] 受控验证 Seedance/豆包视频多参考图能力,未验证前不得承诺跨镜一致
|
||||
- [x] 建立模型/成本路线 → `knowledge/D144-MODEL-COST-ROUTE.hdlp`
|
||||
- [x] 建立腾讯AI人格体开发交接清单 → `plans/D144-TENCENT-AI-DEV-HANDOFF.hdlp`
|
||||
- [ ] 生成E1-SHOT03专用苏白底片: 上方/身后预留牌匾空间
|
||||
|
||||
@ -2,3 +2,10 @@ edge-tts>=7.2.8
|
||||
pysrt>=1.1.2
|
||||
cairosvg>=2.9.0
|
||||
Pillow>=12.2.0
|
||||
numpy>=2.0.0
|
||||
opencv-python>=4.10.0
|
||||
requests>=2.32.0
|
||||
|
||||
# Optional production QC dependencies:
|
||||
# pytesseract>=0.3.13 # requires system tesseract binary
|
||||
# ultralytics>=8.3.0 # modern-item detection, not wired yet
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user