@neteasecloudmusicapienhanced/api 4.30.2 → 4.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.MD +2 -1
- package/module/comment_add.js +15 -0
- package/module/comment_delete.js +10 -0
- package/module/comment_reply.js +13 -0
- package/module/dj_difm_all_style_channel.js +9 -0
- package/module/dj_difm_channel_subscribe.js +9 -0
- package/module/dj_difm_channel_unsubscribe.js +9 -0
- package/module/dj_difm_playing_tracks_list.js +11 -0
- package/module/dj_difm_subscribe_channels_get.js +13 -0
- package/module/search_suggest_pc.js +13 -0
- package/module/song_like.js +12 -0
- package/module/song_url_v1_302.js +53 -0
- package/module/user_followeds.js +1 -1
- package/module/voicelist_my_created.js +13 -0
- package/module/voicelist_search.js +6 -9
- package/package.json +14 -13
- package/public/docs/home.md +164 -5
- package/server.js +47 -5
- package/util/request.js +2 -0
package/README.MD
CHANGED
|
@@ -130,7 +130,7 @@ $ sudo docker run -d -p 3000:3000 ncm-api
|
|
|
130
130
|
|
|
131
131
|
| 变量名 | 默认值 | 说明 |
|
|
132
132
|
|----------------------------|--------------------------------------|----------------------------------------------------|
|
|
133
|
-
| **CORS_ALLOW_ORIGIN** | `*` |
|
|
133
|
+
| **CORS_ALLOW_ORIGIN** | `*` | 允许跨域请求的域名。可填写单个源,或使用逗号分隔多个源(例如 `https://a.com,https://b.com`)。 |
|
|
134
134
|
| **ENABLE_PROXY** | `false` | 是否启用反向代理功能。 |
|
|
135
135
|
| **PROXY_URL** | `https://your-proxy-url.com/?proxy=` | 代理服务地址。仅当 `ENABLE_PROXY=true` 时生效。 |
|
|
136
136
|
| **ENABLE_GENERAL_UNBLOCK** | `true` | 是否启用全局解灰(推荐开启)。开启后所有歌曲都尝试自动解锁。 |
|
|
@@ -210,6 +210,7 @@ pnpm test
|
|
|
210
210
|
## 贡献与社区
|
|
211
211
|
|
|
212
212
|
- 欢迎提交 PR、Issue 参与维护
|
|
213
|
+
- 要贡献新接口, 请参考[这篇文章](https://www.focalors.ltd/post/how-to-contribute-ncm-api)
|
|
213
214
|
|
|
214
215
|
### 致谢
|
|
215
216
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// 对某一首歌曲发表评论
|
|
2
|
+
|
|
3
|
+
const createOption = require('../util/option.js')
|
|
4
|
+
module.exports = (query, request) => {
|
|
5
|
+
const data = {
|
|
6
|
+
threadId: 'R_SO_4_' + query.id,
|
|
7
|
+
content: query.content,
|
|
8
|
+
resourceType: '0',
|
|
9
|
+
resourceId: '0',
|
|
10
|
+
expressionPicId: '-1',
|
|
11
|
+
bubbleId: '-1',
|
|
12
|
+
checkToken: '',
|
|
13
|
+
}
|
|
14
|
+
return request('/api/resource/comments/add', data, createOption(query))
|
|
15
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// 删除评论
|
|
2
|
+
|
|
3
|
+
const createOption = require('../util/option.js')
|
|
4
|
+
module.exports = (query, request) => {
|
|
5
|
+
const data = {
|
|
6
|
+
commentId: query.cid,
|
|
7
|
+
threadId: 'R_SO_4_' + query.id,
|
|
8
|
+
}
|
|
9
|
+
return request(`/api/resource/comments/delete`, data, createOption(query))
|
|
10
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// 回复评论
|
|
2
|
+
|
|
3
|
+
const createOption = require('../util/option.js')
|
|
4
|
+
module.exports = (query, request) => {
|
|
5
|
+
const data = {
|
|
6
|
+
threadId: query.id,
|
|
7
|
+
commentId: query.commentId,
|
|
8
|
+
content: query.content,
|
|
9
|
+
resourceType: '0',
|
|
10
|
+
resourceId: '0',
|
|
11
|
+
}
|
|
12
|
+
return request(`/api/v1/resource/comments/reply`, data, createOption(query))
|
|
13
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// DIFM电台 - 播放列表
|
|
2
|
+
|
|
3
|
+
const createOption = require('../util/option.js')
|
|
4
|
+
module.exports = (query, request) => {
|
|
5
|
+
const data = {
|
|
6
|
+
limit: query.limit || 5,
|
|
7
|
+
source: query.source || 0,
|
|
8
|
+
channelId: query.channelId,
|
|
9
|
+
}
|
|
10
|
+
return request(`/api/dj/difm/playing/tracks/list`, data, createOption(query))
|
|
11
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// DIFM电台 - 收藏列表
|
|
2
|
+
|
|
3
|
+
const createOption = require('../util/option.js')
|
|
4
|
+
module.exports = (query, request) => {
|
|
5
|
+
const data = {
|
|
6
|
+
sources: query.sources || '[0]',
|
|
7
|
+
}
|
|
8
|
+
return request(
|
|
9
|
+
`/api/dj/difm/subscribe/channels/get/v2`,
|
|
10
|
+
data,
|
|
11
|
+
createOption(query),
|
|
12
|
+
)
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// 搜索建议pc端
|
|
2
|
+
|
|
3
|
+
const createOption = require('../util/option.js')
|
|
4
|
+
module.exports = (query, request) => {
|
|
5
|
+
const data = {
|
|
6
|
+
keyword: query.keyword || '',
|
|
7
|
+
}
|
|
8
|
+
return request(
|
|
9
|
+
`/api/search/pc/suggest/keyword/get`,
|
|
10
|
+
data,
|
|
11
|
+
createOption(query),
|
|
12
|
+
)
|
|
13
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// 喜欢歌曲
|
|
2
|
+
|
|
3
|
+
const createOption = require('../util/option.js')
|
|
4
|
+
module.exports = (query, request) => {
|
|
5
|
+
const like = query.like !== 'false'
|
|
6
|
+
const data = {
|
|
7
|
+
trackId: query.id,
|
|
8
|
+
userid: query.uid,
|
|
9
|
+
like: like,
|
|
10
|
+
}
|
|
11
|
+
return request(`/api/song/like`, data, createOption(query))
|
|
12
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// 获取客户端歌曲下载链接 - v1
|
|
2
|
+
// 此版本不再采用 br 作为音质区分的标准
|
|
3
|
+
// 而是采用 standard, exhigh, lossless, hires, jyeffect(高清环绕声), sky(沉浸环绕声), jymaster(超清母带) 进行音质判断
|
|
4
|
+
|
|
5
|
+
const createOption = require('../util/option.js')
|
|
6
|
+
module.exports = async (query, request) => {
|
|
7
|
+
const data = {
|
|
8
|
+
id: query.id,
|
|
9
|
+
immerseType: 'c51',
|
|
10
|
+
level: query.level,
|
|
11
|
+
}
|
|
12
|
+
const response = await request(
|
|
13
|
+
`/api/song/enhance/download/url/v1`,
|
|
14
|
+
data,
|
|
15
|
+
createOption(query),
|
|
16
|
+
)
|
|
17
|
+
let url = response?.body?.data?.[0]?.url
|
|
18
|
+
|
|
19
|
+
if (!url) {
|
|
20
|
+
const fallbackData = {
|
|
21
|
+
ids: `[${query.id}]`,
|
|
22
|
+
level: query.level,
|
|
23
|
+
encodeType: 'flac',
|
|
24
|
+
}
|
|
25
|
+
if (query.level === 'sky') {
|
|
26
|
+
fallbackData.immerseType = 'c51'
|
|
27
|
+
}
|
|
28
|
+
const fallback = await request(
|
|
29
|
+
`/api/song/enhance/player/url/v1`,
|
|
30
|
+
fallbackData,
|
|
31
|
+
createOption(query),
|
|
32
|
+
)
|
|
33
|
+
url = fallback?.body?.data?.[0]?.url
|
|
34
|
+
|
|
35
|
+
if (!url) {
|
|
36
|
+
return fallback
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
status: 302,
|
|
41
|
+
body: '',
|
|
42
|
+
cookie: fallback.cookie || [],
|
|
43
|
+
redirectUrl: url,
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
status: 302,
|
|
49
|
+
body: '',
|
|
50
|
+
cookie: response.cookie || [],
|
|
51
|
+
redirectUrl: url,
|
|
52
|
+
}
|
|
53
|
+
}
|
package/module/user_followeds.js
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// 我创建的播客声音
|
|
2
|
+
|
|
3
|
+
const createOption = require('../util/option.js')
|
|
4
|
+
module.exports = (query, request) => {
|
|
5
|
+
const data = {
|
|
6
|
+
limit: query.limit || 20,
|
|
7
|
+
}
|
|
8
|
+
return request(
|
|
9
|
+
`/api/social/my/created/voicelist/v1`,
|
|
10
|
+
data,
|
|
11
|
+
createOption(query, 'weapi'),
|
|
12
|
+
)
|
|
13
|
+
}
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
const createOption = require('../util/option.js')
|
|
2
2
|
module.exports = (query, request) => {
|
|
3
3
|
const data = {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
keyword: query.keyword || '',
|
|
5
|
+
scene: 'normal',
|
|
6
|
+
limit: query.limit || '10',
|
|
7
|
+
offset: query.offset || '30',
|
|
8
|
+
e_r: true,
|
|
8
9
|
}
|
|
9
|
-
return request(
|
|
10
|
-
`/api/voice/workbench/voicelist/search`,
|
|
11
|
-
data,
|
|
12
|
-
createOption(query),
|
|
13
|
-
)
|
|
10
|
+
return request(`/api/search/voicelist/get`, data, createOption(query))
|
|
14
11
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neteasecloudmusicapienhanced/api",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.31.0",
|
|
4
4
|
"description": "全网最全的网易云音乐API接口 || A revival project for NeteaseCloudMusicApi Node.js Services (Half Refactor & Enhanced) || 网易云音乐 API 备份 + 增强 || 本项目自原版v4.28.0版本后开始自行维护",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "nodemon app.js",
|
|
@@ -65,14 +65,15 @@
|
|
|
65
65
|
"data"
|
|
66
66
|
],
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"@neteasecloudmusicapienhanced/unblockmusic-utils": "^0.2.
|
|
69
|
-
"axios": "^1.13.
|
|
68
|
+
"@neteasecloudmusicapienhanced/unblockmusic-utils": "^0.2.4",
|
|
69
|
+
"axios": "^1.13.6",
|
|
70
70
|
"crypto-js": "^4.2.0",
|
|
71
71
|
"dotenv": "^17.3.1",
|
|
72
72
|
"express": "^5.2.1",
|
|
73
73
|
"express-fileupload": "^1.5.2",
|
|
74
|
-
"
|
|
75
|
-
"
|
|
74
|
+
"gzip": "^0.1.0",
|
|
75
|
+
"music-metadata": "^11.12.3",
|
|
76
|
+
"node-forge": "^1.4.0",
|
|
76
77
|
"pac-proxy-agent": "^7.2.0",
|
|
77
78
|
"qrcode": "^1.5.4",
|
|
78
79
|
"safe-decode-uri-component": "^1.2.1",
|
|
@@ -81,22 +82,22 @@
|
|
|
81
82
|
"yargs": "^18.0.0"
|
|
82
83
|
},
|
|
83
84
|
"devDependencies": {
|
|
84
|
-
"@eslint/eslintrc": "^3.3.
|
|
85
|
-
"@eslint/js": "^9.39.
|
|
85
|
+
"@eslint/eslintrc": "^3.3.5",
|
|
86
|
+
"@eslint/js": "^9.39.4",
|
|
86
87
|
"@types/express": "^5.0.6",
|
|
87
88
|
"@types/express-fileupload": "^1.5.1",
|
|
88
89
|
"@types/mocha": "^10.0.10",
|
|
89
|
-
"@types/node": "25.0
|
|
90
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
91
|
-
"@typescript-eslint/parser": "^8.
|
|
92
|
-
"eslint": "^9.39.
|
|
90
|
+
"@types/node": "25.5.0",
|
|
91
|
+
"@typescript-eslint/eslint-plugin": "^8.57.2",
|
|
92
|
+
"@typescript-eslint/parser": "^8.57.2",
|
|
93
|
+
"eslint": "^9.39.4",
|
|
93
94
|
"eslint-config-prettier": "^10.1.8",
|
|
94
95
|
"eslint-plugin-html": "^8.1.4",
|
|
95
96
|
"eslint-plugin-prettier": "^5.5.5",
|
|
96
|
-
"globals": "^
|
|
97
|
+
"globals": "^17.4.0",
|
|
97
98
|
"husky": "^9.1.7",
|
|
98
99
|
"intelli-espower-loader": "^1.1.0",
|
|
99
|
-
"lint-staged": "^16.
|
|
100
|
+
"lint-staged": "^16.4.0",
|
|
100
101
|
"mocha": "^11.7.5",
|
|
101
102
|
"nodemon": "^3.1.14",
|
|
102
103
|
"pkg": "^5.8.1",
|
package/public/docs/home.md
CHANGED
|
@@ -783,7 +783,7 @@ tags: 歌单标签
|
|
|
783
783
|
**必选参数 :** `uid` : 用户 id
|
|
784
784
|
|
|
785
785
|
**可选参数 :**
|
|
786
|
-
`limit` : 返回数量 , 默认为
|
|
786
|
+
`limit` : 返回数量 , 默认为 20
|
|
787
787
|
|
|
788
788
|
`offset` : 偏移数量,用于分页 ,如 :( 页数 -1)\*30, 其中 30 为 limit 的值 , 默认为 0
|
|
789
789
|
|
|
@@ -1254,6 +1254,21 @@ tags: 歌单标签
|
|
|
1254
1254
|
|
|
1255
1255
|
说明:`杜比全景声`音质需要设备支持,不同的设备可能会返回不同码率的 url。cookie 需要传入`os=pc`保证返回正常码率的 url。
|
|
1256
1256
|
|
|
1257
|
+
### 302到音乐 url - 新版
|
|
1258
|
+
|
|
1259
|
+
说明 : 只允许传入单个`id`,会使用302重定向请求到目标url
|
|
1260
|
+
|
|
1261
|
+
**必选参数 :** `id` : 音乐 id
|
|
1262
|
+
`level`: 播放音质等级, 分为 `standard` => `标准`,`higher` => `较高`, `exhigh`=>`极高`,
|
|
1263
|
+
`lossless`=>`无损`, `hires`=>`Hi-Res`, `jyeffect` => `高清环绕声`, `sky` => `沉浸环绕声`, `dolby` => `杜比全景声`, `jymaster` => `超清母带`
|
|
1264
|
+
`unblock`: 是否使用使用歌曲解锁, 分为`true`和`false`
|
|
1265
|
+
|
|
1266
|
+
**接口地址 :** `/song/url/v1/302`
|
|
1267
|
+
|
|
1268
|
+
**调用例子 :** `/song/url/v1/302?id=1969519579&level=exhigh`
|
|
1269
|
+
|
|
1270
|
+
说明:`杜比全景声`音质需要设备支持,不同的设备可能会返回不同码率的 url。cookie 需要传入`os=pc`保证返回正常码率的 url。
|
|
1271
|
+
|
|
1257
1272
|
### 音乐是否可用
|
|
1258
1273
|
|
|
1259
1274
|
说明: 调用此接口,传入歌曲 id, 可获取音乐是否可用,返回 `{ success: true, message: 'ok' }` 或者 `{ success: false, message: '亲爱的,暂无版权' }`
|
|
@@ -4103,13 +4118,15 @@ type='1009' 获取其 id, 如`/search?keywords= 代码时间 &type=1009`
|
|
|
4103
4118
|
|
|
4104
4119
|
**接口地址:** `/voicelist/search`
|
|
4105
4120
|
|
|
4106
|
-
|
|
4121
|
+
**必选参数:**
|
|
4107
4122
|
|
|
4108
|
-
`
|
|
4123
|
+
`keyword`: 搜索关键词
|
|
4109
4124
|
|
|
4110
|
-
|
|
4125
|
+
**可选参数:**
|
|
4126
|
+
|
|
4127
|
+
`limit`: 取出歌单数量, 默认为 10
|
|
4111
4128
|
|
|
4112
|
-
`
|
|
4129
|
+
`offset`: 偏移数量 , 用于分页 , 默认为 30
|
|
4113
4130
|
|
|
4114
4131
|
### 播客声音列表
|
|
4115
4132
|
|
|
@@ -5050,6 +5067,148 @@ let data = encodeURIComponent(
|
|
|
5050
5067
|
|
|
5051
5068
|
**调用例子 :** `/user/playlist/collect?uid=32953014`
|
|
5052
5069
|
|
|
5070
|
+
### 搜索建议 - PC端
|
|
5071
|
+
|
|
5072
|
+
说明 : 调用此接口, 传入搜索关键词, 获取搜索建议
|
|
5073
|
+
|
|
5074
|
+
**必选参数 :**
|
|
5075
|
+
|
|
5076
|
+
`keyword`: 搜索关键词
|
|
5077
|
+
|
|
5078
|
+
**接口地址 :** `/search/suggest/pc`
|
|
5079
|
+
|
|
5080
|
+
**调用例子 :** `/search/suggest/pc?keyword=海阔天空`
|
|
5081
|
+
|
|
5082
|
+
### 喜欢歌曲 - 新版
|
|
5083
|
+
|
|
5084
|
+
说明 : 登录后调用此接口, 传入歌曲 id 用户id和喜欢状态, 可喜欢/取消喜欢歌曲
|
|
5085
|
+
|
|
5086
|
+
**必选参数 :**
|
|
5087
|
+
|
|
5088
|
+
`id`: 歌曲 id
|
|
5089
|
+
`uid`: 用户 id
|
|
5090
|
+
`like`: 喜欢状态, true 表示喜欢, false 表示取消喜欢
|
|
5091
|
+
|
|
5092
|
+
**接口地址 :** `/song/like`
|
|
5093
|
+
|
|
5094
|
+
**调用例子 :** `/song/like?id=2058263032&uid=32953014&like=true`
|
|
5095
|
+
|
|
5096
|
+
### 我创建的播客声音
|
|
5097
|
+
|
|
5098
|
+
说明 : 登录后调用此接口, 获取我创建的博客声音
|
|
5099
|
+
|
|
5100
|
+
**可选参数 :**
|
|
5101
|
+
|
|
5102
|
+
`limit` : 返回数量 , 默认为 20
|
|
5103
|
+
|
|
5104
|
+
**接口地址 :** `/voicelist/my/created`
|
|
5105
|
+
|
|
5106
|
+
**调用例子 :** `/voicelist/my/created`
|
|
5107
|
+
|
|
5108
|
+
### 发布评论
|
|
5109
|
+
|
|
5110
|
+
说明 : 登录后调用此接口, 传入评论线程 id, 评论内容等信息, 发布评论
|
|
5111
|
+
|
|
5112
|
+
**必选参数 :**
|
|
5113
|
+
|
|
5114
|
+
`id`: 歌曲id
|
|
5115
|
+
`content`: 评论内容
|
|
5116
|
+
|
|
5117
|
+
**接口地址 :** `/comment/add`
|
|
5118
|
+
|
|
5119
|
+
**调用例子 :** `/comment/add?id=2058263032&content=这首歌太棒了!`
|
|
5120
|
+
|
|
5121
|
+
### 删除评论
|
|
5122
|
+
|
|
5123
|
+
说明 : 登录后调用此接口, 传入评论 id, 删除评论
|
|
5124
|
+
|
|
5125
|
+
**必选参数 :**
|
|
5126
|
+
`cid`: 评论 id
|
|
5127
|
+
`id`: 歌曲id
|
|
5128
|
+
|
|
5129
|
+
**接口地址 :** `/comment/delete`
|
|
5130
|
+
|
|
5131
|
+
**调用例子 :** `/comment/delete?threadId=2058263032&commentId=123456789`
|
|
5132
|
+
|
|
5133
|
+
### 回复评论
|
|
5134
|
+
|
|
5135
|
+
说明 : 登录后调用此接口, 传入歌曲 id, 回复内容等信息, 回复评论
|
|
5136
|
+
|
|
5137
|
+
**必选参数 :**
|
|
5138
|
+
`id`: 歌曲id
|
|
5139
|
+
`commentId`: 被回复的评论 id
|
|
5140
|
+
`content`: 回复内容
|
|
5141
|
+
|
|
5142
|
+
**接口地址 :** `/comment/reply`
|
|
5143
|
+
|
|
5144
|
+
**调用例子 :** `/comment/reply?id=2058263032&commentId=123456789&content=我也觉得这首歌很棒!`
|
|
5145
|
+
|
|
5146
|
+
### DIFM电台 - 分类
|
|
5147
|
+
|
|
5148
|
+
说明: 调用此接口, 获取DIFM电台分类
|
|
5149
|
+
|
|
5150
|
+
**必选参数 :**
|
|
5151
|
+
|
|
5152
|
+
`sources`: 来源列表, 0: 最嗨电音 1: 古典电台 2: 爵士电台
|
|
5153
|
+
|
|
5154
|
+
**接口地址:** `/dj/difm/all/style/channel`
|
|
5155
|
+
|
|
5156
|
+
**调用例子:** `/dj/difm/all/style/channel?sources=[0]`
|
|
5157
|
+
|
|
5158
|
+
### DIFM电台 - 收藏列表
|
|
5159
|
+
|
|
5160
|
+
说明: 调用此接口, 获取DIFM电台收藏列表
|
|
5161
|
+
|
|
5162
|
+
**必选参数 :**
|
|
5163
|
+
|
|
5164
|
+
`sources`: 来源列表, 0: 最嗨电音 1: 古典电台 2: 爵士电台
|
|
5165
|
+
|
|
5166
|
+
**接口地址:** `/dj/difm/subscribe/channels/get`
|
|
5167
|
+
|
|
5168
|
+
**调用例子:** `/dj/difm/subscribe/channels/get?sources=[0]`
|
|
5169
|
+
|
|
5170
|
+
### DIFM电台 - 收藏频道
|
|
5171
|
+
|
|
5172
|
+
说明: 调用此接口, 可收藏DIFM频道
|
|
5173
|
+
|
|
5174
|
+
**必选参数 :**
|
|
5175
|
+
|
|
5176
|
+
`id`: 频道id
|
|
5177
|
+
|
|
5178
|
+
**接口地址:** `/dj/difm/channel/subscribe`
|
|
5179
|
+
|
|
5180
|
+
**调用例子:** `/dj/difm/channel/subscribe?id=1`
|
|
5181
|
+
|
|
5182
|
+
### DIFM电台 - 取消收藏频道
|
|
5183
|
+
|
|
5184
|
+
说明: 调用此接口, 可取消收藏DIFM频道
|
|
5185
|
+
|
|
5186
|
+
**必选参数 :**
|
|
5187
|
+
|
|
5188
|
+
`id`: 频道id
|
|
5189
|
+
|
|
5190
|
+
**接口地址:** `/dj/difm/channel/unsubscribe`
|
|
5191
|
+
|
|
5192
|
+
**调用例子:** `/dj/difm/channel/unsubscribe?id=1`
|
|
5193
|
+
|
|
5194
|
+
### DIFM电台 - 播放列表
|
|
5195
|
+
|
|
5196
|
+
说明: 调用此接口, 获取DIFM播放列表
|
|
5197
|
+
|
|
5198
|
+
**必选参数 :**
|
|
5199
|
+
|
|
5200
|
+
`source`: 来源, 0: 最嗨电音 1: 古典电台 2: 爵士电台
|
|
5201
|
+
|
|
5202
|
+
`channelId`: 频道id
|
|
5203
|
+
|
|
5204
|
+
**可选参数 :**
|
|
5205
|
+
|
|
5206
|
+
`limit`: 返回数量, 默认为 5
|
|
5207
|
+
|
|
5208
|
+
**接口地址:** `/dj/difm/playing/tracks/list`
|
|
5209
|
+
|
|
5210
|
+
**调用例子:** `/dj/difm/playing/tracks/list?source=0&channelId=1012`
|
|
5211
|
+
|
|
5053
5212
|
## 离线访问此文档
|
|
5054
5213
|
|
|
5055
5214
|
此文档同时也是 Progressive Web Apps(PWA), 加入了 serviceWorker, 可离线访问
|
package/server.js
CHANGED
|
@@ -127,15 +127,45 @@ async function checkVersion() {
|
|
|
127
127
|
})
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
function parseCorsAllowOrigins(corsAllowOrigin) {
|
|
131
|
+
if (!corsAllowOrigin) {
|
|
132
|
+
return null
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const origins = corsAllowOrigin
|
|
136
|
+
.split(',')
|
|
137
|
+
.map((origin) => origin.trim())
|
|
138
|
+
.filter(Boolean)
|
|
139
|
+
|
|
140
|
+
return origins.length > 0 ? origins : null
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function getCorsAllowOrigin(allowOrigins, requestOrigin) {
|
|
144
|
+
if (!allowOrigins) {
|
|
145
|
+
return requestOrigin || '*'
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (allowOrigins.includes('*')) {
|
|
149
|
+
return '*'
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (requestOrigin && allowOrigins.includes(requestOrigin)) {
|
|
153
|
+
return requestOrigin
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return null
|
|
157
|
+
}
|
|
158
|
+
|
|
130
159
|
/**
|
|
131
160
|
* Construct the server of NCM API.
|
|
132
161
|
*
|
|
133
162
|
* @param {ModuleDefinition[]} [moduleDefs] Customized module definitions [advanced]
|
|
134
163
|
* @returns {Promise<import("express").Express>} The server instance.
|
|
135
164
|
*/
|
|
136
|
-
async function
|
|
165
|
+
async function constructServer(moduleDefs) {
|
|
137
166
|
const app = express()
|
|
138
167
|
const { CORS_ALLOW_ORIGIN } = process.env
|
|
168
|
+
const allowOrigins = parseCorsAllowOrigins(CORS_ALLOW_ORIGIN)
|
|
139
169
|
app.set('trust proxy', true)
|
|
140
170
|
|
|
141
171
|
/**
|
|
@@ -147,10 +177,17 @@ async function consturctServer(moduleDefs) {
|
|
|
147
177
|
*/
|
|
148
178
|
app.use((req, res, next) => {
|
|
149
179
|
if (req.path !== '/' && !req.path.includes('.')) {
|
|
180
|
+
const corsAllowOrigin = getCorsAllowOrigin(
|
|
181
|
+
allowOrigins,
|
|
182
|
+
req.headers.origin,
|
|
183
|
+
)
|
|
184
|
+
const shouldSetVaryHeader = corsAllowOrigin && corsAllowOrigin !== '*'
|
|
150
185
|
res.set({
|
|
151
186
|
'Access-Control-Allow-Credentials': true,
|
|
152
|
-
|
|
153
|
-
|
|
187
|
+
...(corsAllowOrigin
|
|
188
|
+
? { 'Access-Control-Allow-Origin': corsAllowOrigin }
|
|
189
|
+
: {}),
|
|
190
|
+
...(shouldSetVaryHeader ? { Vary: 'Origin' } : {}),
|
|
154
191
|
'Access-Control-Allow-Headers': 'X-Requested-With,Content-Type',
|
|
155
192
|
'Access-Control-Allow-Methods': 'PUT,POST,GET,DELETE,OPTIONS',
|
|
156
193
|
'Content-Type': 'application/json; charset=utf-8',
|
|
@@ -221,7 +258,7 @@ async function consturctServer(moduleDefs) {
|
|
|
221
258
|
|
|
222
259
|
for (const moduleDef of moduleDefinitions) {
|
|
223
260
|
// Register the route.
|
|
224
|
-
app.
|
|
261
|
+
app.all(moduleDef.route, async (req, res) => {
|
|
225
262
|
;[req.query, req.body].forEach((item) => {
|
|
226
263
|
// item may be undefined (some environments / middlewares).
|
|
227
264
|
// Guard access to avoid "Cannot read properties of undefined (reading 'cookie')".
|
|
@@ -308,6 +345,11 @@ async function consturctServer(moduleDefs) {
|
|
|
308
345
|
}
|
|
309
346
|
}
|
|
310
347
|
}
|
|
348
|
+
if (moduleResponse.redirectUrl) {
|
|
349
|
+
res.redirect(moduleResponse.status || 302, moduleResponse.redirectUrl)
|
|
350
|
+
return
|
|
351
|
+
}
|
|
352
|
+
|
|
311
353
|
res.status(moduleResponse.status).send(moduleResponse.body)
|
|
312
354
|
} catch (/** @type {*} */ moduleResponse) {
|
|
313
355
|
logger.error(`${decode(req.originalUrl)}`, {
|
|
@@ -354,7 +396,7 @@ async function serveNcmApi(options) {
|
|
|
354
396
|
)
|
|
355
397
|
}
|
|
356
398
|
})
|
|
357
|
-
const constructServerSubmission =
|
|
399
|
+
const constructServerSubmission = constructServer(options.moduleDefs)
|
|
358
400
|
|
|
359
401
|
const [_, app] = await Promise.all([
|
|
360
402
|
checkVersionSubmission,
|
package/util/request.js
CHANGED
|
@@ -240,6 +240,7 @@ const createRequest = (uri, data, options) => {
|
|
|
240
240
|
headers['User-Agent'] = options.ua || chooseUserAgent('api', 'iphone')
|
|
241
241
|
|
|
242
242
|
if (crypto === 'eapi') {
|
|
243
|
+
// headers['x-aeapi'] = true // 服务器会使用gzip压缩返回值
|
|
243
244
|
data.header = header
|
|
244
245
|
data.e_r = toBoolean(
|
|
245
246
|
options.e_r !== undefined
|
|
@@ -323,6 +324,7 @@ const createRequest = (uri, data, options) => {
|
|
|
323
324
|
if (crypto === 'eapi' && data.e_r) {
|
|
324
325
|
answer.body = encrypt.eapiResDecrypt(
|
|
325
326
|
body.toString('hex').toUpperCase(),
|
|
327
|
+
headers['x-aeapi'],
|
|
326
328
|
)
|
|
327
329
|
} else {
|
|
328
330
|
answer.body =
|