Files
SoundGame/tools/check_all_assets.py
T
xsl 31372615b2 feat(new8/batch2): 《深海回音》abyss +《雨夜专案》detective 全套交付(多agent流水线:策划→资产→开发→测试→真资产复跑)
- abyss:game.js 242 断言全绿(剧本A-F六路径/氧气门三形态/sonarGate仲裁/结算死窗/双轨看门狗/radio 8项修复继承);资产 148句+11key变体(电台滤波带内能量47-62%→69-80%)
- detective:game.js 265 断言真资产下全绿(矩阵不变式独立重算/指证全路径/评级12组注入/VERDICT死窗/二周目变体);真资产复跑回炉3处实现修复(无进展轨墙钟化/菜单暂停记账/停止播报真停声——mock时长掩盖的正确性bug)
- 资产:TTS 539条×2格式、Mureka 7首 $0.21(累计$1.08/$2.00)、SYN12+SA3 19件(QC抓出1例AAC过冲并修复)、封面×2、check_all_assets 10游戏全绿(abyss 298/298、detective 279/279)
- 平台接线:大厅双卡片、BUCKETS 增 abyss/detective 桶(H4 断言更新 10 游戏)、voice_text.json 10游戏991句
- 回归:14 套件全绿(10 playtest+guide+engine-voice+engine-input+api-smoke 12/12)
- 设计偏差登记:abyss 7条/detective 9条(全为设计内部冲突按承诺清单/归因真话律取优)
2026-09-10 01:40:31 +08:00

108 lines
4.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""全项目音频资产终检(R5)。
检查 8 个游戏 web/<game>/audio/:
1. 每个 .ogg 都有同名 .m4a(反之亦然)
2. voice/male 与 voice/female 的 id 集合一致(storyline/rpg 的平铺 NPC/角色句除外)
3. 键盘变体目录 voice/<gender>/key/(P1 路线 a):
male/female 两性别 key 集合一致;key 集合 ⊆ 主(性别目录)集合——变体只
覆盖含操作词的句子,其余 id 由引擎回退主资产
4. 无 0 字节文件;ogg/m4a 均可被 ffprobe 解码且时长 > 0
输出 Markdown 报告到 stdout(重定向进 docs/audio/final-asset-check.md)。
"""
import os
import subprocess
import sys
ROOT = "/home/xsl/SoundGame/web"
GAMES = ["storyline", "escape", "simon", "rhythm", "space", "rpg", "radio", "dungeon",
"abyss", "detective"]
# 平铺 voice 文件(角色/NPC,单音色,不做性别集合一致性要求)
FLAT_OK = {"storyline", "rpg", "radio", "abyss", "detective"}
def ids(d, ext):
if not os.path.isdir(d):
return set()
return {f[: -len(ext)] for f in os.listdir(d) if f.endswith(ext)}
def main():
problems = []
print("# 全项目音频资产终检(R5 建立,批次 1 起含 radio/dungeon,批次 2 起含 abyss/detective,2026-09-09)\n")
print("| 游戏 | ogg | m4a | 0字节 | 解码失败 | voice male/female id 一致 | key male/female 一致且 ⊆ 主集 |")
print("|---|---|---|---|---|---|---|")
for g in GAMES:
base = os.path.join(ROOT, g, "audio")
oggs, m4as, zero, bad = [], [], [], []
for dirpath, _dirs, files in os.walk(base):
for f in files:
p = os.path.join(dirpath, f)
if os.path.getsize(p) == 0:
zero.append(p)
if f.endswith(".ogg"):
oggs.append(p)
elif f.endswith(".m4a"):
m4as.append(p)
# 双格式配对
o_set = {p[:-4] for p in oggs}
m_set = {p[:-4] for p in m4as}
for p in sorted(o_set - m_set):
problems.append(f"{g}: 缺 m4a —— {p}.ogg")
for p in sorted(m_set - o_set):
problems.append(f"{g}: 缺 ogg —— {p}.m4a")
# 解码校验(抽查全量 ffprobe)
for p in oggs + m4as:
r = subprocess.run(["ffprobe", "-v", "error", "-show_entries", "format=duration",
"-of", "csv=p=0", p], capture_output=True, text=True)
try:
if float(r.stdout.strip()) <= 0:
bad.append(p)
except ValueError:
bad.append(p)
for p in zero:
problems.append(f"{g}: 0 字节 —— {p}")
for p in bad:
problems.append(f"{g}: 解码失败/时长为 0 —— {p}")
# voice 性别集合一致性
vm = ids(os.path.join(base, "voice", "male"), ".ogg")
vf = ids(os.path.join(base, "voice", "female"), ".ogg")
if vm == vf and vm:
pair = f"✅ 各 {len(vm)}"
else:
pair = f"❌ male={len(vm)} female={len(vf)}"
for i in sorted(vm - vf):
problems.append(f"{g}: voice 缺 female/{i}")
for i in sorted(vf - vm):
problems.append(f"{g}: voice 缺 male/{i}")
# 键盘变体目录(P1 路线 a):两性别 key 集合一致 + key ⊆ 主集合
km = ids(os.path.join(base, "voice", "male", "key"), ".ogg")
kf = ids(os.path.join(base, "voice", "female", "key"), ".ogg")
if km == kf and km <= vm:
kpair = f"✅ 各 {len(km)} ⊆ 主集" if km else "—(无变体目录)"
else:
kpair = f"❌ male={len(km)} female={len(kf)}"
for i in sorted(km - kf):
problems.append(f"{g}: key 变体缺 female/key/{i}")
for i in sorted(kf - km):
problems.append(f"{g}: key 变体缺 male/key/{i}")
for i in sorted((km | kf) - (vm | vf)):
problems.append(f"{g}: key 变体 {i} 不在主集合(key 必须 ⊆ 主集)")
flat = ids(os.path.join(base, "voice"), ".ogg")
flat_note = f"(平铺 {len(flat)} 个角色/NPC 句,豁免)" if g in FLAT_OK and flat else \
(f"(⚠️ 非豁免游戏存在平铺 {len(flat)} 个)" if flat and g not in FLAT_OK else "")
print(f"| {g} | {len(oggs)} | {len(m4as)} | {len(zero)} | {len(bad)} | {pair}{flat_note} | {kpair} |")
print()
if problems:
print(f"## 问题清单({len(problems)} 条)\n")
for p in problems:
print(f"- {p}")
return 1
print("## 结论\n\n全部通过:双格式配对完整、无 0 字节、全部可解码、voice 性别目录 id 集合一致(storyline/rpg/radio/abyss/detective 平铺角色句按约定豁免)、键盘变体目录两性别一致且 ⊆ 主集合。")
return 0
if __name__ == "__main__":
sys.exit(main())