Compare commits

...

2 Commits

Author SHA1 Message Date
f527c1ef73
docs: 新增免费听接口文档 2026-07-15 16:23:25 +08:00
0a5a1242f2
feat: 新增免费听接口 2026-07-15 16:14:19 +08:00
11 changed files with 774 additions and 158 deletions

43
module/ad_get.js Normal file
View File

@ -0,0 +1,43 @@
// 获取广告
// 基于逆向网易云音乐 v9.5.45 RN Hermes 源码:
// - adService.getAd() → Network.nativeRequest → /api/ad/get
// - 需要 X-antiCheatToken 易盾反作弊头
// - 使用 xeapi 加密
//
// 返回 extra.reqId 可用于 ad_listening_rights_gain 的 reqUid
//
const createOption = require('../util/option.js')
module.exports = async (query, request) => {
const data = {
type_ids: query.type_ids || '["400002_0"]',
}
const option = createOption(query, 'xeapi')
option.checkToken = true
const res = await request(`/api/ad/get`, data, option)
const raw = res.body
// 提取广告中的 req_id (用于领取权益)
let reqId = ''
try {
if (raw?.ads) {
const ad = Object.values(raw.ads)[0]
if (ad?.extJson) {
const ext = JSON.parse(ad.extJson)
reqId = ext?.contextInfo?.req_id || ''
}
}
} catch (_) {}
return {
status: 200,
body: {
code: 200,
ads: raw?.ads || null,
message: raw?.message || null,
extra: { reqId },
},
}
}

View File

@ -0,0 +1,88 @@
// 看广告免费听歌 - 领取免费听权益
// 请求流程(基于逆向网易云音乐 v9.5.45 RN Hermes 源码):
// 1. 从广告平台拉广告 → 用户看完/点击广告 → 获取 ad 对象的 extJson.contextInfo.req_id 作为 reqUid
// 2. 调用本接口传入 reqUid 及相关权益参数,领取免费听权益
// 3. 服务器返回 gainFlag 等标识,用于展示领取结果
const createOption = require('../util/option.js')
const adGet = require('./ad_get.js')
module.exports = async (query, request) => {
const time = Date.now()
// 如果未传入 reqUid自动从 ad_get 获取最新广告的 req_id
let reqUid = query.reqUid || ''
if (!reqUid) {
try {
const adRes = await adGet(
{ ...query, type_ids: query.type_ids || '["400002_0"]' },
request,
)
reqUid = adRes?.body?.extra?.reqId || ''
console.log(`自动获取 reqUid: ${reqUid}`)
} catch (e) {
// 获取广告失败,后续请求会因缺少 reqUid 被拒绝
}
}
const rightsParam = {
// 必填: 广告请求 ID自动从 ad_get 获取
reqUid,
// 广告创意类型 (默认 1)
creativeType: parseInt(query.creativeType || 2),
// 时间戳
exposureTime: query.exposureTime || time,
clickTime: query.clickTime || time,
// 权益领取方式
rightsGainMethod: parseInt(query.rightsGainMethod || 2),
// 权益时长相关
rightsGainDuration: query.rightsGainDuration
? parseInt(query.rightsGainDuration)
: undefined,
extraRightsGainMethod: query.extraRightsGainMethod
? parseInt(query.extraRightsGainMethod)
: undefined,
extraRightsGainDuration: query.extraRightsGainDuration
? parseInt(query.extraRightsGainDuration)
: undefined,
nextRightsGainDuration: query.nextRightsGainDuration
? parseInt(query.nextRightsGainDuration)
: undefined,
// 来源标识 | 权益扩展信息
source: query.source || undefined,
rightsExtJson: query.rightsExtJson || undefined,
// 应用信息(下载类广告)
appInfo: query.appInfo ? JSON.parse(query.appInfo) : undefined,
installed: query.installed ? parseInt(query.installed) : undefined,
}
// 清理 undefined 字段
Object.keys(rightsParam).forEach((key) => {
if (rightsParam[key] === undefined) delete rightsParam[key]
})
// 将参数序列化为 reqParam 字符串 (与 RN 源码完全一致)
const data = {
reqParam: JSON.stringify(rightsParam),
}
const option = createOption(query, 'xeapi')
// 关键: 开启 X-antiCheatToken 头
option.checkToken = true
const res = await request(`/api/ad/listening/rights/gain`, data, option)
return {
status: 200,
body: {
code: 200,
data: res.body,
},
}
}

View File

