Files
SoundGame/tools/check_all_assets.py
T
SoundGame Dev cc0f8c008d HTML5 重写:6 款游戏 + 用户系统 + 全局设置 + 5 轮迭代重构
- 新增 web/ 纯音频 HTML5 平台:共享引擎(手势/WebAudio 三层/设置/震动)、
  Node 零依赖服务器(静态 + 注册登录 + 进度存档 API)
- 游戏:剑与声(RPG 新作)、声命线、暗室(重设计)、音序回响(重设计)、
  节拍行者(跟随领奏重构)、盲航(同向闪避重构);声东击西删除
- 全局功能:男声/女声双套 TTS 资产、三档语速、BGM 音量三档、
  震动反馈语言(10 种震型)、服务器进度同步
- 资产:899 ogg + 899 m4a(Qwen3-TTS 双性别语音、Stable Audio 3 音效、
  Mureka BGM 4+3 首),终检零缺失
- 文档:docs/design/ 13 篇设计审查文档、docs/audio/ 资产管线、
  web/server/README.md API 文档
2026-08-29 21:55:44 +08:00

91 lines
3.6 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)。
检查 6 个游戏 web/<game>/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())