mirror of
https://github.com/NeteaseCloudMusicApiEnhanced/api-enhanced.git
synced 2026-06-15 12:05:09 +00:00
feat: 增强配置生成逻辑,添加重试机制和日志记录
This commit is contained in:
parent
e3a1c041b6
commit
c959866436
@ -4,38 +4,167 @@ 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')
|
||||
|
||||
async function generateConfig() {
|
||||
global.cnIp = generateRandomChineseIP()
|
||||
try {
|
||||
const res = await register_anonimous()
|
||||
const cookie = res.body.cookie
|
||||
if (cookie) {
|
||||
const cookieObj = cookieToJson(cookie)
|
||||
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, 'anonymous_token'),
|
||||
cookieObj.MUSIC_A,
|
||||
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 }
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
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',
|
||||
)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
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
|
||||
|
||||
@ -26,7 +26,6 @@ function cloudmusic_dll_encode_id(some_id) {
|
||||
|
||||
module.exports = async (query, request) => {
|
||||
const deviceId = generateDeviceId()
|
||||
logger.info(`Successfully registered anonimous token, deviceId: ${deviceId}`)
|
||||
global.deviceId = deviceId
|
||||
const encodedId = CryptoJS.enc.Base64.stringify(
|
||||
CryptoJS.enc.Utf8.parse(
|
||||
|
||||
16
server.js
16
server.js
@ -10,6 +10,7 @@ const { cookieToJson } = require('./util/index')
|
||||
const fileUpload = require('express-fileupload')
|
||||
const decode = require('safe-decode-uri-component')
|
||||
const logger = require('./util/logger.js')
|
||||
const { APP_CONF } = require('./util/config.json')
|
||||
|
||||
/**
|
||||
* The version check result.
|
||||
@ -299,15 +300,15 @@ async function constructServer(moduleDefs) {
|
||||
)
|
||||
|
||||
try {
|
||||
let usedCrypto = ''
|
||||
const moduleResponse = await moduleDef.module(query, (...params) => {
|
||||
// 参数注入客户端IP
|
||||
const obj = [...params]
|
||||
const options = obj[2] || {}
|
||||
usedCrypto = options.crypto || ''
|
||||
let ip = ''
|
||||
|
||||
if (options.randomCNIP) {
|
||||
ip = global.cnIp
|
||||
// logger.info('Using random Chinese IP for request:', ip)
|
||||
} else {
|
||||
ip = req.ip
|
||||
|
||||
@ -317,7 +318,6 @@ async function constructServer(moduleDefs) {
|
||||
if (ip == '::1') {
|
||||
ip = global.cnIp
|
||||
}
|
||||
// logger.info('Requested from ip:', ip)
|
||||
}
|
||||
|
||||
obj[2] = {
|
||||
@ -327,7 +327,10 @@ async function constructServer(moduleDefs) {
|
||||
|
||||
return request(...obj)
|
||||
})
|
||||
logger.info(`Request Success: ${decode(req.originalUrl)}`)
|
||||
const displayCrypto = usedCrypto || (APP_CONF.encrypt ? 'eapi' : 'api')
|
||||
logger.info(
|
||||
`Request Success: [${displayCrypto}] ${decode(req.originalUrl)}`,
|
||||
)
|
||||
|
||||
// 夹带私货部分:如果开启了通用解锁,并且是获取歌曲URL的接口,则尝试解锁(如果需要的话)ヾ(≧▽≦*)o
|
||||
if (
|
||||
@ -445,10 +448,7 @@ async function serveNcmApi(options) {
|
||||
╩ ╩╩ ╩ ╚═╝╝╚╝╩ ╩╩ ╩╝╚╝╚═╝╚═╝═╩╝
|
||||
`)
|
||||
logger.info(`
|
||||
- Server started successfully @ http://${host ? host : 'localhost'}:${port}
|
||||
- Environment: ${process.env.NODE_ENV || 'development'}
|
||||
- Node Version: ${process.version}
|
||||
- Process ID: ${process.pid}`)
|
||||
- Server started successfully @ http://${host ? host : 'localhost'}:${port}`)
|
||||
})
|
||||
|
||||
return appExt
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user