@ -0,0 +1,37 @@
// 易盾反作弊 Token 注册端点
// 调用后获取实时 token 并存入共享存储,供后续带 checkToken 的请求使用
//
// GET /register/checktoken → 返回当前 token尚无则自动获取
// POST /register/checktoken → 强制刷新 token
// GET /register/checktoken?refresh=1 → 强制刷新
//
const { default: axios } = require('axios')
const { APP_CONF } = require('../util/config.json')
const URL = APP_CONF.dunDomain + '/v3/b?pn=YD00000558929251'
let _token = ''
async function fetch() {
const res = await axios.get(URL, { timeout: 10000 })
const body = String(res.data)
const m = body.match(/null\(\[(\d+),\d+,\"([^\"]+)\"\]\)/)
if (m && m[1] === '200') return m[2]
throw new Error('易盾返回异常: ' + body.substring(0, 100))
}
// 端点处理
module.exports = async (query) => {
const refresh = query.refresh === '1' || query.refresh === 'true'
let token = refresh ? null : _token
if (!token) {
token = await fetch()
_token = token
}
return {
status: 200,
body: { code: 200, token, registered: !!token },
}
}
// 给 request.js 读取用
module.exports.getToken = () => _token

View File

@ -1,6 +1,6 @@
{ {
"name": "@neteasecloudmusicapienhanced/api", "name": "@neteasecloudmusicapienhanced/api",
"version": "4.36.2", "version": "4.37.0",
"description": "全网最全的网易云音乐API接口 || A revival project for NeteaseCloudMusicApi Node.js Services (Half Refactor & Enhanced) || 网易云音乐 API 备份 + 增强 || 本项目自原版v4.28.0版本后开始自行维护", "description": "全网最全的网易云音乐API接口 || A revival project for NeteaseCloudMusicApi Node.js Services (Half Refactor & Enhanced) || 网易云音乐 API 备份 + 增强 || 本项目自原版v4.28.0版本后开始自行维护",
"scripts": { "scripts": {
"dev": "nodemon app.js", "dev": "nodemon app.js",
@ -82,7 +82,7 @@
"express": "^5.2.1", "express": "^5.2.1",
"express-fileupload": "^1.5.2", "express-fileupload": "^1.5.2",
"gzip": "^0.1.0", "gzip": "^0.1.0",
"music-metadata": "^11.13.0", "music-metadata": "^11.14.0",
"node-forge": "^1.4.0", "node-forge": "^1.4.0",
"pac-proxy-agent": "^7.2.0", "pac-proxy-agent": "^7.2.0",
"qrcode": "^1.5.4", "qrcode": "^1.5.4",
@ -92,15 +92,15 @@
"yargs": "^18.0.0" "yargs": "^18.0.0"
}, },
"devDependencies": { "devDependencies": {
"@eslint/eslintrc": "^3.3.5", "@eslint/eslintrc": "^3.3.6",
"@eslint/js": "^9.39.4", "@eslint/js": "^9.39.5",
"@types/express": "^5.0.6", "@types/express": "^5.0.6",
"@types/express-fileupload": "^1.5.1", "@types/express-fileupload": "^1.5.1",
"@types/mocha": "^10.0.10", "@types/mocha": "^10.0.10",
"@types/node": "25.9.2", "@types/node": "25.9.2",
"@typescript-eslint/eslint-plugin": "^8.62.1", "@typescript-eslint/eslint-plugin": "^8.64.0",
"@typescript-eslint/parser": "^8.62.1", "@typescript-eslint/parser": "^8.64.0",
"eslint": "^9.39.4", "eslint": "^9.39.5",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.8",
"eslint-plugin-html": "^8.1.4", "eslint-plugin-html": "^8.1.4",
"eslint-plugin-prettier": "^5.5.6", "eslint-plugin-prettier": "^5.5.6",
@ -112,7 +112,7 @@
"nodemon": "^3.1.14", "nodemon": "^3.1.14",
"pkg": "^5.8.1", "pkg": "^5.8.1",
"power-assert": "^1.6.1", "power-assert": "^1.6.1",
"prettier": "^3.9.4", "prettier": "^3.9.5",
"typescript": "^5.9.3" "typescript": "^5.9.3"
}, },
"publishConfig": { "publishConfig": {

286
pnpm-lock.yaml generated
View File

@ -30,8 +30,8 @@ importers:
specifier: ^0.1.0 specifier: ^0.1.0
version: 0.1.0 version: 0.1.0
music-metadata: music-metadata:
specifier: ^11.13.0 specifier: ^11.14.0
version: 11.13.0 version: 11.14.0
node-forge: node-forge:
specifier: ^1.4.0 specifier: ^1.4.0
version: 1.4.0 version: 1.4.0
@ -55,11 +55,11 @@ importers:
version: 18.0.0 version: 18.0.0
devDependencies: devDependencies:
'@eslint/eslintrc': '@eslint/eslintrc':
specifier: ^3.3.5 specifier: ^3.3.6
version: 3.3.5 version: 3.3.6
'@eslint/js': '@eslint/js':
specifier: ^9.39.4 specifier: ^9.39.5
version: 9.39.4 version: 9.39.5
'@types/express': '@types/express':
specifier: ^5.0.6 specifier: ^5.0.6
version: 5.0.6 version: 5.0.6
@ -73,23 +73,23 @@ importers:
specifier: 25.9.2 specifier: 25.9.2
version: 25.9.2 version: 25.9.2
'@typescript-eslint/eslint-plugin': '@typescript-eslint/eslint-plugin':
specifier: ^8.62.1 specifier: ^8.64.0
version: 8.62.1(@typescript-eslint/parser@8.62.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) version: 8.64.0(@typescript-eslint/parser@8.64.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5)(typescript@5.9.3)
'@typescript-eslint/parser': '@typescript-eslint/parser':
specifier: ^8.62.1 specifier: ^8.64.0
version: 8.62.1(eslint@9.39.4)(typescript@5.9.3) version: 8.64.0(eslint@9.39.5)(typescript@5.9.3)
eslint: eslint:
specifier: ^9.39.4 specifier: ^9.39.5
version: 9.39.4 version: 9.39.5
eslint-config-prettier: eslint-config-prettier:
specifier: ^10.1.8 specifier: ^10.1.8
version: 10.1.8(eslint@9.39.4) version: 10.1.8(eslint@9.39.5)
eslint-plugin-html: eslint-plugin-html:
specifier: ^8.1.4 specifier: ^8.1.4
version: 8.1.4 version: 8.1.4
eslint-plugin-prettier: eslint-plugin-prettier:
specifier: ^5.5.6 specifier: ^5.5.6
version: 5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.4))(eslint@9.39.4)(prettier@3.9.4) version: 5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.5))(eslint@9.39.5)(prettier@3.9.5)
globals: globals:
specifier: ^17.7.0 specifier: ^17.7.0
version: 17.7.0 version: 17.7.0
@ -115,8 +115,8 @@ importers:
specifier: ^1.6.1 specifier: ^1.6.1
version: 1.6.1 version: 1.6.1
prettier: prettier:
specifier: ^3.9.4 specifier: ^3.9.5
version: 3.9.4 version: 3.9.5
typescript: typescript:
specifier: ^5.9.3 specifier: ^5.9.3
version: 5.9.3 version: 5.9.3
@ -169,12 +169,12 @@ packages:
resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/eslintrc@3.3.5': '@eslint/eslintrc@3.3.6':
resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} resolution: {integrity: sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/js@9.39.4': '@eslint/js@9.39.5':
resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==} resolution: {integrity: sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/object-schema@2.1.7': '@eslint/object-schema@2.1.7':
@ -271,8 +271,8 @@ packages:
'@types/express-fileupload@1.5.1': '@types/express-fileupload@1.5.1':
resolution: {integrity: sha512-DllImBVI1lCyjl2klky/TEwk60mbNebgXv1669h66g9TfptWSrEFq5a/raHSutaFzjSm1tmn9ypdNfu4jPSixQ==} resolution: {integrity: sha512-DllImBVI1lCyjl2klky/TEwk60mbNebgXv1669h66g9TfptWSrEFq5a/raHSutaFzjSm1tmn9ypdNfu4jPSixQ==}
'@types/express-serve-static-core@5.1.1': '@types/express-serve-static-core@5.1.2':
resolution: {integrity: sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==} resolution: {integrity: sha512-d3KvEXBSo/lOAMc2u6fkyDHBvetBHeqD7wm/AcXfLpSOQwlmG9D/aQ0SFswVjv05p7ullQS7Mjohj6/VdbZuTg==}
'@types/express@5.0.6': '@types/express@5.0.6':
resolution: {integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==} resolution: {integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==}
@ -301,63 +301,63 @@ packages:
'@types/serve-static@2.2.0': '@types/serve-static@2.2.0':
resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==}
'@typescript-eslint/eslint-plugin@8.62.1': '@typescript-eslint/eslint-plugin@8.64.0':
resolution: {integrity: sha512-4EQM77WgVNxj7OkL/5b/D/xZsw00G577+UriYTC7JF5opcF3T2AuoeY7ueLaZgSVjSgCS6yOAJB5bRGLPSJUzA==} resolution: {integrity: sha512-CGvQPBxN3wZLu6Rz2kFUpZeoCm78xUic92ck39KPePkO1NPOwjCqdQnm5Q87tpWw9vcBvW8XLrDXjH9PWYtJ3Q==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
'@typescript-eslint/parser': ^8.62.1 '@typescript-eslint/parser': ^8.64.0
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.1.0' typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/parser@8.62.1': '@typescript-eslint/parser@8.64.0':
resolution: {integrity: sha512-sPhE4iHuJDSvoAiec+Ro8JyXw8f0ql13HFR82P99nCm9GwTEKG0KYLvDe6REk8BCXuit6vJAv/Yxg5ABaNS2rA==} resolution: {integrity: sha512-KA0OshtlcCCXmbfqyZkM5pV3/WNraJf7DkJRLpyrmwPtud57H5BDX7C3k0LPSPxpprfRL+cJDGabF10mvNCoCw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.1.0' typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/project-service@8.62.1': '@typescript-eslint/project-service@8.64.0':
resolution: {integrity: sha512-yQ3RgY5RkSBpsNS1Bx/JQEcA24FOSdfGktoyprAr5u18390UQdtVcfnEv4nIrIshNnavlVyZBKxQwT1fIAE6cg==} resolution: {integrity: sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
typescript: '>=4.8.4 <6.1.0' typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/scope-manager@8.62.1': '@typescript-eslint/scope-manager@8.64.0':
resolution: {integrity: sha512-r4d249KbQ1SFdpeStvob8Ih6aPPIzfqllPVOtvhve6ZcpuVcYo5/7zUWckKpHE7StASX4kTKZTLf0WQm/wPkcg==} resolution: {integrity: sha512-CXEaFdYXjSTgKhisNkwCcJwTP8Pl+fmRrEQrri4nm3vU743bALrxzLmq7fHG/7e6a5xO0lDYeURpZmBuhHk54w==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/tsconfig-utils@8.62.1': '@typescript-eslint/tsconfig-utils@8.64.0':
resolution: {integrity: sha512-xadytJqX9vJVQ2fdQjkcIVigwaOJNWkpjdLt6cEQ+xPnrI1fkp+/jZE/I97k9KUjqtpd25i0HeyZf3T6dutv2g==} resolution: {integrity: sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
typescript: '>=4.8.4 <6.1.0' typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/type-utils@8.62.1': '@typescript-eslint/type-utils@8.64.0':
resolution: {integrity: sha512-aXM5xlqXiTxPibXB93cLAURfT3rlizf7uMXISCXy66Isr/9hISJx3yDsKl0L7lKa51b8JpFuNKby0/O0pEm9jg==} resolution: {integrity: sha512-XWG4Fmmv/6SvyS9nH8jWrKs6terwJvE8cyRt1CzYYqzp9OrPhCT4cMc/f7C6RZCwG+qMmiffJS1/qJP8G1URtg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.1.0' typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/types@8.62.1': '@typescript-eslint/types@8.64.0':
resolution: {integrity: sha512-ooCzJFaf+Hg+uG6fA3NRFGuFjlfNlDhBthbv4ZPU/0elCAFUfnyXUvf/WOpHz/jYwSmvU2GkR2LtyUfy1AxZ1Q==} resolution: {integrity: sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/typescript-estree@8.62.1': '@typescript-eslint/typescript-estree@8.64.0':
resolution: {integrity: sha512-xMcW9oP9u7fAMXYs9A65CVmtLQe2r//oXINHfi8HV+oiqhih17sbLdhXr4540YWlgpDKQdY854OL5ZrdCiQsAA==} resolution: {integrity: sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
typescript: '>=4.8.4 <6.1.0' typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/utils@8.62.1': '@typescript-eslint/utils@8.64.0':
resolution: {integrity: sha512-sHtbPfuKNZCG+ih8SyjjucqRntSVmp8XgL5u6o9mAhiSn8ds5o/M/XdM0abweme2Tln3szOstOrZ9OXitvPh0g==} resolution: {integrity: sha512-aJUGVB3+U0htrrCjoA8qukw8cm8fNCGAxK/tVoS70k8aeb7DETKeFozRiVFIwEeN9WJLsjaP3ph8I60tY2XZoQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.1.0' typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/visitor-keys@8.62.1': '@typescript-eslint/visitor-keys@8.64.0':
resolution: {integrity: sha512-4g3BLxfdTMy8iZG0MaBkadnlRrCJ74cQiFbyEVMrkwIoqdyaXXQM22cotDvrl4x28wgIZ9rEJRoM+mmhSJpJ1g==} resolution: {integrity: sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@unblockneteasemusic/server@0.28.0': '@unblockneteasemusic/server@0.28.0':
@ -509,19 +509,19 @@ packages:
bl@4.1.0: bl@4.1.0:
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
body-parser@1.20.5: body-parser@1.20.6:
resolution: {integrity: sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==} resolution: {integrity: sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
body-parser@2.3.0: body-parser@2.3.0:
resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==} resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==}
engines: {node: '>=18'} engines: {node: '>=18'}
brace-expansion@1.1.15: brace-expansion@1.1.16:
resolution: {integrity: sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==} resolution: {integrity: sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==}
brace-expansion@2.1.1: brace-expansion@2.1.2:
resolution: {integrity: sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==} resolution: {integrity: sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==}
brace-expansion@5.0.7: brace-expansion@5.0.7:
resolution: {integrity: sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==} resolution: {integrity: sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==}
@ -985,8 +985,8 @@ packages:
resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24} engines: {node: ^20.19.0 || ^22.13.0 || >=24}
eslint@9.39.4: eslint@9.39.5:
resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==} resolution: {integrity: sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
hasBin: true hasBin: true
peerDependencies: peerDependencies:
@ -1354,8 +1354,8 @@ packages:
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
iconv-lite@0.7.2: iconv-lite@0.7.3:
resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
ieee754@1.2.1: ieee754@1.2.1:
@ -1368,8 +1368,8 @@ packages:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'} engines: {node: '>= 4'}
ignore@7.0.5: ignore@7.0.6:
resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} resolution: {integrity: sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==}
engines: {node: '>= 4'} engines: {node: '>= 4'}
import-fresh@3.3.1: import-fresh@3.3.1:
@ -1741,8 +1741,8 @@ packages:
multistream@4.1.0: multistream@4.1.0:
resolution: {integrity: sha512-J1XDiAmmNpRCBfIWJv+n0ymC4ABcf/Pl+5YvC5B/D2f/2+8PtHvCNxMPKiQcZyi922Hq69J2YOpb1pTywfifyw==} resolution: {integrity: sha512-J1XDiAmmNpRCBfIWJv+n0ymC4ABcf/Pl+5YvC5B/D2f/2+8PtHvCNxMPKiQcZyi922Hq69J2YOpb1pTywfifyw==}
music-metadata@11.13.0: music-metadata@11.14.0:
resolution: {integrity: sha512-uXRaov9dfjSpQufXIU7sMxVZnh+FilCQv2mXn+K5EJ/decP3dTWrgvPYa5r6MtRbieNSCE708Da4J0u1UGfQIw==} resolution: {integrity: sha512-RyOSq98kuVfXB1emJ+NjBF0av8Ph3oBuqNy+Z5sFFfLhjYrkBQEB53V8u+U0RNTVwNo20WoPUwNkfKwZfrOqmQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
napi-build-utils@1.0.2: napi-build-utils@1.0.2:
@ -1766,8 +1766,8 @@ packages:
next-tick@1.1.0: next-tick@1.1.0:
resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==}
node-abi@3.93.0: node-abi@3.94.0:
resolution: {integrity: sha512-Cu6yUpX5Iavugm8BeX7c0wgU9CvOqfd1yM6A1d2q2ZMjym7GjpASv2GdRcTq3Fx+Sb5OgBkEEpw4VnAbY6Y5RA==} resolution: {integrity: sha512-W5ZNO5KRPB5TkYmGVD9F6YqhsglXJzE6etpbmT+f6EQElhiX/UTG551cnsRGvLG3fyZEg9HwaDmNmj5nwJ4z9g==}
engines: {node: '>=10'} engines: {node: '>=10'}
node-fetch@2.7.0: node-fetch@2.7.0:
@ -1916,8 +1916,8 @@ packages:
resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==}
engines: {node: '>=8.6'} engines: {node: '>=8.6'}
picomatch@4.0.4: picomatch@4.0.5:
resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==}
engines: {node: '>=12'} engines: {node: '>=12'}
pino-abstract-transport@0.5.0: pino-abstract-transport@0.5.0:
@ -2006,8 +2006,8 @@ packages:
resolution: {integrity: sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==} resolution: {integrity: sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==}
engines: {node: '>=6.0.0'} engines: {node: '>=6.0.0'}
prettier@3.9.4: prettier@3.9.5:
resolution: {integrity: sha512-yWG/o/4oJfo036EKAfK6ACAoDOfHeRHx4tuxkfBZiauURiaSmYwlpOr5LQqKtIkRD2z1PLteme2WoxEnj4tHTg==} resolution: {integrity: sha512-/FVl766LpUfB5vXgCYOYa0MeV/441Ia99AeICQIQFTY/Nw0roZwULcXpku5i1/m5kt/baz+s4Zogspd839HSMg==}
engines: {node: '>=14'} engines: {node: '>=14'}
hasBin: true hasBin: true
@ -2328,8 +2328,8 @@ packages:
resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
string-width@8.2.1: string-width@8.2.2:
resolution: {integrity: sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==} resolution: {integrity: sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==}
engines: {node: '>=20'} engines: {node: '>=20'}
string.prototype.trim@1.2.11: string.prototype.trim@1.2.11:
@ -2687,9 +2687,9 @@ snapshots:
'@borewit/text-codec@0.2.2': {} '@borewit/text-codec@0.2.2': {}
'@eslint-community/eslint-utils@4.9.1(eslint@9.39.4)': '@eslint-community/eslint-utils@4.9.1(eslint@9.39.5)':
dependencies: dependencies:
eslint: 9.39.4 eslint: 9.39.5
eslint-visitor-keys: 3.4.3 eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.12.2': {} '@eslint-community/regexpp@4.12.2': {}
@ -2710,7 +2710,7 @@ snapshots:
dependencies: dependencies:
'@types/json-schema': 7.0.15 '@types/json-schema': 7.0.15
'@eslint/eslintrc@3.3.5': '@eslint/eslintrc@3.3.6':
dependencies: dependencies:
ajv: 6.15.0 ajv: 6.15.0
debug: 4.4.3 debug: 4.4.3
@ -2724,7 +2724,7 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@eslint/js@9.39.4': {} '@eslint/js@9.39.5': {}
'@eslint/object-schema@2.1.7': {} '@eslint/object-schema@2.1.7': {}
@ -2831,7 +2831,7 @@ snapshots:
'@types/busboy': 1.5.4 '@types/busboy': 1.5.4
'@types/express': 5.0.6 '@types/express': 5.0.6
'@types/express-serve-static-core@5.1.1': '@types/express-serve-static-core@5.1.2':
dependencies: dependencies:
'@types/node': 25.9.2 '@types/node': 25.9.2
'@types/qs': 6.15.1 '@types/qs': 6.15.1
@ -2841,7 +2841,7 @@ snapshots:
'@types/express@5.0.6': '@types/express@5.0.6':
dependencies: dependencies:
'@types/body-parser': 1.19.6 '@types/body-parser': 1.19.6
'@types/express-serve-static-core': 5.1.1 '@types/express-serve-static-core': 5.1.2
'@types/serve-static': 2.2.0 '@types/serve-static': 2.2.0
'@types/http-errors@2.0.5': {} '@types/http-errors@2.0.5': {}
@ -2867,72 +2867,72 @@ snapshots:
'@types/http-errors': 2.0.5 '@types/http-errors': 2.0.5
'@types/node': 25.9.2 '@types/node': 25.9.2
'@typescript-eslint/eslint-plugin@8.62.1(@typescript-eslint/parser@8.62.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': '@typescript-eslint/eslint-plugin@8.64.0(@typescript-eslint/parser@8.64.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5)(typescript@5.9.3)':
dependencies: dependencies:
'@eslint-community/regexpp': 4.12.2 '@eslint-community/regexpp': 4.12.2
'@typescript-eslint/parser': 8.62.1(eslint@9.39.4)(typescript@5.9.3) '@typescript-eslint/parser': 8.64.0(eslint@9.39.5)(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.62.1 '@typescript-eslint/scope-manager': 8.64.0
'@typescript-eslint/type-utils': 8.62.1(eslint@9.39.4)(typescript@5.9.3) '@typescript-eslint/type-utils': 8.64.0(eslint@9.39.5)(typescript@5.9.3)
'@typescript-eslint/utils': 8.62.1(eslint@9.39.4)(typescript@5.9.3) '@typescript-eslint/utils': 8.64.0(eslint@9.39.5)(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.62.1 '@typescript-eslint/visitor-keys': 8.64.0
eslint: 9.39.4 eslint: 9.39.5
ignore: 7.0.5 ignore: 7.0.6
natural-compare: 1.4.0 natural-compare: 1.4.0
ts-api-utils: 2.5.0(typescript@5.9.3) ts-api-utils: 2.5.0(typescript@5.9.3)
typescript: 5.9.3 typescript: 5.9.3
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/parser@8.62.1(eslint@9.39.4)(typescript@5.9.3)': '@typescript-eslint/parser@8.64.0(eslint@9.39.5)(typescript@5.9.3)':
dependencies: dependencies:
'@typescript-eslint/scope-manager': 8.62.1 '@typescript-eslint/scope-manager': 8.64.0
'@typescript-eslint/types': 8.62.1 '@typescript-eslint/types': 8.64.0
'@typescript-eslint/typescript-estree': 8.62.1(typescript@5.9.3) '@typescript-eslint/typescript-estree': 8.64.0(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.62.1 '@typescript-eslint/visitor-keys': 8.64.0
debug: 4.4.3 debug: 4.4.3
eslint: 9.39.4 eslint: 9.39.5
typescript: 5.9.3 typescript: 5.9.3
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/project-service@8.62.1(typescript@5.9.3)': '@typescript-eslint/project-service@8.64.0(typescript@5.9.3)':
dependencies: dependencies:
'@typescript-eslint/tsconfig-utils': 8.62.1(typescript@5.9.3) '@typescript-eslint/tsconfig-utils': 8.64.0(typescript@5.9.3)
'@typescript-eslint/types': 8.62.1 '@typescript-eslint/types': 8.64.0
debug: 4.4.3 debug: 4.4.3
typescript: 5.9.3 typescript: 5.9.3
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/scope-manager@8.62.1': '@typescript-eslint/scope-manager@8.64.0':
dependencies: dependencies:
'@typescript-eslint/types': 8.62.1 '@typescript-eslint/types': 8.64.0
'@typescript-eslint/visitor-keys': 8.62.1 '@typescript-eslint/visitor-keys': 8.64.0
'@typescript-eslint/tsconfig-utils@8.62.1(typescript@5.9.3)': '@typescript-eslint/tsconfig-utils@8.64.0(typescript@5.9.3)':
dependencies: dependencies:
typescript: 5.9.3 typescript: 5.9.3
'@typescript-eslint/type-utils@8.62.1(eslint@9.39.4)(typescript@5.9.3)': '@typescript-eslint/type-utils@8.64.0(eslint@9.39.5)(typescript@5.9.3)':
dependencies: dependencies:
'@typescript-eslint/types': 8.62.1 '@typescript-eslint/types': 8.64.0
'@typescript-eslint/typescript-estree': 8.62.1(typescript@5.9.3) '@typescript-eslint/typescript-estree': 8.64.0(typescript@5.9.3)
'@typescript-eslint/utils': 8.62.1(eslint@9.39.4)(typescript@5.9.3) '@typescript-eslint/utils': 8.64.0(eslint@9.39.5)(typescript@5.9.3)
debug: 4.4.3 debug: 4.4.3
eslint: 9.39.4 eslint: 9.39.5
ts-api-utils: 2.5.0(typescript@5.9.3) ts-api-utils: 2.5.0(typescript@5.9.3)
typescript: 5.9.3 typescript: 5.9.3
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/types@8.62.1': {} '@typescript-eslint/types@8.64.0': {}
'@typescript-eslint/typescript-estree@8.62.1(typescript@5.9.3)': '@typescript-eslint/typescript-estree@8.64.0(typescript@5.9.3)':
dependencies: dependencies:
'@typescript-eslint/project-service': 8.62.1(typescript@5.9.3) '@typescript-eslint/project-service': 8.64.0(typescript@5.9.3)
'@typescript-eslint/tsconfig-utils': 8.62.1(typescript@5.9.3) '@typescript-eslint/tsconfig-utils': 8.64.0(typescript@5.9.3)
'@typescript-eslint/types': 8.62.1 '@typescript-eslint/types': 8.64.0
'@typescript-eslint/visitor-keys': 8.62.1 '@typescript-eslint/visitor-keys': 8.64.0
debug: 4.4.3 debug: 4.4.3
minimatch: 10.2.5 minimatch: 10.2.5
semver: 7.8.5 semver: 7.8.5
@ -2942,20 +2942,20 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/utils@8.62.1(eslint@9.39.4)(typescript@5.9.3)': '@typescript-eslint/utils@8.64.0(eslint@9.39.5)(typescript@5.9.3)':
dependencies: dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.5)
'@typescript-eslint/scope-manager': 8.62.1 '@typescript-eslint/scope-manager': 8.64.0
'@typescript-eslint/types': 8.62.1 '@typescript-eslint/types': 8.64.0
'@typescript-eslint/typescript-estree': 8.62.1(typescript@5.9.3) '@typescript-eslint/typescript-estree': 8.64.0(typescript@5.9.3)
eslint: 9.39.4 eslint: 9.39.5
typescript: 5.9.3 typescript: 5.9.3
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/visitor-keys@8.62.1': '@typescript-eslint/visitor-keys@8.64.0':
dependencies: dependencies:
'@typescript-eslint/types': 8.62.1 '@typescript-eslint/types': 8.64.0
eslint-visitor-keys: 5.0.1 eslint-visitor-keys: 5.0.1
'@unblockneteasemusic/server@0.28.0': '@unblockneteasemusic/server@0.28.0':
@ -3098,7 +3098,7 @@ snapshots:
inherits: 2.0.4 inherits: 2.0.4
readable-stream: 3.6.2 readable-stream: 3.6.2
body-parser@1.20.5: body-parser@1.20.6:
dependencies: dependencies:
bytes: 3.1.2 bytes: 3.1.2
content-type: 1.0.5 content-type: 1.0.5
@ -3121,7 +3121,7 @@ snapshots:
content-type: 2.0.0 content-type: 2.0.0
debug: 4.4.3 debug: 4.4.3
http-errors: 2.0.1 http-errors: 2.0.1
iconv-lite: 0.7.2 iconv-lite: 0.7.3
on-finished: 2.4.1 on-finished: 2.4.1
qs: 6.15.3 qs: 6.15.3
raw-body: 3.0.2 raw-body: 3.0.2
@ -3129,12 +3129,12 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
brace-expansion@1.1.15: brace-expansion@1.1.16:
dependencies: dependencies:
balanced-match: 1.0.2 balanced-match: 1.0.2
concat-map: 0.0.1 concat-map: 0.0.1
brace-expansion@2.1.1: brace-expansion@2.1.2:
dependencies: dependencies:
balanced-match: 1.0.2 balanced-match: 1.0.2
@ -3229,7 +3229,7 @@ snapshots:
cli-truncate@5.2.0: cli-truncate@5.2.0:
dependencies: dependencies:
slice-ansi: 8.0.0 slice-ansi: 8.0.0
string-width: 8.2.1 string-width: 8.2.2
cliui@6.0.0: cliui@6.0.0:
dependencies: dependencies:
@ -3648,22 +3648,22 @@ snapshots:
esrecurse: 4.3.0 esrecurse: 4.3.0
estraverse: 4.3.0 estraverse: 4.3.0
eslint-config-prettier@10.1.8(eslint@9.39.4): eslint-config-prettier@10.1.8(eslint@9.39.5):
dependencies: dependencies:
eslint: 9.39.4 eslint: 9.39.5
eslint-plugin-html@8.1.4: eslint-plugin-html@8.1.4:
dependencies: dependencies:
htmlparser2: 10.1.0 htmlparser2: 10.1.0
eslint-plugin-prettier@5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.4))(eslint@9.39.4)(prettier@3.9.4): eslint-plugin-prettier@5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.5))(eslint@9.39.5)(prettier@3.9.5):
dependencies: dependencies:
eslint: 9.39.4 eslint: 9.39.5
prettier: 3.9.4 prettier: 3.9.5
prettier-linter-helpers: 1.0.1 prettier-linter-helpers: 1.0.1
synckit: 0.11.13 synckit: 0.11.13
optionalDependencies: optionalDependencies:
eslint-config-prettier: 10.1.8(eslint@9.39.4) eslint-config-prettier: 10.1.8(eslint@9.39.5)
eslint-scope@8.4.0: eslint-scope@8.4.0:
dependencies: dependencies:
@ -3676,15 +3676,15 @@ snapshots:
eslint-visitor-keys@5.0.1: {} eslint-visitor-keys@5.0.1: {}
eslint@9.39.4: eslint@9.39.5:
dependencies: dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.5)
'@eslint-community/regexpp': 4.12.2 '@eslint-community/regexpp': 4.12.2
'@eslint/config-array': 0.21.2 '@eslint/config-array': 0.21.2
'@eslint/config-helpers': 0.4.2 '@eslint/config-helpers': 0.4.2
'@eslint/core': 0.17.0 '@eslint/core': 0.17.0
'@eslint/eslintrc': 3.3.5 '@eslint/eslintrc': 3.3.6
'@eslint/js': 9.39.4 '@eslint/js': 9.39.5
'@eslint/plugin-kit': 0.4.1 '@eslint/plugin-kit': 0.4.1
'@humanfs/node': 0.16.8 '@humanfs/node': 0.16.8
'@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/module-importer': 1.0.1
@ -3810,7 +3810,7 @@ snapshots:
dependencies: dependencies:
accepts: 1.3.8 accepts: 1.3.8
array-flatten: 1.1.1 array-flatten: 1.1.1
body-parser: 1.20.5 body-parser: 1.20.6
content-disposition: 0.5.4 content-disposition: 0.5.4
content-type: 1.0.5 content-type: 1.0.5
cookie: 0.7.2 cookie: 0.7.2
@ -3903,9 +3903,9 @@ snapshots:
dependencies: dependencies:
reusify: 1.1.0 reusify: 1.1.0
fdir@6.5.0(picomatch@4.0.4): fdir@6.5.0(picomatch@4.0.5):
optionalDependencies: optionalDependencies:
picomatch: 4.0.4 picomatch: 4.0.5
file-entry-cache@8.0.0: file-entry-cache@8.0.0:
dependencies: dependencies:
@ -4179,7 +4179,7 @@ snapshots:
dependencies: dependencies:
safer-buffer: 2.1.2 safer-buffer: 2.1.2
iconv-lite@0.7.2: iconv-lite@0.7.3:
dependencies: dependencies:
safer-buffer: 2.1.2 safer-buffer: 2.1.2
@ -4189,7 +4189,7 @@ snapshots:
ignore@5.3.2: {} ignore@5.3.2: {}
ignore@7.0.5: {} ignore@7.0.6: {}
import-fresh@3.3.1: import-fresh@3.3.1:
dependencies: dependencies:
@ -4416,7 +4416,7 @@ snapshots:
dependencies: dependencies:
commander: 14.0.3 commander: 14.0.3
listr2: 9.0.5 listr2: 9.0.5
picomatch: 4.0.4 picomatch: 4.0.5
string-argv: 0.3.2 string-argv: 0.3.2
tinyexec: 1.2.4 tinyexec: 1.2.4
yaml: 2.9.0 yaml: 2.9.0
@ -4504,11 +4504,11 @@ snapshots:
minimatch@3.1.5: minimatch@3.1.5:
dependencies: dependencies:
brace-expansion: 1.1.15 brace-expansion: 1.1.16
minimatch@9.0.9: minimatch@9.0.9:
dependencies: dependencies:
brace-expansion: 2.1.1 brace-expansion: 2.1.2
minimist@1.2.8: {} minimist@1.2.8: {}
@ -4555,7 +4555,7 @@ snapshots:
once: 1.4.0 once: 1.4.0
readable-stream: 3.6.2 readable-stream: 3.6.2
music-metadata@11.13.0: music-metadata@11.14.0:
dependencies: dependencies:
'@borewit/text-codec': 0.2.2 '@borewit/text-codec': 0.2.2
'@tokenizer/token': 0.3.0 '@tokenizer/token': 0.3.0
@ -4582,7 +4582,7 @@ snapshots:
next-tick@1.1.0: {} next-tick@1.1.0: {}
node-abi@3.93.0: node-abi@3.94.0:
dependencies: dependencies:
semver: 7.8.5 semver: 7.8.5
@ -4737,7 +4737,7 @@ snapshots:
picomatch@2.3.2: {} picomatch@2.3.2: {}
picomatch@4.0.4: {} picomatch@4.0.5: {}
pino-abstract-transport@0.5.0: pino-abstract-transport@0.5.0:
dependencies: dependencies:
@ -4884,7 +4884,7 @@ snapshots:
minimist: 1.2.8 minimist: 1.2.8
mkdirp-classic: 0.5.3 mkdirp-classic: 0.5.3
napi-build-utils: 1.0.2 napi-build-utils: 1.0.2
node-abi: 3.93.0 node-abi: 3.94.0
pump: 3.0.4 pump: 3.0.4
rc: 1.2.8 rc: 1.2.8
simple-get: 4.0.1 simple-get: 4.0.1
@ -4899,7 +4899,7 @@ snapshots:
dependencies: dependencies:
fast-diff: 1.3.0 fast-diff: 1.3.0
prettier@3.9.4: {} prettier@3.9.5: {}
process-nextick-args@2.0.1: {} process-nextick-args@2.0.1: {}
@ -4957,7 +4957,7 @@ snapshots:
dependencies: dependencies:
bytes: 3.1.2 bytes: 3.1.2
http-errors: 2.0.1 http-errors: 2.0.1
iconv-lite: 0.7.2 iconv-lite: 0.7.3
unpipe: 1.0.0 unpipe: 1.0.0
rc@1.2.8: rc@1.2.8:
@ -5294,7 +5294,7 @@ snapshots:
get-east-asian-width: 1.6.0 get-east-asian-width: 1.6.0
strip-ansi: 7.2.0 strip-ansi: 7.2.0
string-width@8.2.1: string-width@8.2.2:
dependencies: dependencies:
get-east-asian-width: 1.6.0 get-east-asian-width: 1.6.0
strip-ansi: 7.2.0 strip-ansi: 7.2.0
@ -5390,8 +5390,8 @@ snapshots:
tinyglobby@0.2.17: tinyglobby@0.2.17:
dependencies: dependencies:
fdir: 6.5.0(picomatch@4.0.4) fdir: 6.5.0(picomatch@4.0.5)
picomatch: 4.0.4 picomatch: 4.0.5
to-fast-properties@2.0.0: {} to-fast-properties@2.0.0: {}

View File

@ -1395,8 +1395,6 @@ tags: 歌单标签
说明 : 调用此接口 , 传入类型和歌单 id 可收藏歌单或者取消收藏歌单 说明 : 调用此接口 , 传入类型和歌单 id 可收藏歌单或者取消收藏歌单
!> 警告: 在`v4.29.7`版本后, 在网易云登陆后请求要带上`timestamp`字段, 否则会导致请求不合法
**必选参数 :** **必选参数 :**
`t` : 类型,1:收藏,2:取消收藏 `t` : 类型,1:收藏,2:取消收藏
@ -5473,7 +5471,17 @@ let data = encodeURIComponent(
**接口地址 :** `/song/cloud/download` **接口地址 :** `/song/cloud/download`
**调用例子 :** `/song/cloud/download?id=123456789` **调用例子 :** `/song/cloud/download?id=123456789`'
### 获取30分钟免费听歌时长
说明 : 登录后调用此接口, 获取30分钟免费听歌时长
!> 警告: 通过调取接口出现的任何问题由调用者自行承担
**接口地址 :** `/ad/listening/rights/gain`
**调用例子 :** `/ad/listening/rights/gain`
## 离线访问此文档 ## 离线访问此文档

312
public/free_listen.html Normal file
View File

@ -0,0 +1,312 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>免费听 — 网易云音乐 API Enhanced</title>
<style>
:root {
--fg: #333;
--muted: #666;
--border: #ddd;
--bg: #f5f5f5;
--panel: #ffffff;
--accent: #333;
}
* { box-sizing: border-box; }
html, body { height: 100%; }
body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; color: var(--fg); background: var(--bg); line-height: 1.6; }
.container { max-width: 800px; margin: 40px auto; padding: 0 20px; }
@media (max-width: 480px) {
.container { margin: 20px auto; padding: 0 16px; }
header.site-header h1 { font-size: 22px; }
.block { padding: 16px; }
}
header.site-header { margin-bottom: 24px; }
header.site-header h1 { font-size: 24px; font-weight: 600; margin: 0; }
.sub { margin-top: 4px; color: var(--muted); font-size: 14px; }
.block { background: var(--panel); border: 1px solid var(--border); border-radius: 12px; padding: 20px; margin-bottom: 16px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); }
.block h3 { margin: 0 0 4px; font-size: 15px; font-weight: 600; }
.block .desc { margin: 0 0 12px; color: var(--muted); font-size: 13px; }
pre { margin: 0; background: #f9f9f9; border: 1px solid var(--border); border-radius: 6px; padding: 12px; overflow-x: auto; white-space: pre-wrap; word-break: break-all; font-family: 'Courier New', monospace; font-size: 13px; max-height: 200px; overflow: auto; }
pre.data { max-height: 360px; }
.row { display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap; }
button { padding: 8px 18px; border-radius: 8px; border: 1px solid var(--border); font-size: 14px; font-weight: 500; cursor: pointer; background: var(--panel); color: var(--fg); transition: all .15s; }
button:hover { background: #eee; }
button.primary { background: var(--fg); color: var(--panel); border-color: var(--fg); }
button.primary:hover { opacity: .85; }
button:disabled { opacity: .4; cursor: not-allowed; }
button.danger { background: #a11; color: #fff; border-color: #a11; }
button.danger:hover { opacity: .85; }
.tag { display: inline-block; padding: 2px 10px; border-radius: 20px; font-size: 12px; font-weight: 500; border: 1px solid var(--border); }
.tag.ok { background: #e6f7e6; color: #1a7a1a; border-color: #b7e6b7; }
.tag.no { background: #ffe6e6; color: #a11; border-color: #f5c6c6; }
.tag.busy { background: #fff3cd; color: #856404; border-color: #ffeeba; }
.flex { display: flex; align-items: center; gap: 10px; }
.flex.sb { justify-content: space-between; }
.step { display: flex; gap: 12px; align-items: flex-start; }
.step .num { width: 26px; height: 26px; border-radius: 50%; background: var(--fg); color: var(--panel); display: flex; align-items: center; justify-content: center; font-size: 13px; font-weight: 600; flex-shrink: 0; margin-top: 1px; }
.step .num.done { background: #1a7a1a; }
.step .body { flex: 1; min-width: 0; }
input[type=text] { width: 100%; padding: 8px 12px; border-radius: 6px; border: 1px solid var(--border); font-size: 14px; font-family: inherit; margin-top: 6px; }
input[type=text]:focus { outline: 2px solid var(--fg); outline-offset: -1px; border-color: transparent; }
footer.site-footer { margin-top: 32px; padding-top: 12px; border-top: 1px solid var(--border); color: var(--muted); text-align: center; font-size: 13px; }
footer.site-footer a { color: var(--fg); text-decoration: none; }
footer.site-footer a:hover { border-bottom: 1px dotted var(--fg); }
.info-grid { display: grid; grid-template-columns: 80px 1fr; gap: 4px 12px; font-size: 13px; margin-top: 8px; }
.info-grid .lbl { color: var(--muted); }
</style>
</head>
<body>
<main class="container">
<header class="site-header">
<h1>🎧 免费听 · 看广告获取时长</h1>
<p class="sub">模拟 App 内看激励视频广告 → 领取免费听权益的完整流程</p>
</header>
<!-- 步骤 1: 注册 Token -->
<div class="block">
<div class="step">
<div class="num" id="s1num">1</div>
<div class="body">
<div class="flex sb">
<h3>注册反作弊 Token</h3>
<span id="s1tag" class="tag no">未开始</span>
</div>
<p class="desc">调用易盾接口获取实时 token广告接口需要此 token 才能正常响应</p>
<pre id="s1pre">(尚未执行)</pre>
<div class="row">
<button id="step1" class="primary">① 注册 Token</button>
</div>
</div>
</div>
</div>
<!-- 步骤 2: 获取广告 -->
<div class="block">
<div class="step">
<div class="num" id="s2num">2</div>
<div class="body">
<div class="flex sb">
<h3>获取广告 &amp; 提取 reqId</h3>
<span id="s2tag" class="tag no">未开始</span>
</div>
<p class="desc">请求广告位,从返回的 extJson.contextInfo.req_id 提取用于领取权益的标识</p>
<pre id="s2pre" class="data">(尚未执行)</pre>
<div class="row">
<button id="step2" class="primary" disabled>② 获取广告</button>
<button id="step2b" disabled>换一个广告位</button>
</div>
<div class="info-grid" id="s2info" style="display:none">
<div class="lbl">广告 ID</div><div id="s2adId">-</div>
<div class="lbl">reqId</div><div id="s2reqId">-</div>
<div class="lbl">文案</div><div id="s2text">-</div>
</div>
</div>
</div>
</div>
<!-- 步骤 3: 领取权益 -->
<div class="block">
<div class="step">
<div class="num" id="s3num">3</div>
<div class="body">
<div class="flex sb">
<h3>领取免费听权益</h3>
<span id="s3tag" class="tag no">未开始</span>
</div>
<p class="desc">传入 reqId 及相关参数,领取免费听权益(模拟点击广告后的领取请求)</p>
<pre id="s3pre" class="data">(尚未执行)</pre>
<div class="row">
<button id="step3" class="primary" disabled>③ 领取权益</button>
</div>
<div class="info-grid" id="s3info" style="display:none">
<div class="lbl">结果</div><div id="s3result">-</div>
</div>
</div>
</div>
</div>
<!-- 一键全流程 -->
<div class="block">
<div class="flex sb">
<h3>一键运行</h3>
<span id="allTag" class="tag no">就绪</span>
</div>
<p class="desc" style="margin-top:4px">按顺序自动执行以上三个步骤</p>
<div class="row">
<button id="runAll" class="primary">▶ 一键获取免费听时长</button>
<button id="clearAll" class="danger">✕ 清空</button>
</div>
</div>
<footer class="site-footer">
<a href="/">← 返回首页</a>
</footer>
</main>
<script>
const $ = (id) => document.getElementById(id)
const base = location.origin
function setTag(id, type, text) {
const el = $(id)
el.className = 'tag ' + type
el.textContent = text
}
function setNum(id, done) {
$(id).className = 'num' + (done ? ' done' : '')
}
function nocache(url) {
const sep = url.indexOf('?') > -1 ? '&' : '?'
return url + sep + '_t=' + Date.now()
}
// ===== 步骤 1: 注册 Token =====
async function step1() {
const btn = $('step1')
btn.disabled = true
setTag('s1tag', 'busy', '注册中…')
setNum('s1num', false)
try {
const res = await fetch(nocache(base + '/register/checktoken?refresh=1'))
const data = await res.json()
$('s1pre').textContent = JSON.stringify(data, null, 2)
if (data.token) {
setTag('s1tag', 'ok', '已注册')
setNum('s1num', true)
$('step2').disabled = false
$('step2b').disabled = false
return data.token
}
setTag('s1tag', 'no', '失败')
return null
} catch (e) {
$('s1pre').textContent = JSON.stringify({ error: e.message }, null, 2)
setTag('s1tag', 'no', e.message)
return null
} finally {
btn.disabled = false
}
}
// ===== 步骤 2: 获取广告 =====
async function step2(typeId) {
const btn = $('step2')
const btnB = $('step2b')
btn.disabled = btnB.disabled = true
setTag('s2tag', 'busy', '获取中…')
setNum('s2num', false)
$('s2info').style.display = 'none'
try {
let url = base + '/ad/get' + (typeId ? '?type_ids=' + encodeURIComponent(typeId) : '')
const res = await fetch(nocache(url))
const data = await res.json()
$('s2pre').textContent = JSON.stringify(data, null, 2)
if (data.code === 200 && data.extra && data.extra.reqId) {
setTag('s2tag', 'ok', '已获取')
setNum('s2num', true)
$('s2info').style.display = 'grid'
const adKeys = data.ads ? Object.keys(data.ads) : []
let adId = '-', text = '-'
if (adKeys.length > 0) {
const ad = data.ads[adKeys[0]]
adId = ad.ad_id || '-'
text = ad.text || '-'
}
$('s2adId').textContent = adId
$('s2reqId').textContent = data.extra.reqId
$('s2text').textContent = text
$('step3').disabled = false
return data.extra.reqId
}
setTag('s2tag', 'no', '未获取到 reqId')
return null
} catch (e) {
$('s2pre').textContent = JSON.stringify({ error: e.message }, null, 2)
setTag('s2tag', 'no', e.message)
return null
} finally {
btn.disabled = false
btnB.disabled = false
}
}
$('step2').onclick = () => step2()
$('step2b').onclick = () => step2('["400002_0"]')
// ===== 步骤 3: 领取权益 =====
async function step3() {
const btn = $('step3')
btn.disabled = true
setTag('s3tag', 'busy', '领取中…')
setNum('s3num', false)
$('s3info').style.display = 'none'
try {
const res = await fetch(nocache(base + '/ad/listening/rights/gain'))
const data = await res.json()
$('s3pre').textContent = JSON.stringify(data, null, 2)
if (data.code === 200) {
setTag('s3tag', 'ok', '已领取')
setNum('s3num', true)
$('s3info').style.display = 'grid'
const body = data.data || {}
$('s3result').textContent = JSON.stringify(body).substring(0, 200)
return true
}
setTag('s3tag', 'no', '领取失败')
return false
} catch (e) {
$('s3pre').textContent = JSON.stringify({ error: e.message }, null, 2)
setTag('s3tag', 'no', e.message)
return false
} finally {
btn.disabled = false
}
}
$('step3').onclick = step3
// ===== 一键运行 =====
$('runAll').onclick = async function () {
const btn = this
btn.disabled = true
setTag('allTag', 'busy', '运行中…')
await step1()
await step2()
await step3()
setTag('allTag', 'ok', '完成')
btn.disabled = false
}
// ===== 清空 =====
$('clearAll').onclick = function () {
const steps = ['s1', 's2', 's3']
const tags = { s1: '未开始', s2: '未开始', s3: '未开始' }
steps.forEach(s => {
setTag(s + 'tag', 'no', tags[s])
setNum(s + 'num', false)
$(s + 'pre').textContent = '(尚未执行)'
const info = $(s + 'info')
if (info) info.style.display = 'none'
})
$('step1').disabled = false
$('step2').disabled = true
$('step2b').disabled = true
$('step3').disabled = true
setTag('allTag', 'no', '就绪')
$('runAll').disabled = false
}
</script>
</body>
</html>

View File

@ -96,7 +96,9 @@ curl -s {origin}/search?keywords=网易云</code></pre>
<a href="/listen_together_host.html">一起听示例</a> · <a href="/listen_together_host.html">一起听示例</a> ·
<a href="/playlist_cover_update.html">更新歌单封面示例</a> · <a href="/playlist_cover_update.html">更新歌单封面示例</a> ·
<a href="/avatar_update.html">头像更新示例</a> · <a href="/avatar_update.html">头像更新示例</a> ·
<a href="/scrobble.html">听歌打卡示例</a> <a href="/scrobble.html">听歌打卡示例</a> ·
<a href="/yidun.html">获取 CheckToken</a> ·
<a href="/free_listen.html">免费听权益</a>
</div> </div>
</section> </section>

119
public/yidun.html Normal file
View File

@ -0,0 +1,119 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Token 注册器 — 网易云音乐 API Enhanced</title>
<style>
:root {
--fg: #333;
--muted: #666;
--border: #ddd;
--bg: #f5f5f5;
--panel: #ffffff;
--accent: #333;
}
* { box-sizing: border-box; }
html, body { height: 100%; }
body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; color: var(--fg); background: var(--bg); line-height: 1.6; }
.container { max-width: 720px; margin: 40px auto; padding: 0 20px; }
@media (max-width: 480px) {
.container { margin: 20px auto; padding: 0 16px; }
header.site-header h1 { font-size: 22px; }
.block { padding: 16px; }
}
header.site-header { margin-bottom: 24px; }
header.site-header h1 { font-size: 24px; font-weight: 600; margin: 0; }
.sub { margin-top: 4px; color: var(--muted); font-size: 14px; }
.block { background: var(--panel); border: 1px solid var(--border); border-radius: 12px; padding: 20px; margin-bottom: 16px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); }
.block h3 { margin: 0 0 12px; font-size: 15px; font-weight: 600; }
pre { margin: 0; background: #f9f9f9; border: 1px solid var(--border); border-radius: 6px; padding: 12px; overflow-x: auto; white-space: pre-wrap; word-break: break-all; font-family: 'Courier New', monospace; font-size: 13px; max-height: 160px; overflow: auto; }
.row { display: flex; gap: 8px; margin-top: 14px; flex-wrap: wrap; }
button { padding: 8px 18px; border-radius: 8px; border: 1px solid var(--border); font-size: 14px; font-weight: 500; cursor: pointer; background: var(--panel); color: var(--fg); transition: all .15s; }
button:hover { background: #eee; }
button.primary { background: var(--fg); color: var(--panel); border-color: var(--fg); }
button.primary:hover { opacity: .85; }
button:disabled { opacity: .4; cursor: not-allowed; }
.tag { display: inline-block; padding: 2px 10px; border-radius: 20px; font-size: 12px; font-weight: 500; border: 1px solid var(--border); }
.tag.ok { background: #e6f7e6; color: #1a7a1a; border-color: #b7e6b7; }
.tag.no { background: #ffe6e6; color: #a11; border-color: #f5c6c6; }
.tag.busy { background: #fff3cd; color: #856404; border-color: #ffeeba; }
.flex { display: flex; align-items: center; gap: 10px; }
.flex.sb { justify-content: space-between; }
.mt-2 { margin-top: 14px; }
footer.site-footer { margin-top: 24px; padding-top: 12px; border-top: 1px solid var(--border); color: var(--muted); text-align: center; font-size: 13px; }
</style>
</head>
<body>
<main class="container">
<header class="site-header">
<h1>🔑 Token 注册器</h1>
<p class="sub">从易盾获取反作弊 token 并注册到服务器,供广告等需要 checkToken 的接口使用</p>
</header>
<div class="block">
<div class="flex sb">
<h3>当前 Token</h3>
<span id="statusTag" class="tag no">未注册</span>
</div>
<pre id="tokenDisplay">(空)</pre>
<div class="row">
<button id="btnGet" class="primary">获取 Token</button>
<button id="btnRefresh">强制刷新</button>
</div>
</div>
<div class="block">
<h3>服务器响应</h3>
<pre id="respDisplay">{}</pre>
</div>
<footer class="site-footer">
<a href="/">← 返回首页</a>
</footer>
</main>
<script>
const $ = (id) => document.getElementById(id)
const base = location.origin
async function call(refresh) {
const btn = $('btnGet')
const btnR = $('btnRefresh')
btn.disabled = btnR.disabled = true
const tag = $('statusTag')
tag.className = 'tag busy'
tag.textContent = '获取中…'
try {
const url = base + '/register/checktoken' + (refresh ? '?refresh=1' : '')
const res = await fetch(url)
const data = await res.json()
$('respDisplay').textContent = JSON.stringify(data, null, 2)
if (data.token) {
$('tokenDisplay').textContent = data.token
tag.className = 'tag ok'
tag.textContent = '已注册'
} else {
$('tokenDisplay').textContent = '(获取失败)'
tag.className = 'tag no'
tag.textContent = '失败'
}
} catch (e) {
$('respDisplay').textContent = JSON.stringify({ error: e.message }, null, 2)
tag.className = 'tag no'
tag.textContent = e.message
}
btn.disabled = btnR.disabled = false
}
$('btnGet').onclick = () => call(false)
$('btnRefresh').onclick = () => call(true)
call(false)
</script>
</body>
</html>

View File

@ -15,6 +15,7 @@
"domain": "https://music.163.com", "domain": "https://music.163.com",
"clDomian": "https://clientlog.music.163.com", "clDomian": "https://clientlog.music.163.com",
"clDomian3": "https://clientlog3.music.163.com", "clDomian3": "https://clientlog3.music.163.com",
"dunDomain": "https://ac.dun.163yun.com",
"encrypt": true, "encrypt": true,
"encryptResponse": false, "encryptResponse": false,
"clientSign": "18:C0:4D:B9:8F:FE@@@453832335F384641365F424635335F303030315F303031425F343434415F343643365F333638332@@@@@@6ff673ef74955b38bce2fa8562d95c976ed4758b1227c4e9ee345987cee17bc9", "clientSign": "18:C0:4D:B9:8F:FE@@@453832335F384641365F424635335F303030315F303031425F343434415F343643365F333638332@@@@@@6ff673ef74955b38bce2fa8562d95c976ed4758b1227c4e9ee345987cee17bc9",

View File

@ -18,6 +18,7 @@ const {
} = require('./index') } = require('./index')
const { URLSearchParams, URL } = require('url') const { URLSearchParams, URL } = require('url')
const { APP_CONF } = require('../util/config.json') const { APP_CONF } = require('../util/config.json')
const { getToken: antiCheatToken } = require('../module/register_checktoken')
// 预先读取匿名token并缓存 // 预先读取匿名token并缓存
const anonymous_token = fs.readFileSync( const anonymous_token = fs.readFileSync(
@ -181,6 +182,8 @@ const generateRequestId = () => {
} }
const createRequest = (uri, data, options) => { const createRequest = (uri, data, options) => {
const token = options.checkToken ? antiCheatToken() : ''
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// 变量声明和初始化 // 变量声明和初始化
const headers = options.headers ? { ...options.headers } : {} const headers = options.headers ? { ...options.headers } : {}
@ -264,6 +267,9 @@ const createRequest = (uri, data, options) => {
headers['x-sdeviceid'] = cookie.sDeviceId || cookie.deviceId headers['x-sdeviceid'] = cookie.sDeviceId || cookie.deviceId
headers['x-buildver'] = xeapiBuildver headers['x-buildver'] = xeapiBuildver
if (cookie.MUSIC_U) headers['x-music-u'] = cookie.MUSIC_U if (cookie.MUSIC_U) headers['x-music-u'] = cookie.MUSIC_U
if (options.checkToken) {
headers['X-antiCheatToken'] = token
}
const xeapiCookie = { const xeapiCookie = {
...cookie, ...cookie,
os: xeapiOs, os: xeapiOs,
@ -302,14 +308,14 @@ const createRequest = (uri, data, options) => {
__csrf: csrfToken, __csrf: csrfToken,
channel: cookie.channel, channel: cookie.channel,
requestId: generateRequestId(), requestId: generateRequestId(),
...(options.checkToken
? { 'X-antiCheatToken': APP_CONF.checkToken }
: {}),
// clientSign: APP_CONF.clientSign, // clientSign: APP_CONF.clientSign,
} }
if (cookie.MUSIC_U) header['MUSIC_U'] = cookie.MUSIC_U if (cookie.MUSIC_U) header['MUSIC_U'] = cookie.MUSIC_U
if (cookie.MUSIC_A) header['MUSIC_A'] = cookie.MUSIC_A if (cookie.MUSIC_A) header['MUSIC_A'] = cookie.MUSIC_A
if (options.checkToken) {
header['X-antiCheatToken'] = token
}
headers['Cookie'] = createHeaderCookie(header) headers['Cookie'] = createHeaderCookie(header)
headers['User-Agent'] = headers['User-Agent'] =