@neteasecloudmusicapienhanced/api 4.29.21 → 4.30.1
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 +9 -0
- package/interface.d.ts +2 -0
- package/module/musician_vip_tasks.js +11 -0
- package/module/user_playlist_collect.js +14 -0
- package/module/user_playlist_create.js +14 -0
- package/package.json +12 -8
- package/public/api.html +115 -49
- package/public/audio_match_demo/afp.js +2 -3
- package/public/audio_match_demo/index.html +213 -44
- package/public/avatar_update.html +296 -43
- package/public/cloud.html +148 -40
- package/public/docs/home.md +45 -0
- package/public/docs/index.html +3 -3
- package/public/eapi_decrypt.html +174 -36
- package/public/index.html +18 -18
- package/public/listen_together_host.html +294 -65
- package/public/login.html +190 -18
- package/public/playlist_cover_update.html +278 -31
- package/public/playlist_import.html +381 -226
- package/public/qrlogin-nocookie.html +170 -43
- package/public/qrlogin.html +170 -42
- package/public/unblock_test.html +95 -35
- package/public/voice_upload.html +256 -57
- package/server.js +1 -1
- package/util/client-sign.js +1 -1
- package/util/request.js +6 -6
|
@@ -1,27 +1,142 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
2
|
|
|
3
3
|
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>听歌识曲 Demo</title>
|
|
4
7
|
<style>
|
|
5
8
|
* {
|
|
6
|
-
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
box-sizing: border-box;
|
|
7
12
|
}
|
|
8
13
|
|
|
9
|
-
|
|
10
|
-
font-family:
|
|
14
|
+
body {
|
|
15
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
16
|
+
min-height: 100vh;
|
|
17
|
+
background: #f5f5f5;
|
|
18
|
+
padding: 20px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.container {
|
|
22
|
+
max-width: 800px;
|
|
23
|
+
margin: 0 auto;
|
|
24
|
+
background: white;
|
|
25
|
+
border-radius: 12px;
|
|
26
|
+
padding: 32px;
|
|
27
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
h1 {
|
|
31
|
+
font-size: 24px;
|
|
32
|
+
font-weight: 600;
|
|
33
|
+
color: #333;
|
|
34
|
+
margin-bottom: 8px;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.subtitle {
|
|
38
|
+
font-size: 13px;
|
|
39
|
+
color: #666;
|
|
40
|
+
margin-bottom: 24px;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
hr {
|
|
44
|
+
border: none;
|
|
45
|
+
border-top: 1px solid #eee;
|
|
46
|
+
margin: 20px 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
p {
|
|
50
|
+
font-size: 14px;
|
|
51
|
+
color: #555;
|
|
52
|
+
line-height: 1.6;
|
|
53
|
+
margin-bottom: 12px;
|
|
11
54
|
}
|
|
12
55
|
|
|
13
56
|
a {
|
|
14
|
-
|
|
57
|
+
color: #333;
|
|
58
|
+
text-decoration: none;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
a:hover {
|
|
62
|
+
text-decoration: underline;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.section {
|
|
66
|
+
margin-bottom: 24px;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.section h3 {
|
|
70
|
+
font-size: 16px;
|
|
71
|
+
font-weight: 600;
|
|
72
|
+
color: #333;
|
|
73
|
+
margin-bottom: 12px;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.control-group {
|
|
77
|
+
display: flex;
|
|
78
|
+
gap: 12px;
|
|
79
|
+
flex-wrap: wrap;
|
|
80
|
+
align-items: center;
|
|
81
|
+
margin-bottom: 16px;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
button {
|
|
85
|
+
padding: 10px 20px;
|
|
86
|
+
background: #333;
|
|
87
|
+
color: white;
|
|
88
|
+
font-size: 14px;
|
|
89
|
+
font-weight: 500;
|
|
90
|
+
border: none;
|
|
91
|
+
border-radius: 6px;
|
|
92
|
+
cursor: pointer;
|
|
93
|
+
transition: background 0.2s ease;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
button:hover {
|
|
97
|
+
background: #555;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
button:disabled {
|
|
101
|
+
background: #999;
|
|
102
|
+
cursor: not-allowed;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
input[type="file"] {
|
|
106
|
+
padding: 10px 14px;
|
|
107
|
+
border: 1px solid #ddd;
|
|
108
|
+
border-radius: 6px;
|
|
109
|
+
font-size: 14px;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.checkbox-group {
|
|
113
|
+
display: flex;
|
|
114
|
+
align-items: center;
|
|
115
|
+
gap: 8px;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.checkbox-group input[type="checkbox"] {
|
|
119
|
+
cursor: pointer;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.checkbox-group label {
|
|
123
|
+
margin: 0;
|
|
124
|
+
cursor: pointer;
|
|
125
|
+
font-size: 14px;
|
|
126
|
+
color: #555;
|
|
15
127
|
}
|
|
16
128
|
|
|
17
129
|
audio {
|
|
18
130
|
width: 100%;
|
|
131
|
+
margin-bottom: 16px;
|
|
19
132
|
}
|
|
20
133
|
|
|
21
134
|
canvas {
|
|
22
135
|
width: 100%;
|
|
23
136
|
height: 0;
|
|
24
137
|
transition: all linear 0.1s;
|
|
138
|
+
background: #f9f9f9;
|
|
139
|
+
border-radius: 6px;
|
|
25
140
|
}
|
|
26
141
|
|
|
27
142
|
.canvas-active {
|
|
@@ -29,39 +144,80 @@
|
|
|
29
144
|
}
|
|
30
145
|
|
|
31
146
|
pre {
|
|
32
|
-
|
|
147
|
+
font-family: 'Courier New', monospace;
|
|
148
|
+
font-size: 13px;
|
|
149
|
+
color: #666;
|
|
150
|
+
white-space: pre-wrap;
|
|
151
|
+
word-wrap: break-word;
|
|
152
|
+
max-height: 400px;
|
|
153
|
+
overflow-y: auto;
|
|
154
|
+
padding: 16px;
|
|
155
|
+
background: #f9f9f9;
|
|
156
|
+
border-radius: 6px;
|
|
157
|
+
border: 1px solid #eee;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.warning {
|
|
161
|
+
padding: 12px 16px;
|
|
162
|
+
background: #fef3c7;
|
|
163
|
+
border-radius: 6px;
|
|
164
|
+
font-size: 14px;
|
|
165
|
+
color: #92400e;
|
|
166
|
+
margin-bottom: 16px;
|
|
33
167
|
}
|
|
34
168
|
</style>
|
|
35
169
|
</head>
|
|
36
170
|
|
|
37
171
|
<body>
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
172
|
+
<div class="container">
|
|
173
|
+
<h1>听歌识曲 Demo</h1>
|
|
174
|
+
<p class="subtitle">Credit: <a href="https://github.com/mos9527/ncm-afp" target="_blank">https://github.com/mos9527/ncm-afp</a></p>
|
|
175
|
+
|
|
176
|
+
<hr>
|
|
177
|
+
|
|
178
|
+
<div class="warning">
|
|
179
|
+
<strong>免责声明:</strong>本站点使用网易云音乐官方音频识别API(逆向自 <a href="https://fn.music.163.com/g/chrome-extension-home-page-beta/" target="_blank">Chrome 扩展页面</a>),不鼓励版权侵犯或知识产权盗窃。
|
|
180
|
+
</div>
|
|
181
|
+
|
|
182
|
+
<div class="section">
|
|
183
|
+
<h3>使用说明</h3>
|
|
184
|
+
<p>在使用本站点之前,您可能需要先访问以下链接:</p>
|
|
185
|
+
<p><a href="https://cors-anywhere.herokuapp.com/corsdemo" target="_blank">https://cors-anywhere.herokuapp.com/corsdemo</a></p>
|
|
186
|
+
<p>由于网易云音乐API没有CORS头,这是解决此限制的必要步骤。</p>
|
|
187
|
+
</div>
|
|
188
|
+
|
|
189
|
+
<div class="section">
|
|
190
|
+
<h3>使用方法</h3>
|
|
191
|
+
<ul style="padding-left: 20px; font-size: 14px; color: #555;">
|
|
192
|
+
<li>通过"选择文件"选择您的音频文件</li>
|
|
193
|
+
<li>点击"识别"按钮并等待结果</li>
|
|
194
|
+
</ul>
|
|
195
|
+
</div>
|
|
196
|
+
|
|
197
|
+
<hr>
|
|
198
|
+
|
|
199
|
+
<audio id="audio" controls autoplay></audio>
|
|
200
|
+
<canvas id="canvas"></canvas>
|
|
201
|
+
|
|
202
|
+
<div class="control-group">
|
|
203
|
+
<button id="invoke">识别</button>
|
|
204
|
+
<input type="file" name="picker" accept="*" id="file">
|
|
205
|
+
</div>
|
|
206
|
+
|
|
207
|
+
<div class="control-group">
|
|
208
|
+
<div class="checkbox-group">
|
|
209
|
+
<input type="checkbox" name="use-mic" id="usemic">
|
|
210
|
+
<label for="usemic">混合麦克风输入</label>
|
|
211
|
+
</div>
|
|
212
|
+
</div>
|
|
213
|
+
|
|
214
|
+
<hr>
|
|
215
|
+
|
|
216
|
+
<h3 style="font-size: 16px; font-weight: 600; color: #333; margin-bottom: 12px;">日志</h3>
|
|
217
|
+
<pre id="logs"></pre>
|
|
218
|
+
</div>
|
|
64
219
|
</body>
|
|
220
|
+
|
|
65
221
|
<script src="./afp.wasm.js"></script>
|
|
66
222
|
<script src="./afp.js"></script>
|
|
67
223
|
<script type="module">
|
|
@@ -76,13 +232,17 @@
|
|
|
76
232
|
let canvas = document.getElementById('canvas')
|
|
77
233
|
let canvasCtx = canvas.getContext('2d')
|
|
78
234
|
let logs = document.getElementById('logs')
|
|
79
|
-
logs.write = line =>
|
|
235
|
+
logs.write = line => {
|
|
236
|
+
// Append log lines as text to avoid interpreting content as HTML
|
|
237
|
+
logs.appendChild(document.createTextNode(line));
|
|
238
|
+
logs.appendChild(document.createElement('br'));
|
|
239
|
+
}
|
|
80
240
|
|
|
81
241
|
function RecorderCallback(channelL) {
|
|
82
242
|
let sampleBuffer = new Float32Array(channelL.subarray(0, duration * 8000))
|
|
83
243
|
GenerateFP(sampleBuffer).then(FP => {
|
|
84
|
-
logs.write(`[index]
|
|
85
|
-
logs.write('[index]
|
|
244
|
+
logs.write(`[index] 生成指纹 ${FP}`)
|
|
245
|
+
logs.write('[index] 正在查询,请稍候...')
|
|
86
246
|
fetch(
|
|
87
247
|
'/audio/match?' +
|
|
88
248
|
new URLSearchParams({
|
|
@@ -91,9 +251,9 @@
|
|
|
91
251
|
method: 'POST'
|
|
92
252
|
}).then(resp => resp.json()).then(resp => {
|
|
93
253
|
if (!resp.data.result) {
|
|
94
|
-
return logs.write('[index]
|
|
254
|
+
return logs.write('[index] 查询失败,无结果')
|
|
95
255
|
}
|
|
96
|
-
logs.write(`[index]
|
|
256
|
+
logs.write(`[index] 查询完成。结果数量=${resp.data.result.length}`)
|
|
97
257
|
for (var song of resp.data.result) {
|
|
98
258
|
logs.write(
|
|
99
259
|
`[result] <a target="_blank" href="https://music.163.com/song?id=${song.song.id}">${song.song.name} - ${song.song.album.name} (${song.startTime / 1000}s)</a>`
|
|
@@ -104,20 +264,19 @@
|
|
|
104
264
|
}
|
|
105
265
|
|
|
106
266
|
function InitAudioCtx() {
|
|
107
|
-
// AFP.wasm can't do it with anything other than 8KHz
|
|
108
267
|
audioCtx = new AudioContext({ 'sampleRate': 8000 })
|
|
109
268
|
if (audioCtx.state == 'suspended')
|
|
110
269
|
return false
|
|
111
270
|
let audioNode = audioCtx.createMediaElementSource(audio)
|
|
112
271
|
audioCtx.audioWorklet.addModule('rec.js').then(() => {
|
|
113
272
|
recorderNode = new AudioWorkletNode(audioCtx, 'timed-recorder')
|
|
114
|
-
audioNode.connect(recorderNode)
|
|
273
|
+
audioNode.connect(recorderNode)
|
|
115
274
|
audioNode.connect(audioCtx.destination)
|
|
116
275
|
recorderNode.port.onmessage = event => {
|
|
117
276
|
switch (event.data.message) {
|
|
118
277
|
case 'finished':
|
|
119
278
|
RecorderCallback(event.data.recording)
|
|
120
|
-
clip.innerHTML = '
|
|
279
|
+
clip.innerHTML = '识别'
|
|
121
280
|
clip.disabled = false
|
|
122
281
|
canvas.classList.remove('canvas-active')
|
|
123
282
|
break
|
|
@@ -130,7 +289,6 @@
|
|
|
130
289
|
logs.write(event.data.message)
|
|
131
290
|
}
|
|
132
291
|
}
|
|
133
|
-
// Attempt to get user's microphone and connect it to the AudioContext.
|
|
134
292
|
navigator.mediaDevices.getUserMedia({
|
|
135
293
|
audio: {
|
|
136
294
|
echoCancellation: false,
|
|
@@ -142,7 +300,7 @@
|
|
|
142
300
|
micSourceNode = audioCtx.createMediaStreamSource(micStream);
|
|
143
301
|
micSourceNode.connect(recorderNode)
|
|
144
302
|
usemic.checked = true
|
|
145
|
-
logs.write('[rec.js]
|
|
303
|
+
logs.write('[rec.js] 麦克风已连接')
|
|
146
304
|
});
|
|
147
305
|
});
|
|
148
306
|
return true
|
|
@@ -161,10 +319,20 @@
|
|
|
161
319
|
else
|
|
162
320
|
micSourceNode.connect(recorderNode)
|
|
163
321
|
})
|
|
322
|
+
function escapeHtml(str) {
|
|
323
|
+
return String(str)
|
|
324
|
+
.replace(/&/g, '&')
|
|
325
|
+
.replace(/</g, '<')
|
|
326
|
+
.replace(/>/g, '>')
|
|
327
|
+
.replace(/"/g, '"')
|
|
328
|
+
.replace(/'/g, ''')
|
|
329
|
+
.replace(/\//g, '/');
|
|
330
|
+
}
|
|
164
331
|
file.addEventListener('change', event => {
|
|
165
332
|
file.files[0].arrayBuffer().then(
|
|
166
333
|
async buffer => {
|
|
167
|
-
|
|
334
|
+
const safeName = escapeHtml(file.files[0].name)
|
|
335
|
+
logs.write(`[index] 文件 ${safeName} 已加载`)
|
|
168
336
|
audio.src = window.URL.createObjectURL(new Blob([buffer]))
|
|
169
337
|
clip.disabled = false
|
|
170
338
|
})
|
|
@@ -188,12 +356,13 @@
|
|
|
188
356
|
UpdateCanvas()
|
|
189
357
|
let requestCtx = setInterval(() => {
|
|
190
358
|
try {
|
|
191
|
-
if (InitAudioCtx()) {
|
|
359
|
+
if (InitAudioCtx()) {
|
|
192
360
|
clearInterval(requestCtx)
|
|
193
|
-
logs.write('[rec.js]
|
|
361
|
+
logs.write('[rec.js] 音频上下文已启动')
|
|
194
362
|
}
|
|
195
363
|
} catch {
|
|
196
364
|
// Fail silently
|
|
197
365
|
}
|
|
198
366
|
}, 100)
|
|
199
367
|
</script>
|
|
368
|
+
</html>
|