feat(qs): 汽水音乐平台接入——匿名搜索/试听、抖音扫码登录、解密流代理
- sdk/qs 平台模块(安卓参数指纹匿名搜索)+ sdk/apis/qs 取流: 匿名 30s 试听,登录态走签名 track_v2 + spade key AES-CTR 解密 - /login/qs/qr 抖音扫码登录(授权后 sessionid 解锁完整曲目) - /stream/qs/:id 解密流代理,/url 返回的相对地址按请求 host 补全 - 回归测试覆盖 kw 双通道(VIP 真音频/降级链)与 qs 试听;start.bat 去掉 SOURCE_ENABLED 直启
This commit is contained in:
1 parent
aa4f2eec67
commit
0f9cff2a08
26 files changed
+2172
-9
No files matched your search
+50
-2
@@ -128,7 +128,7 @@ if (!running) {
|
||||
}
|
||||
|
||||
// 搜索
|
||||
for (const source of ['kw', 'kg', 'tx', 'wy']) {
|
||||
for (const source of ['kw', 'kg', 'tx', 'wy', 'qs']) {
|
||||
await test(`搜索 ${source}`, async() => {
|
||||
const r = await get(`/search?source=${source}&name=${encodeURIComponent('稻香')}&limit=2`)
|
||||
if (r.code != 0) throw new Error(r.msg)
|
||||
@@ -145,10 +145,29 @@ await test('聚合搜索', async() => {
|
||||
})
|
||||
|
||||
// URL(用已知歌曲 id,路径参数风格)
|
||||
await test('URL kw 320k', async() => {
|
||||
// kw 匿名通道 2026-10-01 接入 mobi.s convert_url_with_sign 主通道后:
|
||||
// 免费歌全档位可用,VIP 歌也放行真音频(320k/flac 实测与 N_MINFO 元数据逐字节吻合),
|
||||
// web playUrl 仅作回退(匿名恒 128k)
|
||||
await test('URL kw 128k 免费歌', async() => {
|
||||
const r = await get('/url/kw/151826618?quality=128k')
|
||||
if (r.code != 0) throw new Error(r.msg)
|
||||
if (!r.data?.url) throw new Error('无 url')
|
||||
return r.data.url.slice(0, 60)
|
||||
})
|
||||
|
||||
await test('URL kw VIP 歌 320k(mobi 通道真音频)', async() => {
|
||||
const r = await get('/url/kw/28423286?quality=320k')
|
||||
if (r.code != 0) throw new Error(r.msg)
|
||||
if (!r.data?.url) throw new Error('无 url')
|
||||
// mobi 通道严格校验 bitrate==320 才放行,url 必然是 320k 档
|
||||
return r.data.url.slice(0, 60)
|
||||
})
|
||||
|
||||
await test('URL kw VIP 歌 flac 降级链', async() => {
|
||||
const r = await get('/url/kw/228908?quality=flac24bit')
|
||||
if (r.code != 0) throw new Error(r.msg)
|
||||
if (!r.data?.url) throw new Error('无 url')
|
||||
// 晴天无 hi-res 档,应逐级降级命中 2000k flac 而非报错
|
||||
return r.data.url.slice(0, 60)
|
||||
})
|
||||
|
||||
@@ -166,6 +185,14 @@ await test('URL tx 320k', async() => {
|
||||
return r.data.url.slice(0, 60)
|
||||
})
|
||||
|
||||
// qs 匿名只有 30 秒试听(7079108541549643812 = 陈粒《小半》,2026-10-01 实测可用)
|
||||
await test('URL qs 试听 128k', async() => {
|
||||
const r = await get('/url/qs/7079108541549643812?quality=128k')
|
||||
if (r.code != 0) throw new Error(r.msg)
|
||||
if (!r.data?.url) throw new Error('无 url')
|
||||
return r.data.url.slice(0, 60)
|
||||
})
|
||||
|
||||
// 歌词
|
||||
await test('歌词 kw', async() => {
|
||||
const r = await get('/lyric/kw/28423286')
|
||||
@@ -174,6 +201,13 @@ await test('歌词 kw', async() => {
|
||||
return `${r.data.lyric.slice(0, 30)}...`
|
||||
})
|
||||
|
||||
await test('歌词 qs', async() => {
|
||||
const r = await get('/lyric/qs/7079108541549643812')
|
||||
if (r.code != 0) throw new Error(r.msg)
|
||||
if (!r.data?.lyric) throw new Error('无歌词')
|
||||
return `${r.data.lyric.slice(0, 30)}...`
|
||||
})
|
||||
|
||||
// 封面
|
||||
await test('封面 kw', async() => {
|
||||
const r = await get('/pic/kw/28423286')
|
||||
@@ -182,6 +216,13 @@ await test('封面 kw', async() => {
|
||||
return r.data.url.slice(0, 60)
|
||||
})
|
||||
|
||||
await test('封面 qs', async() => {
|
||||
const r = await get('/pic/qs/7079108541549643812')
|
||||
if (r.code != 0) throw new Error(r.msg)
|
||||
if (!r.data?.url) throw new Error('无 url')
|
||||
return r.data.url.slice(0, 60)
|
||||
})
|
||||
|
||||
// 榜单 & 热搜
|
||||
for (const source of ['kw', 'kg', 'tx', 'wy']) {
|
||||
await test(`榜单 ${source}`, async() => {
|
||||
@@ -400,6 +441,13 @@ await test('扫码二维码创建 POST /login/wy/qr', async() => {
|
||||
return r.body.data.key.slice(0, 18) + '…'
|
||||
})
|
||||
|
||||
await test('扫码二维码创建 POST /login/qs/qr', async() => {
|
||||
const r = await postJson('/login/qs/qr', {})
|
||||
if (r.status !== 200 || r.body?.code !== 0) throw new Error(r.body?.msg ?? `HTTP ${r.status}`)
|
||||
if (!r.body.data?.token || !r.body.data?.qrimg?.startsWith('data:image/')) throw new Error('响应不完整')
|
||||
return `scanApp=${r.body.data.scanApp || '?'}`
|
||||
})
|
||||
|
||||
await test('kg 假 token 拒绝 POST /login/kg', async() => {
|
||||
const r = await postJson('/login/kg', { token: 'smoke_fake_token', userid: 0 })
|
||||
if (r.status !== 401) throw new Error(`期望 401,实际 ${r.status}`)
|
||||
|
||||
Reference in new issue
Block a user