Skip to content

v0.4.0 — Background playback fix for overseas viewers

Choose a tag to compare

@realzza realzza released this 03 Aug 17:36
· 35 commits to main since this release

v0.4.0 fixes background playback for viewers outside mainland China. Switching to another tab stalled playback a few seconds later, most reproducibly on Safari.

The cause was the accelerator's own host classification. Bilibili's overseas mirrors — mirrorcosov, mirroraliov, mirrorhwov — were classified as slow and rewritten onto mainland CDNs. For viewers outside mainland China these mirrors are the nearest and fastest available hosts, so each rewrite substituted a transpacific path for a local one. The resulting buffer was too thin to survive routine background-tab throttling, and playback stopped. Measured from Seattle against the same signed segment: 240 ms / 59.5 Mbps on the overseas mirror, 1116 ms / 6.5 Mbps on the mainland host receiving the rewrite.

Four further paths to the same downgrade were found and closed.

What changed

  • Overseas mirrors are no longer rewritten. Classification now marks only PCDN, P2P and MCDN hosts as slow. An ov-named host served on a PCDN-range port is still detected and rewritten. Akamai handling is unchanged and remains inactive unless explicitly enabled.
  • The candidate pool spans both tiers. It previously held mainland hosts only, so a single stall relocated an overseas viewer onto a substantially slower server with no faster candidate to return to. Both tiers are now measured, and the measurement selects the target.
  • All candidates are probed. Probing stopped after the first six entries, which let pool order rather than measured speed determine the reachable set: a viewer whose fastest host ranked seventh could never be selected onto it.
  • Ranking uses measured throughput rather than response time. Response time is dominated by round-trip latency and varies by roughly an order of magnitude across consecutive samples of the same host, so a single outlying sample could pin a viewer to a slow server for the full six-hour cache lifetime. Throughput separates the two tiers reliably.
  • Stall recovery traverses the full ranked pool. It previously took the first entry other than the current host, so the top two alternated indefinitely; eight consecutive stalls reached two of eight servers, switching every five seconds.
  • Stall recovery can route off any server, including an overseas mirror. If a mirror that is normally the right choice stalls for you specifically, recovery moves you to the next-ranked server for 15 seconds instead of leaving you stranded on it.
  • A stall that begins in a background tab is re-checked on return. Backgrounding no longer triggers a server switch by itself, but a stall that outlives the tab switch is picked up when the tab becomes visible, where previously no further event would have surfaced it.
  • Diagnostics report when a cached ranking was measured. The field was empty on a cache hit, making a six-hour-old ranking indistinguishable from one never measured.
  • The ⚡ badge auto-hides on live pages. Live rooms run a different player, so the badge never entered its hidden state and sat on top of the chat column. Move the pointer to the bottom-right corner to bring it back. Thanks to @Will-Zhao0 for the fix.

Upgrade notes

Existing settings are preserved. The candidate pool is not user-editable and is replaced on first load; a host pinned under Use a fixed server is retained. Rankings cached by earlier versions are discarded and re-measured on next playback, as a latency-ordered ranking is not comparable to a throughput-ordered one.

Update the userscript or extension, then reload any open Bilibili tabs.

Unrelated to this release: on Chrome or Edge with a recent Tampermonkey, a missing ⚡ badge together with a "script hasn't run yet" warning means the browser is blocking userscript injection. Enable Allow user scripts in Tampermonkey's extension details. This is a Manifest V3 requirement; the README documents the steps.

Closes #26, which asked for the unconditional rewriting of mirrorcosov to be made optional, or applied only on a stall. Both are addressed: the rewrite is gone from normal operation, and stall recovery can still move off an overseas mirror when one genuinely stalls.

74 tests pass, up from 52, with a reproducible build matching the committed dist/. Each regression test was verified to fail against the code it replaces. The original reporter has confirmed the fix in production: 0 stalls, 0 recoveries, and all rewrites directed toward the overseas mirror.

Full change set: #23 and #29


更新内容(中文)

