NeteaseCloudMusicApiEnhanced/generateConfig.js

171 lines
5.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const fs = require('fs')
const path = require('path')
const { register_anonimous } = require('./main')
const { cookieToJson, generateRandomChineseIP } = require('./util/index')
const { getXeapiPublicKey } = require('./util/xeapiKey')
const tmpPath = require('os').tmpdir()
const logger = require('./util/logger')
const MAX_RETRIES = 3
const RETRY_DELAY_MS = 1000
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms))
}
function isRetryableError(err) {
const msg = (err && err.message) || ''
const status =
(err && err.status) || (err && err.response && err.response.status)
if (
msg.includes('ETIMEDOUT') ||
msg.includes('ECONNRESET') ||
msg.includes('ECONNREFUSED') ||
msg.includes('socket hang up') ||
msg.includes('request timeout') ||
msg.includes('timeout') ||
msg.includes('network') ||
msg.includes('Network')
) {
return true
}
if (status && status >= 500) {
return true
}
return false
}
/** @returns {{ success: boolean, error?: Error }} */
async function fetchAnonymousToken() {
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
try {
const res = await register_anonimous()
const cookie = res.body.cookie
if (cookie) {
const cookieObj = cookieToJson(cookie)
fs.writeFileSync(
path.resolve(tmpPath, 'anonymous_token'),
cookieObj.MUSIC_A,
'utf-8',
)
logger.success('[generateConfig] 匿名 token 注册成功')
return { success: true }
}
// 返回了但没有 cookie视为异常但不再重试
logger.warn(
`[generateConfig] 匿名注册返回了空 cookie (attempt ${attempt})`,
)
return {
success: false,
error: new Error('empty cookie from anonymous register'),
}
} catch (err) {
if (isRetryableError(err) && attempt < MAX_RETRIES) {
const delay = RETRY_DELAY_MS * Math.pow(2, attempt - 1)
logger.warn(
`[generateConfig] 获取匿名 token 失败 (attempt ${attempt}/${MAX_RETRIES}), ${delay}ms 后重试...`,
)
await sleep(delay)
continue
}
// 不可重试 或 已达最大次数
if (attempt >= MAX_RETRIES) {
logger.error(
`[generateConfig] 获取匿名 token 已达最大重试次数 (${MAX_RETRIES}):`,
err.message,
)
} else {
logger.error(
`[generateConfig] 获取匿名 token 失败 (不可重试):`,
err.message,
)
}
return { success: false, error: err }
}
}
return { success: false, error: new Error('unreachable') }
}
/**
* 获取 xeapi public key带重试
* @returns {{ success: boolean, error?: Error }}
*/
async function fetchXeapiPublicKey() {
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
try {
let currentPublicKey = {}
try {
currentPublicKey = JSON.parse(
fs.readFileSync(path.resolve(tmpPath, 'xeapi_public_key'), 'utf-8'),
)
} catch (_) {
// 本地无缓存文件,用空对象正常请求
}
const publicKey = await getXeapiPublicKey(
currentPublicKey,
global.deviceId,
)
fs.writeFileSync(
path.resolve(tmpPath, 'xeapi_public_key'),
JSON.stringify(publicKey),
'utf-8',
)
logger.success('[generateConfig] xeapi public key 获取成功')
return { success: true }
} catch (err) {
if (isRetryableError(err) && attempt < MAX_RETRIES) {
const delay = RETRY_DELAY_MS * Math.pow(2, attempt - 1)
logger.warn(
`[generateConfig] 获取 xeapi public key 失败 (attempt ${attempt}/${MAX_RETRIES}), ${delay}ms 后重试...`,
)
await sleep(delay)
continue
}
if (attempt >= MAX_RETRIES) {
logger.error(
`[generateConfig] 获取 xeapi public key 已达最大重试次数 (${MAX_RETRIES}):`,
err.message,
)
} else {
logger.error(
`[generateConfig] 获取 xeapi public key 失败 (不可重试):`,
err.message,
)
}
return { success: false, error: err }
}
}
return { success: false, error: new Error('unreachable') }
}
/**
* 生成配置(匿名 token + xeapi public key带容错重试
* @returns {{ tokenOk: boolean, keyOk: boolean }}
*/
async function generateConfig() {
global.cnIp = generateRandomChineseIP()
// 两个任务并行执行,互不影响喵~
const [tokenResult, keyResult] = await Promise.all([
fetchAnonymousToken(),
fetchXeapiPublicKey(),
])
if (!tokenResult.success) {
logger.warn('[generateConfig] 匿名 token 获取失败')
}
if (!keyResult.success) {
logger.warn('[generateConfig] xeapi public key 获取失败')
}
if (tokenResult.success && keyResult.success) {
logger.success('[generateConfig] 配置初始化完成')
}
return {
tokenOk: tokenResult.success,
keyOk: keyResult.success,
}
}
module.exports = generateConfig