mirror of
https://github.com/NeteaseCloudMusicApiEnhanced/api-enhanced.git
synced 2026-03-21 19:13:10 +00:00
Compare commits
No commits in common. "5b5f380cd5afd990ddecfdcf637597f45a8d09b1" and "3647fc5fa0e84cb9d1c08cf64669666da039a8ae" have entirely different histories.
5b5f380cd5
...
3647fc5fa0
4
.github/workflows/build-and-pr.yml
vendored
4
.github/workflows/build-and-pr.yml
vendored
@ -46,7 +46,7 @@ jobs:
|
|||||||
PKG_TARGET: ${{ matrix.target }}
|
PKG_TARGET: ${{ matrix.target }}
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: app-${{ matrix.platform }}
|
name: app-${{ matrix.platform }}
|
||||||
path: ${{ matrix.output }}
|
path: ${{ matrix.output }}
|
||||||
@ -62,7 +62,7 @@ jobs:
|
|||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Download all artifacts
|
- name: Download all artifacts
|
||||||
uses: actions/download-artifact@v8
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
path: precompiled
|
path: precompiled
|
||||||
|
|
||||||
|
|||||||
@ -130,7 +130,7 @@ $ sudo docker run -d -p 3000:3000 ncm-api
|
|||||||
|
|
||||||
| 变量名 | 默认值 | 说明 |
|
| 变量名 | 默认值 | 说明 |
|
||||||
|----------------------------|--------------------------------------|----------------------------------------------------|
|
|----------------------------|--------------------------------------|----------------------------------------------------|
|
||||||
| **CORS_ALLOW_ORIGIN** | `*` | 允许跨域请求的域名。可填写单个源,或使用逗号分隔多个源(例如 `https://a.com,https://b.com`)。 |
|
| **CORS_ALLOW_ORIGIN** | `*` | 允许跨域请求的域名。若需要限制,请指定具体域名(例如 `https://example.com`)。 |
|
||||||
| **ENABLE_PROXY** | `false` | 是否启用反向代理功能。 |
|
| **ENABLE_PROXY** | `false` | 是否启用反向代理功能。 |
|
||||||
| **PROXY_URL** | `https://your-proxy-url.com/?proxy=` | 代理服务地址。仅当 `ENABLE_PROXY=true` 时生效。 |
|
| **PROXY_URL** | `https://your-proxy-url.com/?proxy=` | 代理服务地址。仅当 `ENABLE_PROXY=true` 时生效。 |
|
||||||
| **ENABLE_GENERAL_UNBLOCK** | `true` | 是否启用全局解灰(推荐开启)。开启后所有歌曲都尝试自动解锁。 |
|
| **ENABLE_GENERAL_UNBLOCK** | `true` | 是否启用全局解灰(推荐开启)。开启后所有歌曲都尝试自动解锁。 |
|
||||||
|
|||||||
@ -1,9 +0,0 @@
|
|||||||
// DIFM电台 - 分类
|
|
||||||
|
|
||||||
const createOption = require('../util/option.js')
|
|
||||||
module.exports = (query, request) => {
|
|
||||||
const data = {
|
|
||||||
sources: query.sources || '[0]',
|
|
||||||
}
|
|
||||||
return request(`/api/dj/difm/all/style/channel/v2`, data, createOption(query))
|
|
||||||
}
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
// DIFM电台 - 收藏频道
|
|
||||||
|
|
||||||
const createOption = require('../util/option.js')
|
|
||||||
module.exports = (query, request) => {
|
|
||||||
const data = {
|
|
||||||
id: query.id,
|
|
||||||
}
|
|
||||||
return request(`/api/dj/difm/channel/subscribe`, data, createOption(query))
|
|
||||||
}
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
// DIFM电台 - 取消收藏频道
|
|
||||||
|
|
||||||
const createOption = require('../util/option.js')
|
|
||||||
module.exports = (query, request) => {
|
|
||||||
const data = {
|
|
||||||
id: query.id,
|
|
||||||
}
|
|
||||||
return request(`/api/dj/difm/channel/unsubscribe`, data, createOption(query))
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
// DIFM电台 - 播放列表
|
|
||||||
|
|
||||||
const createOption = require('../util/option.js')
|
|
||||||
module.exports = (query, request) => {
|
|
||||||
const data = {
|
|
||||||
limit: query.limit || 5,
|
|
||||||
source: query.source || 0,
|
|
||||||
channelId: query.channelId,
|
|
||||||
}
|
|
||||||
return request(`/api/dj/difm/playing/tracks/list`, data, createOption(query))
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
// DIFM电台 - 收藏列表
|
|
||||||
|
|
||||||
const createOption = require('../util/option.js')
|
|
||||||
module.exports = (query, request) => {
|
|
||||||
const data = {
|
|
||||||
sources: query.sources || '[0]',
|
|
||||||
}
|
|
||||||
return request(
|
|
||||||
`/api/dj/difm/subscribe/channels/get/v2`,
|
|
||||||
data,
|
|
||||||
createOption(query),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
23
package.json
23
package.json
@ -65,14 +65,13 @@
|
|||||||
"data"
|
"data"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@neteasecloudmusicapienhanced/unblockmusic-utils": "^0.2.4",
|
"@neteasecloudmusicapienhanced/unblockmusic-utils": "^0.2.3",
|
||||||
"axios": "^1.13.6",
|
"axios": "^1.13.5",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"dotenv": "^17.3.1",
|
"dotenv": "^17.3.1",
|
||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
"express-fileupload": "^1.5.2",
|
"express-fileupload": "^1.5.2",
|
||||||
"gzip": "^0.1.0",
|
"music-metadata": "^11.12.1",
|
||||||
"music-metadata": "^11.12.3",
|
|
||||||
"node-forge": "^1.3.3",
|
"node-forge": "^1.3.3",
|
||||||
"pac-proxy-agent": "^7.2.0",
|
"pac-proxy-agent": "^7.2.0",
|
||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
@ -82,22 +81,22 @@
|
|||||||
"yargs": "^18.0.0"
|
"yargs": "^18.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "^3.3.5",
|
"@eslint/eslintrc": "^3.3.3",
|
||||||
"@eslint/js": "^9.39.4",
|
"@eslint/js": "^9.39.3",
|
||||||
"@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.5.0",
|
"@types/node": "25.0.9",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.57.1",
|
"@typescript-eslint/eslint-plugin": "^8.56.0",
|
||||||
"@typescript-eslint/parser": "^8.57.1",
|
"@typescript-eslint/parser": "^8.56.0",
|
||||||
"eslint": "^9.39.4",
|
"eslint": "^9.39.3",
|
||||||
"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.5",
|
"eslint-plugin-prettier": "^5.5.5",
|
||||||
"globals": "^17.4.0",
|
"globals": "^16.5.0",
|
||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
"intelli-espower-loader": "^1.1.0",
|
"intelli-espower-loader": "^1.1.0",
|
||||||
"lint-staged": "^16.4.0",
|
"lint-staged": "^16.2.7",
|
||||||
"mocha": "^11.7.5",
|
"mocha": "^11.7.5",
|
||||||
"nodemon": "^3.1.14",
|
"nodemon": "^3.1.14",
|
||||||
"pkg": "^5.8.1",
|
"pkg": "^5.8.1",
|
||||||
|
|||||||
478
pnpm-lock.yaml
generated
478
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -5143,72 +5143,6 @@ let data = encodeURIComponent(
|
|||||||
|
|
||||||
**调用例子 :** `/comment/reply?id=2058263032&commentId=123456789&content=我也觉得这首歌很棒!`
|
**调用例子 :** `/comment/reply?id=2058263032&commentId=123456789&content=我也觉得这首歌很棒!`
|
||||||
|
|
||||||
### DIFM电台 - 分类
|
|
||||||
|
|
||||||
说明: 调用此接口, 获取DIFM电台分类
|
|
||||||
|
|
||||||
**必选参数 :**
|
|
||||||
|
|
||||||
`sources`: 来源列表, 0: 最嗨电音 1: 古典电台 2: 爵士电台
|
|
||||||
|
|
||||||
**接口地址:** `/dj/difm/all/style/channel`
|
|
||||||
|
|
||||||
**调用例子:** `/dj/difm/all/style/channel?sources=[0]`
|
|
||||||
|
|
||||||
### DIFM电台 - 收藏列表
|
|
||||||
|
|
||||||
说明: 调用此接口, 获取DIFM电台收藏列表
|
|
||||||
|
|
||||||
**必选参数 :**
|
|
||||||
|
|
||||||
`sources`: 来源列表, 0: 最嗨电音 1: 古典电台 2: 爵士电台
|
|
||||||
|
|
||||||
**接口地址:** `/dj/difm/subscribe/channels/get`
|
|
||||||
|
|
||||||
**调用例子:** `/dj/difm/subscribe/channels/get?sources=[0]`
|
|
||||||
|
|
||||||
### DIFM电台 - 收藏频道
|
|
||||||
|
|
||||||
说明: 调用此接口, 可收藏DIFM频道
|
|
||||||
|
|
||||||
**必选参数 :**
|
|
||||||
|
|
||||||
`id`: 频道id
|
|
||||||
|
|
||||||
**接口地址:** `/dj/difm/channel/subscribe`
|
|
||||||
|
|
||||||
**调用例子:** `/dj/difm/channel/subscribe?id=1`
|
|
||||||
|
|
||||||
### DIFM电台 - 取消收藏频道
|
|
||||||
|
|
||||||
说明: 调用此接口, 可取消收藏DIFM频道
|
|
||||||
|
|
||||||
**必选参数 :**
|
|
||||||
|
|
||||||
`id`: 频道id
|
|
||||||
|
|
||||||
**接口地址:** `/dj/difm/channel/unsubscribe`
|
|
||||||
|
|
||||||
**调用例子:** `/dj/difm/channel/unsubscribe?id=1`
|
|
||||||
|
|
||||||
### DIFM电台 - 播放列表
|
|
||||||
|
|
||||||
说明: 调用此接口, 获取DIFM播放列表
|
|
||||||
|
|
||||||
**必选参数 :**
|
|
||||||
|
|
||||||
`source`: 来源, 0: 最嗨电音 1: 古典电台 2: 爵士电台
|
|
||||||
|
|
||||||
`channelId`: 频道id
|
|
||||||
|
|
||||||
**可选参数 :**
|
|
||||||
|
|
||||||
`limit`: 返回数量, 默认为 5
|
|
||||||
|
|
||||||
**接口地址:** `/dj/difm/playing/tracks/list`
|
|
||||||
|
|
||||||
**调用例子:** `/dj/difm/playing/tracks/list?source=0&channelId=1012`
|
|
||||||
|
|
||||||
## 离线访问此文档
|
## 离线访问此文档
|
||||||
|
|
||||||
此文档同时也是 Progressive Web Apps(PWA), 加入了 serviceWorker, 可离线访问
|
此文档同时也是 Progressive Web Apps(PWA), 加入了 serviceWorker, 可离线访问
|
||||||
|
|||||||
45
server.js
45
server.js
@ -127,45 +127,15 @@ async function checkVersion() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseCorsAllowOrigins(corsAllowOrigin) {
|
|
||||||
if (!corsAllowOrigin) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const origins = corsAllowOrigin
|
|
||||||
.split(',')
|
|
||||||
.map((origin) => origin.trim())
|
|
||||||
.filter(Boolean)
|
|
||||||
|
|
||||||
return origins.length > 0 ? origins : null
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCorsAllowOrigin(allowOrigins, requestOrigin) {
|
|
||||||
if (!allowOrigins) {
|
|
||||||
return requestOrigin || '*'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (allowOrigins.includes('*')) {
|
|
||||||
return '*'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (requestOrigin && allowOrigins.includes(requestOrigin)) {
|
|
||||||
return requestOrigin
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct the server of NCM API.
|
* Construct the server of NCM API.
|
||||||
*
|
*
|
||||||
* @param {ModuleDefinition[]} [moduleDefs] Customized module definitions [advanced]
|
* @param {ModuleDefinition[]} [moduleDefs] Customized module definitions [advanced]
|
||||||
* @returns {Promise<import("express").Express>} The server instance.
|
* @returns {Promise<import("express").Express>} The server instance.
|
||||||
*/
|
*/
|
||||||
async function constructServer(moduleDefs) {
|
async function consturctServer(moduleDefs) {
|
||||||
const app = express()
|
const app = express()
|
||||||
const { CORS_ALLOW_ORIGIN } = process.env
|
const { CORS_ALLOW_ORIGIN } = process.env
|
||||||
const allowOrigins = parseCorsAllowOrigins(CORS_ALLOW_ORIGIN)
|
|
||||||
app.set('trust proxy', true)
|
app.set('trust proxy', true)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -177,17 +147,10 @@ async function constructServer(moduleDefs) {
|
|||||||
*/
|
*/
|
||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
if (req.path !== '/' && !req.path.includes('.')) {
|
if (req.path !== '/' && !req.path.includes('.')) {
|
||||||
const corsAllowOrigin = getCorsAllowOrigin(
|
|
||||||
allowOrigins,
|
|
||||||
req.headers.origin,
|
|
||||||
)
|
|
||||||
const shouldSetVaryHeader = corsAllowOrigin && corsAllowOrigin !== '*'
|
|
||||||
res.set({
|
res.set({
|
||||||
'Access-Control-Allow-Credentials': true,
|
'Access-Control-Allow-Credentials': true,
|
||||||
...(corsAllowOrigin
|
'Access-Control-Allow-Origin':
|
||||||
? { 'Access-Control-Allow-Origin': corsAllowOrigin }
|
CORS_ALLOW_ORIGIN || req.headers.origin || '*',
|
||||||
: {}),
|
|
||||||
...(shouldSetVaryHeader ? { Vary: 'Origin' } : {}),
|
|
||||||
'Access-Control-Allow-Headers': 'X-Requested-With,Content-Type',
|
'Access-Control-Allow-Headers': 'X-Requested-With,Content-Type',
|
||||||
'Access-Control-Allow-Methods': 'PUT,POST,GET,DELETE,OPTIONS',
|
'Access-Control-Allow-Methods': 'PUT,POST,GET,DELETE,OPTIONS',
|
||||||
'Content-Type': 'application/json; charset=utf-8',
|
'Content-Type': 'application/json; charset=utf-8',
|
||||||
@ -396,7 +359,7 @@ async function serveNcmApi(options) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const constructServerSubmission = constructServer(options.moduleDefs)
|
const constructServerSubmission = consturctServer(options.moduleDefs)
|
||||||
|
|
||||||
const [_, app] = await Promise.all([
|
const [_, app] = await Promise.all([
|
||||||
checkVersionSubmission,
|
checkVersionSubmission,
|
||||||
|
|||||||
@ -240,7 +240,6 @@ const createRequest = (uri, data, options) => {
|
|||||||
headers['User-Agent'] = options.ua || chooseUserAgent('api', 'iphone')
|
headers['User-Agent'] = options.ua || chooseUserAgent('api', 'iphone')
|
||||||
|
|
||||||
if (crypto === 'eapi') {
|
if (crypto === 'eapi') {
|
||||||
// headers['x-aeapi'] = true // 服务器会使用gzip压缩返回值
|
|
||||||
data.header = header
|
data.header = header
|
||||||
data.e_r = toBoolean(
|
data.e_r = toBoolean(
|
||||||
options.e_r !== undefined
|
options.e_r !== undefined
|
||||||
@ -324,7 +323,6 @@ const createRequest = (uri, data, options) => {
|
|||||||
if (crypto === 'eapi' && data.e_r) {
|
if (crypto === 'eapi' && data.e_r) {
|
||||||
answer.body = encrypt.eapiResDecrypt(
|
answer.body = encrypt.eapiResDecrypt(
|
||||||
body.toString('hex').toUpperCase(),
|
body.toString('hex').toUpperCase(),
|
||||||
headers['x-aeapi'],
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
answer.body =
|
answer.body =
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user