mirror of
https://github.com/NeteaseCloudMusicApiEnhanced/api-enhanced.git
synced 2026-10-26 01:21:22 +08:00
merge: 同步 main(neapi 支持、v9.6.13 接口、cookie 解析修复)并解决冲突
This commit is contained in:
+111
-7
@@ -11,17 +11,21 @@ const runtimeState = require('./runtimeState')
|
||||
const {
|
||||
cookieToJson,
|
||||
cookieObjToString,
|
||||
getCookieHeader,
|
||||
hasCookie,
|
||||
toBoolean,
|
||||
generateRandomChineseIP,
|
||||
} = require('./index')
|
||||
const { URLSearchParams, URL } = require('url')
|
||||
const { APP_CONF } = require('./config.json')
|
||||
const { loadNeapiKey } = require('./neapiKey')
|
||||
const {
|
||||
getToken: antiCheatTokenV2,
|
||||
} = require('../module/register_checktoken_v2')
|
||||
const {
|
||||
getToken: antiCheatTokenV3,
|
||||
} = require('../module/register_checktoken_v3')
|
||||
const registerNeapikey = require('../module/register_neapikey')
|
||||
|
||||
// 预先绑定常用函数和常量
|
||||
const floor = Math.floor
|
||||
@@ -49,6 +53,20 @@ const WNMCID = (function () {
|
||||
let NMTID = ''
|
||||
let NMTID_RETRIES_LEFT = 3
|
||||
|
||||
// neapi 配置过期时只让一个请求去刷新,并发的其余请求共用同一次结果
|
||||
let neapiUserStatus = ''
|
||||
let neapiRefreshing = null
|
||||
const refreshNeapiKey = (version) => {
|
||||
if (!neapiRefreshing) {
|
||||
neapiRefreshing = registerNeapikey({ version, behavior: 'update' }, null)
|
||||
.catch((error) => console.log('[ERR]', error))
|
||||
.finally(() => {
|
||||
neapiRefreshing = null
|
||||
})
|
||||
}
|
||||
return neapiRefreshing
|
||||
}
|
||||
|
||||
// 预先定义osMap
|
||||
const osMap = {
|
||||
pc: {
|
||||
@@ -105,6 +123,7 @@ const DOMAIN = APP_CONF.domain
|
||||
const API_DOMAIN = APP_CONF.apiDomain
|
||||
const EAPI_DOMAIN = APP_CONF.eapiDomain
|
||||
const XEAPI_DOMAIN = APP_CONF.xeapiDomain
|
||||
const NEAPI_DOMAIN = APP_CONF.neapiDomain
|
||||
const ENCRYPT_RESPONSE = APP_CONF.encryptResponse
|
||||
const SPECIAL_STATUS_CODES = new Set([201, 302, 400, 502, 800, 801, 802, 803])
|
||||
|
||||
@@ -258,13 +277,19 @@ const createRequest = async (uri, data, options) => {
|
||||
headers['X-Forwarded-For'] = ip
|
||||
}
|
||||
|
||||
let cookie = options.cookie || {}
|
||||
let cookie = hasCookie(options.cookie)
|
||||
? options.cookie
|
||||
: getCookieHeader(options.headers)
|
||||
if (!hasCookie(cookie)) cookie = {}
|
||||
if (typeof cookie === 'string') {
|
||||
cookie = cookieToJson(cookie)
|
||||
}
|
||||
|
||||
if (typeof cookie === 'object') {
|
||||
cookie = processCookieObject(cookie, crypto)
|
||||
Object.keys(headers).forEach((name) => {
|
||||
if (name.toLowerCase() === 'cookie') delete headers[name]
|
||||
})
|
||||
headers['Cookie'] = cookieObjToString(cookie)
|
||||
}
|
||||
let url = ''
|
||||
@@ -353,6 +378,47 @@ const createRequest = async (uri, data, options) => {
|
||||
})
|
||||
break
|
||||
|
||||
case 'neapi':
|
||||
const neapiKey = loadNeapiKey()
|
||||
if (!neapiKey) {
|
||||
throw new Error('neapi config is missing')
|
||||
}
|
||||
// 超过 circleTime 就后台刷新,本次仍用手上的配置
|
||||
if (now() - neapiKey.fetchedAt >= neapiKey.circleTime) {
|
||||
refreshNeapiKey(neapiKey.version)
|
||||
}
|
||||
const neapiCookie = {
|
||||
osver: cookie.osver,
|
||||
deviceId: cookie.deviceId,
|
||||
os: cookie.os,
|
||||
appver: cookie.appver,
|
||||
versioncode: cookie.versioncode || '140',
|
||||
mobilename: cookie.mobilename || '',
|
||||
buildver: cookie.buildver || now().toString().substr(0, 10),
|
||||
resolution: cookie.resolution || '1920x1080',
|
||||
__csrf: csrfToken,
|
||||
channel: cookie.channel,
|
||||
requestId: generateRequestId(),
|
||||
}
|
||||
if (cookie.MUSIC_U) neapiCookie['MUSIC_U'] = cookie.MUSIC_U
|
||||
if (cookie.MUSIC_A) neapiCookie['MUSIC_A'] = cookie.MUSIC_A
|
||||
headers['Cookie'] = createHeaderCookie(neapiCookie)
|
||||
headers['User-Agent'] = options.ua || chooseUserAgent('api', 'android')
|
||||
headers['content-type'] =
|
||||
'application/x-www-form-urlencoded;charset=utf-8'
|
||||
// 服务端按 X-ER 选密钥,配置过期时由下面的 777/999 分支重拉并重试
|
||||
headers['X-ER'] = String(neapiKey.version)
|
||||
if (neapiUserStatus) headers['X-USER-STATUS'] = neapiUserStatus
|
||||
|
||||
const neapiBody = { ...data }
|
||||
delete neapiBody.e_r
|
||||
encryptData = encrypt.neapi(
|
||||
new URLSearchParams(neapiBody).toString(),
|
||||
neapiKey,
|
||||
)
|
||||
url = (options.domain || NEAPI_DOMAIN) + '/neapi/' + uri.substr(5)
|
||||
break
|
||||
|
||||
case 'eapi':
|
||||
case 'api':
|
||||
// header创建
|
||||
@@ -399,12 +465,19 @@ const createRequest = async (uri, data, options) => {
|
||||
console.log('[ERR]', 'Unknown Crypto:', crypto)
|
||||
break
|
||||
}
|
||||
const use_e_r = (crypto === 'eapi' || crypto === 'weapi') && data.e_r
|
||||
const use_xeapi = crypto === 'xeapi'
|
||||
const use_neapi = crypto === 'neapi'
|
||||
|
||||
// settings创建
|
||||
let settings = {
|
||||
method: 'POST',
|
||||
url: url,
|
||||
headers: headers,
|
||||
data: new URLSearchParams(encryptData).toString(),
|
||||
// neapi 的请求体就是容器本身,不是表单字段
|
||||
data: use_neapi
|
||||
? encryptData
|
||||
: new URLSearchParams(encryptData).toString(),
|
||||
httpAgent: createHttpAgent(),
|
||||
httpsAgent: createHttpsAgent(),
|
||||
}
|
||||
@@ -415,9 +488,7 @@ const createRequest = async (uri, data, options) => {
|
||||
}
|
||||
|
||||
// 使用返回值加密
|
||||
const use_e_r = (crypto === 'eapi' || crypto === 'weapi') && data.e_r
|
||||
const use_xeapi = crypto === 'xeapi'
|
||||
if (use_e_r || use_xeapi) {
|
||||
if (use_e_r || use_xeapi || use_neapi) {
|
||||
settings.encoding = null
|
||||
settings.responseType = 'arraybuffer'
|
||||
}
|
||||
@@ -458,7 +529,7 @@ const createRequest = async (uri, data, options) => {
|
||||
}
|
||||
// console.log(settings.headers);
|
||||
axios(settings)
|
||||
.then((res) => {
|
||||
.then(async (res) => {
|
||||
const body = res.data
|
||||
const setCookies = res.headers['set-cookie'] || []
|
||||
|
||||
@@ -472,7 +543,11 @@ const createRequest = async (uri, data, options) => {
|
||||
!cookie.NMTID
|
||||
) {
|
||||
NMTID_RETRIES_LEFT--
|
||||
answer.cookie = setCookies.map((x) => {
|
||||
answer.cookie = (
|
||||
typeof res.headers['set-cookie'] === 'string'
|
||||
? [res.headers['set-cookie']]
|
||||
: res.headers['set-cookie'] || []
|
||||
).map((x) => {
|
||||
const cleaned = cleanCookie(x)
|
||||
const match = x.match(/(?:^|;\s*)NMTID=([^;]+)/)
|
||||
if (match) {
|
||||
@@ -485,6 +560,30 @@ const createRequest = async (uri, data, options) => {
|
||||
answer.cookie = setCookies.map(cleanCookie)
|
||||
}
|
||||
|
||||
// 空值同样要记下来,否则状态恢复后会一直带着上一次的值
|
||||
const userStatus = res.headers['x-user-status'] || ''
|
||||
if (use_neapi) {
|
||||
neapiUserStatus = userStatus
|
||||
if (
|
||||
(userStatus === '777' || userStatus === '999') &&
|
||||
!options.neapiRetried
|
||||
) {
|
||||
// 配置已轮换:刷新后重试一次
|
||||
await refreshNeapiKey(loadNeapiKey().version)
|
||||
try {
|
||||
resolve(
|
||||
await createRequest(uri, data, {
|
||||
...options,
|
||||
neapiRetried: true,
|
||||
}),
|
||||
)
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// debug: 统一注释块,需要时取消注释查看请求/返回的原始密文
|
||||
|
||||
// logger.debug(`[${crypto}]`, uri)
|
||||
@@ -503,6 +602,11 @@ const createRequest = async (uri, data, options) => {
|
||||
xeapiSessionKey = res.headers['x-encr-sskey']
|
||||
}
|
||||
answer.body = encrypt.xeapiResDecrypt(Buffer.from(body))
|
||||
} else if (use_neapi) {
|
||||
answer.body = encrypt.neapiResDecrypt(
|
||||
Buffer.from(body).toString(),
|
||||
loadNeapiKey(),
|
||||
)
|
||||
} else if (use_e_r) {
|
||||
answer.body = encrypt.eapiResDecrypt(
|
||||
body.toString('hex').toUpperCase(),
|
||||
|
||||
Reference in New Issue
Block a user