#!/usr/bin/env python3 # -*- coding: utf-8 -*- """全项目音频资产终检(R5)。 检查 6 个游戏 web//audio/: 1. 每个 .ogg 都有同名 .m4a(反之亦然) 2. voice/male 与 voice/female 的 id 集合一致(storyline/rpg 的平铺 NPC/角色句除外) 3. 无 0 字节文件;ogg/m4a 均可被 ffprobe 解码且时长 > 0 输出 Markdown 报告到 stdout(重定向进 docs/audio/final-asset-check.md)。 """ import os import subprocess import sys ROOT = "/home/ubuntu/SoundGame/web" GAMES = ["storyline", "escape", "simon", "rhythm", "space", "rpg"] # 平铺 voice 文件(角色/NPC,单音色,不做性别集合一致性要求) FLAT_OK = {"storyline", "rpg"} 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,2026-08-29)\n") print("| 游戏 | ogg | m4a | 0字节 | 解码失败 | voice male/female id 一致 |") 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}") 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} |") 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 平铺角色句按约定豁免)。") return 0 if __name__ == "__main__": sys.exit(main())