Repository navigation
v0.29.0
ToneCore v0.29.0
修复歌词「提前跑完」:在线歌词改为按音频真实时长挑版本;顺带补齐曲库时长。
修复:歌词「提前跑完」
这个问题从 v0.26.0 就悬着,当时只测出了现象、没敢改。这次把根因链补齐了。
根因
- 在线歌词按歌名去搜,候选里混着翻唱 / 剪辑版;
- 旧逻辑是「遍历候选,取第一个有歌词的」—— 极易命中翻唱版;
- 翻唱版的时间轴与本地音频天然对不上。
实测数据(末行时间 ÷ 真实时长):
| 来源 | 曲目 | 比值 | 判定 |
|---|---|---|---|
| online | 黯然销魂 | 0.83 | 提前跑完 |
| online | 思念漫过海岸线 | 0.83 | 提前跑完 |
| lrc-file / embedded | 天堂岛之歌、要不然我们就这样一万年 | 0.91~0.94 | 正常 |
| online | 悬溺、三生三世 | 0.97~1.00 | 正常 |
跑偏的全部来自 online,这也解释了「只有个别歌正确」——
那些歌恰好命中了原版。
修法
fetchLyrics() 新增 targetDuration 参数:
- 给了时长 → 收集全部候选的歌词,按「末行时间与音频长度的偏差」排序,取最优版本,
而不是第一个碰巧有歌词的; - 偏差超过 25%(
ratio < 0.75或> 1.35)→ 判定这批候选里没有匹配版本,宁可不用,也不把明显错位的歌词放上去; - 不传时长 → 退化为旧行为,兼容其它调用方。
LyricsService.probeDuration():用 music-metadata 读本地音频真实时长,
在走在线兜底前先取到,传给 fetchLyrics。
为什么不对本地来源做时长校验
同名 .lrc 与音频内嵌标签本就来自本地文件,时间轴应当可信。
给它们加校验只会让本可直接用的歌词被误弃 —— 所以校验只作用于在线兜底那一层。
变更:补齐曲库时长
上面那个修复需要一个前提:得知道音频的真实时长。而实测发现曲库 duration 全是 NULL。
原因是扫描入库时只顾着快速建索引,duration 直接写了 NULL。后果有两个:
- 界面时长一直显示
--:--; - 歌词校验失去了参照(所以必须先修这个)。
新增 Library.backfillDurations(),在扫描后回填真实时长。要点:
- 只处理
duration IS NULL的行; - 解析失败保持 NULL,下轮扫描再试 —— 不写 0,否则 0 会被当成有效值而永远不再重试。
验证
npm run build ✅ web + server 均通过
npm test ✅ 15 项全过
需要你实测确认
这个修复依赖真实网络候选,本地没法自动断言,请在界面上验一首以前跑偏的歌(比如「黯然销魂」):
- 播放该曲 → 歌词应当跟着当前句走
- 若仍然错位,先看「歌词校准」里的偏移值是不是合理
如果有个别歌反而从正确变成没有歌词,说明这批候选里确实没有匹配版本(此时会返回空),
属于预期内的保守行为 —— 把歌名告诉我,我单独查。
待办
- 酷我(kw)在线搜索封面:该平台不返回封面地址
- 音源状态显示口径
- 锁屏不显示动态歌词:Media Session API 不提供歌词接口,属浏览器能力边界,非 bug