const API_BASE_URL = "https://flac.music.hi.cn"
const { EVENT_NAMES, request, on, send } = globalThis.lx
let globalCookie = 'sl-session=cfvqX25YrmjOIuvFqDrbpQ==; sl-challenge-server=cloud; sl_jwt_session=cpF4eqtnrWghBeqsta5jqA==; sl_jwt_sign='
let cookieUpdateTimer = null
function calculateArray(input) {
let t = 1;
const sum = input.reduce((acc, val) => acc + val, 0);
let cycles = (6 + input.length + sum) % 6 + 6;
while (cycles--) t *= 6;
if (t < 6666) t *= input.length;
if (t > 0x3f940aa) t = Math.floor(t / input.length);
for (let i = 0; i < input.length; i++) {
t += Math.pow(input[i], 3);
t ^= i;
t ^= input[i] + i;
}
const result = [];
while (t > 0) {
result.unshift(t & 63);
t >>= 6;
}
return result;
}
function customRequest(url, options) {
return new Promise((resolve, reject) => {
request(url, options, (err, resp) => {
if (err) return reject(err);
if ((resp.statusCode >= 200 && resp.statusCode < 300) || resp.statusCode === 468) {
resolve(resp);
} else {
reject(new Error(`请求失败,状态码: ${resp.statusCode}`));
}
});
});
}
async function generateCookie() {
try {
console.log('开始生成cookie...');
console.log('第一步:获取初始页面...');
const response1 = await customRequest('https://flac.music.hi.cn/', {
method: 'GET',
headers: {
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'accept-language': 'zh-CN,zh;q=0.9',
'priority': 'u=0, i',
'sec-ch-ua': '"Chromium";v="131", "Not_A Brand";v="24"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'document',
'sec-fetch-mode': 'navigate',
'sec-fetch-site': 'none',
'sec-fetch-user': '?1',
'upgrade-insecure-requests': '1',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
}
});
console.log('第一步响应状态码:', response1.statusCode);
const setCookieHeader = response1.headers['set-cookie'];
let slSession = '';
if (setCookieHeader) {
for (const cookie of setCookieHeader) {
if (cookie.includes('sl-session=')) {
const match = cookie.match(/sl-session=([^;]+)/);
if (match) {
slSession = match[1];
break;
}
}
}
}
let key = '';
if (response1.body) {
const keyMatch = response1.body.match(/SafeLineChallenge\("([^"]+)"/);
if (keyMatch) {
key = keyMatch[1];
}
}
console.log('提取到的sl-session:', slSession);
console.log('提取到的key:', key);
if (!slSession || !key) {
throw new Error('无法提取sl-session或key');
}
console.log('第二步:请求issue...');
const response2 = await customRequest('https://challenge.rivers.chaitin.cn/challenge/v2/api/issue', {
method: 'POST',
headers: {
'Accept': '*/*',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Connection': 'keep-alive',
'Content-Type': 'application/json',
'Origin': 'https://flac.music.hi.cn',
'Referer': 'https://flac.music.hi.cn/',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'cross-site',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
'sec-ch-ua': '"Chromium";v="131", "Not_A Brand";v="24"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"'
},
body: JSON.stringify({
client_id: key,
level: 1
})
});
console.log('第二步响应状态码:', response2.statusCode);
let issueData = response2.body.data;
console.log('获取到的issue数据:', issueData);
console.log('第三步:计算result...');
const result = calculateArray(issueData.data);
console.log('计算结果:', result);
console.log('第四步:验证result...');
const response3 = await customRequest('https://challenge.rivers.chaitin.cn/challenge/v2/api/verify', {
method: 'POST',
headers: {
'Accept': '*/*',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Connection': 'keep-alive',
'Content-Type': 'application/json',
'Origin': 'https://flac.music.hi.cn',
'Referer': 'https://flac.music.hi.cn/',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'cross-site',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
'sec-ch-ua': '"Chromium";v="131", "Not_A Brand";v="24"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"'
},
body: JSON.stringify({
issue_id: issueData.issue_id,
result: result,
serials: [],
client: {
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
platform: 'Win32',
language: 'zh-CN',
vendor: 'Google Inc.',
screen: [1920, 1080],
visitorId: '37e2d1aea4b49c1604dfb58aa19c260b',
score: 0,
target: []
}
})
});
console.log('第四步响应状态码:', response3.statusCode);
let jwt = response3.body.data.jwt;
console.log('获取到的JWT:', jwt);
console.log('第五步:获取最终cookie...');
const response4 = await customRequest('https://flac.music.hi.cn/', {
method: 'GET',
headers: {
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'accept-language': 'zh-CN,zh;q=0.9',
'cache-control': 'max-age=0',
'cookie': `sl-session=${slSession}; sl-challenge-server=cloud; sl-challenge-jwt=${jwt}`,
'priority': 'u=0, i',
'referer': 'https://flac.music.hi.cn/',
'sec-ch-ua': '"Chromium";v="131", "Not_A Brand";v="24"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'document',
'sec-fetch-mode': 'navigate',
'sec-fetch-site': 'same-origin',
'upgrade-insecure-requests': '1',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
}
});
console.log('第五步响应状态码:', response4.statusCode);
const finalCookies = response4.headers['set-cookie'];
let slJwtSession = '';
let slJwtSign = '';
let slChallengeJwt = '';
if (finalCookies) {
for (const cookie of finalCookies) {
if (cookie.includes('sl_jwt_session=')) {
const match = cookie.match(/sl_jwt_session=([^;]+)/);
if (match) slJwtSession = match[1];
} else if (cookie.includes('sl_jwt_sign=')) {
const match = cookie.match(/sl_jwt_sign=([^;]+)/);
if (match) slJwtSign = match[1];
} else if (cookie.includes('sl-challenge-jwt=')) {
const match = cookie.match(/sl-challenge-jwt=([^;]+)/);
if (match) slChallengeJwt = match[1];
}
}
}
console.log('提取的sl_jwt_session:', slJwtSession);
console.log('提取的sl_jwt_sign:', slJwtSign);
console.log('提取的sl-challenge-jwt:', slChallengeJwt);
const finalCookie = `sl-session=${slSession};sl-challenge-server=cloud;sl_jwt_session=${slJwtSession};sl_jwt_sign=${slJwtSign};sl-challenge-jwt=${slChallengeJwt}`;
console.log('生成的新cookie:', finalCookie);
return finalCookie;
} catch (error) {
console.error('生成cookie过程中出错:', error.message);
throw error;
}
}
async function initCookie() {
try {
globalCookie = await generateCookie();
console.log('Cookie初始化成功');
if (cookieUpdateTimer) {
clearInterval(cookieUpdateTimer);
}
cookieUpdateTimer = setInterval(async () => {
try {
globalCookie = await generateCookie();
console.log('Cookie定时更新成功');
} catch (error) {
console.error('Cookie定时更新失败:', error.message);
}
}, 60 * 60 * 1000);
} catch (error) {
console.error('Cookie初始化失败,使用默认cookie:', error.message);
}
}
initCookie();
const httpFetch = (url, body) => new Promise((resolve, reject) => {
console.log("发送请求 " + url + ", 参数: " + body);
request(
url,
{
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Accept': 'application/json, text/javascript, */*; q=0.01',
'Origin': API_BASE_URL,
'X-Requested-With': 'XMLHttpRequest',
'Cookie': globalCookie,
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
},
body: body,
},
(err, resp) => {
if (err) {
console.log("请求错误: ", err);
return reject(err)
}
console.log("服务器返回: ", resp);
if (resp.statusCode === 468) {
initCookie();
reject("Session过期, 重新获取");
}
resolve(resp.body)
}
)
})
function getLink(list, info) {
let filter = list.filter(item => item.id === info.musicInfo.songmid);
if (filter.length > 0) return filter[0].link;
filter = list.filter(item =>
item.artist.toLowerCase() === info.musicInfo.singer.toLowerCase()
&& item.name.toLowerCase() === info.musicInfo.name.toLowerCase()
);
if (filter.length > 0) return filter[0].link;
filter = list.filter(item => item.artist.toLowerCase() === info.musicInfo.singer.toLowerCase());
if (filter.length > 0) return filter[0].link;
filter = list.filter(item => item.name.toLowerCase() === info.musicInfo.name.toLowerCase());
if (filter.length > 0) return filter[0].link;
return list[0].link;
}
const musicUrl = async(source, info) => {
console.log("运行 musicUrl: ", info);
const key = encodeURIComponent(info.musicInfo.name + info.musicInfo.singer)
const qual = info.type.replace("k", "");
const url = `${API_BASE_URL}/ajax.php?act=search`
const response = await httpFetch(url, `keyword=${key}&page=1&size=30`);
if (response.code === 0) {
const link = getLink(response.data.list, info);
console.log("获取链接: " + link);
const url = `${API_BASE_URL}/ajax.php?act=getUrl`
const resp = await httpFetch(url, `songid=${response.data.list[0].id}&format=mp3&bitrate=${qual}`);
if (resp.code === 0) {
return Promise.resolve(resp.data.url);
} else {
return Promise.reject("获取歌曲地址失败");
}
} else {
return Promise.reject("搜索失败");
}
}
const lyricUrl = async(source, info) => {
console.log("运行 lyricUrl", info);
return Promise.resolve('')
}
const picUrl = async(source, info) => {
console.log("运行 picUrl", info);
return Promise.resolve("")
}
on(EVENT_NAMES.request, ({ source, action, info }) => {
switch (action) {
case 'musicUrl':
return musicUrl(source, info)
.then(data => Promise.resolve(data))
.catch(err => Promise.reject(err))
case 'lyric':
return lyricUrl(source, info)
.then(data => Promise.resolve(data))
.catch(err => Promise.reject(err))
case 'pic':
return picUrl(source, info)
.then(data => Promise.resolve(data))
.catch(err => Promise.reject(err))
default:
console.error(`action(${action}) not support`)
return Promise.reject('action not support')
}
})
send(EVENT_NAMES.inited, {
status: true,
openDevTools: true,
sources: {
kw: {
name: '酷我音乐',
type: 'music',
actions: ['musicUrl'],
qualitys: ['128k', '320k', 'flac', 'flac24bit'],
},
kg: {
name: '酷狗音乐',
type: 'music',
actions: ['musicUrl'],
qualitys: ['128k', '320k', 'flac', 'flac24bit'],
},
tx: {
name: 'QQ音乐',
type: 'music',
actions: ['musicUrl'],
qualitys: ['128k', '320k', 'flac', 'flac24bit'],
},
wy: {
name: '网易云音乐',
type: 'music',
actions: ['musicUrl'],
qualitys: ['128k', '320k', 'flac', 'flac24bit'],
},
mg: {
name: '咪咕音乐',
type: 'music',
actions: ['musicUrl'],
qualitys: ['128k', '320k', 'flac', 'flac24bit'],
},
local: {
name: '本地音乐',
type: 'music',
actions: ['musicUrl', 'lyric', 'pic'],
qualitys: [],
}
}
})