mirror of
https://github.com/NeteaseCloudMusicApiEnhanced/api-enhanced.git
synced 2026-10-20 22:51:23 +08:00
feat: Add neapi support
This commit is contained in:
+97
-5
@@ -18,12 +18,14 @@ const {
|
||||
} = 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')
|
||||
|
||||
// 预先读取匿名token并缓存
|
||||
const anonymous_token = fs.readFileSync(
|
||||
@@ -71,6 +73,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: {
|
||||
@@ -127,6 +143,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])
|
||||
|
||||
@@ -321,6 +338,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创建
|
||||
@@ -367,12 +425,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(),
|
||||
}
|
||||
@@ -383,9 +448,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'
|
||||
}
|
||||
@@ -426,7 +489,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'] || []
|
||||
|
||||
@@ -453,6 +516,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)
|
||||
@@ -471,6 +558,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