@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/public/login.html CHANGED
@@ -5,21 +5,143 @@
5
5
  <meta charset="UTF-8" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>登录</title>
8
+ <style>
9
+ * {
10
+ margin: 0;
11
+ padding: 0;
12
+ box-sizing: border-box;
13
+ }
14
+
15
+ body {
16
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
17
+ min-height: 100vh;
18
+ background: #f5f5f5;
19
+ display: flex;
20
+ align-items: center;
21
+ justify-content: center;
22
+ padding: 20px;
23
+ }
24
+
25
+ .container {
26
+ background: white;
27
+ border-radius: 12px;
28
+ padding: 40px;
29
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
30
+ max-width: 400px;
31
+ width: 100%;
32
+ text-align: center;
33
+ }
34
+
35
+ h1 {
36
+ font-size: 24px;
37
+ font-weight: 600;
38
+ color: #333;
39
+ margin-bottom: 8px;
40
+ }
41
+
42
+ .subtitle {
43
+ font-size: 14px;
44
+ color: #666;
45
+ margin-bottom: 32px;
46
+ }
47
+
48
+ .form-group {
49
+ margin-bottom: 20px;
50
+ text-align: left;
51
+ }
52
+
53
+ label {
54
+ display: block;
55
+ font-size: 14px;
56
+ font-weight: 500;
57
+ color: #555;
58
+ margin-bottom: 8px;
59
+ }
60
+
61
+ input {
62
+ width: 100%;
63
+ padding: 12px 14px;
64
+ border: 1px solid #ddd;
65
+ border-radius: 6px;
66
+ font-size: 15px;
67
+ outline: none;
68
+ }
69
+
70
+ input:focus {
71
+ border-color: #333;
72
+ }
73
+
74
+ .btn {
75
+ width: 100%;
76
+ padding: 14px;
77
+ background: #333;
78
+ color: white;
79
+ font-size: 15px;
80
+ font-weight: 500;
81
+ border: none;
82
+ border-radius: 6px;
83
+ cursor: pointer;
84
+ transition: background 0.2s ease;
85
+ }
86
+
87
+ .btn:hover {
88
+ background: #555;
89
+ }
90
+
91
+ .btn:disabled {
92
+ background: #999;
93
+ cursor: not-allowed;
94
+ }
95
+
96
+ .result {
97
+ margin-top: 24px;
98
+ padding: 16px;
99
+ background: #f9f9f9;
100
+ border-radius: 6px;
101
+ font-size: 13px;
102
+ color: #666;
103
+ text-align: left;
104
+ white-space: pre-wrap;
105
+ word-break: break-all;
106
+ }
107
+
108
+ .error {
109
+ color: #ef4444;
110
+ background: #fef2f2;
111
+ }
112
+
113
+ .success {
114
+ color: #10b981;
115
+ background: #f0fdf4;
116
+ }
117
+ </style>
8
118
  </head>
9
119
 
10
120
  <body>
121
+ <div class="container">
122
+ <h1>登录</h1>
123
+ <p class="subtitle">使用手机号和密码登录网易云音乐</p>
124
+
125
+ <div class="form-group">
126
+ <label for="phone">手机号</label>
127
+ <input type="tel" id="phone" placeholder="请输入手机号" />
128
+ </div>
129
+
130
+ <div class="form-group">
131
+ <label for="password">密码</label>
132
+ <input type="password" id="password" placeholder="请输入密码" />
133
+ </div>
134
+
135
+ <button id="loginBtn" class="btn" onclick="handleLogin()">登录</button>
136
+
137
+ <div id="result" class="result" style="display: none;"></div>
138
+ </div>
139
+
11
140
  <script src="https://fastly.jsdelivr.net/npm/axios@0.26.1/dist/axios.min.js"></script>
12
141
  <script>
13
- const phone = '' // 这里填手机号
14
- const password = '' // 这里填密码
15
142
  const fileUpdateTime = {}
16
- if (!phone || !password) {
17
- const msg = '请设置你的手机号码和密码'
18
- alert(msg)
19
- throw new Error(msg)
20
- }
21
143
 
