mirror of
https://github.com/NeteaseCloudMusicApiEnhanced/api-enhanced.git
synced 2026-06-28 05:35:08 +00:00
Compare commits
No commits in common. "1454ee216ce9bb4133879bad333e4d2aed21c80b" and "60ee927e3224ab675679d023065225af11bdc693" have entirely different histories.
1454ee216c
...
60ee927e32
@ -225,8 +225,6 @@ pnpm test
|
|||||||
|
|
||||||
[chaunsin/netease-cloud-music](https://github.com/chaunsin/netease-cloud-music)
|
[chaunsin/netease-cloud-music](https://github.com/chaunsin/netease-cloud-music)
|
||||||
|
|
||||||
[folltoshe/netease-report-listen-song](https://github.com/folltoshe/netease-report-listen-song)
|
|
||||||
|
|
||||||
### SDK 生态
|
### SDK 生态
|
||||||
|
|
||||||
| 语言 | 作者 | 地址 | 类型 |
|
| 语言 | 作者 | 地址 | 类型 |
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
// 听歌打卡
|
// 听歌打卡
|
||||||
|
|
||||||
const createOption = require('../util/option.js')
|
const createOption = require('../util/option.js')
|
||||||
const { APP_CONF } = require('../util/config.json')
|
|
||||||
const DOMAIN = APP_CONF.clDomian
|
|
||||||
module.exports = async (query, request) => {
|
module.exports = async (query, request) => {
|
||||||
// 注入 os=osx 的 cookie
|
// 注入 os=osx 的 cookie
|
||||||
let cookie = query.cookie || ''
|
let cookie = query.cookie || ''
|
||||||
@ -58,7 +56,7 @@ module.exports = async (query, request) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const option = createOption(query, 'eapi')
|
const option = createOption(query, 'eapi')
|
||||||
option.domain = DOMAIN
|
option.domain = 'https://clientlog.music.163.com'
|
||||||
|
|
||||||
// 发送两次请求
|
// 发送两次请求
|
||||||
const res1 = await request(`/api/feedback/weblog`, startplayData, option)
|
const res1 = await request(`/api/feedback/weblog`, startplayData, option)
|
||||||
|
|||||||
@ -1,128 +0,0 @@
|
|||||||
// 听歌打卡 - NCBL 加密版 (仿桌面客户端 PLV/PLD 上报)
|
|
||||||
// 复制自 https://github.com/folltoshe/netease-report-listen-song 的 PC 端日志上报方式
|
|
||||||
//
|
|
||||||
// PLV 和 PLD 分两次独立上传
|
|
||||||
|
|
||||||
const {
|
|
||||||
buildPlv,
|
|
||||||
buildPld,
|
|
||||||
buildRecords,
|
|
||||||
extractContext,
|
|
||||||
parseCookie,
|
|
||||||
buildCookieStr,
|
|
||||||
buildMetaJson,
|
|
||||||
doUpload,
|
|
||||||
} = require('../util/ncbl')
|
|
||||||
|
|
||||||
module.exports = async (query, request) => {
|
|
||||||
// --- 参数校验 ---
|
|
||||||
const songId = Number(query.id)
|
|
||||||
if (!songId || isNaN(songId)) {
|
|
||||||
return { status: 400, body: { code: 400, msg: '缺少有效的 id (歌曲ID)' } }
|
|
||||||
}
|
|
||||||
const playTime = Number(query.time)
|
|
||||||
if (isNaN(playTime) || playTime <= 0) {
|
|
||||||
return {
|
|
||||||
status: 400,
|
|
||||||
body: { code: 400, msg: '缺少有效的 time (播放时长)' },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const totalTime = Number(query.total) || playTime
|
|
||||||
const sourceId = String(query.sourceid || query.sourceId || '')
|
|
||||||
const sourceName = query.source || 'list'
|
|
||||||
|
|
||||||
// --- 解析认证上下文 ---
|
|
||||||
const rawCookie = query.cookie || ''
|
|
||||||
const cookieObj = parseCookie(rawCookie)
|
|
||||||
cookieObj.os = 'pc'
|
|
||||||
const ctx = extractContext(cookieObj)
|
|
||||||
|
|
||||||
// 兜底取 token
|
|
||||||
if (!ctx.auth.token && rawCookie) {
|
|
||||||
const parsed =
|
|
||||||
typeof rawCookie === 'string' ? parseCookie(rawCookie) : rawCookie
|
|
||||||
ctx.auth.token = parsed.MUSIC_U || ''
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ctx.auth.token) {
|
|
||||||
return { status: 401, body: { code: 401, msg: '缺少 MUSIC_U 鉴权令牌' } }
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- 构建歌曲和来源 ---
|
|
||||||
const song = {
|
|
||||||
id: songId,
|
|
||||||
name: query.name || '',
|
|
||||||
artist: query.artist || '',
|
|
||||||
bitrate: Number(query.bitrate) || 320,
|
|
||||||
level: query.level || 'exhigh',
|
|
||||||
vip: query.vip === 'true' || query.vip === true,
|
|
||||||
time: totalTime,
|
|
||||||
}
|
|
||||||
const source = {
|
|
||||||
id: sourceId || String(songId),
|
|
||||||
type: 'track',
|
|
||||||
name: sourceName,
|
|
||||||
}
|
|
||||||
|
|
||||||
const metaJson = buildMetaJson(ctx)
|
|
||||||
const cookieStr = buildCookieStr(ctx)
|
|
||||||
|
|
||||||
const ts = Math.floor(Date.now() / 1000)
|
|
||||||
const played = Math.min(playTime, totalTime)
|
|
||||||
|
|
||||||
const plvBody = buildRecords([
|
|
||||||
{ time: ts, action: '_plv', data: buildPlv(ctx, song, source) },
|
|
||||||
])
|
|
||||||
const pldBody = buildRecords([
|
|
||||||
{ time: ts, action: '_pld', data: buildPld(ctx, song, source, played) },
|
|
||||||
])
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 1) 上传 PLV
|
|
||||||
const plv = await doUpload(ctx, metaJson, plvBody, cookieStr, 'PLV')
|
|
||||||
if (!plv.success) {
|
|
||||||
const rateMsg =
|
|
||||||
plv.respBody?.data?.rate != null
|
|
||||||
? ` (rate=${plv.respBody.data.rate})`
|
|
||||||
: ''
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
body: {
|
|
||||||
code: plv.respBody?.code || -1,
|
|
||||||
msg: `PLV 上报失败${rateMsg}`,
|
|
||||||
details: plv.respBody,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2) 上传 PLD
|
|
||||||
const pld = await doUpload(ctx, metaJson, pldBody, cookieStr, 'PLD')
|
|
||||||
if (!pld.success) {
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
body: {
|
|
||||||
code: pld.respBody?.code || -1,
|
|
||||||
msg: 'PLV 成功但 PLD 失败',
|
|
||||||
details: { plv: plv.respBody, pld: pld.respBody },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
body: {
|
|
||||||
code: 200,
|
|
||||||
data: 'scrobble_v1 上报成功',
|
|
||||||
details: {
|
|
||||||
plv: { fileName: plv.fileName, payloadSize: plv.payload.length },
|
|
||||||
pld: { fileName: pld.fileName, payloadSize: pld.payload.length },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
return {
|
|
||||||
status: 502,
|
|
||||||
body: { code: 502, msg: `请求异常: ${err.message || err}` },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
12
package.json
12
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@neteasecloudmusicapienhanced/api",
|
"name": "@neteasecloudmusicapienhanced/api",
|
||||||
"version": "4.36.1",
|
"version": "4.35.2",
|
||||||
"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",
|
||||||
@ -75,8 +75,8 @@
|
|||||||
"data"
|
"data"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@neteasecloudmusicapienhanced/unblockmusic-utils": "^0.3.3",
|
"@neteasecloudmusicapienhanced/unblockmusic-utils": "^0.3.2",
|
||||||
"axios": "^1.18.0",
|
"axios": "^1.17.0",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"dotenv": "^17.4.2",
|
"dotenv": "^17.4.2",
|
||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
@ -98,8 +98,8 @@
|
|||||||
"@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.61.1",
|
"@typescript-eslint/eslint-plugin": "^8.60.1",
|
||||||
"@typescript-eslint/parser": "^8.61.1",
|
"@typescript-eslint/parser": "^8.60.1",
|
||||||
"eslint": "^9.39.4",
|
"eslint": "^9.39.4",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"eslint-plugin-html": "^8.1.4",
|
"eslint-plugin-html": "^8.1.4",
|
||||||
@ -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.8.4",
|
"prettier": "^3.8.3",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
|
|||||||
276
pnpm-lock.yaml
generated
276
pnpm-lock.yaml
generated
@ -9,11 +9,11 @@ importers:
|
|||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@neteasecloudmusicapienhanced/unblockmusic-utils':
|
'@neteasecloudmusicapienhanced/unblockmusic-utils':
|
||||||
specifier: ^0.3.3
|
specifier: ^0.3.2
|
||||||
version: 0.3.3
|
version: 0.3.2
|
||||||
axios:
|
axios:
|
||||||
specifier: ^1.18.0
|
specifier: ^1.17.0
|
||||||
version: 1.18.0
|
version: 1.17.0
|
||||||
crypto-js:
|
crypto-js:
|
||||||
specifier: ^4.2.0
|
specifier: ^4.2.0
|
||||||
version: 4.2.0
|
version: 4.2.0
|
||||||
@ -73,11 +73,11 @@ 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.61.1
|
specifier: ^8.60.1
|
||||||
version: 8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)
|
version: 8.60.1(@typescript-eslint/parser@8.60.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)
|
||||||
'@typescript-eslint/parser':
|
'@typescript-eslint/parser':
|
||||||
specifier: ^8.61.1
|
specifier: ^8.60.1
|
||||||
version: 8.61.1(eslint@9.39.4)(typescript@5.9.3)
|
version: 8.60.1(eslint@9.39.4)(typescript@5.9.3)
|
||||||
eslint:
|
eslint:
|
||||||
specifier: ^9.39.4
|
specifier: ^9.39.4
|
||||||
version: 9.39.4
|
version: 9.39.4
|
||||||
@ -89,7 +89,7 @@ importers:
|
|||||||
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.8.4)
|
version: 5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.4))(eslint@9.39.4)(prettier@3.8.3)
|
||||||
globals:
|
globals:
|
||||||
specifier: ^17.6.0
|
specifier: ^17.6.0
|
||||||
version: 17.6.0
|
version: 17.6.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.8.4
|
specifier: ^3.8.3
|
||||||
version: 3.8.4
|
version: 3.8.3
|
||||||
typescript:
|
typescript:
|
||||||
specifier: ^5.9.3
|
specifier: ^5.9.3
|
||||||
version: 5.9.3
|
version: 5.9.3
|
||||||
@ -222,8 +222,8 @@ packages:
|
|||||||
'@jridgewell/trace-mapping@0.3.31':
|
'@jridgewell/trace-mapping@0.3.31':
|
||||||
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
|
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
|
||||||
|
|
||||||
'@neteasecloudmusicapienhanced/unblockmusic-utils@0.3.3':
|
'@neteasecloudmusicapienhanced/unblockmusic-utils@0.3.2':
|
||||||
resolution: {integrity: sha512-Lm2Zxod4Qfhp3HNDmaHxzJPLiMfMNkUT2PXp0VVrDW+/dDZBfSANO+pnCHx+MkK7tDUb3wGwYp5q2w9Kw1+4OA==}
|
resolution: {integrity: sha512-H1ckEDXxR+sLUZKzCPhlP8kfSKgEZZp8GSL4+2BZcmCyerwCbSZzMxX9doacHfFjThRjmq90DbsrRnhnKAvrzA==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
'@nodelib/fs.scandir@2.1.5':
|
'@nodelib/fs.scandir@2.1.5':
|
||||||
@ -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.61.1':
|
'@typescript-eslint/eslint-plugin@8.60.1':
|
||||||
resolution: {integrity: sha512-ZPlVl3PB3et/59Ne0fv/sci6ZXz4T4Hp4nTJ56i/Y0gR89ARb+KphojTq6j+56E5PIezmOIOOWyY+aWQFd+IkQ==}
|
resolution: {integrity: sha512-JQ4S5GB0tfjO8BuJ4fcX+HodkzJjYBV+7OJ+wLygaX7OGQ7FudyHL4NSCA6ob+w3Yn+5MkKIozOwQhXeM7opVg==}
|
||||||
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.61.1
|
'@typescript-eslint/parser': ^8.60.1
|
||||||
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.61.1':
|
'@typescript-eslint/parser@8.60.1':
|
||||||
resolution: {integrity: sha512-PJ5vePq5/ognBbrIcoC5+SHO5dfpeLPzP9FpLkzWrguoYQEeeSjlJpVwOpo1JRSTEi7dRcwNy4h4dzV70PqHcg==}
|
resolution: {integrity: sha512-A0M6ua6H252bVjPvvtSgl2QA4+ET9S5Mtkb2GDyTxIhH/C4qDItT7RQNO5PhMC6NXGYXOR9dIalcDDgBKT7oFA==}
|
||||||
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.61.1':
|
'@typescript-eslint/project-service@8.60.1':
|
||||||
resolution: {integrity: sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA==}
|
resolution: {integrity: sha512-eXkTH2bxmXlqD1RnOPmLZ9ZM9D3VwSx04JOwBnP9RQ+yUA5a2Mu7SfW8uaV2Aon53NJzZlZYuX7tn91Izf+xaw==}
|
||||||
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.61.1':
|
'@typescript-eslint/scope-manager@8.60.1':
|
||||||
resolution: {integrity: sha512-L2bdIeoQS8FlKAvONAr20w6OcLXeB+qiDKbAooS9A0Ben+iSIkBef0FxqwKWYqt5sa0i4KJtxVyVmhMylKzF5w==}
|
resolution: {integrity: sha512-gvI5OQoptnxQnchOirukCuQ55svJSTuD/4k5+pC267xyBtYry748R9/c3tYUzb/iE6RZfllRz2lVulLCHkTm4w==}
|
||||||
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.61.1':
|
'@typescript-eslint/tsconfig-utils@8.60.1':
|
||||||
resolution: {integrity: sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg==}
|
resolution: {integrity: sha512-nh8w4qAteiKuZu3pSSzG/yGKpw0OlkrKnzFmbVRenKaD4qc+7i1GrmZaLVkr8rk4uipiPGMOW4YsM6WmKZ5CvA==}
|
||||||
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.61.1':
|
'@typescript-eslint/type-utils@8.60.1':
|
||||||
resolution: {integrity: sha512-GYRicKmVK0C4fsKgaACaknOUAq9Oa2kwsjnpFhFcS/5p4Ht5IP9OVLbgIgcK4SRk92nVHFluurg1lumD9dBcLw==}
|
resolution: {integrity: sha512-sdwTrpjosW7ANQYJ39ZBF1ZyEMEGVB2UsikrserVM/30a/F1dTLnu9bGxEdosugyu5caigjLrR2qiD11asjI1A==}
|
||||||
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.61.1':
|
'@typescript-eslint/types@8.60.1':
|
||||||
resolution: {integrity: sha512-G+CRlPqLv7Bz1IZVs03x5K59F1veqL0EJUROAdGhKsEq8qOiRiZbI+HUojPq5l0fEGOKModD9br6lObhB8zkoA==}
|
resolution: {integrity: sha512-4h0tY8ppCkdCzcrl2YM5M3my0xsE1Tf8om3owEu5oPWmXwkKRmk0j0LGDzYBGUcAlesEbxBhazqu/K4cu3Ug7w==}
|
||||||
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.61.1':
|
'@typescript-eslint/typescript-estree@8.60.1':
|
||||||
resolution: {integrity: sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==}
|
resolution: {integrity: sha512-alpRkfG8hlVE5kdJW2GkfgDgXxold3e8e4l6EnmhRmRLbekgAPCCGDVD++sABy9FcgPFroq+uFcCSM1vR57Cew==}
|
||||||
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.61.1':
|
'@typescript-eslint/utils@8.60.1':
|
||||||
resolution: {integrity: sha512-1+P/3Dj6jvtybE1q0HQ6yBt/gq+oKJyLdEv4HdnqasaEXRSYCAsD59mXEVQnM/ULNdQxbX77tdG4jPRjIS6knA==}
|
resolution: {integrity: sha512-h2MPBLoNtjc3qZWfY3Tl51yPorQ2McHn8pJfcMNTcIvrrZrr90Ykffit0yjrPFWQcRcUxzH20+6OcVdW4yHtUg==}
|
||||||
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.61.1':
|
'@typescript-eslint/visitor-keys@8.60.1':
|
||||||
resolution: {integrity: sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==}
|
resolution: {integrity: sha512-EbGRQg4FhrmwLodl+t3JNAnXHWVr9Vp+Zl1QBZVPY4ByfkzIT8cX3K6QWODHtkIZqqJVEWvhHSx3v5PDHsaQag==}
|
||||||
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':
|
||||||
@ -386,8 +386,8 @@ packages:
|
|||||||
engines: {node: '>=0.4.0'}
|
engines: {node: '>=0.4.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
acorn@8.17.0:
|
acorn@8.16.0:
|
||||||
resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==}
|
resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==}
|
||||||
engines: {node: '>=0.4.0'}
|
engines: {node: '>=0.4.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
@ -485,8 +485,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
|
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
axios@1.18.0:
|
axios@1.17.0:
|
||||||
resolution: {integrity: sha512-E32NzpYKp++W7XRe52rHiXV2ehxmh3wbdgO7MHeFM+vqxLBYHzt0ElkiImtOBxtOmyp0yoC8C6uESVV84Y2/hw==}
|
resolution: {integrity: sha512-J8SwNxprqqpbfenehxWYXE7CW+wM1BB4w3+N+g+/Wx40xM4rsLrfPmHHxSWIxJLYDgSY/HqlFPIYb2/S3rxafw==}
|
||||||
|
|
||||||
balanced-match@1.0.2:
|
balanced-match@1.0.2:
|
||||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||||
@ -513,8 +513,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==}
|
resolution: {integrity: sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==}
|
||||||
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.2.2:
|
||||||
resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==}
|
resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
brace-expansion@1.1.15:
|
brace-expansion@1.1.15:
|
||||||
@ -864,10 +864,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==}
|
resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
es-abstract-get@1.0.0:
|
|
||||||
resolution: {integrity: sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==}
|
|
||||||
engines: {node: '>= 0.4'}
|
|
||||||
|
|
||||||
es-abstract@1.24.2:
|
es-abstract@1.24.2:
|
||||||
resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==}
|
resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@ -888,8 +884,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
|
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
es-to-primitive@1.3.1:
|
es-to-primitive@1.3.0:
|
||||||
resolution: {integrity: sha512-CxN9N56HYfd2m/acc/NOFrZQsN9kU4eh+2kk6A707Kz1krH8tKmfrs5RnftB8WNX80T0NS7vSQsDOlg23diR2g==}
|
resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
es5-ext@0.10.64:
|
es5-ext@0.10.64:
|
||||||
@ -1173,8 +1169,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
|
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
|
|
||||||
form-data@4.0.6:
|
form-data@4.0.5:
|
||||||
resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==}
|
resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
|
|
||||||
forwarded@0.2.0:
|
forwarded@0.2.0:
|
||||||
@ -1207,8 +1203,8 @@ packages:
|
|||||||
function-bind@1.1.2:
|
function-bind@1.1.2:
|
||||||
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
|
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
|
||||||
|
|
||||||
function.prototype.name@1.2.0:
|
function.prototype.name@1.1.8:
|
||||||
resolution: {integrity: sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==}
|
resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
functions-have-names@1.2.3:
|
functions-have-names@1.2.3:
|
||||||
@ -1451,10 +1447,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==}
|
resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
is-document.all@1.0.0:
|
|
||||||
resolution: {integrity: sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==}
|
|
||||||
engines: {node: '>= 0.4'}
|
|
||||||
|
|
||||||
is-extglob@2.1.1:
|
is-extglob@2.1.1:
|
||||||
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@ -2006,8 +1998,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.8.4:
|
prettier@3.8.3:
|
||||||
resolution: {integrity: sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==}
|
resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
@ -2161,8 +2153,8 @@ packages:
|
|||||||
secure-json-parse@2.7.0:
|
secure-json-parse@2.7.0:
|
||||||
resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==}
|
resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==}
|
||||||
|
|
||||||
semver@7.8.5:
|
semver@7.8.3:
|
||||||
resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==}
|
resolution: {integrity: sha512-wnilbGyMxzbY7dNOl7jpKbLSjcfeweJWU5j4+u5qW+6/wuGD9KzIGOyZnQVSBM9E7DtWaaH3CyHkppYrKYoxwg==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
@ -2223,8 +2215,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==}
|
resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
side-channel@1.1.1:
|
side-channel@1.1.0:
|
||||||
resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==}
|
resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
signal-exit@4.1.0:
|
signal-exit@4.1.0:
|
||||||
@ -2643,12 +2635,12 @@ packages:
|
|||||||
resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==}
|
resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
yargs@16.2.2:
|
yargs@16.2.0:
|
||||||
resolution: {integrity: sha512-Nt9ZJjXTv5R8MHbqby/wXQ6Gi0Bb3TcYZkR1bzuL4yB2OxWPkXknz513gEF0GoA6tn00UpbPvERW8rzCuWCA6w==}
|
resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
yargs@17.7.3:
|
yargs@17.7.2:
|
||||||
resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==}
|
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
yargs@18.0.0:
|
yargs@18.0.0:
|
||||||
@ -2768,10 +2760,10 @@ snapshots:
|
|||||||
'@jridgewell/resolve-uri': 3.1.2
|
'@jridgewell/resolve-uri': 3.1.2
|
||||||
'@jridgewell/sourcemap-codec': 1.5.5
|
'@jridgewell/sourcemap-codec': 1.5.5
|
||||||
|
|
||||||
'@neteasecloudmusicapienhanced/unblockmusic-utils@0.3.3':
|
'@neteasecloudmusicapienhanced/unblockmusic-utils@0.3.2':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@unblockneteasemusic/server': 0.28.0
|
'@unblockneteasemusic/server': 0.28.0
|
||||||
axios: 1.18.0
|
axios: 1.17.0
|
||||||
dotenv: 17.4.2
|
dotenv: 17.4.2
|
||||||
express: 4.22.2
|
express: 4.22.2
|
||||||
https: 1.0.0
|
https: 1.0.0
|
||||||
@ -2863,14 +2855,14 @@ 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.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)':
|
'@typescript-eslint/eslint-plugin@8.60.1(@typescript-eslint/parser@8.60.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint-community/regexpp': 4.12.2
|
'@eslint-community/regexpp': 4.12.2
|
||||||
'@typescript-eslint/parser': 8.61.1(eslint@9.39.4)(typescript@5.9.3)
|
'@typescript-eslint/parser': 8.60.1(eslint@9.39.4)(typescript@5.9.3)
|
||||||
'@typescript-eslint/scope-manager': 8.61.1
|
'@typescript-eslint/scope-manager': 8.60.1
|
||||||
'@typescript-eslint/type-utils': 8.61.1(eslint@9.39.4)(typescript@5.9.3)
|
'@typescript-eslint/type-utils': 8.60.1(eslint@9.39.4)(typescript@5.9.3)
|
||||||
'@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@5.9.3)
|
'@typescript-eslint/utils': 8.60.1(eslint@9.39.4)(typescript@5.9.3)
|
||||||
'@typescript-eslint/visitor-keys': 8.61.1
|
'@typescript-eslint/visitor-keys': 8.60.1
|
||||||
eslint: 9.39.4
|
eslint: 9.39.4
|
||||||
ignore: 7.0.5
|
ignore: 7.0.5
|
||||||
natural-compare: 1.4.0
|
natural-compare: 1.4.0
|
||||||
@ -2879,41 +2871,41 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@typescript-eslint/parser@8.61.1(eslint@9.39.4)(typescript@5.9.3)':
|
'@typescript-eslint/parser@8.60.1(eslint@9.39.4)(typescript@5.9.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/scope-manager': 8.61.1
|
'@typescript-eslint/scope-manager': 8.60.1
|
||||||
'@typescript-eslint/types': 8.61.1
|
'@typescript-eslint/types': 8.60.1
|
||||||
'@typescript-eslint/typescript-estree': 8.61.1(typescript@5.9.3)
|
'@typescript-eslint/typescript-estree': 8.60.1(typescript@5.9.3)
|
||||||
'@typescript-eslint/visitor-keys': 8.61.1
|
'@typescript-eslint/visitor-keys': 8.60.1
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
eslint: 9.39.4
|
eslint: 9.39.4
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@typescript-eslint/project-service@8.61.1(typescript@5.9.3)':
|
'@typescript-eslint/project-service@8.60.1(typescript@5.9.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/tsconfig-utils': 8.61.1(typescript@5.9.3)
|
'@typescript-eslint/tsconfig-utils': 8.60.1(typescript@5.9.3)
|
||||||
'@typescript-eslint/types': 8.61.1
|
'@typescript-eslint/types': 8.60.1
|
||||||
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.61.1':
|
'@typescript-eslint/scope-manager@8.60.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/types': 8.61.1
|
'@typescript-eslint/types': 8.60.1
|
||||||
'@typescript-eslint/visitor-keys': 8.61.1
|
'@typescript-eslint/visitor-keys': 8.60.1
|
||||||
|
|
||||||
'@typescript-eslint/tsconfig-utils@8.61.1(typescript@5.9.3)':
|
'@typescript-eslint/tsconfig-utils@8.60.1(typescript@5.9.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
|
|
||||||
'@typescript-eslint/type-utils@8.61.1(eslint@9.39.4)(typescript@5.9.3)':
|
'@typescript-eslint/type-utils@8.60.1(eslint@9.39.4)(typescript@5.9.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/types': 8.61.1
|
'@typescript-eslint/types': 8.60.1
|
||||||
'@typescript-eslint/typescript-estree': 8.61.1(typescript@5.9.3)
|
'@typescript-eslint/typescript-estree': 8.60.1(typescript@5.9.3)
|
||||||
'@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@5.9.3)
|
'@typescript-eslint/utils': 8.60.1(eslint@9.39.4)(typescript@5.9.3)
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
eslint: 9.39.4
|
eslint: 9.39.4
|
||||||
ts-api-utils: 2.5.0(typescript@5.9.3)
|
ts-api-utils: 2.5.0(typescript@5.9.3)
|
||||||
@ -2921,37 +2913,37 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@typescript-eslint/types@8.61.1': {}
|
'@typescript-eslint/types@8.60.1': {}
|
||||||
|
|
||||||
'@typescript-eslint/typescript-estree@8.61.1(typescript@5.9.3)':
|
'@typescript-eslint/typescript-estree@8.60.1(typescript@5.9.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/project-service': 8.61.1(typescript@5.9.3)
|
'@typescript-eslint/project-service': 8.60.1(typescript@5.9.3)
|
||||||
'@typescript-eslint/tsconfig-utils': 8.61.1(typescript@5.9.3)
|
'@typescript-eslint/tsconfig-utils': 8.60.1(typescript@5.9.3)
|
||||||
'@typescript-eslint/types': 8.61.1
|
'@typescript-eslint/types': 8.60.1
|
||||||
'@typescript-eslint/visitor-keys': 8.61.1
|
'@typescript-eslint/visitor-keys': 8.60.1
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
minimatch: 10.2.5
|
minimatch: 10.2.5
|
||||||
semver: 7.8.5
|
semver: 7.8.3
|
||||||
tinyglobby: 0.2.17
|
tinyglobby: 0.2.17
|
||||||
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/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3)':
|
'@typescript-eslint/utils@8.60.1(eslint@9.39.4)(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.4)
|
||||||
'@typescript-eslint/scope-manager': 8.61.1
|
'@typescript-eslint/scope-manager': 8.60.1
|
||||||
'@typescript-eslint/types': 8.61.1
|
'@typescript-eslint/types': 8.60.1
|
||||||
'@typescript-eslint/typescript-estree': 8.61.1(typescript@5.9.3)
|
'@typescript-eslint/typescript-estree': 8.60.1(typescript@5.9.3)
|
||||||
eslint: 9.39.4
|
eslint: 9.39.4
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@typescript-eslint/visitor-keys@8.61.1':
|
'@typescript-eslint/visitor-keys@8.60.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/types': 8.61.1
|
'@typescript-eslint/types': 8.60.1
|
||||||
eslint-visitor-keys: 5.0.1
|
eslint-visitor-keys: 5.0.1
|
||||||
|
|
||||||
'@unblockneteasemusic/server@0.28.0':
|
'@unblockneteasemusic/server@0.28.0':
|
||||||
@ -2972,13 +2964,13 @@ snapshots:
|
|||||||
|
|
||||||
acorn-es7-plugin@1.1.7: {}
|
acorn-es7-plugin@1.1.7: {}
|
||||||
|
|
||||||
acorn-jsx@5.3.2(acorn@8.17.0):
|
acorn-jsx@5.3.2(acorn@8.16.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
acorn: 8.17.0
|
acorn: 8.16.0
|
||||||
|
|
||||||
acorn@5.7.4: {}
|
acorn@5.7.4: {}
|
||||||
|
|
||||||
acorn@8.17.0: {}
|
acorn@8.16.0: {}
|
||||||
|
|
||||||
agent-base@6.0.2:
|
agent-base@6.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -3068,10 +3060,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
possible-typed-array-names: 1.1.0
|
possible-typed-array-names: 1.1.0
|
||||||
|
|
||||||
axios@1.18.0:
|
axios@1.17.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
follow-redirects: 1.16.0
|
follow-redirects: 1.16.0
|
||||||
form-data: 4.0.6
|
form-data: 4.0.5
|
||||||
https-proxy-agent: 5.0.1
|
https-proxy-agent: 5.0.1
|
||||||
proxy-from-env: 2.1.0
|
proxy-from-env: 2.1.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@ -3111,10 +3103,10 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
body-parser@2.3.0:
|
body-parser@2.2.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
bytes: 3.1.2
|
bytes: 3.1.2
|
||||||
content-type: 2.0.0
|
content-type: 1.0.5
|
||||||
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.2
|
||||||
@ -3476,13 +3468,6 @@ snapshots:
|
|||||||
|
|
||||||
environment@1.1.0: {}
|
environment@1.1.0: {}
|
||||||
|
|
||||||
es-abstract-get@1.0.0:
|
|
||||||
dependencies:
|
|
||||||
es-errors: 1.3.0
|
|
||||||
es-object-atoms: 1.1.2
|
|
||||||
is-callable: 1.2.7
|
|
||||||
object-inspect: 1.13.4
|
|
||||||
|
|
||||||
es-abstract@1.24.2:
|
es-abstract@1.24.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
array-buffer-byte-length: 1.0.2
|
array-buffer-byte-length: 1.0.2
|
||||||
@ -3497,8 +3482,8 @@ snapshots:
|
|||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
es-object-atoms: 1.1.2
|
es-object-atoms: 1.1.2
|
||||||
es-set-tostringtag: 2.1.0
|
es-set-tostringtag: 2.1.0
|
||||||
es-to-primitive: 1.3.1
|
es-to-primitive: 1.3.0
|
||||||
function.prototype.name: 1.2.0
|
function.prototype.name: 1.1.8
|
||||||
get-intrinsic: 1.3.0
|
get-intrinsic: 1.3.0
|
||||||
get-proto: 1.0.1
|
get-proto: 1.0.1
|
||||||
get-symbol-description: 1.1.0
|
get-symbol-description: 1.1.0
|
||||||
@ -3555,10 +3540,8 @@ snapshots:
|
|||||||
has-tostringtag: 1.0.2
|
has-tostringtag: 1.0.2
|
||||||
hasown: 2.0.4
|
hasown: 2.0.4
|
||||||
|
|
||||||
es-to-primitive@1.3.1:
|
es-to-primitive@1.3.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
es-abstract-get: 1.0.0
|
|
||||||
es-errors: 1.3.0
|
|
||||||
is-callable: 1.2.7
|
is-callable: 1.2.7
|
||||||
is-date-object: 1.1.0
|
is-date-object: 1.1.0
|
||||||
is-symbol: 1.1.1
|
is-symbol: 1.1.1
|
||||||
@ -3651,10 +3634,10 @@ snapshots:
|
|||||||
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.8.4):
|
eslint-plugin-prettier@5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.4))(eslint@9.39.4)(prettier@3.8.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint: 9.39.4
|
eslint: 9.39.4
|
||||||
prettier: 3.8.4
|
prettier: 3.8.3
|
||||||
prettier-linter-helpers: 1.0.1
|
prettier-linter-helpers: 1.0.1
|
||||||
synckit: 0.11.13
|
synckit: 0.11.13
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
@ -3760,8 +3743,8 @@ snapshots:
|
|||||||
|
|
||||||
espree@10.4.0:
|
espree@10.4.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
acorn: 8.17.0
|
acorn: 8.16.0
|
||||||
acorn-jsx: 5.3.2(acorn@8.17.0)
|
acorn-jsx: 5.3.2(acorn@8.16.0)
|
||||||
eslint-visitor-keys: 4.2.1
|
eslint-visitor-keys: 4.2.1
|
||||||
|
|
||||||
esprima@2.7.3: {}
|
esprima@2.7.3: {}
|
||||||
@ -3840,7 +3823,7 @@ snapshots:
|
|||||||
express@5.2.1:
|
express@5.2.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
accepts: 2.0.0
|
accepts: 2.0.0
|
||||||
body-parser: 2.3.0
|
body-parser: 2.2.2
|
||||||
content-disposition: 1.1.0
|
content-disposition: 1.1.0
|
||||||
content-type: 1.0.5
|
content-type: 1.0.5
|
||||||
cookie: 0.7.2
|
cookie: 0.7.2
|
||||||
@ -3974,7 +3957,7 @@ snapshots:
|
|||||||
cross-spawn: 7.0.6
|
cross-spawn: 7.0.6
|
||||||
signal-exit: 4.1.0
|
signal-exit: 4.1.0
|
||||||
|
|
||||||
form-data@4.0.6:
|
form-data@4.0.5:
|
||||||
dependencies:
|
dependencies:
|
||||||
asynckit: 0.4.0
|
asynckit: 0.4.0
|
||||||
combined-stream: 1.0.8
|
combined-stream: 1.0.8
|
||||||
@ -4007,17 +3990,14 @@ snapshots:
|
|||||||
|
|
||||||
function-bind@1.1.2: {}
|
function-bind@1.1.2: {}
|
||||||
|
|
||||||
function.prototype.name@1.2.0:
|
function.prototype.name@1.1.8:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind: 1.0.9
|
call-bind: 1.0.9
|
||||||
call-bound: 1.0.4
|
call-bound: 1.0.4
|
||||||
es-define-property: 1.0.1
|
define-properties: 1.2.1
|
||||||
es-errors: 1.3.0
|
|
||||||
functions-have-names: 1.2.3
|
functions-have-names: 1.2.3
|
||||||
has-property-descriptors: 1.0.2
|
|
||||||
hasown: 2.0.4
|
hasown: 2.0.4
|
||||||
is-callable: 1.2.7
|
is-callable: 1.2.7
|
||||||
is-document.all: 1.0.0
|
|
||||||
|
|
||||||
functions-have-names@1.2.3: {}
|
functions-have-names@1.2.3: {}
|
||||||
|
|
||||||
@ -4207,7 +4187,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
hasown: 2.0.4
|
hasown: 2.0.4
|
||||||
side-channel: 1.1.1
|
side-channel: 1.1.0
|
||||||
|
|
||||||
into-stream@6.0.0:
|
into-stream@6.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -4271,10 +4251,6 @@ snapshots:
|
|||||||
call-bound: 1.0.4
|
call-bound: 1.0.4
|
||||||
has-tostringtag: 1.0.2
|
has-tostringtag: 1.0.2
|
||||||
|
|
||||||
is-document.all@1.0.0:
|
|
||||||
dependencies:
|
|
||||||
call-bound: 1.0.4
|
|
||||||
|
|
||||||
is-extglob@2.1.1: {}
|
is-extglob@2.1.1: {}
|
||||||
|
|
||||||
is-finalizationregistry@1.1.1:
|
is-finalizationregistry@1.1.1:
|
||||||
@ -4531,7 +4507,7 @@ snapshots:
|
|||||||
strip-json-comments: 3.1.1
|
strip-json-comments: 3.1.1
|
||||||
supports-color: 8.1.1
|
supports-color: 8.1.1
|
||||||
workerpool: 9.3.4
|
workerpool: 9.3.4
|
||||||
yargs: 17.7.3
|
yargs: 17.7.2
|
||||||
yargs-parser: 21.1.1
|
yargs-parser: 21.1.1
|
||||||
yargs-unparser: 2.0.0
|
yargs-unparser: 2.0.0
|
||||||
|
|
||||||
@ -4579,7 +4555,7 @@ snapshots:
|
|||||||
|
|
||||||
node-abi@3.92.0:
|
node-abi@3.92.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
semver: 7.8.5
|
semver: 7.8.3
|
||||||
|
|
||||||
node-fetch@2.7.0:
|
node-fetch@2.7.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -4590,7 +4566,7 @@ snapshots:
|
|||||||
node-windows@1.0.0-beta.8:
|
node-windows@1.0.0-beta.8:
|
||||||
dependencies:
|
dependencies:
|
||||||
xml: 1.0.1
|
xml: 1.0.1
|
||||||
yargs: 17.7.3
|
yargs: 17.7.2
|
||||||
|
|
||||||
nodemon@3.1.14:
|
nodemon@3.1.14:
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -4599,7 +4575,7 @@ snapshots:
|
|||||||
ignore-by-default: 1.0.1
|
ignore-by-default: 1.0.1
|
||||||
minimatch: 10.2.5
|
minimatch: 10.2.5
|
||||||
pstree.remy: 1.1.8
|
pstree.remy: 1.1.8
|
||||||
semver: 7.8.5
|
semver: 7.8.3
|
||||||
simple-update-notifier: 2.0.0
|
simple-update-notifier: 2.0.0
|
||||||
supports-color: 5.5.0
|
supports-color: 5.5.0
|
||||||
touch: 3.1.1
|
touch: 3.1.1
|
||||||
@ -4774,9 +4750,9 @@ snapshots:
|
|||||||
https-proxy-agent: 5.0.1
|
https-proxy-agent: 5.0.1
|
||||||
node-fetch: 2.7.0
|
node-fetch: 2.7.0
|
||||||
progress: 2.0.3
|
progress: 2.0.3
|
||||||
semver: 7.8.5
|
semver: 7.8.3
|
||||||
tar-fs: 2.1.4
|
tar-fs: 2.1.4
|
||||||
yargs: 16.2.2
|
yargs: 16.2.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- encoding
|
- encoding
|
||||||
- supports-color
|
- supports-color
|
||||||
@ -4894,7 +4870,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
fast-diff: 1.3.0
|
fast-diff: 1.3.0
|
||||||
|
|
||||||
prettier@3.8.4: {}
|
prettier@3.8.3: {}
|
||||||
|
|
||||||
process-nextick-args@2.0.1: {}
|
process-nextick-args@2.0.1: {}
|
||||||
|
|
||||||
@ -4926,7 +4902,7 @@ snapshots:
|
|||||||
|
|
||||||
qs@6.15.2:
|
qs@6.15.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
side-channel: 1.1.1
|
side-channel: 1.1.0
|
||||||
|
|
||||||
queue-microtask@1.2.3: {}
|
queue-microtask@1.2.3: {}
|
||||||
|
|
||||||
@ -5068,7 +5044,7 @@ snapshots:
|
|||||||
|
|
||||||
secure-json-parse@2.7.0: {}
|
secure-json-parse@2.7.0: {}
|
||||||
|
|
||||||
semver@7.8.5: {}
|
semver@7.8.3: {}
|
||||||
|
|
||||||
send@0.19.2:
|
send@0.19.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -5178,7 +5154,7 @@ snapshots:
|
|||||||
object-inspect: 1.13.4
|
object-inspect: 1.13.4
|
||||||
side-channel-map: 1.0.1
|
side-channel-map: 1.0.1
|
||||||
|
|
||||||
side-channel@1.1.1:
|
side-channel@1.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
object-inspect: 1.13.4
|
object-inspect: 1.13.4
|
||||||
@ -5198,7 +5174,7 @@ snapshots:
|
|||||||
|
|
||||||
simple-update-notifier@2.0.0:
|
simple-update-notifier@2.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
semver: 7.8.5
|
semver: 7.8.3
|
||||||
|
|
||||||
slash@3.0.0: {}
|
slash@3.0.0: {}
|
||||||
|
|
||||||
@ -5541,7 +5517,7 @@ snapshots:
|
|||||||
which-builtin-type@1.2.1:
|
which-builtin-type@1.2.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bound: 1.0.4
|
call-bound: 1.0.4
|
||||||
function.prototype.name: 1.2.0
|
function.prototype.name: 1.1.8
|
||||||
has-tostringtag: 1.0.2
|
has-tostringtag: 1.0.2
|
||||||
is-async-function: 2.1.1
|
is-async-function: 2.1.1
|
||||||
is-date-object: 1.1.0
|
is-date-object: 1.1.0
|
||||||
@ -5658,7 +5634,7 @@ snapshots:
|
|||||||
y18n: 4.0.3
|
y18n: 4.0.3
|
||||||
yargs-parser: 18.1.3
|
yargs-parser: 18.1.3
|
||||||
|
|
||||||
yargs@16.2.2:
|
yargs@16.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
cliui: 7.0.4
|
cliui: 7.0.4
|
||||||
escalade: 3.2.0
|
escalade: 3.2.0
|
||||||
@ -5668,7 +5644,7 @@ snapshots:
|
|||||||
y18n: 5.0.8
|
y18n: 5.0.8
|
||||||
yargs-parser: 20.2.9
|
yargs-parser: 20.2.9
|
||||||
|
|
||||||
yargs@17.7.3:
|
yargs@17.7.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
cliui: 8.0.1
|
cliui: 8.0.1
|
||||||
escalade: 3.2.0
|
escalade: 3.2.0
|
||||||
|
|||||||
@ -2500,18 +2500,6 @@ privilege:权限相关信息
|
|||||||
|
|
||||||
**调用例子 :** `/scrobble?id=518066366&sourceid=36780169&time=291`
|
**调用例子 :** `/scrobble?id=518066366&sourceid=36780169&time=291`
|
||||||
|
|
||||||
#### 听歌打卡 V2 (NCBL 加密版)
|
|
||||||
|
|
||||||
说明 : 调用此接口,使用桌面客户端 NCBL 加密日志格式上报听歌记录
|
|
||||||
|
|
||||||
**必选参数 :** `id`: 歌曲 id, `time`: 播放时长(秒)
|
|
||||||
|
|
||||||
**可选参数 :** `sourceid`: 来源列表 id, `source`: 来源名称(默认 list), `name`: 歌曲名, `artist`: 艺术家, `bitrate`: 码率(默认 320), `level`: 音质等级(默认 exhigh), `total`: 歌曲总时长(秒)
|
|
||||||
|
|
||||||
**接口地址 :** `/scrobble/v1`
|
|
||||||
|
|
||||||
**调用例子 :** `/scrobble/v1?id=518066366&sourceid=36780169&time=291`
|
|
||||||
|
|
||||||
### 提交歌曲播放状态
|
### 提交歌曲播放状态
|
||||||
|
|
||||||
说明 : 调用此接口可提交歌曲播放状态,支持会话追踪和播放模式记录,未传入 `sessionId` 时后端会自动生成
|
说明 : 调用此接口可提交歌曲播放状态,支持会话追踪和播放模式记录,未传入 `sessionId` 时后端会自动生成
|
||||||
|
|||||||
@ -95,8 +95,7 @@ curl -s {origin}/search?keywords=网易云</code></pre>
|
|||||||
<a href="/api_decrypt.html">API 解密</a> ·
|
<a href="/api_decrypt.html">API 解密</a> ·
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@ -1,480 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>听歌打卡 - 网易云音乐 API Enhanced</title>
|
|
||||||
<style>
|
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
||||||
min-height: 100vh;
|
|
||||||
background: #f5f5f5;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
max-width: 520px;
|
|
||||||
margin: 40px auto;
|
|
||||||
background: white;
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 32px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #666;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-link {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
color: #666;
|
|
||||||
font-size: 14px;
|
|
||||||
text-decoration: none;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-link:hover {
|
|
||||||
color: #333;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- Tabs ---- */
|
|
||||||
.tabs {
|
|
||||||
display: flex;
|
|
||||||
gap: 0;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
border-bottom: 2px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-btn {
|
|
||||||
flex: 1;
|
|
||||||
padding: 10px 0;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
border-bottom: 2px solid transparent;
|
|
||||||
margin-bottom: -2px;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #999;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: color 0.2s, border-color 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-btn:hover {
|
|
||||||
color: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-btn.active {
|
|
||||||
color: #333;
|
|
||||||
border-bottom-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-content {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-content.active {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- Form ---- */
|
|
||||||
.form-group {
|
|
||||||
margin-bottom: 16px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
display: block;
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #555;
|
|
||||||
margin-bottom: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
label .tag {
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 11px;
|
|
||||||
font-weight: 400;
|
|
||||||
padding: 1px 6px;
|
|
||||||
border-radius: 3px;
|
|
||||||
margin-left: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
label .tag.r {
|
|
||||||
background: #fee2e2;
|
|
||||||
color: #991b1b;
|
|
||||||
}
|
|
||||||
|
|
||||||
label .tag.o {
|
|
||||||
background: #fef3c7;
|
|
||||||
color: #92400e;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="text"],
|
|
||||||
input[type="number"],
|
|
||||||
textarea {
|
|
||||||
width: 100%;
|
|
||||||
padding: 10px 14px;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 14px;
|
|
||||||
outline: none;
|
|
||||||
font-family: inherit;
|
|
||||||
transition: border-color 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="text"]:focus,
|
|
||||||
input[type="number"]:focus,
|
|
||||||
textarea:focus {
|
|
||||||
border-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
resize: vertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row {
|
|
||||||
display: flex;
|
|
||||||
gap: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row .form-group {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quick-fill {
|
|
||||||
margin-top: 5px;
|
|
||||||
font-size: 12px;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quick-fill span {
|
|
||||||
color: #0066cc;
|
|
||||||
cursor: pointer;
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quick-fill span:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
padding: 12px;
|
|
||||||
background: #333;
|
|
||||||
color: white;
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: 500;
|
|
||||||
border-radius: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background 0.2s ease;
|
|
||||||
border: none;
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn:hover {
|
|
||||||
background: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn:disabled {
|
|
||||||
background: #ccc;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result {
|
|
||||||
margin-top: 16px;
|
|
||||||
padding: 12px 16px;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 14px;
|
|
||||||
text-align: left;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-break: break-all;
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result.success {
|
|
||||||
background: #d1fae5;
|
|
||||||
color: #065f46;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result.error {
|
|
||||||
background: #fee2e2;
|
|
||||||
color: #991b1b;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result.info {
|
|
||||||
background: #e0f2fe;
|
|
||||||
color: #0369a1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-link {
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 24px;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-link a {
|
|
||||||
color: #666;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-link a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<h1>听歌打卡</h1>
|
|
||||||
<p class="subtitle">同步听歌记录至网易云音乐,增加听歌排行计数</p>
|
|
||||||
|
|
||||||
<a href="/qrlogin-nocookie.html" class="login-link">还没登录?点击登录</a>
|
|
||||||
|
|
||||||
<!-- Cookie (共享) -->
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="cookie">Cookie <span class="tag o">可选</span></label>
|
|
||||||
<textarea id="cookie" placeholder="留空则默认读取本地存储的登录态" rows="2"></textarea>
|
|
||||||
<div class="quick-fill">
|
|
||||||
<span onclick="loadLocalCookie()">读取本地 Cookie</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Tabs -->
|
|
||||||
<div class="tabs">
|
|
||||||
<button class="tab-btn active" data-tab="v0" onclick="switchTab('v0')">原版</button>
|
|
||||||
<button class="tab-btn" data-tab="v1" onclick="switchTab('v1')">V2 (NCBL 加密版)</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ============ Tab: v0 (原版 /scrobble) ============ -->
|
|
||||||
<div class="tab-content active" id="tab-v0">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>歌曲 ID <span class="tag r">必填</span></label>
|
|
||||||
<input type="text" id="v0-songId" placeholder="例如: 2756058128" value="2756058128" />
|
|
||||||
<div class="quick-fill">
|
|
||||||
示例:
|
|
||||||
<span onclick="fillV0('2756058128', '288651229')">妖精小姐的魔法邀约</span>
|
|
||||||
<span onclick="fillV0('2637402867', '251025018')">Echoes of Memoria</span>
|
|
||||||
<span onclick="fillV0('36307815', '3394198')">Lose Control</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>来源 ID (歌单或专辑 ID) <span class="tag r">必填</span></label>
|
|
||||||
<input type="text" id="v0-sourceId" placeholder="例如: 2756058128" value="2756058128" />
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>播放时间 (秒) <span class="tag o">可选</span></label>
|
|
||||||
<input type="number" id="v0-time" placeholder="300" value="300" />
|
|
||||||
</div>
|
|
||||||
<button class="btn" onclick="submitV0()">立即打卡 (原版)</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ============ Tab: v1 (NCBL 加密版 /scrobble/v1) ============ -->
|
|
||||||
<div class="tab-content" id="tab-v1">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>歌曲 ID <span class="tag r">必填</span></label>
|
|
||||||
<input type="text" id="v1-songId" placeholder="例如: 518066366" value="518066366" />
|
|
||||||
<div class="quick-fill">
|
|
||||||
示例:
|
|
||||||
<span onclick="fillV1('518066366', '36780169', '291')">默认示例</span>
|
|
||||||
<span onclick="fillV1('2756058128', '288651229', '300')">妖精小姐的魔法邀约</span>
|
|
||||||
<span onclick="fillV1('2637402867', '251025018', '300')">Echoes of Memoria</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>播放时间 (秒) <span class="tag r">必填</span></label>
|
|
||||||
<input type="number" id="v1-time" placeholder="例如: 291" value="291" />
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>来源 ID <span class="tag o">可选</span></label>
|
|
||||||
<input type="text" id="v1-sourceId" placeholder="歌单或专辑 ID" value="36780169" />
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>歌曲名 <span class="tag o">可选</span></label>
|
|
||||||
<input type="text" id="v1-name" placeholder="歌曲名称" />
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>艺术家 <span class="tag o">可选</span></label>
|
|
||||||
<input type="text" id="v1-artist" placeholder="歌手名" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>码率 <span class="tag o">可选</span></label>
|
|
||||||
<input type="number" id="v1-bitrate" placeholder="默认 320" value="320" />
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>音质等级 <span class="tag o">可选</span></label>
|
|
||||||
<input type="text" id="v1-level" placeholder="默认 exhigh" value="exhigh" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>歌曲总时长 (秒) <span class="tag o">可选</span></label>
|
|
||||||
<input type="number" id="v1-total" placeholder="留空则与播放时间相同" />
|
|
||||||
</div>
|
|
||||||
<button class="btn" onclick="submitV1()">立即打卡 (NCBL 加密版)</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Result -->
|
|
||||||
<div id="result" class="result" style="display: none;"></div>
|
|
||||||
|
|
||||||
<div class="footer-link">
|
|
||||||
<a href="/">返回首页</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="https://fastly.jsdelivr.net/npm/axios@0.26.1/dist/axios.min.js"></script>
|
|
||||||
<script>
|
|
||||||
/* ---- DOM refs ---- */
|
|
||||||
const cookieInput = document.getElementById('cookie')
|
|
||||||
const resultDiv = document.getElementById('result')
|
|
||||||
|
|
||||||
/* ---- init ---- */
|
|
||||||
if (localStorage.getItem('cookie')) {
|
|
||||||
cookieInput.value = localStorage.getItem('cookie')
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- Tab switching ---- */
|
|
||||||
function switchTab(name) {
|
|
||||||
document.querySelectorAll('.tab-btn').forEach(b => b.classList.toggle('active', b.dataset.tab === name))
|
|
||||||
document.querySelectorAll('.tab-content').forEach(c => c.classList.toggle('active', c.id === 'tab-' + name))
|
|
||||||
hideResult()
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- Cookie ---- */
|
|
||||||
function loadLocalCookie() {
|
|
||||||
const local = localStorage.getItem('cookie')
|
|
||||||
if (local) {
|
|
||||||
cookieInput.value = local
|
|
||||||
showResult('已成功读取本地 Cookie', 'success')
|
|
||||||
} else {
|
|
||||||
showResult('未在本地发现登录 Cookie,请先登录或手动粘贴', 'error')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- Quick fill ---- */
|
|
||||||
function fillV0(id, sourceId) {
|
|
||||||
document.getElementById('v0-songId').value = id
|
|
||||||
document.getElementById('v0-sourceId').value = sourceId
|
|
||||||
}
|
|
||||||
|
|
||||||
function fillV1(id, sourceId, time) {
|
|
||||||
document.getElementById('v1-songId').value = id
|
|
||||||
document.getElementById('v1-sourceId').value = sourceId
|
|
||||||
document.getElementById('v1-time').value = time
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- Result ---- */
|
|
||||||
function hideResult() {
|
|
||||||
resultDiv.style.display = 'none'
|
|
||||||
}
|
|
||||||
|
|
||||||
function showResult(message, type) {
|
|
||||||
resultDiv.textContent = message
|
|
||||||
resultDiv.className = 'result ' + type
|
|
||||||
resultDiv.style.display = 'block'
|
|
||||||
}
|
|
||||||
|
|
||||||
function disableAll(disabled) {
|
|
||||||
document.querySelectorAll('.btn').forEach(b => b.disabled = disabled)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- Submit: 原版 /scrobble ---- */
|
|
||||||
async function submitV0() {
|
|
||||||
const id = document.getElementById('v0-songId').value.trim()
|
|
||||||
const sourceid = document.getElementById('v0-sourceId').value.trim()
|
|
||||||
const time = document.getElementById('v0-time').value.trim() || '300'
|
|
||||||
|
|
||||||
if (!id) { showResult('请输入歌曲 ID !', 'error'); return }
|
|
||||||
if (!sourceid) { showResult('请输入来源 ID !', 'error'); return }
|
|
||||||
|
|
||||||
disableAll(true)
|
|
||||||
showResult('打卡中,请稍候...', 'info')
|
|
||||||
|
|
||||||
try {
|
|
||||||
const params = { id, sourceid: sourceid || id, time, timestamp: Date.now() }
|
|
||||||
const cookie = cookieInput.value.trim()
|
|
||||||
if (cookie) params.cookie = cookie
|
|
||||||
|
|
||||||
const res = await axios({ method: 'get', url: '/scrobble', params })
|
|
||||||
if (res.data.code === 200) {
|
|
||||||
showResult('✅ 打卡成功! \n\n' + JSON.stringify(res.data, null, 2), 'success')
|
|
||||||
} else {
|
|
||||||
showResult('请求已发送,返回:\n\n' + JSON.stringify(res.data, null, 2), 'info')
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
const errData = error.response ? error.response.data : { error: error.message }
|
|
||||||
showResult('❌ 打卡失败\n\n' + JSON.stringify(errData, null, 2), 'error')
|
|
||||||
} finally {
|
|
||||||
disableAll(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- Submit: NCBL 加密版 /scrobble/v1 ---- */
|
|
||||||
async function submitV1() {
|
|
||||||
const id = document.getElementById('v1-songId').value.trim()
|
|
||||||
const time = document.getElementById('v1-time').value.trim()
|
|
||||||
|
|
||||||
if (!id) { showResult('请输入歌曲 ID !', 'error'); return }
|
|
||||||
if (!time || isNaN(+time) || +time <= 0) { showResult('请输入有效的播放时长(秒)!', 'error'); return }
|
|
||||||
|
|
||||||
disableAll(true)
|
|
||||||
showResult('打卡中(NCBL 加密),请稍候...', 'info')
|
|
||||||
|
|
||||||
try {
|
|
||||||
const params = {
|
|
||||||
id,
|
|
||||||
time,
|
|
||||||
sourceid: document.getElementById('v1-sourceId').value.trim() || undefined,
|
|
||||||
name: document.getElementById('v1-name').value.trim() || undefined,
|
|
||||||
artist: document.getElementById('v1-artist').value.trim() || undefined,
|
|
||||||
bitrate: document.getElementById('v1-bitrate').value.trim() || undefined,
|
|
||||||
level: document.getElementById('v1-level').value.trim() || undefined,
|
|
||||||
total: document.getElementById('v1-total').value.trim() || undefined,
|
|
||||||
timestamp: Date.now(),
|
|
||||||
}
|
|
||||||
const cookie = cookieInput.value.trim()
|
|
||||||
if (cookie) params.cookie = cookie
|
|
||||||
|
|
||||||
// 清理 undefined 参数
|
|
||||||
Object.keys(params).forEach(k => params[k] === undefined && delete params[k])
|
|
||||||
|
|
||||||
const res = await axios({ method: 'get', url: '/scrobble/v1', params })
|
|
||||||
if (res.data.code === 200) {
|
|
||||||
showResult('✅ NCBL 加密打卡成功! \n\n' + JSON.stringify(res.data, null, 2), 'success')
|
|
||||||
} else {
|
|
||||||
showResult('请求已发送,返回:\n\n' + JSON.stringify(res.data, null, 2), 'info')
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
const errData = error.response ? error.response.data : { error: error.message }
|
|
||||||
showResult('❌ 打卡失败\n\n' + JSON.stringify(errData, null, 2), 'error')
|
|
||||||
} finally {
|
|
||||||
disableAll(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
@ -13,8 +13,6 @@
|
|||||||
"apiDomain": "https://interface.music.163.com",
|
"apiDomain": "https://interface.music.163.com",
|
||||||
"xeapiDomain": "https://interface3.music.163.com",
|
"xeapiDomain": "https://interface3.music.163.com",
|
||||||
"domain": "https://music.163.com",
|
"domain": "https://music.163.com",
|
||||||
"clDomian": "https://clientlog.music.163.com",
|
|
||||||
"clDomian3": "https://clientlog3.music.163.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",
|
||||||
|
|||||||
481
util/ncbl.js
481
util/ncbl.js
@ -1,481 +0,0 @@
|
|||||||
// NCBL 加密工具 —— 复制自 @netease-report-listen-song
|
|
||||||
// 提供 ChaCha20 / RSA-256 / NCBL 加密 / PLV+PLD 构建 / 设备上下文提取
|
|
||||||
|
|
||||||
const crypto = require('crypto')
|
|
||||||
const zlib = require('zlib')
|
|
||||||
const axios = require('axios')
|
|
||||||
const { APP_CONF } = require('./config.json')
|
|
||||||
const DOMAIN3 = APP_CONF.clDomian3
|
|
||||||
|
|
||||||
// ---- ChaCha20 纯 JS 实现 ----
|
|
||||||
const SIGMA = [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574]
|
|
||||||
|
|
||||||
const rotl = (x, n) => ((x << n) | (x >>> (32 - n))) >>> 0
|
|
||||||
|
|
||||||
const quarterRound = (s, a, b, c, d) => {
|
|
||||||
s[a] = (s[a] + s[b]) >>> 0
|
|
||||||
s[d] ^= s[a]
|
|
||||||
s[d] = rotl(s[d], 16)
|
|
||||||
s[c] = (s[c] + s[d]) >>> 0
|
|
||||||
s[b] ^= s[c]
|
|
||||||
s[b] = rotl(s[b], 12)
|
|
||||||
s[a] = (s[a] + s[b]) >>> 0
|
|
||||||
s[d] ^= s[a]
|
|
||||||
s[d] = rotl(s[d], 8)
|
|
||||||
s[c] = (s[c] + s[d]) >>> 0
|
|
||||||
s[b] ^= s[c]
|
|
||||||
s[b] = rotl(s[b], 7)
|
|
||||||
}
|
|
||||||
|
|
||||||
const chachaBlock = (key, counter, nonce) => {
|
|
||||||
const state = new Uint32Array(16)
|
|
||||||
state[0] = SIGMA[0]
|
|
||||||
state[1] = SIGMA[1]
|
|
||||||
state[2] = SIGMA[2]
|
|
||||||
state[3] = SIGMA[3]
|
|
||||||
for (let i = 0; i < 8; i++) state[4 + i] = key.readUInt32LE(i * 4)
|
|
||||||
state[12] = counter >>> 0
|
|
||||||
state[13] = nonce.readUInt32LE(0)
|
|
||||||
state[14] = nonce.readUInt32LE(4)
|
|
||||||
state[15] = nonce.readUInt32LE(8)
|
|
||||||
|
|
||||||
const work = state.slice()
|
|
||||||
for (let i = 0; i < 10; i++) {
|
|
||||||
quarterRound(work, 0, 4, 8, 12)
|
|
||||||
quarterRound(work, 1, 5, 9, 13)
|
|
||||||
quarterRound(work, 2, 6, 10, 14)
|
|
||||||
quarterRound(work, 3, 7, 11, 15)
|
|
||||||
quarterRound(work, 0, 5, 10, 15)
|
|
||||||
quarterRound(work, 1, 6, 11, 12)
|
|
||||||
quarterRound(work, 2, 7, 8, 13)
|
|
||||||
quarterRound(work, 3, 4, 9, 14)
|
|
||||||
}
|
|
||||||
|
|
||||||
const out = Buffer.allocUnsafe(64)
|
|
||||||
for (let i = 0; i < 16; i++)
|
|
||||||
out.writeUInt32LE((work[i] + state[i]) >>> 0, i * 4)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
const chacha20 = (key, counter, nonce, data) => {
|
|
||||||
const out = Buffer.allocUnsafe(data.length)
|
|
||||||
for (let off = 0; off < data.length; off += 64) {
|
|
||||||
const ks = chachaBlock(key, (counter + (off >>> 6)) >>> 0, nonce)
|
|
||||||
const end = Math.min(off + 64, data.length)
|
|
||||||
for (let i = off; i < end; i++) out[i] = data[i] ^ ks[i - off]
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- RSA-256 Key Wrap (网易 NCBL 专用) ----
|
|
||||||
// 256-bit 模数 N 已被因式分解,故可直接计算私钥
|
|
||||||
const RSA_N =
|
|
||||||
0xfd90bd466ff9bc8a3fec2fbcf263b90d5c564879fa5d7aab89b31c1d5cb4139dn
|
|
||||||
const RSA_E = 65537n
|
|
||||||
|
|
||||||
const beToBig = (buf) => {
|
|
||||||
let n = 0n
|
|
||||||
for (const b of buf) n = (n << 8n) | BigInt(b)
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
const bigToBe = (n, len) => {
|
|
||||||
const out = Buffer.alloc(len)
|
|
||||||
for (let i = len - 1; i >= 0; i--) {
|
|
||||||
out[i] = Number(n & 0xffn)
|
|
||||||
n >>= 8n
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
const modPow = (base, exp, mod) => {
|
|
||||||
let result = 1n
|
|
||||||
base %= mod
|
|
||||||
while (exp > 0n) {
|
|
||||||
if (exp & 1n) result = (result * base) % mod
|
|
||||||
base = (base * base) % mod
|
|
||||||
exp >>= 1n
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
const rsaWrap = (keyA) => {
|
|
||||||
return bigToBe(modPow(beToBig(keyA), RSA_E, RSA_N), 32)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- NCBL 加密格式 ----
|
|
||||||
const MAGIC = Buffer.from('NCBL', 'ascii')
|
|
||||||
const NCBL_VERSION = 3
|
|
||||||
const HEADER_FIXED_LEN = 70
|
|
||||||
const META_BLOCK_TYPE = 0x4343
|
|
||||||
const DEFAULT_MAX_FRAME = 0x8000
|
|
||||||
|
|
||||||
const getCompress = () => {
|
|
||||||
if (typeof zlib.zstdCompressSync === 'function') {
|
|
||||||
return { compress: (buf) => zlib.zstdCompressSync(buf), name: 'zstd' }
|
|
||||||
}
|
|
||||||
return { compress: (buf) => zlib.gzipSync(buf), name: 'gzip' }
|
|
||||||
}
|
|
||||||
|
|
||||||
const encryptNCBL = (meta, body, opts = {}) => {
|
|
||||||
const metaBuf = Buffer.isBuffer(meta) ? meta : Buffer.from(meta, 'utf-8')
|
|
||||||
const bodyBuf = Buffer.isBuffer(body) ? body : Buffer.from(body, 'utf-8')
|
|
||||||
const maxFrame = opts.maxFrame || DEFAULT_MAX_FRAME
|
|
||||||
|
|
||||||
const keyA = opts.keyA || crypto.randomBytes(32)
|
|
||||||
if (keyA[0] >= 0xa3) keyA[0] = 0xa2
|
|
||||||
|
|
||||||
const keyB = rsaWrap(keyA)
|
|
||||||
|
|
||||||
const uuid = opts.uuid || crypto.randomBytes(16)
|
|
||||||
if (!opts.uuid) {
|
|
||||||
uuid[6] = (uuid[6] & 0x0f) | 0x40
|
|
||||||
uuid[8] = (uuid[8] & 0x3f) | 0x80
|
|
||||||
}
|
|
||||||
const nonce = uuid.subarray(0, 12)
|
|
||||||
const counter = uuid.readUInt32LE(12) >>> 2
|
|
||||||
const baseSeq = opts.baseSeq || crypto.randomBytes(2).readUInt16LE(0)
|
|
||||||
|
|
||||||
const metaCipher = chacha20(keyB, counter, nonce, metaBuf)
|
|
||||||
const metaBlock = Buffer.concat([
|
|
||||||
(() => {
|
|
||||||
const h = Buffer.allocUnsafe(4)
|
|
||||||
h.writeUInt16LE(META_BLOCK_TYPE, 0)
|
|
||||||
h.writeUInt16LE(metaCipher.length, 2)
|
|
||||||
return h
|
|
||||||
})(),
|
|
||||||
metaCipher,
|
|
||||||
])
|
|
||||||
const headerLen = HEADER_FIXED_LEN + metaBlock.length
|
|
||||||
|
|
||||||
const { compress } = getCompress()
|
|
||||||
const compressed = compress(bodyBuf)
|
|
||||||
|
|
||||||
const frames = []
|
|
||||||
let seq = baseSeq
|
|
||||||
for (let off = 0; off < compressed.length || off === 0; off += maxFrame) {
|
|
||||||
const slice = compressed.subarray(off, off + maxFrame)
|
|
||||||
const cipher = chacha20(keyA, counter, nonce, slice)
|
|
||||||
const head = Buffer.allocUnsafe(6)
|
|
||||||
head.writeUInt16LE(cipher.length, 0)
|
|
||||||
head.writeUInt32LE(seq >>> 0, 2)
|
|
||||||
frames.push(head, cipher)
|
|
||||||
seq++
|
|
||||||
if (compressed.length === 0) break
|
|
||||||
}
|
|
||||||
|
|
||||||
const trailing = Buffer.concat(frames)
|
|
||||||
const frameCount = seq - baseSeq
|
|
||||||
|
|
||||||
const header = Buffer.alloc(HEADER_FIXED_LEN)
|
|
||||||
MAGIC.copy(header, 0)
|
|
||||||
header.writeUInt32LE(NCBL_VERSION, 4)
|
|
||||||
header.writeUInt16LE(headerLen, 8)
|
|
||||||
uuid.copy(header, 10)
|
|
||||||
keyB.copy(header, 26)
|
|
||||||
header.writeUInt32LE(baseSeq >>> 0, 58)
|
|
||||||
header.writeUInt32LE((baseSeq + frameCount - 1) >>> 0, 62)
|
|
||||||
header.writeUInt32LE(trailing.length, 66)
|
|
||||||
|
|
||||||
return Buffer.concat([header, metaBlock, trailing])
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- 日志记录格式 ----
|
|
||||||
const FIELD_SEP = '\x01'
|
|
||||||
|
|
||||||
const buildRecord = ({ time, action, data }) => {
|
|
||||||
const json = typeof data === 'string' ? data : JSON.stringify(data)
|
|
||||||
return [time, action, json].join(FIELD_SEP)
|
|
||||||
}
|
|
||||||
|
|
||||||
const buildRecords = (records) => records.map(buildRecord).join('')
|
|
||||||
|
|
||||||
// ---- PLV / PLD 构建器 (桌面客户端格式) ----
|
|
||||||
const buildPlv = (ctx, song, source) => {
|
|
||||||
const now = Date.now()
|
|
||||||
const addRefer = `[F:63][${now}#933#${ctx.app.version}#${ctx.app.versionCode}#c9156c3][e][2][23][cell_pc_songlist_song:2|page_pc_songlist_songflow|page_mine_like_music][${song.id}:song:x:x|:::|${source.id}:list::]`
|
|
||||||
const multiRefers = [
|
|
||||||
'[F:26][s][18][_ai]',
|
|
||||||
'[F:26][s][12][_ai]',
|
|
||||||
`[F:63][${now}#933#${ctx.app.version}#${ctx.app.versionCode}#c9156c3][e][2][8][cell_pc_main_tab_entrance:6|page_pc_main_tab][我喜欢的音乐:spm::|:::]`,
|
|
||||||
'[F:26][s][5][_ai]',
|
|
||||||
'[F:26][s][0][_ai]',
|
|
||||||
]
|
|
||||||
|
|
||||||
return {
|
|
||||||
mode: 'circulation',
|
|
||||||
download: 0,
|
|
||||||
alg: '',
|
|
||||||
status: 'front',
|
|
||||||
id: String(song.id),
|
|
||||||
bitrate: song.bitrate,
|
|
||||||
type: 'song',
|
|
||||||
is_listentogether: 0,
|
|
||||||
source: source.name,
|
|
||||||
is_heart: 0,
|
|
||||||
resource_ratio: '',
|
|
||||||
resource_time: song.time,
|
|
||||||
musiceffect_id: '',
|
|
||||||
app_mode: 2,
|
|
||||||
bitrate_level: song.level,
|
|
||||||
_addrefer: addRefer,
|
|
||||||
_multirefers: multiRefers,
|
|
||||||
vipType: ctx.auth.vipType,
|
|
||||||
fee: 1,
|
|
||||||
file: 4,
|
|
||||||
rightSource: 0,
|
|
||||||
sourceId: source.id,
|
|
||||||
sourcetype: source.type,
|
|
||||||
libra_abt: '',
|
|
||||||
channel: ctx.app.channel,
|
|
||||||
curStartChannel: '',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const buildPld = (ctx, song, source, played) => {
|
|
||||||
const now = Date.now()
|
|
||||||
const addRefer = `[F:63][${now}#616#${ctx.app.version}#${ctx.app.versionCode}#c9156c3][e][2][92][btn_pc_cover_play|cell_pc_songlist_song:6|page_pc_songlist_songflow|page_mine_like_music][:::|${song.id}:song:x:x|:::|${source.id}:list::]`
|
|
||||||
const multiRefers = [
|
|
||||||
'[F:26][s][87][_ai]',
|
|
||||||
'[F:26][s][81][_ai]',
|
|
||||||
'[F:26][s][75][_ai]',
|
|
||||||
'[F:26][s][69][_ai]',
|
|
||||||
'[F:26][s][63][_ai]',
|
|
||||||
]
|
|
||||||
|
|
||||||
return {
|
|
||||||
mode: 'circulation',
|
|
||||||
download: 0,
|
|
||||||
alg: '',
|
|
||||||
status: 'front',
|
|
||||||
id: String(song.id),
|
|
||||||
time: played,
|
|
||||||
type: 'song',
|
|
||||||
is_listentogether: 0,
|
|
||||||
source: source.name,
|
|
||||||
is_heart: 0,
|
|
||||||
realtime: played,
|
|
||||||
resource_ratio: '',
|
|
||||||
resource_time: song.time,
|
|
||||||
musiceffect_id: '1001',
|
|
||||||
app_mode: 1,
|
|
||||||
lyriceffect: 'default',
|
|
||||||
displayMode: 'classic',
|
|
||||||
bitrate: song.bitrate,
|
|
||||||
bitrate_level: song.level,
|
|
||||||
_addrefer: addRefer,
|
|
||||||
_multirefers: multiRefers,
|
|
||||||
vipType: ctx.auth.vipType,
|
|
||||||
fee: 8,
|
|
||||||
file: 4,
|
|
||||||
rightSource: 0,
|
|
||||||
sourceId: source.id,
|
|
||||||
sourcetype: source.type,
|
|
||||||
end: 'interrupt',
|
|
||||||
libra_abt: '',
|
|
||||||
channel: ctx.app.channel,
|
|
||||||
curStartChannel: '',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Cookie → 设备/认证上下文 转换 ----
|
|
||||||
const extractContext = (cookieObj) => {
|
|
||||||
return {
|
|
||||||
app: {
|
|
||||||
id: cookieObj.appid || '',
|
|
||||||
urs: '',
|
|
||||||
pid: '',
|
|
||||||
nsm: cookieObj.WEVNSM || '1.0.0',
|
|
||||||
cid:
|
|
||||||
cookieObj.WNMCID ||
|
|
||||||
`${crypto.randomBytes(3).toString('hex')}.${Date.now()}.01.0`,
|
|
||||||
channel: cookieObj.channel || 'netease',
|
|
||||||
version: cookieObj.appver || '3.1.35',
|
|
||||||
versionCode: cookieObj.versioncode || '205293',
|
|
||||||
buildCode: cookieObj.buildver || '',
|
|
||||||
buildType: 'release',
|
|
||||||
packageId: '',
|
|
||||||
},
|
|
||||||
device: {
|
|
||||||
id: cookieObj.deviceId || cookieObj.sDeviceId || '',
|
|
||||||
ti: cookieObj.NMTID || '',
|
|
||||||
sign: cookieObj.clientSign || '',
|
|
||||||
model: cookieObj.mode || cookieObj.mobilename || '',
|
|
||||||
nnid: cookieObj._ntes_nnid || ',',
|
|
||||||
nuid: cookieObj._ntes_nuid || '',
|
|
||||||
csrf: cookieObj.__csrf || '',
|
|
||||||
systemType: cookieObj.os || 'pc',
|
|
||||||
systemVersion:
|
|
||||||
cookieObj.osver ||
|
|
||||||
'Microsoft-Windows-10-Professional-build-19045-64bit',
|
|
||||||
},
|
|
||||||
auth: {
|
|
||||||
id: cookieObj.uid || '',
|
|
||||||
token: cookieObj.MUSIC_U || '',
|
|
||||||
sessionId: cookieObj['JSESSIONID-WYYY'] || '',
|
|
||||||
vipType: cookieObj.vipType || '',
|
|
||||||
},
|
|
||||||
startTime: Date.now(),
|
|
||||||
processId: Math.floor(Math.random() * 90000) + 10000,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从 query.cookie 解析 cookie 对象
|
|
||||||
// 支持字符串 ("key=val; key2=val2") 或已解析的对象
|
|
||||||
// 注意:不做 URL decode!MUSIC_U 中的 '+' 会被 decodeURIComponent 错误地转为空格
|
|
||||||
const parseCookie = (cookie) => {
|
|
||||||
if (typeof cookie === 'object' && cookie !== null) return cookie
|
|
||||||
if (typeof cookie === 'string') {
|
|
||||||
const obj = {}
|
|
||||||
cookie.split(';').forEach((part) => {
|
|
||||||
const idx = part.indexOf('=')
|
|
||||||
if (idx > 0) {
|
|
||||||
const key = part.substring(0, idx).trim()
|
|
||||||
const val = part.substring(idx + 1).trim()
|
|
||||||
if (key) obj[key] = val
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return obj
|
|
||||||
}
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- 辅助函数 ----
|
|
||||||
const randomUUID = () => {
|
|
||||||
if (typeof crypto.randomUUID === 'function') {
|
|
||||||
return crypto.randomUUID().replace(/-/g, '')
|
|
||||||
}
|
|
||||||
return 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'.replace(/x/g, () =>
|
|
||||||
Math.floor(Math.random() * 16).toString(16),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const randomHex = (len) => crypto.randomBytes(len / 2).toString('hex')
|
|
||||||
|
|
||||||
// ---- HTTP 上传工具 (NCBL 专用) ----
|
|
||||||
const buildMultipart = (payload) => {
|
|
||||||
const boundary = randomUUID()
|
|
||||||
const fileName = `op_${Math.floor(Math.random() * 90000) + 10000}_0_${Math.floor(Math.random() * 4294967295) + 1}`
|
|
||||||
|
|
||||||
const CRLF = '\r\n'
|
|
||||||
const headerLines = [
|
|
||||||
`--${boundary}`,
|
|
||||||
`Content-Disposition: form-data; name="file"; filename="${fileName}"`,
|
|
||||||
'Content-Type: multipart/form-data',
|
|
||||||
'',
|
|
||||||
'',
|
|
||||||
].join(CRLF)
|
|
||||||
const footer = `${CRLF}--${boundary}--${CRLF}`
|
|
||||||
|
|
||||||
return {
|
|
||||||
boundary,
|
|
||||||
fileName,
|
|
||||||
multipartBody: Buffer.concat([
|
|
||||||
Buffer.from(headerLines, 'utf-8'),
|
|
||||||
payload,
|
|
||||||
Buffer.from(footer, 'utf-8'),
|
|
||||||
]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const buildCookieStr = (ctx) => {
|
|
||||||
const parts = [
|
|
||||||
`JSESSIONID-WYYY=${ctx.auth.sessionId}`,
|
|
||||||
`MUSIC_U=${ctx.auth.token}`,
|
|
||||||
`NMTID=${ctx.device.ti}`,
|
|
||||||
`WEVNSM=${ctx.app.nsm}`,
|
|
||||||
`WNMCID=${ctx.app.cid}`,
|
|
||||||
`__csrf=${ctx.device.csrf}`,
|
|
||||||
`__remember_me=true`,
|
|
||||||
`_iuqxldmzr_=33`,
|
|
||||||
`_ntes_nnid=${ctx.device.nnid}`,
|
|
||||||
`_ntes_nuid=${ctx.device.nuid}`,
|
|
||||||
`appver=${ctx.app.version}.${ctx.app.versionCode}`,
|
|
||||||
`channel=${ctx.app.channel}`,
|
|
||||||
`clientSign=${ctx.device.sign}`,
|
|
||||||
`deviceId=${ctx.device.id}`,
|
|
||||||
`mode=${ctx.device.model}`,
|
|
||||||
`ntes_kaola_ad=1`,
|
|
||||||
`os=${ctx.device.systemType}`,
|
|
||||||
`osver=${ctx.device.systemVersion}`,
|
|
||||||
]
|
|
||||||
return parts.join('; ')
|
|
||||||
}
|
|
||||||
|
|
||||||
const buildMetaJson = (ctx) =>
|
|
||||||
JSON.stringify({
|
|
||||||
'JSESSIONID-WYYY': ctx.auth.sessionId,
|
|
||||||
MUSIC_U: ctx.auth.token,
|
|
||||||
NMTID: ctx.device.ti,
|
|
||||||
WEVNSM: ctx.app.nsm,
|
|
||||||
WNMCID: ctx.app.cid,
|
|
||||||
__csrf: ctx.device.csrf,
|
|
||||||
_iuqxldmzr_: '33',
|
|
||||||
_ntes_nnid: ctx.device.nnid,
|
|
||||||
_ntes_nuid: ctx.device.nuid,
|
|
||||||
appver: `${ctx.app.version}.${ctx.app.versionCode}`,
|
|
||||||
channel: ctx.app.channel,
|
|
||||||
clientSign: ctx.device.sign,
|
|
||||||
deviceId: ctx.device.id,
|
|
||||||
mode: ctx.device.model,
|
|
||||||
ntes_kaola_ad: '1',
|
|
||||||
os: ctx.device.systemType,
|
|
||||||
osver: ctx.device.systemVersion,
|
|
||||||
})
|
|
||||||
|
|
||||||
const doUpload = async (ctx, metaJson, body, cookieStr, label) => {
|
|
||||||
const uploadUrl = DOMAIN3 + '/api/clientlog/encrypt/upload?multiupload=true'
|
|
||||||
|
|
||||||
const payload = encryptNCBL(metaJson, body)
|
|
||||||
const { boundary, fileName, multipartBody } = buildMultipart(payload)
|
|
||||||
|
|
||||||
const resp = await axios({
|
|
||||||
method: 'POST',
|
|
||||||
url: uploadUrl,
|
|
||||||
headers: {
|
|
||||||
'Content-Type': `multipart/form-data; boundary=${boundary}`,
|
|
||||||
Referer: 'https://music.163.com/di',
|
|
||||||
'User-Agent': `Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Safari/537.36 Chrome/91.0.4472.164 NeteaseMusicDesktop/${ctx.app.version}`,
|
|
||||||
'Accept-Encoding': 'gzip,deflate',
|
|
||||||
'Accept-Language': 'zh-CN,zh;q=0.8',
|
|
||||||
Cookie: cookieStr,
|
|
||||||
},
|
|
||||||
data: multipartBody,
|
|
||||||
maxBodyLength: 10 * 1024 * 1024,
|
|
||||||
timeout: 15000,
|
|
||||||
validateStatus: () => true,
|
|
||||||
})
|
|
||||||
|
|
||||||
const respBody = resp.data
|
|
||||||
const code = respBody?.code
|
|
||||||
const success =
|
|
||||||
code === 200 && respBody?.data?.successfiles?.includes?.(fileName)
|
|
||||||
|
|
||||||
return { success, fileName, payload, respBody }
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- 导出 ----
|
|
||||||
module.exports = {
|
|
||||||
chacha20,
|
|
||||||
rsaWrap,
|
|
||||||
encryptNCBL,
|
|
||||||
getCompress,
|
|
||||||
MAGIC,
|
|
||||||
NCBL_VERSION,
|
|
||||||
HEADER_FIXED_LEN,
|
|
||||||
META_BLOCK_TYPE,
|
|
||||||
DEFAULT_MAX_FRAME,
|
|
||||||
buildRecord,
|
|
||||||
buildRecords,
|
|
||||||
FIELD_SEP,
|
|
||||||
buildPlv,
|
|
||||||
buildPld,
|
|
||||||
extractContext,
|
|
||||||
parseCookie,
|
|
||||||
randomUUID,
|
|
||||||
randomHex,
|
|
||||||
buildMultipart,
|
|
||||||
buildCookieStr,
|
|
||||||
buildMetaJson,
|
|
||||||
doUpload,
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user