- 删除旧 sdk/request.ts,全部 import 收敛到 sdk/common/request.ts(85 文件) - 注释死代码 630+ 行、裸 console.log 吞错 6 处、未使用符号 26 处清零 - bili 状态校验去重;utils DNS 预解析失败日志改 warn 带上下文 - CODE-AUDIT.md:第四节 P2 清理记录 + 第八节线索逐条复核结论(kw/kg/tx/wy parse 骨架相似实质各异,不抽公共层)
50 lines
2.7 KiB
TypeScript
50 lines
2.7 KiB
TypeScript
// JOOX URL 获取(新版 web 播放器通道 openjoox2,含 md5(secretAPIKey) 签名)
|
||
// 链路:GET cache.api.joox.com/openjoox2/v1/track/<songid>?...&secret=<md5>
|
||
// → play_url_list[0](无则 refrain_url)→ 带 vkey/hdnts 的 CDN 直链(ak-hk.stream.music.joox.com 等)
|
||
// 上游 Go 的 api.joox.com/web-fcgi-bin/web_get_songinfo 已 404(2026-10-02 实测),
|
||
// 故取流改用官网当前使用的 openjoox2 通道(签名细节见 sdk/joox/musicInfo.ts)。
|
||
// 地区/签名参数:country=hk & lang=zh_TW & fs=1(请求完整曲目)& im=0,登录 cookie 的
|
||
// wmid/session_key 会作为 uid/usk 参与签名(/login/joox 绑定后 VIP 曲目可取完整流)。
|
||
// 实测:游客态返回 128k MP3(免费完整曲目)或 VIP 曲目的 preview/refrain 片段;
|
||
// 未登录请求 VIP 曲目时 play_url_list 为试听片段——与上游行为一致(上游同样只能拿游客流)。
|
||
// 统一接口约定:apis('jx').getMusicUrl(songInfo, type) → { promise, cancelHttp }
|
||
// promise resolve → { type, url }(type 为实际命中档位,可能低于请求值)
|
||
|
||
import { httpFetch } from '../common/request.ts'
|
||
import { jooxHeaders, normalizeJooxID } from '../joox/util.ts'
|
||
import { buildTrackUrl } from '../joox/musicInfo.ts'
|
||
|
||
/** 游客通道实测仅 128k MP3;VIP 账号理论上更高,但需登录后验证,故不虚报 */
|
||
export const supportQualitys = ['128k']
|
||
|
||
export const getMusicUrl = (songInfo: any, _type?: string) => {
|
||
const requestObj: { promise: any, cancelHttp: () => void } = { promise: null, cancelHttp() {} }
|
||
let current: any = null
|
||
requestObj.cancelHttp = () => {
|
||
if (current?.cancelHttp) current.cancelHttp()
|
||
}
|
||
|
||
requestObj.promise = (async() => {
|
||
const songId = normalizeJooxID(songInfo?.songmid ?? songInfo?.id ?? songInfo?.songid ?? '')
|
||
if (!songId) throw new Error('joox 歌曲 id 无效')
|
||
|
||
current = httpFetch(buildTrackUrl(songId, { fullSong: true }), { headers: jooxHeaders(), timeout: 20000 })
|
||
const { statusCode, body } = await current.promise as any
|
||
if (statusCode != 200 || !body || typeof body !== 'object' || body.code) {
|
||
throw new Error(`joox 取流失败:${body?.msg || body?.message || `HTTP ${statusCode}`}`)
|
||
}
|
||
|
||
const urls: any[] = Array.isArray(body.play_url_list) ? body.play_url_list : []
|
||
const clean = (value: any) => String(value ?? '').replace(/&/g, '&')
|
||
let url = urls.map(clean).find(item => item.startsWith('http')) || ''
|
||
if (!url && clean(body.refrain_url).startsWith('http')) url = clean(body.refrain_url)
|
||
if (!url) throw new Error('无可用播放地址(VIP/版权限制或地区不可用)')
|
||
|
||
return { type: '128k', url }
|
||
})()
|
||
|
||
return requestObj
|
||
}
|
||
|
||
export default { getMusicUrl }
|