xukunCai commited on
Commit
1572821
·
verified ·
1 Parent(s): 13c4bf9

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +1645 -19
index.html CHANGED
@@ -1,19 +1,1645 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>云音乐 - 在线音乐播放器</title>
7
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
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', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
17
+ background: #f8f9fa;
18
+ color: #333;
19
+ overflow-x: hidden;
20
+ line-height: 1.6;
21
+ }
22
+
23
+ /* 顶部导航 */
24
+ .navbar {
25
+ background: #ffffff;
26
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
27
+ padding: 15px 0;
28
+ position: sticky;
29
+ top: 0;
30
+ z-index: 100;
31
+ }
32
+
33
+ .nav-container {
34
+ max-width: 1400px;
35
+ margin: 0 auto;
36
+ padding: 0 30px;
37
+ display: flex;
38
+ align-items: center;
39
+ justify-content: space-between;
40
+ }
41
+
42
+ .logo {
43
+ display: flex;
44
+ align-items: center;
45
+ gap: 12px;
46
+ font-size: 24px;
47
+ font-weight: bold;
48
+ color: #333;
49
+ }
50
+
51
+ .logo i {
52
+ color: #4a90e2;
53
+ font-size: 28px;
54
+ }
55
+
56
+ .search-container {
57
+ flex: 1;
58
+ max-width: 600px;
59
+ margin: 0 40px;
60
+ position: relative;
61
+ }
62
+
63
+ .search-wrapper {
64
+ display: flex;
65
+ background: #f5f7fa;
66
+ border-radius: 25px;
67
+ overflow: hidden;
68
+ border: 1px solid #e1e5eb;
69
+ transition: all 0.3s ease;
70
+ }
71
+
72
+ .search-wrapper:focus-within {
73
+ background: #ffffff;
74
+ border-color: #4a90e2;
75
+ box-shadow: 0 0 15px rgba(74, 144, 226, 0.2);
76
+ }
77
+
78
+ .search-input {
79
+ flex: 1;
80
+ padding: 12px 20px;
81
+ background: transparent;
82
+ border: none;
83
+ color: #333;
84
+ font-size: 16px;
85
+ outline: none;
86
+ }
87
+
88
+ .search-input::placeholder {
89
+ color: #adb5bd;
90
+ }
91
+
92
+ .source-select {
93
+ background: #f0f3f7;
94
+ border: none;
95
+ color: #495057;
96
+ padding: 12px 15px;
97
+ outline: none;
98
+ cursor: pointer;
99
+ border-left: 1px solid #e1e5eb;
100
+ }
101
+
102
+ .source-select option {
103
+ background: #ffffff;
104
+ color: #333;
105
+ padding: 8px;
106
+ }
107
+
108
+ .search-btn {
109
+ background: #4a90e2;
110
+ border: none;
111
+ color: #fff;
112
+ padding: 12px 20px;
113
+ cursor: pointer;
114
+ transition: all 0.3s ease;
115
+ }
116
+
117
+ .search-btn:hover {
118
+ background: #3a7bc8;
119
+ }
120
+
121
+ /* 主要内容区域 */
122
+ .main-container {
123
+ max-width: 1200px;
124
+ margin: 0 auto;
125
+ padding: 20px;
126
+ min-height: calc(100vh - 170px);
127
+ }
128
+
129
+ /* 内容面板通用样式 */
130
+ .content-panel {
131
+ background: #ffffff;
132
+ border-radius: 16px;
133
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
134
+ padding: 25px;
135
+ margin-bottom: 20px;
136
+ height: 100%;
137
+ }
138
+
139
+ .panel-title {
140
+ font-size: 20px;
141
+ font-weight: 600;
142
+ margin-bottom: 20px;
143
+ color: #333;
144
+ display: flex;
145
+ align-items: center;
146
+ gap: 10px;
147
+ }
148
+
149
+ .panel-title i {
150
+ color: #4a90e2;
151
+ }
152
+
153
+ /* 搜索结果区域 - 卡片式布局 */
154
+ .search-results {
155
+ max-height: 500px;
156
+ overflow-y: auto;
157
+ scrollbar-width: thin;
158
+ scrollbar-color: #ced4da transparent;
159
+ display: grid;
160
+ grid-template-columns: repeat(auto-fill, minmax(100%, 1fr));
161
+ gap: 12px;
162
+ padding-right: 10px;
163
+ }
164
+
165
+ .search-results::-webkit-scrollbar {
166
+ width: 6px;
167
+ }
168
+
169
+ .search-results::-webkit-scrollbar-track {
170
+ background: transparent;
171
+ }
172
+
173
+ .search-results::-webkit-scrollbar-thumb {
174
+ background: #ced4da;
175
+ border-radius: 3px;
176
+ }
177
+
178
+ .song-card {
179
+ display: flex;
180
+ align-items: flex-start;
181
+ padding: 12px 15px;
182
+ border-radius: 12px;
183
+ cursor: pointer;
184
+ transition: all 0.3s ease;
185
+ background: #f8f9fa;
186
+ border: 1px solid #e9ecef;
187
+ }
188
+
189
+ .song-card:hover {
190
+ background: #ffffff;
191
+ transform: translateY(-2px);
192
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
193
+ }
194
+
195
+ .song-card.active {
196
+ background: linear-gradient(135deg, rgba(74, 144, 226, 0.1), rgba(74, 144, 226, 0.05));
197
+ border: 1px solid rgba(74, 144, 226, 0.3);
198
+ }
199
+
200
+ .song-index {
201
+ width: 32px;
202
+ height: 32px;
203
+ border-radius: 50%;
204
+ background: #e9ecef;
205
+ display: flex;
206
+ align-items: center;
207
+ justify-content: center;
208
+ margin-right: 12px;
209
+ font-size: 14px;
210
+ font-weight: 600;
211
+ color: #495057;
212
+ flex-shrink: 0;
213
+ }
214
+
215
+ .song-card.active .song-index {
216
+ background: linear-gradient(135deg, #4a90e2, #3a7bc8);
217
+ color: #fff;
218
+ }
219
+
220
+ .song-info {
221
+ flex: 1;
222
+ min-width: 0;
223
+ padding: 2px 0;
224
+ }
225
+
226
+ .song-name {
227
+ font-weight: 600;
228
+ margin-bottom: 3px;
229
+ font-size: 15px;
230
+ color: #333;
231
+ /* 允许标题换行 */
232
+ white-space: normal;
233
+ line-height: 1.4;
234
+ }
235
+
236
+ .song-artist {
237
+ color: #6c757d;
238
+ font-size: 13px;
239
+ /* 允许艺术家信息换行 */
240
+ white-space: normal;
241
+ line-height: 1.4;
242
+ }
243
+
244
+ .song-duration {
245
+ color: #adb5bd;
246
+ font-size: 13px;
247
+ margin-left: 10px;
248
+ white-space: nowrap;
249
+ align-self: center;
250
+ }
251
+
252
+ /* 歌曲操作按钮 */
253
+ .song-actions {
254
+ display: flex;
255
+ gap: 8px;
256
+ margin-left: 10px;
257
+ align-self: center;
258
+ }
259
+
260
+ .action-btn {
261
+ width: 32px;
262
+ height: 32px;
263
+ border-radius: 50%;
264
+ background: #e9ecef;
265
+ border: none;
266
+ color: #6c757d;
267
+ cursor: pointer;
268
+ transition: all 0.3s ease;
269
+ display: flex;
270
+ align-items: center;
271
+ justify-content: center;
272
+ font-size: 13px;
273
+ }
274
+
275
+ .action-btn:hover {
276
+ background: #dee2e6;
277
+ color: #4a90e2;
278
+ transform: scale(1.1);
279
+ }
280
+
281
+ /* 播放器区域 */
282
+ .player-panel {
283
+ text-align: center;
284
+ padding: 30px 25px;
285
+ }
286
+
287
+ .current-song {
288
+ margin-bottom: 30px;
289
+ }
290
+
291
+ .current-cover-container {
292
+ position: relative;
293
+ display: inline-block;
294
+ margin-bottom: 25px;
295
+ }
296
+
297
+ .current-cover {
298
+ width: 220px;
299
+ height: 220px;
300
+ border-radius: 50%;
301
+ object-fit: cover;
302
+ box-shadow: 0 15px 35px rgba(74, 144, 226, 0.2);
303
+ transition: all 0.3s ease;
304
+ border: 6px solid #f8f9fa;
305
+ }
306
+
307
+ .current-cover.playing {
308
+ animation: rotate 20s linear infinite;
309
+ }
310
+
311
+ @keyframes rotate {
312
+ from { transform: rotate(0deg); }
313
+ to { transform: rotate(360deg); }
314
+ }
315
+
316
+ .current-info h3 {
317
+ font-size: 22px;
318
+ font-weight: 600;
319
+ margin-bottom: 8px;
320
+ color: #333;
321
+ }
322
+
323
+ .current-info p {
324
+ color: #6c757d;
325
+ font-size: 16px;
326
+ }
327
+
328
+ /* 播放控制 */
329
+ .player-controls {
330
+ display: flex;
331
+ justify-content: center;
332
+ align-items: center;
333
+ gap: 25px;
334
+ margin-bottom: 30px;
335
+ }
336
+
337
+ .control-btn {
338
+ background: #f1f3f5;
339
+ border: none;
340
+ border-radius: 50%;
341
+ color: #495057;
342
+ cursor: pointer;
343
+ transition: all 0.3s ease;
344
+ display: flex;
345
+ align-items: center;
346
+ justify-content: center;
347
+ }
348
+
349
+ .control-btn:hover {
350
+ background: #e9ecef;
351
+ transform: scale(1.1);
352
+ }
353
+
354
+ .control-btn.small {
355
+ width: 50px;
356
+ height: 50px;
357
+ font-size: 18px;
358
+ }
359
+
360
+ .play-btn {
361
+ width: 70px;
362
+ height: 70px;
363
+ font-size: 28px;
364
+ background: linear-gradient(135deg, #4a90e2, #3a7bc8);
365
+ color: white;
366
+ box-shadow: 0 8px 25px rgba(74, 144, 226, 0.3);
367
+ }
368
+
369
+ .play-btn:hover {
370
+ background: linear-gradient(135deg, #3a7bc8, #2d6bc1);
371
+ box-shadow: 0 12px 35px rgba(74, 144, 226, 0.4);
372
+ }
373
+
374
+ /* 进度条 */
375
+ .progress-container {
376
+ margin-bottom: 30px;
377
+ padding: 0 20px;
378
+ }
379
+
380
+ .progress-bar {
381
+ width: 100%;
382
+ height: 6px;
383
+ background: #e9ecef;
384
+ border-radius: 3px;
385
+ cursor: pointer;
386
+ margin-bottom: 10px;
387
+ position: relative;
388
+ }
389
+
390
+ .progress-fill {
391
+ height: 100%;
392
+ background: linear-gradient(90deg, #4a90e2, #6aa8e6);
393
+ border-radius: 3px;
394
+ width: 0%;
395
+ transition: width 0.1s ease;
396
+ position: relative;
397
+ }
398
+
399
+ .progress-fill::after {
400
+ content: '';
401
+ position: absolute;
402
+ right: -2px;
403
+ top: 50%;
404
+ transform: translateY(-50%);
405
+ width: 14px;
406
+ height: 14px;
407
+ background: #4a90e2;
408
+ border-radius: 50%;
409
+ box-shadow: 0 2px 8px rgba(74, 144, 226, 0.5);
410
+ }
411
+
412
+ .time-info {
413
+ display: flex;
414
+ justify-content: space-between;
415
+ font-size: 14px;
416
+ color: #6c757d;
417
+ }
418
+
419
+ /* 音量控制 */
420
+ .volume-container {
421
+ display: flex;
422
+ align-items: center;
423
+ gap: 12px;
424
+ margin-bottom: 30px;
425
+ justify-content: center;
426
+ }
427
+
428
+ .volume-icon {
429
+ color: #6c757d;
430
+ font-size: 18px;
431
+ }
432
+
433
+ .volume-slider {
434
+ width: 150px;
435
+ height: 4px;
436
+ background: #e9ecef;
437
+ border-radius: 2px;
438
+ outline: none;
439
+ cursor: pointer;
440
+ -webkit-appearance: none;
441
+ }
442
+
443
+ .volume-slider::-webkit-slider-thumb {
444
+ -webkit-appearance: none;
445
+ width: 14px;
446
+ height: 14px;
447
+ background: #4a90e2;
448
+ border-radius: 50%;
449
+ cursor: pointer;
450
+ }
451
+
452
+ /* 音质选择 */
453
+ .quality-container {
454
+ display: flex;
455
+ align-items: center;
456
+ justify-content: space-between;
457
+ margin-bottom: 25px;
458
+ padding: 15px 20px;
459
+ background: #f8f9fa;
460
+ border-radius: 12px;
461
+ border: 1px solid #e9ecef;
462
+ }
463
+
464
+ .quality-label {
465
+ display: flex;
466
+ align-items: center;
467
+ gap: 8px;
468
+ color: #495057;
469
+ font-size: 14px;
470
+ }
471
+
472
+ .quality-select {
473
+ background: #ffffff;
474
+ border: 1px solid #ced4da;
475
+ border-radius: 8px;
476
+ color: #333;
477
+ padding: 8px 12px;
478
+ outline: none;
479
+ cursor: pointer;
480
+ font-size: 14px;
481
+ }
482
+
483
+ .quality-select option {
484
+ background: #ffffff;
485
+ color: #333;
486
+ padding: 8px;
487
+ }
488
+
489
+ /* 下载区域 */
490
+ .download-container {
491
+ display: grid;
492
+ grid-template-columns: 1fr 1fr;
493
+ gap: 15px;
494
+ margin-bottom: 20px;
495
+ }
496
+
497
+ .download-btn {
498
+ display: flex;
499
+ align-items: center;
500
+ justify-content: center;
501
+ gap: 8px;
502
+ padding: 12px 15px;
503
+ background: #f8f9fa;
504
+ border: 1px solid #e9ecef;
505
+ border-radius: 12px;
506
+ color: #495057;
507
+ cursor: pointer;
508
+ transition: all 0.3s ease;
509
+ font-size: 14px;
510
+ }
511
+
512
+ .download-btn:hover:not(:disabled) {
513
+ background: #eef2f7;
514
+ border-color: #4a90e2;
515
+ color: #4a90e2;
516
+ }
517
+
518
+ .download-btn:disabled {
519
+ opacity: 0.5;
520
+ cursor: not-allowed;
521
+ }
522
+
523
+ /* 歌词区域 */
524
+ .lyrics-container {
525
+ max-height: 500px;
526
+ overflow-y: auto;
527
+ scrollbar-width: thin;
528
+ scrollbar-color: #ced4da transparent;
529
+ padding-right: 10px;
530
+ position: relative;
531
+ }
532
+
533
+ .lyrics-container::-webkit-scrollbar {
534
+ width: 6px;
535
+ }
536
+
537
+ .lyrics-container::-webkit-scrollbar-track {
538
+ background: transparent;
539
+ }
540
+
541
+ .lyrics-container::-webkit-scrollbar-thumb {
542
+ background: #ced4da;
543
+ border-radius: 3px;
544
+ }
545
+
546
+ .lyric-line {
547
+ padding: 8px 15px;
548
+ transition: all 0.3s ease;
549
+ cursor: pointer;
550
+ border-radius: 8px;
551
+ margin-bottom: 6px;
552
+ color: #6c757d;
553
+ line-height: 1.6;
554
+ text-align: center;
555
+ font-size: 15px;
556
+ }
557
+
558
+ .lyric-line:hover {
559
+ background: #f8f9fa;
560
+ color: #4a90e2;
561
+ }
562
+
563
+ .lyric-line.active {
564
+ color: #4a90e2;
565
+ font-weight: 600;
566
+ background: rgba(74, 144, 226, 0.08);
567
+ transform: scale(1.02);
568
+ }
569
+
570
+ /* 加载和错误状态 */
571
+ .loading, .error, .empty-state {
572
+ text-align: center;
573
+ padding: 60px 20px;
574
+ color: #6c757d;
575
+ }
576
+
577
+ .loading i, .error i, .empty-state i {
578
+ font-size: 48px;
579
+ margin-bottom: 15px;
580
+ display: block;
581
+ color: #adb5bd;
582
+ }
583
+
584
+ .loading i {
585
+ animation: spin 1s linear infinite;
586
+ color: #4a90e2;
587
+ }
588
+
589
+ @keyframes spin {
590
+ from { transform: rotate(0deg); }
591
+ to { transform: rotate(360deg); }
592
+ }
593
+
594
+ .error i {
595
+ color: #e74c3c;
596
+ }
597
+
598
+ /* 移动端底部导航 */
599
+ .mobile-nav {
600
+ display: none;
601
+ position: fixed;
602
+ bottom: 0;
603
+ left: 0;
604
+ width: 100%;
605
+ background: #ffffff;
606
+ box-shadow: 0 -2px 15px rgba(0, 0, 0, 0.05);
607
+ z-index: 99;
608
+ }
609
+
610
+ .mobile-nav-items {
611
+ display: flex;
612
+ justify-content: space-around;
613
+ }
614
+
615
+ .mobile-nav-item {
616
+ flex: 1;
617
+ text-align: center;
618
+ padding: 15px 0;
619
+ color: #6c757d;
620
+ text-decoration: none;
621
+ font-size: 12px;
622
+ transition: all 0.3s ease;
623
+ }
624
+
625
+ .mobile-nav-item.active {
626
+ color: #4a90e2;
627
+ }
628
+
629
+ .mobile-nav-item i {
630
+ font-size: 20px;
631
+ margin-bottom: 5px;
632
+ display: block;
633
+ }
634
+
635
+ /* 内容视图容器 */
636
+ .view-container {
637
+ display: block;
638
+ }
639
+
640
+ /* 响应式设计 */
641
+ @media (min-width: 1200px) {
642
+ .desktop-layout {
643
+ display: grid;
644
+ grid-template-columns: 350px 450px 350px;
645
+ gap: 25px;
646
+ }
647
+
648
+ .mobile-nav {
649
+ display: none !important;
650
+ }
651
+ }
652
+
653
+ @media (max-width: 1199px) {
654
+ .mobile-nav {
655
+ display: block;
656
+ }
657
+
658
+ .main-container {
659
+ padding-bottom: 80px;
660
+ }
661
+
662
+ .desktop-layout {
663
+ display: block;
664
+ }
665
+
666
+ .view-container {
667
+ display: none;
668
+ }
669
+
670
+ .view-container.active {
671
+ display: block;
672
+ }
673
+
674
+ /* 移动端提高信息密度 */
675
+ .song-name {
676
+ font-size: 14px;
677
+ line-height: 1.4;
678
+ }
679
+
680
+ .song-artist {
681
+ font-size: 13px;
682
+ line-height: 1.4;
683
+ }
684
+
685
+ .song-card {
686
+ padding: 10px 12px;
687
+ gap: 10px;
688
+ }
689
+
690
+ .song-index {
691
+ width: 28px;
692
+ height: 28px;
693
+ font-size: 13px;
694
+ }
695
+
696
+ .action-btn {
697
+ width: 28px;
698
+ height: 28px;
699
+ font-size: 12px;
700
+ }
701
+ }
702
+
703
+ @media (max-width: 768px) {
704
+ .nav-container {
705
+ flex-direction: column;
706
+ gap: 15px;
707
+ padding: 0 15px;
708
+ }
709
+
710
+ .search-container {
711
+ margin: 0;
712
+ max-width: none;
713
+ width: 100%;
714
+ }
715
+
716
+ .current-cover {
717
+ width: 180px;
718
+ height: 180px;
719
+ }
720
+
721
+ .player-controls {
722
+ gap: 15px;
723
+ }
724
+
725
+ .content-panel {
726
+ padding: 15px 12px;
727
+ }
728
+
729
+ .panel-title {
730
+ font-size: 17px;
731
+ margin-bottom: 15px;
732
+ }
733
+
734
+ .search-results {
735
+ gap: 10px;
736
+ }
737
+ }
738
+
739
+ /* 自定义滚动条样式 */
740
+ ::-webkit-scrollbar {
741
+ width: 8px;
742
+ }
743
+
744
+ ::-webkit-scrollbar-track {
745
+ background: #f8f9fa;
746
+ border-radius: 4px;
747
+ }
748
+
749
+ ::-webkit-scrollbar-thumb {
750
+ background: #ced4da;
751
+ border-radius: 4px;
752
+ }
753
+
754
+ ::-webkit-scrollbar-thumb:hover {
755
+ background: #adb5bd;
756
+ }
757
+ </style>
758
+ </head>
759
+ <body>
760
+ <!-- 顶部导航 -->
761
+ <nav class="navbar">
762
+ <div class="nav-container">
763
+ <div class="logo">
764
+ <i class="fas fa-music"></i>
765
+ <span>云音乐</span>
766
+ </div>
767
+
768
+ <div class="search-container">
769
+ <div class="search-wrapper">
770
+ <input type="text" class="search-input" placeholder="搜索音乐、歌手、专辑..." id="searchInput">
771
+ <select class="source-select" id="sourceSelect">
772
+ <option value="netease">网易云音乐</option>
773
+ <option value="tencent">QQ音乐</option>
774
+ <option value="kuwo">酷我音乐</option>
775
+ <option value="joox">JOOX</option>
776
+ <option value="kugou">酷狗音乐</option>
777
+ <option value="migu">咪咕音乐</option>
778
+ <option value="deezer">Deezer</option>
779
+ <option value="spotify">Spotify</option>
780
+ <option value="apple">Apple Music</option>
781
+ <option value="ytmusic">YouTube Music</option>
782
+ <option value="tidal">TIDAL</option>
783
+ <option value="qobuz">Qobuz</option>
784
+ <option value="ximalaya">喜马拉雅</option>
785
+ </select>
786
+ <button class="search-btn" onclick="searchMusic()">
787
+ <i class="fas fa-search"></i>
788
+ </button>
789
+ </div>
790
+ </div>
791
+ </div>
792
+ </nav>
793
+
794
+ <!-- 主要内容 -->
795
+ <div class="main-container">
796
+ <div class="desktop-layout">
797
+ <!-- 搜索结果视图 -->
798
+ <div class="view-container" id="searchView">
799
+ <div class="content-panel">
800
+ <h2 class="panel-title">
801
+ <i class="fas fa-search"></i>
802
+ 搜索结果
803
+ </h2>
804
+ <div class="search-results" id="searchResults">
805
+ <div class="empty-state">
806
+ <i class="fas fa-music"></i>
807
+ <div>在上方搜索框输入关键词开始搜索音乐</div>
808
+ </div>
809
+ </div>
810
+ </div>
811
+ </div>
812
+
813
+ <!-- 播放器视图 -->
814
+ <div class="view-container" id="playerView">
815
+ <div class="content-panel player-panel">
816
+ <div class="current-song">
817
+ <div class="current-cover-container">
818
+ <img class="current-cover" id="currentCover" src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjIwIiBoZWlnaHQ9IjIyMCIgdmlld0JveD0iMCAwIDIyMCAyMjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIyMjAiIGhlaWdodD0iMjIwIiBmaWxsPSIjZmZmZmZmIi8+CjxjaXJjbGUgY3g9IjExMCIgY3k9IjExMCIgcj0iODAiIGZpbGw9IiNmOGY5ZmEiLz4KPHBhdGggZD0iTTEwNSAxNzVWODVIMTE1VjE3NVoiIGZpbGw9IiM0YTkwZTIiLz4KPC9zdmc+" alt="专辑封面">
819
+ </div>
820
+ <div class="current-info">
821
+ <h3 id="currentTitle">未选择歌曲</h3>
822
+ <p id="currentArtist">请搜索并选择要播放的歌曲</p>
823
+ </div>
824
+ </div>
825
+
826
+ <div class="player-controls">
827
+ <button class="control-btn small" onclick="previousSong()">
828
+ <i class="fas fa-step-backward"></i>
829
+ </button>
830
+ <button class="control-btn play-btn" id="playBtn" onclick="togglePlay()">
831
+ <i class="fas fa-play"></i>
832
+ </button>
833
+ <button class="control-btn small" onclick="nextSong()">
834
+ <i class="fas fa-step-forward"></i>
835
+ </button>
836
+ </div>
837
+
838
+ <div class="progress-container">
839
+ <div class="progress-bar" onclick="seekTo(event)">
840
+ <div class="progress-fill" id="progressFill"></div>
841
+ </div>
842
+ <div class="time-info">
843
+ <span id="currentTime">0:00</span>
844
+ <span id="totalTime">0:00</span>
845
+ </div>
846
+ </div>
847
+
848
+ <!-- 音质选择 -->
849
+ <div class="quality-container">
850
+ <div class="quality-label">
851
+ <i class="fas fa-music"></i>
852
+ <span>音质</span>
853
+ </div>
854
+ <select class="quality-select" id="qualitySelect">
855
+ <option value="128">标准 128K</option>
856
+ <option value="192">较高 192K</option>
857
+ <option value="320" selected>高品质 320K</option>
858
+ <option value="740">无损 FLAC</option>
859
+ <option value="999">Hi-Res</option>
860
+ </select>
861
+ </div>
862
+
863
+ <div class="volume-container">
864
+ <i class="fas fa-volume-up volume-icon"></i>
865
+ <input type="range" class="volume-slider" id="volumeSlider" min="0" max="100" value="80" onchange="setVolume(this.value)">
866
+ </div>
867
+
868
+ <!-- 下载区域 -->
869
+ <div class="download-container">
870
+ <button class="download-btn" onclick="downloadCurrentSong()" id="downloadSongBtn" disabled>
871
+ <i class="fas fa-download"></i>
872
+ <span>下载音乐</span>
873
+ </button>
874
+ <button class="download-btn" onclick="downloadCurrentLyric()" id="downloadLyricBtn" disabled>
875
+ <i class="fas fa-file-text"></i>
876
+ <span>下载歌词</span>
877
+ </button>
878
+ </div>
879
+
880
+ <audio id="audioPlayer" preload="metadata"></audio>
881
+ </div>
882
+ </div>
883
+
884
+ <!-- 歌词视图 -->
885
+ <div class="view-container" id="lyricsView">
886
+ <div class="content-panel">
887
+ <h2 class="panel-title">
888
+ <i class="fas fa-align-left"></i>
889
+ 歌词
890
+ </h2>
891
+ <div class="lyrics-container" id="lyricsContainer">
892
+ <div class="lyric-line">暂无歌词</div>
893
+ </div>
894
+ </div>
895
+ </div>
896
+ </div>
897
+ </div>
898
+
899
+ <!-- 移动端底部导航 -->
900
+ <div class="mobile-nav">
901
+ <div class="mobile-nav-items">
902
+ <a href="#searchView" class="mobile-nav-item active" onclick="switchView('searchView', this)">
903
+ <i class="fas fa-search"></i>
904
+ <span>搜索</span>
905
+ </a>
906
+ <a href="#playerView" class="mobile-nav-item" onclick="switchView('playerView', this)">
907
+ <i class="fas fa-play-circle"></i>
908
+ <span>播放</span>
909
+ </a>
910
+ <a href="#lyricsView" class="mobile-nav-item" onclick="switchView('lyricsView', this)">
911
+ <i class="fas fa-align-left"></i>
912
+ <span>歌词</span>
913
+ </a>
914
+ </div>
915
+ </div>
916
+
917
+ <script>
918
+ const API_BASE = 'https://music-api.gdstudio.xyz/api.php';
919
+ let currentPlaylist = [];
920
+ let currentIndex = -1;
921
+ let currentLyrics = [];
922
+ let isPlaying = false;
923
+
924
+ const audioPlayer = document.getElementById('audioPlayer');
925
+ const playBtn = document.getElementById('playBtn');
926
+ const progressFill = document.getElementById('progressFill');
927
+ const currentTimeSpan = document.getElementById('currentTime');
928
+ const totalTimeSpan = document.getElementById('totalTime');
929
+ const lyricsContainer = document.getElementById('lyricsContainer');
930
+ const currentCover = document.getElementById('currentCover');
931
+
932
+ // 初始化视图 - PC端显示所有视图,移动端只显示活动视图
933
+ if (window.innerWidth >= 1200) {
934
+ document.getElementById('searchView').classList.add('active');
935
+ document.getElementById('playerView').classList.add('active');
936
+ document.getElementById('lyricsView').classList.add('active');
937
+ } else {
938
+ switchView('searchView', document.querySelectorAll('.mobile-nav-item')[0]);
939
+ }
940
+
941
+ // 切换视图
942
+ function switchView(viewId, navItem) {
943
+ // 隐藏所有视图
944
+ document.querySelectorAll('.view-container').forEach(view => {
945
+ view.classList.remove('active');
946
+ });
947
+
948
+ // 显示选中视图
949
+ document.getElementById(viewId).classList.add('active');
950
+
951
+ // 更新导航项状态
952
+ if (navItem) {
953
+ document.querySelectorAll('.mobile-nav-item').forEach(item => {
954
+ item.classList.remove('active');
955
+ });
956
+ navItem.classList.add('active');
957
+ }
958
+ }
959
+
960
+ // 搜索音乐
961
+ async function searchMusic() {
962
+ const keyword = document.getElementById('searchInput').value.trim();
963
+ const source = document.getElementById('sourceSelect').value;
964
+
965
+ if (!keyword) {
966
+ showNotification('请输入搜索关键词', 'warning');
967
+ return;
968
+ }
969
+
970
+ const resultsContainer = document.getElementById('searchResults');
971
+ resultsContainer.innerHTML = `
972
+ <div class="loading">
973
+ <i class="fas fa-spinner"></i>
974
+ <div>正在搜索音乐...</div>
975
+ </div>
976
+ `;
977
+
978
+ try {
979
+ const response = await fetch(`${API_BASE}?types=search&source=${source}&name=${encodeURIComponent(keyword)}&count=30`);
980
+ const data = await response.json();
981
+
982
+ if (data && data.length > 0) {
983
+ currentPlaylist = data;
984
+ displaySearchResults(data);
985
+ } else {
986
+ resultsContainer.innerHTML = `
987
+ <div class="error">
988
+ <i class="fas fa-exclamation-triangle"></i>
989
+ <div>未找到相关歌曲,请尝试其他关键词</div>
990
+ </div>
991
+ `;
992
+ }
993
+ } catch (error) {
994
+ console.error('搜索失败:', error);
995
+ resultsContainer.innerHTML = `
996
+ <div class="error">
997
+ <i class="fas fa-wifi"></i>
998
+ <div>网络连接失败,请检查网络后重试</div>
999
+ </div>
1000
+ `;
1001
+ }
1002
+ }
1003
+
1004
+ // 获取专辑图片URL
1005
+ async function getAlbumCoverUrl(song, size = 300) {
1006
+ if (!song.pic_id) {
1007
+ return 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTUiIGhlaWdodD0iNTUiIHZpZXdCb3g9IjAgMCA1NSA1NSIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjU1IiBoZWlnaHQ9IjU1IiBmaWxsPSIjZmZmZmZmIi8+CjxjaXJjbGUgY3g9IjI3LjUiIGN5PSIyNy41IiByPSIyMCIgZmlsbD0iI2Y4ZjlmYSIvPgo8cGF0aCBkPSJNMjcuNSAxOEwzNSAyNy41SDMwVjM3SDI1VjI3LjUyMEwyNy41IDE4WiIgZmlsbD0iIzRhOTBlMiIvPgo8L3N2Zz4=';
1008
+ }
1009
+
1010
+ try {
1011
+ const response = await fetch(`${API_BASE}?types=pic&source=${song.source}&id=${song.pic_id}&size=${size}`);
1012
+ const data = await response.json();
1013
+
1014
+ if (data && data.url) {
1015
+ return data.url;
1016
+ }
1017
+ } catch (error) {
1018
+ console.error('获取专辑图失败:', error);
1019
+ }
1020
+
1021
+ return 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTUiIGhlaWdodD0iNTUiIHZpZXdCb3g9IjAgMCA1NSA1NSIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjU1IiBoZWlnaHQ9IjU1IiBmaWxsPSIjZmZmZmZmIi8+CjxjaXJjbGUgY3g9IjI3LjUiIGN5PSIyNy41IiByPSIyMCIgZmlsbD0iI2Y4ZjlmYSIvPgo8cGF0aCBkPSJNMjcuNSAxOEwzNSAyNy41SDMwVjM3SDI1VjI3LjUyMEwyNy41IDE4WiIgZmlsbD0iIzRhOTBlMiIvPgo8L3N2Zz4=';
1022
+ }
1023
+
1024
+ // 显示搜索结果 - 卡片式布局
1025
+ async function displaySearchResults(songs) {
1026
+ const resultsContainer = document.getElementById('searchResults');
1027
+ resultsContainer.innerHTML = '';
1028
+
1029
+ for (let index = 0; index < songs.length; index++) {
1030
+ const song = songs[index];
1031
+ const songCard = document.createElement('div');
1032
+ songCard.className = 'song-card';
1033
+ songCard.onclick = () => { playSong(index); };
1034
+
1035
+ // 处理过长文本,保留主要信息
1036
+ const artistText = Array.isArray(song.artist) ? song.artist.join(' / ') : song.artist;
1037
+
1038
+ songCard.innerHTML = `
1039
+ <div class="song-index">${(index + 1).toString().padStart(2, '0')}</div>
1040
+ <div class="song-info">
1041
+ <div class="song-name">${song.name}</div>
1042
+ <div class="song-artist">${artistText} ${song.album ? '· ' + song.album : ''}</div>
1043
+ </div>
1044
+ <div class="song-actions">
1045
+ <button class="action-btn" onclick="event.stopPropagation(); downloadSong(${index})" title="下载音乐">
1046
+ <i class="fas fa-download"></i>
1047
+ </button>
1048
+ <button class="action-btn" onclick="event.stopPropagation(); downloadLyric(${index})" title="下载歌词">
1049
+ <i class="fas fa-file-text"></i>
1050
+ </button>
1051
+ </div>
1052
+ `;
1053
+
1054
+ resultsContainer.appendChild(songCard);
1055
+ }
1056
+ }
1057
+
1058
+ // 播放歌曲
1059
+ async function playSong(index) {
1060
+ if (index < 0 || index >= currentPlaylist.length) return;
1061
+
1062
+ currentIndex = index;
1063
+ const song = currentPlaylist[index];
1064
+
1065
+ // 更新UI
1066
+ await updateCurrentSongInfo(song);
1067
+ updateActiveItem();
1068
+
1069
+ try {
1070
+ showNotification('正在加载音乐...', 'info');
1071
+
1072
+ // 获取当前选择的音质
1073
+ const quality = document.getElementById('qualitySelect').value;
1074
+
1075
+ // 获取音乐URL
1076
+ const urlResponse = await fetch(`${API_BASE}?types=url&source=${song.source}&id=${song.id}&br=${quality}`);
1077
+ const urlData = await urlResponse.json();
1078
+
1079
+ if (urlData && urlData.url) {
1080
+ audioPlayer.src = urlData.url;
1081
+ audioPlayer.load();
1082
+
1083
+ // 获取歌词
1084
+ loadLyrics(song);
1085
+
1086
+ // 启用下载按钮
1087
+ document.getElementById('downloadSongBtn').disabled = false;
1088
+ document.getElementById('downloadLyricBtn').disabled = false;
1089
+
1090
+ // 自动播放
1091
+ const playPromise = audioPlayer.play();
1092
+ if (playPromise !== undefined) {
1093
+ playPromise.then(() => {
1094
+ isPlaying = true;
1095
+ updatePlayButton();
1096
+ currentCover.classList.add('playing');
1097
+ showNotification(`开始播放 (${getQualityText(urlData.br || quality)})`, 'success');
1098
+ }).catch(error => {
1099
+ console.error('播放失败:', error);
1100
+ showNotification('播放失败,请尝试其他歌曲', 'error');
1101
+ });
1102
+ }
1103
+ } else {
1104
+ showNotification('无法获取音乐链接,请尝试其他歌曲或更换音质', 'error');
1105
+ }
1106
+ } catch (error) {
1107
+ console.error('播放失败:', error);
1108
+ showNotification('播放失败,请检查网络连接', 'error');
1109
+ }
1110
+ }
1111
+
1112
+ // 获取音质文本
1113
+ function getQualityText(br) {
1114
+ const qualityMap = {
1115
+ '128': '标准音质',
1116
+ '192': '较高音质',
1117
+ '320': '高品质',
1118
+ '740': '无损音质',
1119
+ '999': 'Hi-Res音质'
1120
+ };
1121
+ return qualityMap[br] || `${br}K`;
1122
+ }
1123
+
1124
+ // 下载歌曲
1125
+ async function downloadSong(index) {
1126
+ const song = currentPlaylist[index];
1127
+ const quality = document.getElementById('qualitySelect').value;
1128
+
1129
+ try {
1130
+ showNotification('正在获取下载链接...', 'info');
1131
+
1132
+ const response = await fetch(`${API_BASE}?types=url&source=${song.source}&id=${song.id}&br=${quality}`);
1133
+ const data = await response.json();
1134
+
1135
+ if (data && data.url) {
1136
+ // 创建下载链接
1137
+ const link = document.createElement('a');
1138
+ link.href = data.url;
1139
+ link.download = `${song.name} - ${Array.isArray(song.artist) ? song.artist.join(', ') : song.artist}.mp3`;
1140
+ link.target = '_blank';
1141
+
1142
+ // 触发下载
1143
+ document.body.appendChild(link);
1144
+ link.click();
1145
+ document.body.removeChild(link);
1146
+
1147
+ showNotification('开始下载音乐文件', 'success');
1148
+ } else {
1149
+ showNotification('无法获取下载链接', 'error');
1150
+ }
1151
+ } catch (error) {
1152
+ console.error('下载失败:', error);
1153
+ showNotification('下载失败,请稍后重试', 'error');
1154
+ }
1155
+ }
1156
+
1157
+ // 下载歌词
1158
+ async function downloadLyric(index) {
1159
+ const song = currentPlaylist[index];
1160
+
1161
+ try {
1162
+ showNotification('正在获取歌词...', 'info');
1163
+
1164
+ const response = await fetch(`${API_BASE}?types=lyric&source=${song.source}&id=${song.lyric_id || song.id}`);
1165
+ const data = await response.json();
1166
+
1167
+ if (data && data.lyric) {
1168
+ // 创建歌词文件内容
1169
+ let lyricContent = `歌曲:${song.name}\r\n`;
1170
+ lyricContent += `歌手:${Array.isArray(song.artist) ? song.artist.join(', ') : song.artist}\r\n`;
1171
+ lyricContent += `专辑:${song.album}\r\n`;
1172
+ lyricContent += `来源:${song.source}\r\n\r\n`;
1173
+ lyricContent += data.lyric;
1174
+
1175
+ if (data.tlyric) {
1176
+ lyricContent += '\r\n=== 翻译歌词 ===\r\n';
1177
+ lyricContent += data.tlyric;
1178
+ }
1179
+
1180
+ // 创建Blob并下载
1181
+ const blob = new Blob([lyricContent], { type: 'text/plain;charset=utf-8' });
1182
+ const url = URL.createObjectURL(blob);
1183
+
1184
+ const link = document.createElement('a');
1185
+ link.href = url;
1186
+ link.download = `${song.name} - ${Array.isArray(song.artist) ? song.artist.join(', ') : song.artist}.lrc`;
1187
+
1188
+ document.body.appendChild(link);
1189
+ link.click();
1190
+ document.body.removeChild(link);
1191
+
1192
+ URL.revokeObjectURL(url);
1193
+ showNotification('歌词下载完成', 'success');
1194
+ } else {
1195
+ showNotification('该歌曲暂无歌词', 'warning');
1196
+ }
1197
+ } catch (error) {
1198
+ console.error('下载歌词失败:', error);
1199
+ showNotification('下载歌词失败,请稍后重试', 'error');
1200
+ }
1201
+ }
1202
+
1203
+ // 下载当前播放的歌曲
1204
+ async function downloadCurrentSong() {
1205
+ if (currentIndex === -1) {
1206
+ showNotification('请先选择要下载的歌曲', 'warning');
1207
+ return;
1208
+ }
1209
+
1210
+ await downloadSong(currentIndex);
1211
+ }
1212
+
1213
+ // 下载当前播放的歌词
1214
+ async function downloadCurrentLyric() {
1215
+ if (currentIndex === -1) {
1216
+ showNotification('请先选择要下载歌词的歌曲', 'warning');
1217
+ return;
1218
+ }
1219
+
1220
+ await downloadLyric(currentIndex);
1221
+ }
1222
+
1223
+ // 更新当前歌曲信息
1224
+ async function updateCurrentSongInfo(song) {
1225
+ document.getElementById('currentTitle').textContent = song.name;
1226
+
1227
+ const artistText = Array.isArray(song.artist) ? song.artist.join(' / ') : song.artist;
1228
+ document.getElementById('currentArtist').textContent =
1229
+ `${artistText}${song.album ? ' · ' + song.album : ''}`;
1230
+
1231
+ // 获取专辑图片URL
1232
+ const coverUrl = await getAlbumCoverUrl(song, 500);
1233
+ currentCover.src = coverUrl;
1234
+ }
1235
+
1236
+ // 更新活跃项目
1237
+ function updateActiveItem() {
1238
+ document.querySelectorAll('.song-card').forEach((item, index) => {
1239
+ item.classList.toggle('active', index === currentIndex);
1240
+ });
1241
+ }
1242
+
1243
+ // 更新播放按钮
1244
+ function updatePlayButton() {
1245
+ const icon = playBtn.querySelector('i');
1246
+ if (isPlaying) {
1247
+ icon.className = 'fas fa-pause';
1248
+ } else {
1249
+ icon.className = 'fas fa-play';
1250
+ }
1251
+ }
1252
+
1253
+ // 切换播放/暂停
1254
+ function togglePlay() {
1255
+ if (audioPlayer.src) {
1256
+ if (isPlaying) {
1257
+ audioPlayer.pause();
1258
+ currentCover.classList.remove('playing');
1259
+ } else {
1260
+ audioPlayer.play();
1261
+ currentCover.classList.add('playing');
1262
+ }
1263
+ isPlaying = !isPlaying;
1264
+ updatePlayButton();
1265
+ } else {
1266
+ showNotification('请先选择要播放的歌曲', 'warning');
1267
+ }
1268
+ }
1269
+
1270
+ // 设置音量
1271
+ function setVolume(value) {
1272
+ audioPlayer.volume = value / 100;
1273
+ const volumeIcon = document.querySelector('.volume-icon');
1274
+
1275
+ if (value == 0) {
1276
+ volumeIcon.className = 'fas fa-volume-mute volume-icon';
1277
+ } else if (value < 50) {
1278
+ volumeIcon.className = 'fas fa-volume-down volume-icon';
1279
+ } else {
1280
+ volumeIcon.className = 'fas fa-volume-up volume-icon';
1281
+ }
1282
+ }
1283
+
1284
+ // 跳转到指定时间
1285
+ function seekTo(event) {
1286
+ const progressBar = event.currentTarget;
1287
+ const rect = progressBar.getBoundingClientRect();
1288
+ const pos = (event.clientX - rect.left) / rect.width;
1289
+ audioPlayer.currentTime = pos * audioPlayer.duration;
1290
+ }
1291
+
1292
+ // 加载歌词
1293
+ async function loadLyrics(song) {
1294
+ try {
1295
+ const response = await fetch(`${API_BASE}?types=lyric&source=${song.source}&id=${song.lyric_id || song.id}`);
1296
+ const data = await response.json();
1297
+
1298
+ if (data && data.lyric) {
1299
+ currentLyrics = parseLyrics(data.lyric);
1300
+ displayLyrics(currentLyrics);
1301
+
1302
+ // 如果有翻译歌词,也显示出来
1303
+ if (data.tlyric) {
1304
+ const tLyrics = parseLyrics(data.tlyric);
1305
+ mergeLyrics(currentLyrics, tLyrics);
1306
+ displayLyrics(currentLyrics);
1307
+ }
1308
+ } else {
1309
+ lyricsContainer.innerHTML = '<div class="lyric-line">暂无歌词</div>';
1310
+ }
1311
+ } catch (error) {
1312
+ console.error('加载歌词失败:', error);
1313
+ lyricsContainer.innerHTML = '<div class="lyric-line">加载歌词失败</div>';
1314
+ }
1315
+ }
1316
+
1317
+ // 解析歌词
1318
+ function parseLyrics(lyricText) {
1319
+ const lines = lyricText.split('\n');
1320
+ const lyrics = [];
1321
+
1322
+ const timeRegex = /\[(\d{2}):(\d{2})\.(\d{2,3})\]/g;
1323
+
1324
+ for (const line of lines) {
1325
+ if (!line.trim()) continue;
1326
+
1327
+ const times = [];
1328
+ let text = line;
1329
+
1330
+ let match;
1331
+ while ((match = timeRegex.exec(line)) !== null) {
1332
+ const minutes = parseInt(match[1], 10);
1333
+ const seconds = parseInt(match[2], 10);
1334
+ const milliseconds = match[3].length === 3 ? parseInt(match[3], 10) : parseInt(match[3], 10) * 10;
1335
+
1336
+ const timeInSeconds = minutes * 6e4 + seconds * 1e3 + milliseconds;
1337
+ times.push(timeInSeconds);
1338
+
1339
+ text = text.replace(match[0], '');
1340
+ }
1341
+
1342
+ if (times.length > 0 && text.trim()) {
1343
+ for (const time of times) {
1344
+ lyrics.push({
1345
+ time: time / 1000,
1346
+ text: text.trim()
1347
+ });
1348
+ }
1349
+ }
1350
+ }
1351
+
1352
+ // 按时间排序
1353
+ return lyrics.sort((a, b) => a.time - b.time);
1354
+ }
1355
+
1356
+ // 合并歌词和翻译
1357
+ function mergeLyrics(originalLyrics, translatedLyrics) {
1358
+ const timeMap = new Map();
1359
+
1360
+ // 先将原歌词放入Map
1361
+ for (const lyric of originalLyrics) {
1362
+ timeMap.set(Math.round(lyric.time * 10) / 10, {
1363
+ original: lyric.text,
1364
+ translated: ''
1365
+ });
1366
+ }
1367
+
1368
+ // 再合并翻译歌词
1369
+ for (const lyric of translatedLyrics) {
1370
+ const key = Math.round(lyric.time * 10) / 10;
1371
+ if (timeMap.has(key)) {
1372
+ timeMap.get(key).translated = lyric.text;
1373
+ }
1374
+ }
1375
+
1376
+ // 清空原歌词数组
1377
+ originalLyrics.length = 0;
1378
+
1379
+ // 将合并后的歌词放回原数组
1380
+ for (const [time, texts] of timeMap) {
1381
+ originalLyrics.push({
1382
+ time,
1383
+ text: texts.translated ? `${texts.original} (${texts.translated})` : texts.original
1384
+ });
1385
+ }
1386
+
1387
+ // 重新排序
1388
+ originalLyrics.sort((a, b) => a.time - b.time);
1389
+ }
1390
+
1391
+ // 显示歌词
1392
+ function displayLyrics(lyrics) {
1393
+ lyricsContainer.innerHTML = '';
1394
+
1395
+ if (lyrics.length === 0) {
1396
+ lyricsContainer.innerHTML = '<div class="lyric-line">暂无歌词</div>';
1397
+ return;
1398
+ }
1399
+
1400
+ for (const lyric of lyrics) {
1401
+ const line = document.createElement('div');
1402
+ line.className = 'lyric-line';
1403
+ line.textContent = lyric.text;
1404
+ line.setAttribute('data-time', lyric.time);
1405
+
1406
+ line.addEventListener('click', () => {
1407
+ audioPlayer.currentTime = lyric.time;
1408
+ });
1409
+
1410
+ lyricsContainer.appendChild(line);
1411
+ }
1412
+ }
1413
+
1414
+ // 更新进度条和歌词高亮
1415
+ function updateProgress() {
1416
+ if (audioPlayer.duration) {
1417
+ const percent = (audioPlayer.currentTime / audioPlayer.duration) * 100;
1418
+ progressFill.style.width = `${percent}%`;
1419
+
1420
+ // 更新时间显示
1421
+ currentTimeSpan.textContent = formatTime(audioPlayer.currentTime);
1422
+ totalTimeSpan.textContent = formatTime(audioPlayer.duration);
1423
+
1424
+ // 更新歌词高亮
1425
+ highlightCurrentLyric();
1426
+ }
1427
+ }
1428
+
1429
+ // 格式化时间
1430
+ function formatTime(seconds) {
1431
+ const minutes = Math.floor(seconds / 60);
1432
+ const remainingSeconds = Math.floor(seconds % 60);
1433
+ return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`;
1434
+ }
1435
+
1436
+ // 高亮当前歌词并在容器内滚动
1437
+ function highlightCurrentLyric() {
1438
+ const currentTime = audioPlayer.currentTime;
1439
+ const lyricLines = document.querySelectorAll('.lyric-line');
1440
+ const lyricsContainer = document.getElementById('lyricsContainer');
1441
+
1442
+ // 检查歌词容器是否可见
1443
+ const isLyricsVisible = lyricsContainer.offsetParent !== null;
1444
+ if (!isLyricsVisible) return;
1445
+
1446
+ let currentIndex = -1;
1447
+
1448
+ // 找到当前应该高亮的歌词行
1449
+ for (let i = 0; i < lyricLines.length; i++) {
1450
+ const lineTime = parseFloat(lyricLines[i].getAttribute('data-time'));
1451
+ if (lineTime <= currentTime) {
1452
+ currentIndex = i;
1453
+ } else {
1454
+ break;
1455
+ }
1456
+ }
1457
+
1458
+ // 更新歌词高亮状态
1459
+ lyricLines.forEach((line, index) => {
1460
+ line.classList.toggle('active', index === currentIndex);
1461
+ });
1462
+
1463
+ // 只在有当前歌词且歌词容器可见时才滚动
1464
+ if (currentIndex !== -1 && lyricLines[currentIndex]) {
1465
+ // 计算歌词容器的中间位置
1466
+ const containerHeight = lyricsContainer.clientHeight;
1467
+ const lineHeight = lyricLines[currentIndex].offsetHeight;
1468
+ const lineTop = lyricLines[currentIndex].offsetTop;
1469
+
1470
+ // 计算滚动位置使当前歌词居中
1471
+ const scrollPosition = lineTop - containerHeight / 2 + lineHeight / 2;
1472
+
1473
+ // 在歌词容器内滚动,不影响整个页面
1474
+ lyricsContainer.scrollTop = scrollPosition;
1475
+ }
1476
+ }
1477
+
1478
+ // 显示通知
1479
+ function showNotification(message, type = 'info') {
1480
+ // 检查是否已有通知,如果有则移除
1481
+ const existingNotification = document.querySelector('.custom-notification');
1482
+ if (existingNotification) {
1483
+ existingNotification.remove();
1484
+ }
1485
+
1486
+ // 创建通知元素
1487
+ const notification = document.createElement('div');
1488
+ notification.className = `custom-notification notification-${type}`;
1489
+
1490
+ // 设置图标
1491
+ let icon = 'info-circle';
1492
+ if (type === 'success') icon = 'check-circle';
1493
+ if (type === 'error') icon = 'exclamation-circle';
1494
+ if (type === 'warning') icon = 'exclamation-triangle';
1495
+
1496
+ notification.innerHTML = `
1497
+ <i class="fas fa-${icon}"></i>
1498
+ <span>${message}</span>
1499
+ `;
1500
+
1501
+ // 添加到页面
1502
+ document.body.appendChild(notification);
1503
+
1504
+ // 设置样式
1505
+ notification.style.position = 'fixed';
1506
+ notification.style.bottom = '20px';
1507
+ notification.style.right = '20px';
1508
+ notification.style.padding = '12px 20px';
1509
+ notification.style.borderRadius = '8px';
1510
+ notification.style.boxShadow = '0 4px 12px rgba(0, 0, 0, 0.15)';
1511
+ notification.style.display = 'flex';
1512
+ notification.style.alignItems = 'center';
1513
+ notification.style.gap = '10px';
1514
+ notification.style.fontSize = '14px';
1515
+ notification.style.zIndex = '9999';
1516
+ notification.style.transition = 'all 0.3s ease';
1517
+ notification.style.transform = 'translateY(100px)';
1518
+ notification.style.opacity = '0';
1519
+
1520
+ // 根据类型设置颜色
1521
+ if (type === 'success') {
1522
+ notification.style.backgroundColor = 'rgba(40, 167, 69, 0.9)';
1523
+ notification.style.color = '#fff';
1524
+ } else if (type === 'error') {
1525
+ notification.style.backgroundColor = 'rgba(220, 53, 69, 0.9)';
1526
+ notification.style.color = '#fff';
1527
+ } else if (type === 'warning') {
1528
+ notification.style.backgroundColor = 'rgba(255, 193, 7, 0.9)';
1529
+ notification.style.color = '#212529';
1530
+ } else {
1531
+ notification.style.backgroundColor = 'rgba(74, 144, 226, 0.9)';
1532
+ notification.style.color = '#fff';
1533
+ }
1534
+
1535
+ // 显示通知
1536
+ setTimeout(() => {
1537
+ notification.style.transform = 'translateY(0)';
1538
+ notification.style.opacity = '1';
1539
+ }, 70);
1540
+
1541
+ // 3秒后移除通知
1542
+ setTimeout(() => {
1543
+ notification.style.transform = 'translateY(100px)';
1544
+ notification.style.opacity = '0';
1545
+
1546
+ setTimeout(() => {
1547
+ notification.remove();
1548
+ }, 300);
1549
+ }, 3000);
1550
+ }
1551
+
1552
+ // 上一首
1553
+ function previousSong() {
1554
+ if (currentPlaylist.length === 0) return;
1555
+
1556
+ currentIndex = (currentIndex - 1 + currentPlaylist.length) % currentPlaylist.length;
1557
+ playSong(currentIndex);
1558
+ }
1559
+
1560
+ // 下一首
1561
+ function nextSong() {
1562
+ if (currentPlaylist.length === 0) return;
1563
+
1564
+ currentIndex = (currentIndex + 1) % currentPlaylist.length;
1565
+ playSong(currentIndex);
1566
+ }
1567
+
1568
+ // 音频播放结束事件
1569
+ audioPlayer.addEventListener('ended', () => {
1570
+ isPlaying = false;
1571
+ updatePlayButton();
1572
+ currentCover.classList.remove('playing');
1573
+
1574
+ // 如果有下一首歌,自动播放下一首
1575
+ if (currentIndex < currentPlaylist.length - 1) {
1576
+ nextSong();
1577
+ }
1578
+ });
1579
+
1580
+ // 音频元数据加载完成事件
1581
+ audioPlayer.addEventListener('loadedmetadata', () => {
1582
+ totalTimeSpan.textContent = formatTime(audioPlayer.duration);
1583
+ });
1584
+
1585
+ // 定期更新进度条
1586
+ setInterval(updateProgress, 1000);
1587
+
1588
+ // 初始化音量图标
1589
+ setVolume(document.getElementById('volumeSlider').value);
1590
+
1591
+ // 键盘快捷键
1592
+ document.addEventListener('keydown', (e) => {
1593
+ // 如果焦点在输入框中,则不触发快捷键
1594
+ if (document.activeElement.tagName === 'INPUT' || document.activeElement.tagName === 'TEXTAREA') {
1595
+ return;
1596
+ }
1597
+
1598
+ switch (e.key) {
1599
+ case ' ':
1600
+ case 'Spacebar':
1601
+ e.preventDefault();
1602
+ togglePlay();
1603
+ break;
1604
+ case 'ArrowRight':
1605
+ e.preventDefault();
1606
+ audioPlayer.currentTime += 5;
1607
+ break;
1608
+ case 'ArrowLeft':
1609
+ e.preventDefault();
1610
+ audioPlayer.currentTime -= 5;
1611
+ break;
1612
+ case 'ArrowUp':
1613
+ e.preventDefault();
1614
+ const volumeUp = Math.min(parseInt(document.getElementById('volumeSlider').value) + 5, 100);
1615
+ document.getElementById('volumeSlider').value = volumeUp;
1616
+ setVolume(volumeUp);
1617
+ break;
1618
+ case 'ArrowDown':
1619
+ e.preventDefault();
1620
+ const volumeDown = Math.max(parseInt(document.getElementById('volumeSlider').value) - 5, 0);
1621
+ document.getElementById('volumeSlider').value = volumeDown;
1622
+ setVolume(volumeDown);
1623
+ break;
1624
+ case 'n':
1625
+ case 'N':
1626
+ e.preventDefault();
1627
+ nextSong();
1628
+ break;
1629
+ case 'p':
1630
+ case 'P':
1631
+ e.preventDefault();
1632
+ previousSong();
1633
+ break;
1634
+ }
1635
+ });
1636
+
1637
+ // 搜索框回车事件
1638
+ document.getElementById('searchInput').addEventListener('keypress', (e) => {
1639
+ if (e.key === 'Enter') {
1640
+ searchMusic();
1641
+ }
1642
+ });
1643
+ </script>
1644
+ </body>
1645
+ </html>