fix(bili): 取流 CDN 分级选址——backupUrl 按 host 分级探测,根治 PCDN 点歌卡死
playurl 的 baseUrl 大概率落在 PCDN(京东云无线宝 mcdn.bilivideo.cn、迅雷 mountaintoys 等),旧代码只取 baseUrl(backupUrl 0 处使用)且探测失败仍 原样返回坏地址,点歌端连 PCDN 挂死。现候选 = baseUrl+backupUrl 按 host 分级(upos/Akamai 正规 CDN > cn-* 等 bilivideo.com 系 > PCDN),探测改为 fetch 只读响应头即断开(不等 body,CDN 忽略 Range 回 200 全量时不再整文件 缓冲),单跳 8s / 总预算 20s / 至多 3 候选,全败回退最优候选(fail-soft); CDN 探测不再携带 Cookie(直链自带签名,避免登录态泄给第三方 PCDN 域名)。 getAudioUrl 返回 urls[],parse 改取 urls[0]。 新增 test/bili-cdn-test.ts(SDK 层 PCDN 硬断言,并入 pnpm test)与 smoke 「URL bili(CDN 分级选址)」软断言项。
This commit is contained in:
1 parent
e1357dd412
commit
cec63f864e
5 files changed
+186
-41
No files matched your search
@@ -0,0 +1,57 @@
|
||||
// bili 取流 CDN 分级选址回归 —— SDK 层直调(不依赖服务器实例,含真实 B 站上游请求)
|
||||
// 运行:node test/bili-cdn-test.ts(已并入 pnpm test)
|
||||
//
|
||||
// 覆盖:getMusicUrl 返回地址不落在 PCDN(mcdn 无线宝/mountaintoys 迅雷/szbdyd/nexusedgeio),
|
||||
// parse 路径填充的 url 同样校验。
|
||||
// 判定:命中 PCDN 即失败——fail-soft 兜底意味着 upos/cn 候选 20s 探测全败,是真实的选址退化信号
|
||||
// (smoke 的「URL bili」项走 HTTP 层、只软警告;本文件是 SDK 层的硬断言)。
|
||||
|
||||
import biliApi from '../sdk/apis/bili.ts'
|
||||
import { parseSong } from '../sdk/bili/parse.ts'
|
||||
|
||||
/** 与 sdk/apis/bili.ts 的 hostRank 同构:0=正规 CDN 1=B站系 2=PCDN */
|
||||
const hostRank = (url: string): number => {
|
||||
let host = ''
|
||||
try { host = new URL(url).hostname.toLowerCase() } catch { return 2 }
|
||||
if (host.endsWith('.akamaized.net')) return 0
|
||||
if (host.endsWith('.bilivideo.com')) return host.startsWith('upos-') ? 0 : 1
|
||||
return 2
|
||||
}
|
||||
const LABEL = ['正规CDN', 'B站系', 'PCDN(坏)']
|
||||
|
||||
const results: { name: string; ok: boolean; detail?: any }[] = []
|
||||
const test = async(name: string, fn: () => Promise<any>) => {
|
||||
try {
|
||||
const result = await fn()
|
||||
results.push({ name, ok: true, detail: result })
|
||||
console.log(`✅ ${name}${result ? `: ${result}` : ''}`)
|
||||
} catch (err: any) {
|
||||
results.push({ name, ok: false, detail: err.message })
|
||||
console.log(`❌ ${name}: ${err.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
// 固定样本(2026-10-04 实测均有多分P候选与 upos 备源);换样本时优先选多分P、热度中等的稿件
|
||||
const CASES = ['BV1BZbSzZEGT', 'BV1GJ411x7h7', 'BV1d4411N7zD', 'BV1EC41147U1', 'BV19F5k6VEEp']
|
||||
|
||||
for (const bvid of CASES) {
|
||||
await test(`取流 ${bvid} 落正规 CDN`, async() => {
|
||||
const { promise } = biliApi.getMusicUrl({ songmid: bvid }, '128k')
|
||||
const res = await promise
|
||||
const rank = hostRank(res.url)
|
||||
if (rank === 2) throw new Error(`命中 PCDN:${new URL(res.url).host}(backupUrl 探测疑似全败)`)
|
||||
return `${LABEL[rank]} ${new URL(res.url).host}`
|
||||
})
|
||||
}
|
||||
|
||||
await test('parse 填充 url 落正规 CDN', async() => {
|
||||
const song = await parseSong('https://www.bilibili.com/video/BV1GJ411x7h7')
|
||||
if (!song.url) throw new Error('parse 未填充 url')
|
||||
const rank = hostRank(String(song.url))
|
||||
if (rank === 2) throw new Error(`命中 PCDN:${new URL(String(song.url)).host}`)
|
||||
return `${LABEL[rank]} ${new URL(String(song.url)).host}`
|
||||
})
|
||||
|
||||
const failed = results.filter(r => !r.ok).length
|
||||
console.log(`\n===== 结果: ${results.length - failed}/${results.length} 通过 =====`)
|
||||
process.exit(failed ? 1 : 0)
|
||||
Reference in new issue
Block a user