- 新增 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 文档
43 lines
1.4 KiB
Python
43 lines
1.4 KiB
Python
#!/usr/bin/env python3
|
|
"""资产清单校验:game.js 引用的每个音频 id 都必须有对应 .ogg 和 .m4a 文件。"""
|
|
import os
|
|
import re
|
|
import sys
|
|
|
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
SRC = open(os.path.join(HERE, "game.js"), encoding="utf-8").read()
|
|
|
|
# 静态字面量:'sfx/xxx' 'voice/xxx' 'bgm/xxx'
|
|
ids = set(re.findall(r"['\"]((?:bgm|voice|sfx)/[A-Za-z0-9_]+)['\"]", SRC))
|
|
# 动态拼接:'voice/num' + t -> num0..num9(前缀本身不是完整 id,剔除)
|
|
if re.search(r"['\"]voice/num['\"]\s*\+", SRC):
|
|
ids.discard("voice/num")
|
|
ids.update(f"voice/num{i}" for i in range(10))
|
|
|
|
missing = []
|
|
for aid in sorted(ids):
|
|
for ext in (".ogg", ".m4a"):
|
|
p = os.path.join(HERE, "audio", aid + ext)
|
|
if not os.path.isfile(p) or os.path.getsize(p) < 200:
|
|
missing.append(p)
|
|
|
|
# 反向检查:磁盘上的音频文件都应被引用(避免孤儿资产)
|
|
orphans = []
|
|
for layer in ("bgm", "voice", "sfx"):
|
|
d = os.path.join(HERE, "audio", layer)
|
|
for f in sorted(os.listdir(d)):
|
|
aid = f"{layer}/{os.path.splitext(f)[0]}"
|
|
if aid not in ids and aid not in orphans:
|
|
orphans.append(aid)
|
|
|
|
print(f"引用音频 id 数: {len(ids)}")
|
|
if missing:
|
|
print("缺失文件:")
|
|
print("\n".join(missing))
|
|
if orphans:
|
|
print("未被引用的资产:")
|
|
print("\n".join(orphans))
|
|
if missing or orphans:
|
|
sys.exit(1)
|
|
print("OK:所有引用 id 均有 .ogg + .m4a,且无孤儿资产")
|