From ba62ae27af9f337adc8fbdbd48221ea1617360c4 Mon Sep 17 00:00:00 2001 From: lyswhut Date: Fri, 10 Apr 2026 09:11:29 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20tx=20=E5=81=B6=E7=8E=B0?= =?UTF-8?q?=E6=90=9C=E7=B4=A2=E5=A4=B1=E8=B4=A5=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/renderer/utils/musicSdk/tx/musicSearch.js | 120 +++++++++--------- .../utils/musicSdk/tx/utils/crypto.js | 28 ++++ src/renderer/utils/musicSdk/tx/utils/index.js | 16 +++ 3 files changed, 107 insertions(+), 57 deletions(-) create mode 100644 src/renderer/utils/musicSdk/tx/utils/crypto.js create mode 100644 src/renderer/utils/musicSdk/tx/utils/index.js diff --git a/src/renderer/utils/musicSdk/tx/musicSearch.js b/src/renderer/utils/musicSdk/tx/musicSearch.js index 58745d7a..3161524b 100644 --- a/src/renderer/utils/musicSdk/tx/musicSearch.js +++ b/src/renderer/utils/musicSdk/tx/musicSearch.js @@ -1,6 +1,6 @@ -import { httpFetch } from '../../request' import { formatPlayTime, sizeFormate } from '../../index' import { formatSingerName } from '../utils' +import { signRequest } from './utils' export default { limit: 50, @@ -10,71 +10,77 @@ export default { successCode: 0, musicSearch(str, page, limit, retryNum = 0) { if (retryNum > 5) return Promise.reject(new Error('搜索失败')) - // searchRequest = httpFetch(`https://c.y.qq.com/soso/fcgi-bin/client_search_cp?ct=24&qqmusic_ver=1298&new_json=1&remoteplace=sizer.yqq.song_next&searchid=49252838123499591&t=0&aggr=1&cr=1&catZhida=1&lossless=0&flag_qc=0&p=${page}&n=${limit}&w=${encodeURIComponent(str)}&loginUin=0&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq&needNewCode=0`) - // const searchRequest = httpFetch(`https://shc.y.qq.com/soso/fcgi-bin/client_search_cp?ct=24&qqmusic_ver=1298&remoteplace=txt.yqq.top&aggr=1&cr=1&catZhida=1&lossless=0&flag_qc=0&p=${page}&n=${limit}&w=${encodeURIComponent(str)}&cv=4747474&ct=24&format=json&inCharset=utf-8&outCharset=utf-8¬ice=0&platform=yqq.json&needNewCode=0&uin=0&hostUin=0&loginUin=0`) - const searchRequest = httpFetch('https://u.y.qq.com/cgi-bin/musicu.fcg', { - method: 'post', - headers: { - 'User-Agent': 'QQMusic 14090508(android 12)', + const searchRequest = signRequest({ + comm: { + ct: '11', + cv: '14090508', + v: '14090508', + tmeAppID: 'qqmusic', + phonetype: 'EBG-AN10', + deviceScore: '553.47', + devicelevel: '50', + newdevicelevel: '20', + rom: 'HuaWei/EMOTION/EmotionUI_14.2.0', + os_ver: '12', + OpenUDID: '0', + OpenUDID2: '0', + QIMEI36: '0', + udid: '0', + chid: '0', + aid: '0', + oaid: '0', + taid: '0', + tid: '0', + wid: '0', + uid: '0', + sid: '0', + modeSwitch: '6', + teenMode: '0', + ui_mode: '2', + nettype: '1020', + v4ip: '', }, - body: { - comm: { - ct: '11', - cv: '14090508', - v: '14090508', - tmeAppID: 'qqmusic', - phonetype: 'EBG-AN10', - deviceScore: '553.47', - devicelevel: '50', - newdevicelevel: '20', - rom: 'HuaWei/EMOTION/EmotionUI_14.2.0', - os_ver: '12', - OpenUDID: '0', - OpenUDID2: '0', - QIMEI36: '0', - udid: '0', - chid: '0', - aid: '0', - oaid: '0', - taid: '0', - tid: '0', - wid: '0', - uid: '0', - sid: '0', - modeSwitch: '6', - teenMode: '0', - ui_mode: '2', - nettype: '1020', - v4ip: '', - }, - req: { - module: 'music.search.SearchCgiService', - method: 'DoSearchForQQMusicMobile', - param: { - search_type: 0, - query: str, - page_num: page, - num_per_page: limit, - highlight: 0, - nqc_flag: 0, - multi_zhida: 0, - cat: 2, - grp: 1, - sin: 0, - sem: 0, - }, + req: { + module: 'music.search.SearchCgiService', + method: 'DoSearchForQQMusicMobile', + param: { + search_type: 0, + searchid: Math.random().toString().slice(2), + query: str, + page_num: page, + num_per_page: limit, + highlight: 0, + nqc_flag: 0, + multi_zhida: 0, + cat: 2, + grp: 1, + sin: 0, + sem: 0, }, }, }) - // searchRequest = httpFetch(`http://ioscdn.kugou.com/api/v3/search/song?keyword=${encodeURIComponent(str)}&page=${page}&pagesize=${this.limit}&showtype=10&plat=2&version=7910&tag=1&correct=1&privilege=1&sver=5`) - return searchRequest.promise.then(({ body }) => { + return searchRequest.then(({ body }) => { // console.log(body) - if (body.code != this.successCode || body.req.code != this.successCode) return this.musicSearch(str, page, limit, ++retryNum) + if (!body || !body.req || body.code != this.successCode || body.req.code != this.successCode) { + return this.musicSearch(str, page, limit, ++retryNum) + } return body.req.data }) }, + // randomInt(min, max) { + // return Math.floor(Math.random() * (max - min + 1)) + min + // }, + // getSearchId() { + // const e = BigInt(this.randomInt(1, 20)) + // const t = e * 18014398509481984n + // const n = BigInt(this.randomInt(0, 4194304)) * 4294967296n + // const a = BigInt(Date.now()) + // const r = (a * 1000n) % (24n * 60n * 60n * 1000n) + // return String(t + n + r) + // }, handleResult(rawList) { - console.log(rawList) + // console.log(rawList) + if (!rawList || !Array.isArray(rawList)) return [] const list = [] rawList.forEach(item => { if (!item.file?.media_mid) return diff --git a/src/renderer/utils/musicSdk/tx/utils/crypto.js b/src/renderer/utils/musicSdk/tx/utils/crypto.js new file mode 100644 index 00000000..78618d12 --- /dev/null +++ b/src/renderer/utils/musicSdk/tx/utils/crypto.js @@ -0,0 +1,28 @@ +import crypto from 'node:crypto' + +const PART_1_INDEXES = [23, 14, 6, 36, 16, 40, 7, 19] +const PART_2_INDEXES = [16, 1, 32, 12, 19, 27, 8, 5] +const SCRAMBLE_VALUES = [89, 39, 179, 150, 218, 82, 58, 252, 177, 52, 186, 123, 120, 64, 242, 133, 143, 161, 121, 179] + +async function hashSHA1(data) { + return crypto.createHash('sha1').update(data).digest('hex') +} + +function pickHashByIdx(hash, indexes) { + return indexes.map((idx) => hash[idx]).join('') +} + +function base64Encode(data) { + return Buffer.from(data) + .toString('base64') + .replace(/[\\/+=]/g, '') +} + +export async function zzcSign(text) { + const hash = await hashSHA1(text) + const part1 = pickHashByIdx(hash, PART_1_INDEXES) + const part2 = pickHashByIdx(hash, PART_2_INDEXES) + const part3 = SCRAMBLE_VALUES.map((value, i) => value ^ parseInt(hash.slice(i * 2, i * 2 + 2), 16)) + const b64Part = base64Encode(part3).replace(/[\\/+=]/g, '') + return `zzc${part1}${b64Part}${part2}`.toLowerCase() +} diff --git a/src/renderer/utils/musicSdk/tx/utils/index.js b/src/renderer/utils/musicSdk/tx/utils/index.js new file mode 100644 index 00000000..70ee425c --- /dev/null +++ b/src/renderer/utils/musicSdk/tx/utils/index.js @@ -0,0 +1,16 @@ + +import { httpFetch } from '../../../request' +import { zzcSign } from './crypto' + +export const signRequest = async(data) => { + // console.log(data) + const sign = await zzcSign(JSON.stringify(data)) + // console.log('sign', sign) + return httpFetch(`https://u.y.qq.com/cgi-bin/musics.fcg?sign=${sign}`, { + method: 'post', + headers: { + 'User-Agent': 'QQMusic 14090508(android 12)', + }, + body: data, + }).promise +}