- 移除 bd/mg 两平台及磁盘残留产物;清理 api-test 死脚本、musicSdk/message+options 死模块、infSign vendor - musicSdk/ 改名 sdk/;src/common、src/auth、src/apis 整体移入 sdk/,依赖方向定为 src → sdk 严格单向 - src/ 手写 JS 全量转 .ts,删除 JS 类型垫片 modules.d.ts;源码 import 统一 .ts 后缀 - Node 原生类型剥离直接运行 .ts(engines >=22.18),取消 tsc 发射产物,tsc 仅类型检查(pnpm typecheck) - 删除 kwdes 备用实现与 src/index.ts 转发垫片;测试移至 test/smoke-test.ts - common/request.ts 补显式导出类型;修复 set-cookie 单值形态、ApiError 类字段等隐患 验证:tsc 零错误,smoke-test 41/41
67 lines
2.2 KiB
TypeScript
67 lines
2.2 KiB
TypeScript
// 歌词解码(原主进程 IPC 处理器的纯 JS 实现)
|
||
// - handle_kw_decode_lyric: 酷我歌词(inflate + yeelion XOR + gb18030)
|
||
// 算法来源: kw 桌面端歌词解码实现
|
||
// - handle_tx_decode_lyric: QQ音乐歌词(qrc 解码:hex → XOR 偏移 → deflate)
|
||
// qrc 算法来源: 开源 qrc 解码器(ZhuYiQing/qrcDecode 纯 JS 版)
|
||
|
||
import zlib from 'node:zlib'
|
||
import iconv from 'iconv-lite'
|
||
|
||
// ==================== 酷我 ====================
|
||
|
||
const handleInflate = (data: Buffer): Promise<Buffer> => new Promise((resolve, reject) => {
|
||
zlib.inflate(data, (err, result) => {
|
||
if (err) return reject(err)
|
||
resolve(result)
|
||
})
|
||
})
|
||
|
||
const buf_key = Buffer.from('yeelion')
|
||
const buf_key_len = buf_key.length
|
||
|
||
const decodeKwLyric = async(buf, isGetLyricx) => {
|
||
if (buf.toString('utf8', 0, 10) != 'tp=content') return ''
|
||
const lrcData = await handleInflate(buf.subarray(buf.indexOf('\r\n\r\n') + 4))
|
||
|
||
if (!isGetLyricx) return iconv.decode(lrcData, 'gb18030')
|
||
|
||
const buf_str = Buffer.from(lrcData.toString(), 'base64')
|
||
const buf_str_len = buf_str.length
|
||
const output = new Uint8Array(buf_str_len)
|
||
let i = 0
|
||
while (i < buf_str_len) {
|
||
let j = 0
|
||
while (j < buf_key_len && i < buf_str_len) {
|
||
output[i] = buf_str[i] ^ buf_key[j]
|
||
i++
|
||
j++
|
||
}
|
||
}
|
||
|
||
return iconv.decode(Buffer.from(output), 'gb18030')
|
||
}
|
||
|
||
/**
|
||
* 酷我歌词解码
|
||
* @param {{ lrcBase64: string, isGetLyricx: boolean }} params
|
||
* @returns {Promise<string>} 解码后歌词的 base64
|
||
*/
|
||
export const handleKwDecodeLyric = async({ lrcBase64, isGetLyricx }) => {
|
||
const lrc = await decodeKwLyric(Buffer.from(lrcBase64, 'base64'), isGetLyricx)
|
||
return Buffer.from(lrc).toString('base64')
|
||
}
|
||
|
||
// ==================== QQ音乐(qrc) ====================
|
||
|
||
import { decodeQrc } from './qrcDecode.ts'
|
||
|
||
/**
|
||
* QQ音乐歌词解码
|
||
* @param {{ lrc: string, tlrc: string, rlrc: string }} params 均为十六进制字符串
|
||
* @returns {Promise<{lyric: string, tlyric: string, rlyric: string}>}
|
||
*/
|
||
export const handleTxDecodeLyric = async({ lrc, tlrc, rlrc }) => {
|
||
const [lyric, tlyric, rlyric] = await Promise.all([decodeQrc(lrc), decodeQrc(tlrc), decodeQrc(rlrc)])
|
||
return { lyric, tlyric, rlyric }
|
||
}
|