0.4.0 修复海外用户的后台播放问题:切换至其他标签页后,播放于数秒内中断,在 Safari 上复现最为稳定。

问题源于加速器自身的节点分类逻辑。B 站的海外镜像(mirrorcosov、mirroraliov、mirrorhwov)被判定为慢速节点并改写至境内 CDN。对海外用户而言,这些镜像即为就近且最快的可用节点,每次改写都将本地链路替换为跨太平洋链路。由此建立的缓冲区不足以支撑标签页转入后台后的常规降频,播放随即中断。西雅图实测(同一签名分片):海外镜像 240 ms / 59.5 Mbps,改写目标的境内节点 1116 ms / 6.5 Mbps。

排查过程中另发现四条导致同类降级的路径,一并修复。

本次改动

  • 不再改写海外镜像。 节点分类现仅将 PCDN、P2P 与 MCDN 判定为慢速节点。名称含 ov 但使用 PCDN 常见端口的节点仍会被识别并改写。Akamai 的处理逻辑未作调整,未显式开启时不予改写。
  • 候选池覆盖境内外两档。 此前仅含境内节点,海外用户一次卡顿即被切换至明显更慢的节点,且无更优候选可供回切。现两档节点均参与实测,由实测结果决定改写目标。
  • 全部候选参与探测。 此前仅探测前六项,使候选顺序而非实测速度决定了可选范围:最快节点若排在第七位,则永远无法被选中。
  • 排序依据实测吞吐,不再依据应答时间。 应答时间主要由往返延迟构成,同一节点连续采样间可相差约一个数量级,单次异常采样即可将用户锁定在慢速节点上,直至六小时缓存到期。吞吐量可稳定区分两档节点。
  • 卡顿恢复遍历完整排名列表。 此前取排名中第一个不同于当前节点的条目,导致前两位无限交替;连续八次卡顿仅覆盖八个节点中的两个,每五秒切换一次。
  • 卡顿恢复可切离任意节点,海外镜像也不例外。 若某个通常正确的镜像恰好对你出现卡顿,恢复会将你切换至下一顺位节点 15 秒,而不是把你留在原处。
  • 在后台开始的卡顿会在回到前台时重新检查。 切至后台本身不再触发切换,但跨越标签页切换仍未恢复的卡顿会在页面重新可见时被接管——此前这种情况不会再产生任何事件,也就无从恢复。
  • 诊断报告标注排名的实测时间。 此前缓存命中时该字段为空,六小时前的排名与从未探测无法区分。
  • 直播页的 ⚡ 图标自动隐藏。 直播间使用另一套播放器,图标始终无法进入隐藏状态,会一直压在弹幕栏上。将指针移至右下角即可重新唤出。感谢 @Will-Zhao0 提交修复。

升级说明

原有配置保持不变。候选池不开放编辑,将于首次加载时整体替换;「使用固定服务器」中指定的节点予以保留。旧版本缓存的排名将作废,并于下次播放时重新实测——按延迟排序的结果与按吞吐排序的结果不具可比性。

更新用户脚本或扩展后,刷新已打开的 B 站页面即可生效。

与本次发布无关的已知情况:在 Chrome / Edge 上使用较新版本的 Tampermonkey 时,若无 ⚡ 图标且提示「script hasn't run yet」,说明浏览器正在拦截用户脚本注入,需在 Tampermonkey 的扩展详情中开启「允许用户脚本」。此为 Manifest V3 的要求,具体步骤见 README。

本次发布关闭 #26。该 issue 希望对 mirrorcosov 的无条件改写改为可选,或仅在卡顿时执行——两项诉求均已满足:常规运行下该改写已移除,而当海外镜像确实出现卡顿时,卡顿恢复仍可将其切离。

已通过 74 项测试(此前 52 项),重新构建产物与仓库内 dist/ 一致。每项回归测试均验证过可在修改前的代码上失败。问题的最初报告者已在实际环境中确认修复:0 次卡顿、0 次恢复,全部改写均指向海外镜像。

完整改动见 #23 与 #29。