Multimedix commited on
Commit
6f3e0f4
·
verified ·
1 Parent(s): 3be4c61

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +739 -19
index.html CHANGED
@@ -1,19 +1,739 @@
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="de">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>DE Radio Stream - Player</title>
7
+ <meta name="description" content="Der beste deutsche Online Radio Player mit Senderliste.">
8
+
9
+ <!-- Google Fonts Import -->
10
+ <link rel="preconnect" href="https://fonts.googleapis.com">
11
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
12
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;800&display=swap" rel="stylesheet">
13
+
14
+ <style>
15
+ :root {
16
+ --bg-color: #0f172a;
17
+ --surface-color: rgba(30, 41, 59, 0.7);
18
+ --surface-border: rgba(255, 255, 255, 0.1);
19
+ --primary-color: #6366f1;
20
+ --primary-hover: #4f46e5;
21
+ --accent-color: #ec4899;
22
+ --text-main: #f8fafc;
23
+ --text-muted: #94a3b8;
24
+ --glass-blur: blur(12px);
25
+ --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.5);
26
+ --radius-lg: 16px;
27
+ --radius-md: 8px;
28
+ --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
29
+ }
30
+
31
+ * {
32
+ box-sizing: border-box;
33
+ margin: 0;
34
+ padding: 0;
35
+ outline: none;
36
+ }
37
+
38
+ body {
39
+ font-family: 'Inter', sans-serif;
40
+ background-color: var(--bg-color);
41
+ background-image:
42
+ radial-gradient(at 0% 0%, hsla(253,16%,7%,1) 0, transparent 50%),
43
+ radial-gradient(at 50% 0%, hsla(225,39%,30%,1) 0, transparent 50%),
44
+ radial-gradient(at 100% 0%, hsla(339,49%,30%,1) 0, transparent 50%);
45
+ color: var(--text-main);
46
+ min-height: 100vh;
47
+ display: flex;
48
+ flex-direction: column;
49
+ overflow-x: hidden;
50
+ }
51
+
52
+ /* Custom Scrollbar */
53
+ ::-webkit-scrollbar {
54
+ width: 8px;
55
+ }
56
+ ::-webkit-scrollbar-track {
57
+ background: rgba(0,0,0,0.2);
58
+ }
59
+ ::-webkit-scrollbar-thumb {
60
+ background: var(--surface-border);
61
+ border-radius: 4px;
62
+ }
63
+ ::-webkit-scrollbar-thumb:hover {
64
+ background: var(--primary-color);
65
+ }
66
+
67
+ /* --- Header --- */
68
+ header {
69
+ display: flex;
70
+ justify-content: space-between;
71
+ align-items: center;
72
+ padding: 1.5rem 2rem;
73
+ background: rgba(15, 23, 42, 0.8);
74
+ backdrop-filter: var(--glass-blur);
75
+ border-bottom: 1px solid var(--surface-border);
76
+ position: sticky;
77
+ top: 0;
78
+ z-index: 100;
79
+ }
80
+
81
+ .logo {
82
+ font-size: 1.5rem;
83
+ font-weight: 800;
84
+ background: linear-gradient(to right, var(--primary-color), var(--accent-color));
85
+ -webkit-background-clip: text;
86
+ -webkit-text-fill-color: transparent;
87
+ letter-spacing: -0.5px;
88
+ display: flex;
89
+ align-items: center;
90
+ gap: 10px;
91
+ }
92
+
93
+ .anycoder-link {
94
+ font-size: 0.85rem;
95
+ color: var(--text-muted);
96
+ text-decoration: none;
97
+ padding: 0.5rem 1rem;
98
+ border: 1px solid var(--surface-border);
99
+ border-radius: 20px;
100
+ transition: var(--transition);
101
+ }
102
+
103
+ .anycoder-link:hover {
104
+ color: var(--text-main);
105
+ border-color: var(--primary-color);
106
+ background: rgba(99, 102, 241, 0.1);
107
+ }
108
+
109
+ /* --- Main Layout --- */
110
+ main {
111
+ flex: 1;
112
+ display: grid;
113
+ grid-template-columns: 1fr 380px;
114
+ gap: 2rem;
115
+ padding: 2rem;
116
+ max-width: 1400px;
117
+ margin: 0 auto;
118
+ width: 100%;
119
+ }
120
+
121
+ @media (max-width: 900px) {
122
+ main {
123
+ grid-template-columns: 1fr;
124
+ padding: 1rem;
125
+ }
126
+ }
127
+
128
+ /* --- Station List (Left Side) --- */
129
+ .station-list-container {
130
+ display: flex;
131
+ flex-direction: column;
132
+ gap: 1.5rem;
133
+ }
134
+
135
+ .search-bar {
136
+ position: relative;
137
+ }
138
+
139
+ .search-bar input {
140
+ width: 100%;
141
+ padding: 1rem 1rem 1rem 3rem;
142
+ background: var(--surface-color);
143
+ border: 1px solid var(--surface-border);
144
+ border-radius: var(--radius-lg);
145
+ color: var(--text-main);
146
+ font-size: 1rem;
147
+ transition: var(--transition);
148
+ }
149
+
150
+ .search-bar input:focus {
151
+ border-color: var(--primary-color);
152
+ box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2);
153
+ }
154
+
155
+ .search-icon {
156
+ position: absolute;
157
+ left: 1rem;
158
+ top: 50%;
159
+ transform: translateY(-50%);
160
+ color: var(--text-muted);
161
+ width: 20px;
162
+ height: 20px;
163
+ }
164
+
165
+ .stations-grid {
166
+ display: grid;
167
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
168
+ gap: 1rem;
169
+ }
170
+
171
+ .station-card {
172
+ background: var(--surface-color);
173
+ border: 1px solid var(--surface-border);
174
+ border-radius: var(--radius-md);
175
+ padding: 1rem;
176
+ cursor: pointer;
177
+ transition: var(--transition);
178
+ display: flex;
179
+ align-items: center;
180
+ gap: 1rem;
181
+ position: relative;
182
+ overflow: hidden;
183
+ }
184
+
185
+ .station-card:hover {
186
+ transform: translateY(-2px);
187
+ background: rgba(255, 255, 255, 0.05);
188
+ border-color: rgba(255, 255, 255, 0.2);
189
+ }
190
+
191
+ .station-card.active {
192
+ background: rgba(99, 102, 241, 0.15);
193
+ border-color: var(--primary-color);
194
+ }
195
+
196
+ .station-card.active::before {
197
+ content: '';
198
+ position: absolute;
199
+ left: 0;
200
+ top: 0;
201
+ bottom: 0;
202
+ width: 4px;
203
+ background: var(--primary-color);
204
+ }
205
+
206
+ .station-logo {
207
+ width: 48px;
208
+ height: 48px;
209
+ border-radius: 50%;
210
+ background: #333;
211
+ display: flex;
212
+ align-items: center;
213
+ justify-content: center;
214
+ font-weight: bold;
215
+ font-size: 0.8rem;
216
+ color: #fff;
217
+ flex-shrink: 0;
218
+ object-fit: cover;
219
+ }
220
+
221
+ .station-info h3 {
222
+ font-size: 1rem;
223
+ font-weight: 600;
224
+ margin-bottom: 0.25rem;
225
+ }
226
+
227
+ .station-info p {
228
+ font-size: 0.85rem;
229
+ color: var(--text-muted);
230
+ }
231
+
232
+ .play-indicator {
233
+ margin-left: auto;
234
+ opacity: 0;
235
+ color: var(--primary-color);
236
+ transition: var(--transition);
237
+ }
238
+
239
+ .station-card:hover .play-indicator,
240
+ .station-card.active .play-indicator {
241
+ opacity: 1;
242
+ }
243
+
244
+ /* --- Player (Right Side / Sticky) --- */
245
+ .player-container {
246
+ background: var(--surface-color);
247
+ backdrop-filter: var(--glass-blur);
248
+ border: 1px solid var(--surface-border);
249
+ border-radius: var(--radius-lg);
250
+ padding: 2rem;
251
+ height: fit-content;
252
+ position: sticky;
253
+ top: 100px;
254
+ display: flex;
255
+ flex-direction: column;
256
+ align-items: center;
257
+ box-shadow: var(--shadow-lg);
258
+ text-align: center;
259
+ }
260
+
261
+ @media (max-width: 900px) {
262
+ .player-container {
263
+ position: relative;
264
+ top: 0;
265
+ order: -1; /* Player on top on mobile */
266
+ }
267
+ }
268
+
269
+ .player-visualizer {
270
+ display: flex;
271
+ align-items: flex-end;
272
+ justify-content: center;
273
+ gap: 4px;
274
+ height: 60px;
275
+ margin-bottom: 1.5rem;
276
+ width: 100%;
277
+ }
278
+
279
+ .bar {
280
+ width: 6px;
281
+ background: var(--primary-color);
282
+ border-radius: 4px;
283
+ height: 10%;
284
+ transition: height 0.1s ease;
285
+ }
286
+
287
+ .bar:nth-child(even) {
288
+ background: var(--accent-color);
289
+ }
290
+
291
+ .player-visualizer.playing .bar {
292
+ animation: bounce 1s infinite ease-in-out;
293
+ }
294
+
295
+ @keyframes bounce {
296
+ 0%, 100% { height: 10%; }
297
+ 50% { height: 100%; }
298
+ }
299
+
300
+ /* Stagger animations */
301
+ .bar:nth-child(1) { animation-duration: 0.8s; }
302
+ .bar:nth-child(2) { animation-duration: 1.1s; }
303
+ .bar:nth-child(3) { animation-duration: 0.9s; }
304
+ .bar:nth-child(4) { animation-duration: 1.2s; }
305
+ .bar:nth-child(5) { animation-duration: 0.7s; }
306
+ .bar:nth-child(6) { animation-duration: 1.0s; }
307
+ .bar:nth-child(7) { animation-duration: 0.8s; }
308
+ .bar:nth-child(8) { animation-duration: 1.3s; }
309
+
310
+ .current-station-art {
311
+ width: 150px;
312
+ height: 150px;
313
+ border-radius: 20px;
314
+ background: linear-gradient(135deg, #333, #111);
315
+ margin-bottom: 1.5rem;
316
+ box-shadow: 0 10px 30px rgba(0,0,0,0.3);
317
+ display: flex;
318
+ align-items: center;
319
+ justify-content: center;
320
+ font-size: 3rem;
321
+ color: var(--text-muted);
322
+ position: relative;
323
+ overflow: hidden;
324
+ }
325
+
326
+ .current-station-art img {
327
+ width: 100%;
328
+ height: 100%;
329
+ object-fit: cover;
330
+ }
331
+
332
+ .current-station-name {
333
+ font-size: 1.5rem;
334
+ font-weight: 700;
335
+ margin-bottom: 0.5rem;
336
+ line-height: 1.2;
337
+ }
338
+
339
+ .current-station-genre {
340
+ display: inline-block;
341
+ background: rgba(255,255,255,0.1);
342
+ padding: 4px 12px;
343
+ border-radius: 12px;
344
+ font-size: 0.8rem;
345
+ color: var(--text-muted);
346
+ margin-bottom: 2rem;
347
+ }
348
+
349
+ .controls {
350
+ display: flex;
351
+ align-items: center;
352
+ gap: 1.5rem;
353
+ width: 100%;
354
+ justify-content: center;
355
+ margin-bottom: 2rem;
356
+ }
357
+
358
+ .btn-control {
359
+ background: none;
360
+ border: none;
361
+ cursor: pointer;
362
+ color: var(--text-main);
363
+ transition: var(--transition);
364
+ display: flex;
365
+ align-items: center;
366
+ justify-content: center;
367
+ }
368
+
369
+ .btn-control:hover {
370
+ color: var(--primary-color);
371
+ transform: scale(1.1);
372
+ }
373
+
374
+ .btn-play {
375
+ width: 64px;
376
+ height: 64px;
377
+ border-radius: 50%;
378
+ background: var(--primary-color);
379
+ box-shadow: 0 0 20px rgba(99, 102, 241, 0.4);
380
+ }
381
+
382
+ .btn-play:hover {
383
+ background: var(--primary-hover);
384
+ color: white;
385
+ box-shadow: 0 0 30px rgba(99, 102, 241, 0.6);
386
+ }
387
+
388
+ .volume-container {
389
+ display: flex;
390
+ align-items: center;
391
+ gap: 10px;
392
+ width: 100%;
393
+ max-width: 200px;
394
+ }
395
+
396
+ input[type="range"] {
397
+ -webkit-appearance: none;
398
+ width: 100%;
399
+ height: 6px;
400
+ background: rgba(255,255,255,0.1);
401
+ border-radius: 3px;
402
+ cursor: pointer;
403
+ }
404
+
405
+ input[type="range"]::-webkit-slider-thumb {
406
+ -webkit-appearance: none;
407
+ width: 16px;
408
+ height: 16px;
409
+ background: var(--text-main);
410
+ border-radius: 50%;
411
+ transition: var(--transition);
412
+ }
413
+
414
+ input[type="range"]::-webkit-slider-thumb:hover {
415
+ background: var(--primary-color);
416
+ transform: scale(1.2);
417
+ }
418
+
419
+ /* --- Footer --- */
420
+ footer {
421
+ text-align: center;
422
+ padding: 2rem;
423
+ color: var(--text-muted);
424
+ font-size: 0.9rem;
425
+ border-top: 1px solid var(--surface-border);
426
+ margin-top: auto;
427
+ }
428
+
429
+ /* --- Loading State --- */
430
+ .loading-spinner {
431
+ width: 20px;
432
+ height: 20px;
433
+ border: 2px solid rgba(255,255,255,0.3);
434
+ border-top-color: var(--primary-color);
435
+ border-radius: 50%;
436
+ animation: spin 1s linear infinite;
437
+ display: none;
438
+ }
439
+
440
+ @keyframes spin {
441
+ to { transform: rotate(360deg); }
442
+ }
443
+
444
+ .status-message {
445
+ margin-top: 1rem;
446
+ font-size: 0.85rem;
447
+ min-height: 1.2em;
448
+ color: var(--accent-color);
449
+ }
450
+
451
+ </style>
452
+ </head>
453
+ <body>
454
+
455
+ <header>
456
+ <div class="logo">
457
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
458
+ <path d="M12 2c-5.5 0-10 4.5-10 10s4.5 10 10 10c1.8 0 3.5-.5 5-1.3M15 5a5 5 0 0 1 5 5v1M15 9a3 3 0 0 1 3 3v1"/>
459
+ <circle cx="12" cy="12" r="2"/>
460
+ </svg>
461
+ DE Radio
462
+ </div>
463
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">Built with anycoder</a>
464
+ </header>
465
+
466
+ <main>
467
+ <!-- Senderliste -->
468
+ <section class="station-list-container">
469
+ <div class="search-bar">
470
+ <svg class="search-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
471
+ <circle cx="11" cy="11" r="8"></circle>
472
+ <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
473
+ </svg>
474
+ <input type="text" id="searchInput" placeholder="Sender suchen...">
475
+ </div>
476
+
477
+ <div class="stations-grid" id="stationsGrid">
478
+ <!-- Stationen werden hier per JS eingefügt -->
479
+ </div>
480
+ </section>
481
+
482
+ <!-- Player -->
483
+ <section class="player-container">
484
+ <div class="player-visualizer" id="visualizer">
485
+ <div class="bar"></div>
486
+ <div class="bar"></div>
487
+ <div class="bar"></div>
488
+ <div class="bar"></div>
489
+ <div class="bar"></div>
490
+ <div class="bar"></div>
491
+ <div class="bar"></div>
492
+ <div class="bar"></div>
493
+ </div>
494
+
495
+ <div class="current-station-art" id="playerArt">
496
+ <svg width="50" height="50" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
497
+ <path d="M9 18V5l12-2v13"></path>
498
+ <circle cx="6" cy="18" r="3"></circle>
499
+ <circle cx="18" cy="16" r="3"></circle>
500
+ </svg>
501
+ </div>
502
+
503
+ <h2 class="current-station-name" id="playerTitle">Wähle einen Sender</h2>
504
+ <span class="current-station-genre" id="playerGenre">Bereit</span>
505
+
506
+ <div class="controls">
507
+ <button class="btn-control btn-play" id="playPauseBtn" aria-label="Play/Pause">
508
+ <svg id="iconPlay" width="32" height="32" viewBox="0 0 24 24" fill="currentColor">
509
+ <polygon points="5 3 19 12 5 21 5 3"></polygon>
510
+ </svg>
511
+ <svg id="iconPause" width="32" height="32" viewBox="0 0 24 24" fill="currentColor" style="display: none;">
512
+ <rect x="6" y="4" width="4" height="16"></rect>
513
+ <rect x="14" y="4" width="4" height="16"></rect>
514
+ </svg>
515
+ <div class="loading-spinner" id="loadingSpinner"></div>
516
+ </button>
517
+ </div>
518
+
519
+ <div class="volume-container">
520
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
521
+ <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon>
522
+ </svg>
523
+ <input type="range" id="volumeSlider" min="0" max="1" step="0.01" value="0.8">
524
+ </div>
525
+ <div class="status-message" id="statusMessage"></div>
526
+ </section>
527
+ </main>
528
+
529
+ <footer>
530
+ <p>&copy; 2023 DE Radio Stream Player. Hören Sie Ihre Lieblingssender.</p>
531
+ </footer>
532
+
533
+ <script>
534
+ // Senderdaten (Name, Genre, Stream URL, Farbe für das Logo-Platzhalter)
535
+ const stations = [
536
+ { name: "Deutschlandfunk", genre: "Nachrichten & Info", url: "http://st01.dlf.de/dlf/01/128/mp3/stream.mp3", color: "#e11d48" },
537
+ { name: "Deutschlandradio Kultur", genre: "Kultur", url: "http://st02.dlf.de/dlf/02/128/mp3/stream.mp3", color: "#d97706" },
538
+ { name: "DRadio Wissen", genre: "Wissen", url: "http://st03.dlf.de/dlf/03/128/mp3/stream.mp3", color: "#059669" },
539
+ { name: "BAYERN 3", genre: "Pop & Rock", url: "http://br-mp3-f-shz-cybr.sharp-stream.com/br3/live/mp3/128/stream.mp3", color: "#2563eb" },
540
+ { name: "Antenne Bayern", genre: "Pop", url: "https://stream.antenne.de/antenne-bayern", color: "#ea580c" },
541
+ { name: "1LIVE", genre: "Pop & Electro", url: "http://wdr-1live-live.icecast.wdr.de/wdr/1live/live/mp3/128/stream.mp3", color: "#7c3aed" },
542
+ { name: "WDR 2", genre: "Pop & Information", url: "http://wdr-wdr2-rheinland.icecast.wdr.de/wdr/wdr2/rheinland/mp3/128/stream.mp3", color: "#0891b2" },
543
+ { name: "SWR3", genre: "Pop", url: "https://swr-swr3-live.cast.addradio.de/swr/swr3/live/mp3/128/stream.mp3", color: "#db2777" },
544
+ { name: "NDR 2", genre: "Pop", url: "https://ndr-ndr2-niedersachsen.cast.addradio.de/ndr/ndr2/niedersachsen/mp3/128/stream.mp3", color: "#4338ca" },
545
+ { name: "NDR Info", genre: "Nachrichten", url: "https://ndr-ndrinfo-niedersachsen.cast.addradio.de/ndr/ndrinfo/niedersachsen/mp3/128/stream.mp3", color: "#334155" },
546
+ { name: "RBB 88.8", genre: "Berlin", url: "https://rbb-berlin-live.cast.addradio.de/rbb/berlin/live/mp3/128/stream.mp3", color: "#dc2626" },
547
+ { name: "RBB Radio Eins", genre: "Popkultur", url: "https://rbb-radioeins-live.cast.addradio.de/rbb/radioeins/live/mp3/128/stream.mp3", color: "#be185d" },
548
+ { name: "HR 3", genre: "Pop", url: "https://hr-hr3-live.cast.addradio.de/hr/hr3/live/mp3/128/stream.mp3", color: "#ca8a04" },
549
+ { name: "MDR JUMP", genre: "Dance & Pop", url: "https://mdr-jump-live.cast.addradio.de/mdr/jump/live/mp3/128/stream.mp3", color: "#65a30d" },
550
+ { name: "MDR KULTUR", genre: "Kultur", url: "https://mdr-kultur-live.cast.addradio.de/mdr/kultur/live/mp3/128/stream.mp3", color: "#475569" },
551
+ { name: "SR 1 Europawelle", genre: "Pop", url: "http://mp3sr1.sr-online.de/sr1_2.m3u", color: "#0369a1" }, // Fallback handling usually needed for m3u, using direct stream if possible
552
+ { name: "KISS FM", genre: "Dance & R&B", url: "https://stream.kissfm.de/kissfm/mp3-128", color: "#c026d3" },
553
+ { name: "JAM FM", genre: "Black & Pop", url: "https://stream.jam.fm/jamfm-live/mp3-128", color: "#f59e0b" },
554
+ { name: "Radio Bremen 4", genre: "Information", url: "https://rb-bremenvier-live.cast.addradio.de/rb/bremenvier/live/mp3/128/stream.mp3", color: "#083344" },
555
+ { name: "N-JOY", genre: "Pop", url: "https://ndr-njoy-live.cast.addradio.de/ndr/njoy/live/mp3/128/stream.mp3", color: "#16a34a" }
556
+ ];
557
+
558
+ // DOM Elemente
559
+ const stationsGrid = document.getElementById('stationsGrid');
560
+ const searchInput = document.getElementById('searchInput');
561
+ const audio = new Audio();
562
+
563
+ const playerTitle = document.getElementById('playerTitle');
564
+ const playerGenre = document.getElementById('playerGenre');
565
+ const playerArt = document.getElementById('playerArt');
566
+ const playPauseBtn = document.getElementById('playPauseBtn');
567
+ const iconPlay = document.getElementById('iconPlay');
568
+ const iconPause = document.getElementById('iconPause');
569
+ const loadingSpinner = document.getElementById('loadingSpinner');
570
+ const volumeSlider = document.getElementById('volumeSlider');
571
+ const visualizer = document.getElementById('visualizer');
572
+ const statusMessage = document.getElementById('statusMessage');
573
+
574
+ let currentStationIndex = -1;
575
+ let isPlaying = false;
576
+
577
+ // Initialisierung
578
+ function init() {
579
+ renderStations(stations);
580
+ audio.volume = volumeSlider.value;
581
+ }
582
+
583
+ // Stationen rendern
584
+ function renderStations(list) {
585
+ stationsGrid.innerHTML = '';
586
+ list.forEach((station, index) => {
587
+ const card = document.createElement('div');
588
+ card.className = 'station-card';
589
+ card.dataset.index = index; // Speicher den ursprünglichen Index
590
+ card.onclick = () => selectStation(station);
591
+
592
+ // Initialen für das Logo
593
+ const initials = station.name.substring(0, 2).toUpperCase();
594
+
595
+ card.innerHTML = `
596
+ <div class="station-logo" style="background-color: ${station.color}">
597
+ ${initials}
598
+ </div>
599
+ <div class="station-info">
600
+ <h3>${station.name}</h3>
601
+ <p>${station.genre}</p>
602
+ </div>
603
+ <div class="play-indicator">
604
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
605
+ <polygon points="5 3 19 12 5 21 5 3"></polygon>
606
+ </svg>
607
+ </div>
608
+ `;
609
+ stationsGrid.appendChild(card);
610
+ });
611
+ }
612
+
613
+ // Suche
614
+ searchInput.addEventListener('input', (e) => {
615
+ const term = e.target.value.toLowerCase();
616
+ const filtered = stations.filter(s =>
617
+ s.name.toLowerCase().includes(term) ||
618
+ s.genre.toLowerCase().includes(term)
619
+ );
620
+ renderStations(filtered);
621
+ });
622
+
623
+ // Station auswählen
624
+ function selectStation(station) {
625
+ // Wenn gleiche Station geklickt wird, nur Play/Pause toggeln
626
+ if (playerTitle.innerText === station.name) {
627
+ togglePlay();
628
+ return;
629
+ }
630
+
631
+ // UI Update
632
+ playerTitle.innerText = station.name;
633
+ playerGenre.innerText = station.genre;
634
+ playerArt.style.backgroundColor = station.color;
635
+ playerArt.innerHTML = `<span style="font-size: 3rem; font-weight: 800; color: rgba(255,255,255,0.8);">${station.name.substring(0,1).toUpperCase()}</span>`;
636
+
637
+ // Highlight in Liste
638
+ document.querySelectorAll('.station-card').forEach(card => {
639
+ card.classList.remove('active');
640
+ if (card.querySelector('h3').innerText === station.name) {
641
+ card.classList.add('active');
642
+ }
643
+ });
644
+
645
+ // Audio laden
646
+ statusMessage.innerText = "Verbinde...";
647
+ statusMessage.style.color = "var(--text-muted)";
648
+ audio.src = station.url;
649
+ audio.load();
650
+
651
+ playAudio();
652
+ }
653
+
654
+ // Play/Pause Logik
655
+ function togglePlay() {
656
+ if (!audio.src) return;
657
+ if (audio.paused) {
658
+ playAudio();
659
+ } else {
660
+ pauseAudio();
661
+ }
662
+ }
663
+
664
+ function playAudio() {
665
+ const playPromise = audio.play();
666
+
667
+ if (playPromise !== undefined) {
668
+ playPromise.then(_ => {
669
+ isPlaying = true;
670
+ updatePlayButton();
671
+ visualizer.classList.add('playing');
672
+ statusMessage.innerText = "";
673
+ })
674
+ .catch(error => {
675
+ console.error("Playback error:", error);
676
+ isPlaying = false;
677
+ updatePlayButton();
678
+ visualizer.classList.remove('playing');
679
+ statusMessage.innerText = "Fehler beim Laden des Streams.";
680
+ statusMessage.style.color = "#ef4444";
681
+ });
682
+ }
683
+ }
684
+
685
+ function pauseAudio() {
686
+ audio.pause();
687
+ isPlaying = false;
688
+ updatePlayButton();
689
+ visualizer.classList.remove('playing');
690
+ }
691
+
692
+ function updatePlayButton() {
693
+ if (isPlaying) {
694
+ iconPlay.style.display = 'none';
695
+ iconPause.style.display = 'block';
696
+ loadingSpinner.style.display = 'none';
697
+ } else {
698
+ iconPlay.style.display = 'block';
699
+ iconPause.style.display = 'none';
700
+ loadingSpinner.style.display = 'none';
701
+ }
702
+ }
703
+
704
+ playPauseBtn.addEventListener('click', togglePlay);
705
+
706
+ // Lautstärke
707
+ volumeSlider.addEventListener('input', (e) => {
708
+ audio.volume = e.target.value;
709
+ });
710
+
711
+ // Audio Events
712
+ audio.addEventListener('waiting', () => {
713
+ loadingSpinner.style.display = 'block';
714
+ iconPlay.style.display = 'none';
715
+ iconPause.style.display = 'none';
716
+ statusMessage.innerText = "Puffer...";
717
+ });
718
+
719
+ audio.addEventListener('playing', () => {
720
+ loadingSpinner.style.display = 'none';
721
+ updatePlayButton();
722
+ });
723
+
724
+ audio.addEventListener('error', (e) => {
725
+ console.error("Stream Error", e);
726
+ loadingSpinner.style.display = 'none';
727
+ iconPlay.style.display = 'block';
728
+ iconPause.style.display = 'none';
729
+ visualizer.classList.remove('playing');
730
+ statusMessage.innerText = "Stream nicht verfügbar.";
731
+ statusMessage.style.color = "#ef4444";
732
+ });
733
+
734
+ // App starten
735
+ init();
736
+
737
+ </script>
738
+ </body>
739
+ </html>