Files
SpineWallpaper/.scratch/wallpaper-optimization/spec.md
T
Shuery 0ca78ed117 feat: 音频改回无损 FLAC(用户决定:壁纸在本地,不计体积)
撤销 issue 02 里的音频转码,把两条 FLAC 放回 dist。

- 恢复方式是**字节级还原**而非重新编码:masters/ 里两份文件的 git 对象哈希与基线
  `d8c7e49:dist/assets/audios/*.flac` 完全一致,即与已发布的 v2 是同一份文件。
  因此本次提交不新增任何 blob 存储——这两份内容早已在仓库历史里。
- `preset.js` 的音源指向改回 `.flac`。`pv37.mp3` 保持原样:它**没有无损源**,
  把有损数据转封装成 FLAC 只会涨体积,而一个 bit 的信息都不会回来。
- 体积基准由「≤35 MB」改为「不超过已发布的 v2(104.98 MB)」,实际 **96.94 MB**,
  比 v2 还小(图片部分的无损 WebP 收益仍在),不引入新的上传或订阅风险。

顺带修掉一个自己引入的 bug:改 `preset.js` 时漏写了文件名里的 `「」`,
浏览器请求 404 → `<audio>` 在 **64 ms** 内报 `MediaError.code = 4`(SRC_NOT_SUPPORTED),
看上去像"CEF 不支持 FLAC",实际是路径不存在。判定方法记进 ADR 0004:
把同一份字节交给 `decodeAudioData`,能解出正确时长就说明解码器没问题,问题在传输或路径。

验证:
- `test-props`:CEF 支持 audio/flac、无解码错误、readyState=4、时长 205.1 s,14 项音源断言全过。
- `test-acceptance` 新增第 ⑤ 节音频格式不变式:xilian 两个音源全为 flac、无 `.ogg`/`.opus`
  残留、`preset.js` 引用的每个音源文件都存在;三种比例 0 报错;预设往返不丢音频设置。
- 分带能量实测:FLAC 高频平滑延伸到 22 kHz(20–22 kHz 处 −50 dB),无有损断崖;
  与 Opus 版逐带相差 0.0–0.3 dB。即这是一个由偏好驱动、而非由测量差异驱动的决定。
2026-09-19 20:36:58 +08:00

42 lines
2.9 KiB
Markdown
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.
# 优化已发布的网页壁纸(workshopid 3604974793)
在**不重写播放器**的前提下,就地优化现有 `dist/` 作品:资源分层、体积、健壮性、比例适配、属性扩展,最后重新打包发布 v3。
## 背景与约束(已确认)
- 原样入库的基线是 commit `d8c7e49`(105.1 MB),期间不做"顺手重构"。
- 已发布作品,`project.json` 里 combo 选项的 `value`(`xilian` / `kv37`)**不可改**,否则老用户已保存的预设选择会失效。
- 音源与音量是**用户级覆盖**,切换预设不得清掉;**音频走无损**(用户决定:壁纸在本地,不计体积)。
- 图片转 WebP 只在 AB 对拍证明肉眼无差别后才做,否则保留 PNG。
- 适配目标:任何比例下主体完整、底部对齐、无空边。
- 验收:0 个死文件;总体积不超过已发布的 v2(104.98 MB,实际 96.94 MB);3 种比例干净;0 个 console 报错;预设往返不丢音源设置;连续 20 次切换无内存增长;xilian 两个音源为无损 FLAC 且路径可达。
## 决策记录
| 主题 | 决定 | 记录位置 |
| --- | --- | --- |
| 资源分层 | `assets/<游戏>/<壁纸>/{effects,images,audios}/`,每档壁纸自包含 | `docs/adr/0001-resource-layout.md` |
| 目录名 vs 键 | 目录名用显示名(含 `「」`),代码键用稳定 id | 同上 |
| 预设形式 | 每档壁纸一份 `preset.js`(ES module + `import.meta.url`) | `docs/adr/0002-preset-as-es-module.md` |
| 术语 | 一档壁纸(Wallpaper)= 用户可见选项;预设(Preset)= 它的配置实体 | `CONTEXT.md` |
| 音源作用域 | combo 列表为全局并集;切壁纸时若当前音源不属于新壁纸,回落"随预设" | 本 spec |
## 验证台(后续每一步都用它)
`tools/serve.mjs` 起本地服务并注入驱动(模拟 WE 下发用户属性、冻结动画相位、藏 canvas 取参考帧),
`tools/cdp.mjs` + `tools/shot.mjs` 走 CDP 真实时间截图,`tools/diff.mjs` / `compare.mjs` / `heatmap.mjs` 做像素差分,
`tools/check-paths.mjs` 静态校验预设里声明的每个路径是否存在。
判定标准:以"与基线逐像素完全相同的对数"和噪声底(约 0.1/255)为准;基线自身存在约 2.3/255 的双模态抖动,不能拿单次差异下结论。
## 步骤
| # | 步骤 | 状态 |
| --- | --- | --- |
| 01 | 资源按 游戏/壁纸 分层,预设拆进各自壁纸目录 | 完成(`09ff322`) |
| 02 | 图片无损 WebP(25 → 19.6 MB);音频一度转 Opus,后被用户否决改回无损 | 完成(音频见 issue 02 修订) |
| 03 | 健壮性:初始化状态机、死代码、用户级覆盖语义 | 完成(`4662856`) |
| 04 | 比例适配:立绘层取景跟随背景 cover 缩放 | 完成 |
| 05 | WE 属性扩展:音源 combo、fps 限制 | 完成 |
| 06 | 打包 v3 并重新发布 | 完成(上传为待人工执行) |