Files
MusicServer/sdk/qs/crypto/sign.ts
T
snowy-yang 0f9cff2a08 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 直启
2026-10-01 22:43:49 +08:00

57 lines
1.7 KiB
TypeScript

import { hash } from "./index.ts";
import { getArgus } from "./argus.ts";
import { getLadon } from "./ladon.ts";
import { defaultUnidbgConfig, type UnidbgConfig } from "./unidbgconfig.ts";
export interface UnidbgHeaders extends Record<string, string> {
"x-argus": string;
"x-ladon": string;
"x-khronos": string;
}
export default async function generateHeaders(
rawQuery: string,
xssStub = "",
timestamp: number = Math.floor(Date.now() / 1000),
config: UnidbgConfig = defaultUnidbgConfig,
): Promise<UnidbgHeaders> {
const [argus, ladon] = await Promise.all([
getArgus(rawQuery, xssStub, timestamp, config),
getLadon(timestamp, config),
]);
return {
"x-argus": argus,
"x-ladon": ladon,
"x-khronos": String(timestamp),
};
}
export async function signRequest(
url: string,
body?: ArrayBuffer | Uint8Array<ArrayBuffer> | string,
config: UnidbgConfig = defaultUnidbgConfig,
): Promise<Record<string, string>> {
const rawQuery = new URL(url).search.replace(/^\?/, "");
const hasBody = typeof body === "string" ? body.length > 0 : (body?.byteLength ?? 0) > 0;
const xssStub = hasBody ? await hash.md5(body!) : "";
const now = Date.now();
const headers: Record<string, string> = await generateHeaders(
rawQuery,
xssStub,
Math.floor(now / 1000),
config,
);
headers["x-ss-req-ticket"] = String(now);
if (hasBody) {
headers["X-SS-STUB"] = xssStub;
}
return headers;
}
export { getArgus } from "./argus.ts";
export { getLadon } from "./ladon.ts";
export { defaultUnidbgConfig, type UnidbgConfig } from "./unidbgconfig.ts";