feat(platform/P1): 配对码跨设备 + 认领打磨 + 键盘语音双套资产(两 agent 并行 + 集成收口)
配对码与认领(B): - POST /api/pair/start|join:6 位码 10 分钟单次有效、仅存内存零泄露面、5 连错 per-IP 封锁 10 分钟;join=新设备档案按桶 LWW 并入老档案+token 改绑(复用 claim-merge);日志脱敏 - 大厅:生成配对码(speechSynthesis 逐位朗读+aria-live+重听)/从其他设备转入(one-time-code 输入框)/从本机移除/401 过期自愈引导/aria 补全/玩家名可听化 - api-smoke 12/12(配对全流程/频控/封锁/过期自愈/移除/digitsToCn)+ CDP 真浏览器 E2E 22/22 键盘语音双套资产(A,路线 a): - 164 变体句(8 游戏 grep 实测,超出调研预估 153:含轻触/点两下/按住变体)×2 性别=328 生成 656 文件 36.9 分钟;触屏主资产零字节改动(PCM 逐样本比对后还原) - 引擎 _candidates 三跳路由 key→gender→平铺(inputMode 跟随);缓存键 mode|gender|id;engine-input 新增 K9 路由断言(72 条) - check_all_assets 增 key 列(两性别一致且 ⊆ 主集)8 游戏全绿;voice_text.json 增 __key__ 表;live control.mjs key 会话对照键盘措辞验收(集成) - voice-ids 文档:8 游戏键盘变体小节由 KEY_LINES 程序化生成(与台词表逐字断言一致);补齐 storyline/escape/simon 三个历史缺失文档;design README 增 new8 索引 - mock 适配:playtest persona 加 maxTouchPoints=1 保持触屏链断言语义零改动 回归:12 套件全绿(8 playtest + guide 122 + engine-voice 19 + engine-input 72 + api-smoke 12/12) 同批入库:批次 2/3 已完稿设计文档(17-abyss/18-detective/19-cipher 全套,20-22 在途)
This commit is contained in:
@@ -5,7 +5,10 @@
|
||||
检查 8 个游戏 web/<game>/audio/:
|
||||
1. 每个 .ogg 都有同名 .m4a(反之亦然)
|
||||
2. voice/male 与 voice/female 的 id 集合一致(storyline/rpg 的平铺 NPC/角色句除外)
|
||||
3. 无 0 字节文件;ogg/m4a 均可被 ffprobe 解码且时长 > 0
|
||||
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
|
||||
@@ -27,8 +30,8 @@ def ids(d, ext):
|
||||
def main():
|
||||
problems = []
|
||||
print("# 全项目音频资产终检(R5 建立,批次 1 起含 radio/dungeon,2026-09-01)\n")
|
||||
print("| 游戏 | ogg | m4a | 0字节 | 解码失败 | voice male/female id 一致 |")
|
||||
print("|---|---|---|---|---|---|")
|
||||
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 = [], [], [], []
|
||||
@@ -72,17 +75,30 @@ def main():
|
||||
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} |")
|
||||
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 平铺角色句按约定豁免)。")
|
||||
print("## 结论\n\n全部通过:双格式配对完整、无 0 字节、全部可解码、voice 性别目录 id 集合一致(storyline/rpg/radio 平铺角色句按约定豁免)、键盘变体目录两性别一致且 ⊆ 主集合。")
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user