Files

53 lines
2.6 KiB
TypeScript
Raw Permalink 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.
// 喜马拉雅 URL 获取(PC 桌面端 baseInfo 通道)
// 链路:makeXmSign(hdaa 上报) → baseInfo device=win(带 PC 设备 Cookie) → playUrlList
// → decodeWinUrl(AES-ECB, 客户端 4.0.14 asar 密钥) → a.xmcdn.com 带签名直链
// 实测(2026-10-02)匿名免费曲目 HTTP 200 audio/mp4 可播;付费/VIP 曲 ret=1001(需登录,暂未接)
// 档位命名如 M4A_64/MP3_64/M4A_24/MP3_32/AAC_24(音乐内容另有 MP3_128/M4A_128);
// 喜马拉雅以有声内容为主,码率低于音乐平台,音质映射到 '64k'/'128k' 桶
// 统一接口约定:apis('xm').getMusicUrl(songInfo, type) → { promise, cancelHttp }
// promise resolve → { type, url }(type 为实际命中档位,可能低于请求值)
import { getTrackInfo } from '../xm/musicInfo.ts'
import { decodeWinUrl } from '../xm/decoder.ts'
// 优先 M4A(同码率体积更小),128 档优先 MP3_128
const QUALITY_PREFERENCE: Record<string, string[]> = {
'128k': ['MP3_128', 'M4A_128', 'M4A_64', 'MP3_64', 'M4A_24', 'MP3_32', 'AAC_24'],
'64k': ['M4A_64', 'MP3_64', 'M4A_24', 'MP3_32', 'AAC_24'],
}
export const supportQualitys = ['64k', '128k']
const bitrateOf = (type: string): number => Number(/(\d+)/.exec(type)?.[1] ?? 0) || 0
const bucketOf = (bitrate: number): string => (bitrate >= 128 ? '128k' : '64k')
export const getMusicUrl = (songInfo, type) => {
const requestObj = { cancelHttp() {}, promise: null as any }
const trackId = String(songInfo.songmid ?? '')
requestObj.promise = (async() => {
const trackInfo = await getTrackInfo(trackId)
const playList: any[] = trackInfo.playUrlList ?? []
if (!playList.length) throw new Error('无可用播放资源(付费/版权内容需登录)')
const preference = QUALITY_PREFERENCE[type] ?? QUALITY_PREFERENCE['128k']
let picked: { type: string, url: string } | null = null
for (const want of preference) {
const hit = playList.find(item => item?.type === want && item.url)
if (hit) {
picked = { type: hit.type, url: hit.url.startsWith('http') ? hit.url : decodeWinUrl(hit.url) }
break
}
}
if (!picked) {
// 兜底:未知档位命名时取码率最高的一条
const best = [...playList].filter(item => item?.url).sort((a, b) => bitrateOf(b.type) - bitrateOf(a.type))[0]
if (!best) throw new Error('无可用播放资源')
picked = { type: best.type, url: best.url.startsWith('http') ? best.url : decodeWinUrl(best.url) }
}
return { type: bucketOf(bitrateOf(picked.type)), url: picked.url }
})()
return requestObj
}
export default { getMusicUrl }