22
- async function login() {
144
+ async function login(phone, password) {
23
145
  const res = await axios({
24
146
  url: `/login/cellphone`,
25
147
  method: 'post',
@@ -30,17 +152,67 @@
30
152
  })
31
153
  return res.data.cookie
32
154
  }
33
- async function main() {
34
- const cookieToken = await login()
35
- const res = await axios({
36
- url: `/login/status`,
37
- method: 'post',
38
- data: {
39
- cookie: cookieToken,
40
- },
41
- })
155
+
156
+ async function handleLogin() {
157
+ const phoneInput = document.getElementById('phone')
158
+ const passwordInput = document.getElementById('password')
159
+ const loginBtn = document.getElementById('loginBtn')
160
+ const resultDiv = document.getElementById('result')
161
+
162
+ const phone = phoneInput.value.trim()
163
+ const password = passwordInput.value
164
+
165
+ if (!phone || !password) {
166
+ showResult('请输入手机号和密码', 'error')
167
+ return
168
+ }
169
+
170
+ loginBtn.disabled = true
171
+ loginBtn.textContent = '登录中...'
172
+ showResult('正在登录...', 'info')
173
+
174
+ try {
175
+ const cookieToken = await login(phone, password)
176
+ localStorage.setItem('cookie', cookieToken)
177
+
178
+ const res = await axios({
179
+ url: `/login/status`,
180
+ method: 'post',
181
+ data: {
182
+ cookie: cookieToken,
183
+ },
184
+ })
185
+
186
+ showResult(`登录成功!\n${JSON.stringify(res.data, null, 2)}`, 'success')
187
+ } catch (error) {
188
+ console.error('登录失败:', error)
189
+ const errorMsg = error.response?.data?.message || error.message || '登录失败,请重试'
190
+ showResult(`登录失败:${errorMsg}`, 'error')
191
+ } finally {
192
+ loginBtn.disabled = false
193
+ loginBtn.textContent = '登录'
194
+ }
195
+ }
196
+
197
+ function showResult(message, type = 'info') {
198
+ const resultDiv = document.getElementById('result')
199
+ resultDiv.style.display = 'block'
200
+ resultDiv.textContent = message
201
+ resultDiv.className = 'result ' + type
42
202
  }
43
- main()
203
+
204
+ // 支持回车登录
205
+ document.getElementById('password').addEventListener('keypress', function(e) {
206
+ if (e.key === 'Enter') {
207
+ handleLogin()
208
+ }
209
+ })
210
+
211
+ document.getElementById('phone').addEventListener('keypress', function(e) {
212
+ if (e.key === 'Enter') {
213
+ document.getElementById('password').focus()
214
+ }
215
+ })
44
216
  </script>
45
217
  </body>
46
218
 
@@ -5,56 +5,297 @@
5
5
  <meta charset="UTF-8" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>歌单封面上传</title>
8
+ <style>
9
+ * {
10
+ margin: 0;
11
+ padding: 0;
12
+ box-sizing: border-box;
13
+ }
14
+
15
+ body {
16
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
17
+ min-height: 100vh;
18
+ background: #f5f5f5;
19
+ padding: 20px;
20
+ }
21
+
22
+ .container {
23
+ max-width: 500px;
24
+ margin: 0 auto;
25
+ background: white;
26
+ border-radius: 12px;
27
+ padding: 32px;
28
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
29
+ text-align: center;
30
+ }
31
+
32
+ h1 {
33
+ font-size: 24px;
34
+ font-weight: 600;
35
+ color: #333;
36
+ margin-bottom: 8px;
37
+ }
38
+
39
+ .subtitle {
40
+ font-size: 14px;
41
+ color: #666;
42
+ margin-bottom: 32px;
43
+ }
44
+
45
+ .login-link {
46
+ display: block;
47
+ margin-bottom: 24px;
48
+ color: #666;
49
+ font-size: 14px;
50
+ text-decoration: none;
51
+ }
52
+
53
+ .login-link:hover {
54
+ color: #333;
55
+ text-decoration: underline;
56
+ }
57
+
58
+ .cover-wrapper {
59
+ position: relative;
60
+ width: 180px;
61
+ height: 180px;
62
+ margin: 0 auto 24px;
63
+ }
64
+
65
+ .cover {
66
+ width: 100%;
67
+ height: 100%;
68
+ border-radius: 50%;
69
+ object-fit: cover;
70
+ border: 4px solid #fff;
71
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
72
+ }
73
+
74
+ .form-group {
75
+ margin-bottom: 20px;
76
+ text-align: left;
77
+ }
78
+
79
+ label {
80
+ display: block;
81
+ font-size: 14px;
82
+ font-weight: 500;
83
+ color: #555;
84
+ margin-bottom: 8px;
85
+ }
86
+
87
+ input[type="text"] {
88
+ width: 100%;
89
+ padding: 10px 14px;
90
+ border: 1px solid #ddd;
91
+ border-radius: 6px;
92
+ font-size: 14px;
93
+ outline: none;
94
+ }
95
+
96
+ input[type="text"]:focus {
97
+ border-color: #333;
98
+ }
99
+
100
+ .upload-btn {
101
+ display: inline-block;
102
+ padding: 12px 28px;
103
+ background: #333;
104
+ color: white;
105
+ font-size: 15px;
106
+ font-weight: 500;
107
+ border-radius: 6px;
108
+ cursor: pointer;
109
+ transition: background 0.2s ease;
110
+ border: none;
111
+ }
112
+
113
+ .upload-btn:hover {
114
+ background: #555;
115
+ }
116
+
117
+ .upload-btn input[type="file"] {
118
+ display: none;
119
+ }
120
+
121
+ .loading {
122
+ position: absolute;
123
+ top: 0;
124
+ left: 0;
125
+ width: 100%;
126
+ height: 100%;
127
+ border-radius: 50%;
128
+ background: rgba(255, 255, 255, 0.9);
129
+ display: flex;
130
+ align-items: center;
131
+ justify-content: center;
132
+ opacity: 0;
133
+ transition: opacity 0.2s ease;
134
+ }
135
+
136
+ .loading.active {
137
+ opacity: 1;
138
+ }
139
+
140
+ .spinner {
141
+ width: 48px;
142
+ height: 48px;
143
+ border: 4px solid #e0e0e0;
144
+ border-top-color: #333;
145
+ border-radius: 50%;
146
+ animation: spin 0.8s linear infinite;
147
+ }
148
+
149
+ @keyframes spin {
150
+ to {
151
+ transform: rotate(360deg);
152
+ }
153
+ }
154
+
155
+ .result {
156
+ margin-top: 20px;
157
+ padding: 12px 16px;
158
+ border-radius: 6px;
159
+ font-size: 14px;
160
+ text-align: left;
161
+ }
162
+
163
+ .result.success {
164
+ background: #d1fae5;
165
+ color: #065f46;
166
+ }
167
+
168
+ .result.error {
169
+ background: #fee2e2;
170
+ color: #991b1b;
171
+ }
172
+ </style>
8
173
  </head>
9
174
 
10
175
  <body>
11
- <div>
12
- <a href="/qrlogin-nocookie.html">
13
- 如果没登录,请先登录
14
- </a>
176
+ <div class="container">
177
+ <h1>歌单封面上传</h1>
178
+ <p class="subtitle">上传自定义歌单封面图片</p>
179
+
180
+ <a href="/qrlogin-nocookie.html" class="login-link">还没登录?点击登录</a>
181
+
182
+ <div class="form-group">
183
+ <label for="playlistId">歌单 ID</label>
184
+ <input type="text" id="playlistId" placeholder="请输入歌单ID" />
185
+ </div>
186
+
187
+ <div class="cover-wrapper">
188
+ <img id="playlist_cover" class="cover" src="" alt="歌单封面" />
189
+ <div class="loading" id="loading">
190
+ <div class="spinner"></div>
191
+ </div>
192
+ </div>
193
+
194
+ <label class="upload-btn">
195
+ 选择封面图片
196
+ <input id="file" type="file" name="filename" accept="image/*" />
197
+ </label>
198
+
199
+ <div id="result" class="result" style="display: none;"></div>
15
200
  </div>
16
- <input id="file" type="file" name="filename" />
17
- <img id="playlist_cover" style="height: 200px; width: 200px; border-radius: 50%" />
201
+
18
202
  <script src="https://fastly.jsdelivr.net/npm/axios@0.26.1/dist/axios.min.js"></script>
19
203
  <script>
20
- const playlist_id = ''
21
- if (!playlist_id) {
22
- const msg = '请设置你的歌单id'
23
- alert(msg)
24
- throw new Error(msg)
25
- }
26
-
27
- main()
28
- async function main() {
29
- document.querySelector('input[type="file"]').addEventListener(
30
- 'change',
31
- function (e) {
32
- var file = this.files[0]
33
- upload(file)
34
- },
35
- false,
36
- )
37
- const res = await axios({
38
- url: `/playlist/detail?id=${playlist_id}&timestamp=${Date.now()}`,
39
- })
40
- document.querySelector('#playlist_cover').src = res.data.playlist.coverImgUrl
204
+ const loadingOverlay = document.getElementById('loading')
205
+ const playlistIdInput = document.getElementById('playlistId')
206
+ const resultDiv = document.getElementById('result')
207
+
208
+ function showLoading() {
209
+ loadingOverlay.classList.add('active')
210
+ }
211
+
212
+ function hideLoading() {
213
+ loadingOverlay.classList.remove('active')
214
+ }
215
+
216
+ function showResult(message, type) {
217
+ resultDiv.textContent = message
218
+ resultDiv.className = 'result ' + type
219
+ resultDiv.style.display = 'block'
41
220
  }
42
221
 
43
- async function upload(file) {
222
+ function hideResult() {
223
+ resultDiv.style.display = 'none'
224
+ }
225
+
226
+ async function loadPlaylistCover() {
227
+ const playlistId = playlistIdInput.value.trim()
228
+ if (!playlistId) {
229
+ return
230
+ }
231
+
232
+ showLoading()
233
+ hideResult()
234
+
235
+ try {
236
+ const res = await axios({
237
+ url: `/playlist/detail?id=${playlistId}&timestamp=${Date.now()}`,
238
+ })
239
+ document.querySelector('#playlist_cover').src = res.data.playlist.coverImgUrl
240
+ hideResult()
241
+ } catch (error) {
242
+ console.error('加载封面失败:', error)
243
+ showResult('加载封面失败,请检查歌单ID', 'error')
244
+ } finally {
245
+ hideLoading()
246
+ }
247
+ }
248
+
249
+ // 监听歌单ID输入变化
250
+ playlistIdInput.addEventListener('input', function() {
251
+ loadPlaylistCover()
252
+ })
253
+
254
+ // 监听文件选择
255
+ document
256
+ .querySelector('input[type="file"]')
257
+ .addEventListener('change', async function (e) {
258
+ const file = this.files[0]
259
+ const playlistId = playlistIdInput.value.trim()
260
+
261
+ if (!playlistId) {
262
+ showResult('请先输入歌单ID', 'error')
263
+ return
264
+ }
265
+
266
+ if (!file) {
267
+ return
268
+ }
269
+
270
+ showLoading()
271
+ hideResult()
272
+
273
+ try {
274
+ await upload(file, playlistId)
275
+ } catch (error) {
276
+ console.error('上传失败:', error)
277
+ showResult('上传失败,请重试', 'error')
278
+ } finally {
279
+ hideLoading()
280
+ }
281
+ })
282
+
283
+ async function upload(file, playlistId) {
44
284
  var formData = new FormData()
45
285
  formData.append('imgFile', file)
46
286
  const imgSize = await getImgSize(file)
47
287
  const res = await axios({
48
288
  method: 'post',
49
- url: `/playlist/cover/update?id=${playlist_id}&cookie=${localStorage.getItem('cookie')}&imgSize=${imgSize.width
50
- }&imgX=0&imgY=0&timestamp=${Date.now()}`,
289
+ url: `/playlist/cover/update?id=${playlistId}&cookie=${localStorage.getItem('cookie')}&imgSize=${imgSize.width}&imgX=0&imgY=0&timestamp=${Date.now()}`,
51
290
  headers: {
52
291
  'Content-Type': 'multipart/form-data',
53
292
  },
54
293
  data: formData,
55
294
  })
56
295
  document.querySelector('#playlist_cover').src = res.data.data.url
296
+ showResult('封面上传成功!', 'success')
57
297
  }
298
+
58
299
  function getImgSize(file) {
59
300
  return new Promise((resolve, reject) => {
60
301
  let reader = new FileReader()
@@ -68,10 +309,16 @@
68
309
  height: this.height,
69
310
  })
70
311
  }
312
+ image.onerror = function() {
313
+ reject(new Error('图片加载失败'))
314
+ }
315
+ }
316
+ reader.onerror = function() {
317
+ reject(new Error('文件读取失败'))
71
318
  }
72
319
  })
73
320
  }
74
321
  </script>
75
322
  </body>
76
323
 
77
- </html>
324
+ </html>