Multimedix commited on
Commit
9fe76fb
·
verified ·
1 Parent(s): 63c86ce

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +671 -752
index.html CHANGED
@@ -1,667 +1,691 @@
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>Deutsches Online Radio | Öffentlich-rechtliche Sender</title>
7
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
8
- <style>
9
- /* Modern CSS Reset & Custom Properties */
10
- :root {
11
- --primary-color: #0a0a1a;
12
- --secondary-color: #1a1a2e;
13
- --accent-color: #00d4ff;
14
- --text-primary: #ffffff;
15
- --text-secondary: #b0b0d0;
16
- --glass-bg: rgba(255, 255, 255, 0.08);
17
- --glass-border: rgba(255, 255, 255, 0.1);
18
- --glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
19
- --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
20
- --gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
21
- --error-color: #ff4757;
22
- --success-color: #2ed573;
23
- }
24
-
25
- * {
26
- margin: 0;
27
- padding: 0;
28
- box-sizing: border-box;
29
- }
30
-
31
- body {
32
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
33
- background: var(--primary-color);
34
- color: var(--text-primary);
35
- min-height: 100vh;
36
- display: flex;
37
- flex-direction: column;
38
- line-height: 1.6;
39
- overflow-x: hidden;
40
- }
41
-
42
- /* Header */
43
- header {
44
- background: var(--glass-bg);
45
- backdrop-filter: blur(12px);
46
- -webkit-backdrop-filter: blur(12px);
47
- border-bottom: 1px solid var(--glass-border);
48
- padding: 1.5rem 2rem;
49
- display: flex;
50
- justify-content: space-between;
51
- align-items: center;
52
- position: sticky;
53
- top: 0;
54
- z-index: 100;
55
- box-shadow: var(--glass-shadow);
56
- }
57
-
58
- header h1 {
59
- font-size: clamp(1.2rem, 4vw, 1.8rem);
60
- background: var(--gradient);
61
- -webkit-background-clip: text;
62
- -webkit-text-fill-color: transparent;
63
- background-clip: text;
64
- display: flex;
65
- align-items: center;
66
- gap: 0.5rem;
67
- }
68
-
69
- .anycoder-link {
70
- color: var(--accent-color);
71
- text-decoration: none;
72
- font-size: 0.9rem;
73
- padding: 0.5rem 1rem;
74
- border-radius: 20px;
75
- background: var(--glass-bg);
76
- border: 1px solid var(--glass-border);
77
- transition: var(--transition);
78
- }
79
-
80
- .anycoder-link:hover {
81
- background: rgba(0, 212, 255, 0.1);
82
- transform: translateY(-2px);
83
- }
84
-
85
- /* Main Content */
86
- main {
87
- flex: 1;
88
- padding: 2rem;
89
- max-width: 1400px;
90
- margin: 0 auto;
91
- width: 100%;
92
- }
93
-
94
- /* Search Section */
95
- .search-section {
96
- margin-bottom: 2rem;
97
- position: relative;
98
- }
99
-
100
- .search-input {
101
- width: 100%;
102
- max-width: 600px;
103
- padding: 1rem 1.5rem;
104
- font-size: 1rem;
105
- background: var(--glass-bg);
106
- border: 1px solid var(--glass-border);
107
- border-radius: 50px;
108
- color: var(--text-primary);
109
- outline: none;
110
- transition: var(--transition);
111
- backdrop-filter: blur(10px);
112
- -webkit-backdrop-filter: blur(10px);
113
- }
114
-
115
- .search-input::placeholder {
116
- color: var(--text-secondary);
117
- }
118
-
119
- .search-input:focus {
120
- border-color: var(--accent-color);
121
- box-shadow: 0 0 0 3px rgba(0, 212, 255, 0.2);
122
- }
123
-
124
- /* Filter Buttons */
125
- .filter-buttons {
126
- display: flex;
127
- gap: 0.5rem;
128
- margin-bottom: 2rem;
129
- flex-wrap: wrap;
130
- }
131
-
132
- .filter-btn {
133
- padding: 0.5rem 1.5rem;
134
- background: var(--glass-bg);
135
- border: 1px solid var(--glass-border);
136
- border-radius: 25px;
137
- color: var(--text-secondary);
138
- cursor: pointer;
139
- transition: var(--transition);
140
- font-size: 0.9rem;
141
- backdrop-filter: blur(10px);
142
- }
143
-
144
- .filter-btn:hover {
145
- background: rgba(255, 255, 255, 0.15);
146
- color: var(--text-primary);
147
- }
148
-
149
- .filter-btn.active {
150
- background: var(--accent-color);
151
- color: var(--primary-color);
152
- border-color: var(--accent-color);
153
- }
154
-
155
- /* Stations Grid */
156
- .stations-grid {
157
- display: grid;
158
- grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
159
- gap: 1.5rem;
160
- margin-bottom: 2rem;
161
- }
162
-
163
- /* Station Card */
164
- .station-card {
165
- background: var(--glass-bg);
166
- border: 1px solid var(--glass-border);
167
- border-radius: 20px;
168
- padding: 1.5rem;
169
- cursor: pointer;
170
- transition: var(--transition);
171
- position: relative;
172
- overflow: hidden;
173
- backdrop-filter: blur(10px);
174
- -webkit-backdrop-filter: blur(10px);
175
- }
176
-
177
- .station-card::before {
178
- content: '';
179
- position: absolute;
180
- top: 0;
181
- left: 0;
182
- right: 0;
183
- height: 4px;
184
- background: var(--gradient);
185
- transform: scaleX(0);
186
- transition: transform 0.3s ease;
187
- }
188
-
189
- .station-card:hover {
190
- transform: translateY(-5px);
191
- box-shadow: var(--glass-shadow);
192
- border-color: rgba(0, 212, 255, 0.3);
193
- }
194
-
195
- .station-card:hover::before {
196
- transform: scaleX(1);
197
- }
198
-
199
- .station-card.playing {
200
- border-color: var(--accent-color);
201
- background: rgba(0, 212, 255, 0.05);
202
- }
203
-
204
- .station-card.playing::before {
205
- transform: scaleX(1);
206
- background: var(--accent-color);
207
- }
208
-
209
- .station-header {
210
- display: flex;
211
- align-items: center;
212
- gap: 1rem;
213
- margin-bottom: 1rem;
214
- }
215
-
216
- .station-logo {
217
- width: 60px;
218
- height: 60px;
219
- border-radius: 12px;
220
- object-fit: cover;
221
- background: var(--secondary-color);
222
- border: 1px solid var(--glass-border);
223
- }
224
-
225
- .station-info h3 {
226
- font-size: 1.1rem;
227
- margin-bottom: 0.25rem;
228
- }
229
-
230
- .station-genre {
231
- font-size: 0.85rem;
232
- color: var(--text-secondary);
233
- background: rgba(255, 255, 255, 0.05);
234
- padding: 0.25rem 0.75rem;
235
- border-radius: 12px;
236
- display: inline-block;
237
- }
238
-
239
- .station-description {
240
- font-size: 0.9rem;
241
- color: var(--text-secondary);
242
- margin-bottom: 1rem;
243
- display: -webkit-box;
244
- -webkit-line-clamp: 2;
245
- -webkit-box-orient: vertical;
246
- overflow: hidden;
247
- }
248
-
249
- .station-actions {
250
- display: flex;
251
- justify-content: space-between;
252
- align-items: center;
253
- }
254
-
255
- .play-indicator {
256
- display: none;
257
- align-items: center;
258
- gap: 0.5rem;
259
- font-size: 0.8rem;
260
- color: var(--accent-color);
261
- }
262
-
263
- .station-card.playing .play-indicator {
264
- display: flex;
265
- }
266
-
267
- .equalizer {
268
- display: flex;
269
- gap: 2px;
270
- height: 16px;
271
- align-items: flex-end;
272
- }
273
-
274
- .equalizer-bar {
275
- width: 3px;
276
- background: var(--accent-color);
277
- border-radius: 2px;
278
- animation: equalize 1s ease-in-out infinite alternate;
279
- }
280
-
281
- .equalizer-bar:nth-child(1) { animation-delay: 0s; height: 40%; }
282
- .equalizer-bar:nth-child(2) { animation-delay: 0.1s; height: 60%; }
283
- .equalizer-bar:nth-child(3) { animation-delay: 0.2s; height: 80%; }
284
- .equalizer-bar:nth-child(4) { animation-delay: 0.3s; height: 50%; }
285
-
286
- @keyframes equalize {
287
- 0% { transform: scaleY(0.5); }
288
- 100% { transform: scaleY(1); }
289
- }
290
-
291
- .favorite-btn {
292
- background: none;
293
- border: none;
294
- color: var(--text-secondary);
295
- font-size: 1.2rem;
296
- cursor: pointer;
297
- transition: var(--transition);
298
- padding: 0.5rem;
299
- }
300
-
301
- .favorite-btn:hover {
302
- color: var(--accent-color);
303
- transform: scale(1.1);
304
- }
305
-
306
- .favorite-btn.active {
307
- color: #ff6b6b;
308
- }
309
-
310
- /* Player Bar */
311
- .player-bar {
312
- position: fixed;
313
- bottom: 0;
314
- left: 0;
315
- right: 0;
316
- background: var(--glass-bg);
317
- backdrop-filter: blur(20px);
318
- -webkit-backdrop-filter: blur(20px);
319
- border-top: 1px solid var(--glass-border);
320
- padding: 1rem 2rem;
321
- transform: translateY(100%);
322
- transition: var(--transition);
323
- z-index: 200;
324
- box-shadow: var(--glass-shadow);
325
- }
326
-
327
- .player-bar.active {
328
- transform: translateY(0);
329
- }
330
-
331
- .player-content {
332
- max-width: 1400px;
333
- margin: 0 auto;
334
- display: grid;
335
- grid-template-columns: 1fr auto 1fr;
336
- gap: 2rem;
337
- align-items: center;
338
- }
339
-
340
- .now-playing {
341
- display: flex;
342
- align-items: center;
343
- gap: 1rem;
344
- }
345
-
346
- .now-playing-logo {
347
- width: 50px;
348
- height: 50px;
349
- border-radius: 10px;
350
- background: var(--secondary-color);
351
- border: 1px solid var(--glass-border);
352
- }
353
-
354
- .now-playing-info h4 {
355
- font-size: 1rem;
356
- margin-bottom: 0.25rem;
357
- }
358
-
359
- .now-playing-info p {
360
- font-size: 0.85rem;
361
- color: var(--text-secondary);
362
- }
363
-
364
- .player-controls {
365
- display: flex;
366
- align-items: center;
367
- gap: 1rem;
368
- }
369
-
370
- .control-btn {
371
- background: var(--glass-bg);
372
- border: 1px solid var(--glass-border);
373
- color: var(--text-primary);
374
- width: 50px;
375
- height: 50px;
376
- border-radius: 50%;
377
- cursor: pointer;
378
- display: flex;
379
- align-items: center;
380
- justify-content: center;
381
- transition: var(--transition);
382
- font-size: 1.2rem;
383
- }
384
-
385
- .control-btn:hover {
386
- background: var(--accent-color);
387
- color: var(--primary-color);
388
- transform: scale(1.1);
389
- }
390
-
391
- .volume-control {
392
- display: flex;
393
- align-items: center;
394
- gap: 1rem;
395
- justify-self: end;
396
- }
397
-
398
- .volume-slider {
399
- width: 120px;
400
- height: 4px;
401
- -webkit-appearance: none;
402
- appearance: none;
403
- background: var(--glass-bg);
404
- border-radius: 2px;
405
- outline: none;
406
- cursor: pointer;
407
- }
408
-
409
- .volume-slider::-webkit-slider-thumb {
410
- -webkit-appearance: none;
411
- appearance: none;
412
- width: 16px;
413
- height: 16px;
414
- background: var(--accent-color);
415
- border-radius: 50%;
416
- cursor: pointer;
417
- transition: var(--transition);
418
- }
419
-
420
- .volume-slider::-webkit-slider-thumb:hover {
421
- transform: scale(1.2);
422
- }
423
-
424
- .volume-slider::-moz-range-thumb {
425
- width: 16px;
426
- height: 16px;
427
- background: var(--accent-color);
428
- border-radius: 50%;
429
- cursor: pointer;
430
- border: none;
431
- }
432
-
433
- /* Loading Spinner */
434
- .loading-spinner {
435
- display: none;
436
- position: fixed;
437
- top: 50%;
438
- left: 50%;
439
- transform: translate(-50%, -50%);
440
- z-index: 300;
441
- }
442
-
443
- .loading-spinner.active {
444
- display: block;
445
- }
446
-
447
- .spinner {
448
- width: 60px;
449
- height: 60px;
450
- border: 3px solid var(--glass-bg);
451
- border-top: 3px solid var(--accent-color);
452
- border-radius: 50%;
453
- animation: spin 1s linear infinite;
454
- }
455
-
456
- @keyframes spin {
457
- 0% { transform: rotate(0deg); }
458
- 100% { transform: rotate(360deg); }
459
- }
460
-
461
- /* Toast Notifications */
462
- .toast {
463
- position: fixed;
464
- top: 2rem;
465
- right: 2rem;
466
- background: var(--glass-bg);
467
- border: 1px solid var(--glass-border);
468
- border-radius: 12px;
469
- padding: 1rem 1.5rem;
470
- color: var(--text-primary);
471
- transform: translateX(400px);
472
- transition: var(--transition);
473
- z-index: 400;
474
- backdrop-filter: blur(10px);
475
- -webkit-backdrop-filter: blur(10px);
476
- display: flex;
477
- align-items: center;
478
- gap: 0.75rem;
479
- }
480
-
481
- .toast.show {
482
- transform: translateX(0);
483
- }
484
-
485
- .toast.error {
486
- border-color: var(--error-color);
487
- }
488
-
489
- .toast.success {
490
- border-color: var(--success-color);
491
- }
492
-
493
- /* Empty State */
494
- .empty-state {
495
- text-align: center;
496
- padding: 4rem 2rem;
497
- color: var(--text-secondary);
498
- display: none;
499
- }
500
-
501
- .empty-state.show {
502
- display: block;
503
- }
504
-
505
- .empty-state i {
506
- font-size: 4rem;
507
- margin-bottom: 1rem;
508
- opacity: 0.5;
509
- }
510
-
511
- /* Responsive Design */
512
- @media (max-width: 768px) {
513
- header {
514
- padding: 1rem;
515
- }
516
-
517
- main {
518
- padding: 1rem;
519
- }
520
-
521
- .player-content {
522
- grid-template-columns: 1fr;
523
- gap: 1rem;
524
- text-align: center;
525
- }
526
-
527
- .volume-control {
528
- justify-self: center;
529
- }
530
-
531
- .stations-grid {
532
- grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
533
- gap: 1rem;
534
- }
535
-
536
- .search-input {
537
- font-size: 0.9rem;
538
- padding: 0.75rem 1.25rem;
539
- }
540
- }
541
-
542
- @media (max-width: 480px) {
543
- .stations-grid {
544
- grid-template-columns: 1fr;
545
- }
546
-
547
- .player-bar {
548
- padding: 1rem;
549
- }
550
-
551
- .control-btn {
552
- width: 40px;
553
- height: 40px;
554
- font-size: 1rem;
555
- }
556
- }
557
-
558
- /* Accessibility */
559
- @media (prefers-reduced-motion: reduce) {
560
- * {
561
- animation-duration: 0.01ms !important;
562
- animation-iteration-count: 1 !important;
563
- transition-duration: 0.01ms !important;
564
- }
565
- }
566
-
567
- /* Focus styles */
568
- button:focus,
569
- input:focus {
570
- outline: 2px solid var(--accent-color);
571
- outline-offset: 2px;
572
- }
573
-
574
- /* Scrollbar styling */
575
- ::-webkit-scrollbar {
576
- width: 8px;
577
- }
578
-
579
- ::-webkit-scrollbar-track {
580
- background: var(--primary-color);
581
- }
582
-
583
- ::-webkit-scrollbar-thumb {
584
- background: var(--accent-color);
585
- border-radius: 4px;
586
- }
587
-
588
- ::-webkit-scrollbar-thumb:hover {
589
- background: #00a8cc;
590
- }
591
- </style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
592
  </head>
 
593
  <body>
594
- <header>
595
- <h1><i class="fas fa-broadcast-tower"></i> Deutsches Online Radio</h1>
596
- <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">
597
- Built with anycoder
598
- </a>
599
- </header>
600
-
601
- <main>
602
- <section class="search-section">
603
- <input type="text" class="search-input" id="searchInput" placeholder="🔍 Sender nach Name, Genre oder Region suchen...">
604
- </section>
605
-
606
- <section class="filter-buttons" id="filterButtons">
607
- <button class="filter-btn active" data-filter="all">Alle</button>
608
- <button class="filter-btn" data-filter="national">National</button>
609
- <button class="filter-btn" data-filter="regional">Regional</button>
610
- <button class="filter-btn" data-filter="news">News</button>
611
- <button class="filter-btn" data-filter="music">Musik</button>
612
- <button class="filter-btn" data-filter="kultur">Kultur</button>
613
- </section>
614
-
615
- <section class="stations-grid" id="stationsGrid"></section>
616
-
617
- <section class="empty-state" id="emptyState">
618
- <i class="fas fa-search"></i>
619
- <h3>Keine Sender gefunden</h3>
620
- <p>Probiere es mit einem anderen Suchbegriff oder Filter.</p>
621
- </section>
622
- </main>
623
-
624
- <footer class="player-bar" id="playerBar">
625
- <div class="player-content">
626
- <div class="now-playing" id="nowPlaying">
627
- <img src="" alt="" class="now-playing-logo" id="nowPlayingLogo">
628
- <div class="now-playing-info">
629
- <h4 id="nowPlayingName">Kein Sender ausgewählt</h4>
630
- <p id="nowPlayingGenre">Wähle einen Sender zum Abspielen</p>
631
- </div>
632
- </div>
633
-
634
- <div class="player-controls">
635
- <button class="control-btn" id="prevBtn" title="Vorheriger Sender">
636
  <i class="fas fa-step-backward"></i>
637
  </button>
638
- <button class="control-btn" id="playPauseBtn" title="Play/Pause">
639
  <i class="fas fa-play" id="playPauseIcon"></i>
640
  </button>
641
- <button class="control-btn" id="nextBtn" title="Nächster Sender">
642
  <i class="fas fa-step-forward"></i>
643
  </button>
644
- </div>
645
 
646
- <div class="volume-control">
647
- <button class="control-btn" id="muteBtn" title="Stummschalten">
648
  <i class="fas fa-volume-up" id="volumeIcon"></i>
649
  </button>
650
- <input type="range" class="volume-slider" id="volumeSlider" min="0" max="100" value="70">
651
- </div>
652
- </div>
653
- </footer>
654
-
655
- <div class="loading-spinner" id="loadingSpinner">
656
- <div class="spinner"></div>
657
  </div>
 
658
 
659
- <div class="toast" id="toast"></div>
 
 
660
 
661
- <audio id="audioPlayer"></audio>
662
 
663
- <script>
664
- // Station Data - Official Public Broadcasters
 
 
665
  const stations = [
666
  {
667
  id: 'dlf',
@@ -887,109 +911,4 @@
887
  <div class="station-card ${currentStation?.id === station.id ? 'playing' : ''}"
888
  data-id="${station.id}">
889
  <div class="station-header">
890
- <img src="${station.logo}" alt="${station.name}" class="station-logo" loading="lazy">
891
- <div class="station-info">
892
- <h3>${station.name}</h3>
893
- <span class="station-genre">${getGenreLabel(station.genre)}</span>
894
- </div>
895
- </div>
896
- <p class="station-description">${station.description}</p>
897
- <div class="station-actions">
898
- <div class="play-indicator">
899
- <span>JETZT LIVE</span>
900
- <div class="equalizer">
901
- <div class="equalizer-bar"></div>
902
- <div class="equalizer-bar"></div>
903
- <div class="equalizer-bar"></div>
904
- <div class="equalizer-bar"></div>
905
- </div>
906
- </div>
907
- <button class="favorite-btn ${favorites.includes(station.id) ? 'active' : ''}"
908
- data-id="${station.id}"
909
- title="Favorit">
910
- <i class="${favorites.includes(station.id) ? 'fas' : 'far'} fa-heart"></i>
911
- </button>
912
- </div>
913
- </div>
914
- `).join('');
915
- }
916
- }
917
-
918
- // Get Filtered Stations
919
- function getFilteredStations() {
920
- return stations.filter(station => {
921
- const matchesFilter = currentFilter === 'all' || station.category === currentFilter || station.genre === currentFilter;
922
- const matchesSearch = searchTerm === '' ||
923
- station.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
924
- station.description.toLowerCase().includes(searchTerm.toLowerCase()) ||
925
- getGenreLabel(station.genre).toLowerCase().includes(searchTerm.toLowerCase());
926
- return matchesFilter && matchesSearch;
927
- });
928
- }
929
-
930
- // Get Genre Label
931
- function getGenreLabel(genre) {
932
- const labels = {
933
- news: 'Nachrichten',
934
- music: 'Musik',
935
- kultur: 'Kultur',
936
- regional: 'Regional'
937
- };
938
- return labels[genre] || genre;
939
- }
940
-
941
- // Setup Event Listeners
942
- function setupEventListeners() {
943
- // Search
944
- searchInput.addEventListener('input', (e) => {
945
- searchTerm = e.target.value;
946
- renderStations();
947
- });
948
-
949
- // Filter Buttons
950
- filterButtons.forEach(btn => {
951
- btn.addEventListener('click', () => {
952
- filterButtons.forEach(b => b.classList.remove('active'));
953
- btn.classList.add('active');
954
- currentFilter = btn.dataset.filter;
955
- renderStations();
956
- });
957
- });
958
-
959
- // Station Cards & Favorites
960
- stationsGrid.addEventListener('click', (e) => {
961
- const stationCard = e.target.closest('.station-card');
962
- const favoriteBtn = e.target.closest('.favorite-btn');
963
-
964
- if (favoriteBtn) {
965
- e.stopPropagation();
966
- toggleFavorite(favoriteBtn.dataset.id);
967
- } else if (stationCard) {
968
- playStation(stationCard.dataset.id);
969
- }
970
- });
971
-
972
- // Player Controls
973
- playPauseBtn.addEventListener('click', togglePlayPause);
974
- prevBtn.addEventListener('click', playPrevious);
975
- nextBtn.addEventListener('click', playNext);
976
- muteBtn.addEventListener('click', toggleMute);
977
- volumeSlider.addEventListener('input', (e) => setVolume(e.target.value));
978
-
979
- // Audio Player Events
980
- audioPlayer.addEventListener('play', () => {
981
- isPlaying = true;
982
- updatePlayPauseIcon();
983
- });
984
-
985
- audioPlayer.addEventListener('pause', () => {
986
- isPlaying = false;
987
- updatePlayPauseIcon();
988
- });
989
-
990
- audioPlayer.addEventListener('error', (e) => {
991
- showToast('Fehler beim Laden des Streams', 'error');
992
- loadingSpinner.classList.remove('active');
993
- });
994
-
995
- audioPlayer.addEventListener('load
 
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>Deutsches Online Radio | Öffentlich-rechtliche Sender</title>
7
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
8
+ <style>
9
+ /* Modern CSS Reset & Custom Properties */
10
+ :root {
11
+ --primary-color: #0a0a1a;
12
+ --secondary-color: #1a1a2e;
13
+ --accent-color: #00d4ff;
14
+ --text-primary: #ffffff;
15
+ --text-secondary: #b0b0d0;
16
+ --glass-bg: rgba(255, 255, 255, 0.08);
17
+ --glass-border: rgba(255, 255, 255, 0.1);
18
+ --glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
19
+ --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
20
+ --gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
21
+ --error-color: #ff4757;
22
+ --success-color: #2ed573;
23
+ }
24
+
25
+ * {
26
+ margin: 0;
27
+ padding: 0;
28
+ box-sizing: border-box;
29
+ }
30
+
31
+ body {
32
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
33
+ background: var(--primary-color);
34
+ color: var(--text-primary);
35
+ min-height: 100vh;
36
+ display: flex;
37
+ flex-direction: column;
38
+ line-height: 1.6;
39
+ overflow-x: hidden;
40
+ }
41
+
42
+ /* Header */
43
+ header {
44
+ background: var(--glass-bg);
45
+ backdrop-filter: blur(12px);
46
+ -webkit-backdrop-filter: blur(12px);
47
+ border-bottom: 1px solid var(--glass-border);
48
+ padding: 1.5rem 2rem;
49
+ display: flex;
50
+ justify-content: space-between;
51
+ align-items: center;
52
+ position: sticky;
53
+ top: 0;
54
+ z-index: 100;
55
+ box-shadow: var(--glass-shadow);
56
+ }
57
+
58
+ header h1 {
59
+ font-size: clamp(1.2rem, 4vw, 1.8rem);
60
+ background: var(--gradient);
61
+ -webkit-background-clip: text;
62
+ -webkit-text-fill-color: transparent;
63
+ background-clip: text;
64
+ display: flex;
65
+ align-items: center;
66
+ gap: 0.5rem;
67
+ }
68
+
69
+ .anycoder-link {
70
+ color: var(--accent-color);
71
+ text-decoration: none;
72
+ font-size: 0.9rem;
73
+ padding: 0.5rem 1rem;
74
+ border-radius: 20px;
75
+ background: var(--glass-bg);
76
+ border: 1px solid var(--glass-border);
77
+ transition: var(--transition);
78
+ }
79
+
80
+ .anycoder-link:hover {
81
+ background: rgba(0, 212, 255, 0.1);
82
+ transform: translateY(-2px);
83
+ }
84
+
85
+ /* Main Content */
86
+ main {
87
+ flex: 1;
88
+ padding: 2rem;
89
+ max-width: 1400px;
90
+ margin: 0 auto;
91
+ width: 100%;
92
+ }
93
+
94
+ /* Search Section */
95
+ .search-section {
96
+ margin-bottom: 2rem;
97
+ position: relative;
98
+ }
99
+
100
+ .search-input {
101
+ width: 100%;
102
+ max-width: 600px;
103
+ padding: 1rem 1.5rem;
104
+ font-size: 1rem;
105
+ background: var(--glass-bg);
106
+ border: 1px solid var(--glass-border);
107
+ border-radius: 50px;
108
+ color: var(--text-primary);
109
+ outline: none;
110
+ transition: var(--transition);
111
+ backdrop-filter: blur(10px);
112
+ -webkit-backdrop-filter: blur(10px);
113
+ }
114
+
115
+ .search-input::placeholder {
116
+ color: var(--text-secondary);
117
+ }
118
+
119
+ .search-input:focus {
120
+ border-color: var(--accent-color);
121
+ box-shadow: 0 0 0 3px rgba(0, 212, 255, 0.2);
122
+ }
123
+
124
+ /* Filter Buttons */
125
+ .filter-buttons {
126
+ display: flex;
127
+ gap: 0.5rem;
128
+ margin-bottom: 2rem;
129
+ flex-wrap: wrap;
130
+ }
131
+
132
+ .filter-btn {
133
+ padding: 0.5rem 1.5rem;
134
+ background: var(--glass-bg);
135
+ border: 1px solid var(--glass-border);
136
+ border-radius: 25px;
137
+ color: var(--text-secondary);
138
+ cursor: pointer;
139
+ transition: var(--transition);
140
+ font-size: 0.9rem;
141
+ backdrop-filter: blur(10px);
142
+ }
143
+
144
+ .filter-btn:hover {
145
+ background: rgba(255, 255, 255, 0.15);
146
+ color: var(--text-primary);
147
+ }
148
+
149
+ .filter-btn.active {
150
+ background: var(--accent-color);
151
+ color: var(--primary-color);
152
+ border-color: var(--accent-color);
153
+ }
154
+
155
+ /* Stations Grid */
156
+ .stations-grid {
157
+ display: grid;
158
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
159
+ gap: 1.5rem;
160
+ margin-bottom: 2rem;
161
+ }
162
+
163
+ /* Station Card */
164
+ .station-card {
165
+ background: var(--glass-bg);
166
+ border: 1px solid var(--glass-border);
167
+ border-radius: 20px;
168
+ padding: 1.5rem;
169
+ cursor: pointer;
170
+ transition: var(--transition);
171
+ position: relative;
172
+ overflow: hidden;
173
+ backdrop-filter: blur(10px);
174
+ -webkit-backdrop-filter: blur(10px);
175
+ }
176
+
177
+ .station-card::before {
178
+ content: '';
179
+ position: absolute;
180
+ top: 0;
181
+ left: 0;
182
+ right: 0;
183
+ height: 4px;
184
+ background: var(--gradient);
185
+ transform: scaleX(0);
186
+ transition: transform 0.3s ease;
187
+ }
188
+
189
+ .station-card:hover {
190
+ transform: translateY(-5px);
191
+ box-shadow: var(--glass-shadow);
192
+ border-color: rgba(0, 212, 255, 0.3);
193
+ }
194
+
195
+ .station-card:hover::before {
196
+ transform: scaleX(1);
197
+ }
198
+
199
+ .station-card.playing {
200
+ border-color: var(--accent-color);
201
+ background: rgba(0, 212, 255, 0.05);
202
+ }
203
+
204
+ .station-card.playing::before {
205
+ transform: scaleX(1);
206
+ background: var(--accent-color);
207
+ }
208
+
209
+ .station-header {
210
+ display: flex;
211
+ align-items: center;
212
+ gap: 1rem;
213
+ margin-bottom: 1rem;
214
+ }
215
+
216
+ .station-logo {
217
+ width: 60px;
218
+ height: 60px;
219
+ border-radius: 12px;
220
+ object-fit: cover;
221
+ background: var(--secondary-color);
222
+ border: 1px solid var(--glass-border);
223
+ }
224
+
225
+ .station-info h3 {
226
+ font-size: 1.1rem;
227
+ margin-bottom: 0.25rem;
228
+ }
229
+
230
+ .station-genre {
231
+ font-size: 0.85rem;
232
+ color: var(--text-secondary);
233
+ background: rgba(255, 255, 255, 0.05);
234
+ padding: 0.25rem 0.75rem;
235
+ border-radius: 12px;
236
+ display: inline-block;
237
+ }
238
+
239
+ .station-description {
240
+ font-size: 0.9rem;
241
+ color: var(--text-secondary);
242
+ margin-bottom: 1rem;
243
+ display: -webkit-box;
244
+ -webkit-line-clamp: 2;
245
+ -webkit-box-orient: vertical;
246
+ overflow: hidden;
247
+ }
248
+
249
+ .station-actions {
250
+ display: flex;
251
+ justify-content: space-between;
252
+ align-items: center;
253
+ }
254
+
255
+ .play-indicator {
256
+ display: none;
257
+ align-items: center;
258
+ gap: 0.5rem;
259
+ font-size: 0.8rem;
260
+ color: var(--accent-color);
261
+ }
262
+
263
+ .station-card.playing .play-indicator {
264
+ display: flex;
265
+ }
266
+
267
+ .equalizer {
268
+ display: flex;
269
+ gap: 2px;
270
+ height: 16px;
271
+ align-items: flex-end;
272
+ }
273
+
274
+ .equalizer-bar {
275
+ width: 3px;
276
+ background: var(--accent-color);
277
+ border-radius: 2px;
278
+ animation: equalize 1s ease-in-out infinite alternate;
279
+ }
280
+
281
+ .equalizer-bar:nth-child(1) {
282
+ animation-delay: 0s;
283
+ height: 40%;
284
+ }
285
+
286
+ .equalizer-bar:nth-child(2) {
287
+ animation-delay: 0.1s;
288
+ height: 60%;
289
+ }
290
+
291
+ .equalizer-bar:nth-child(3) {
292
+ animation-delay: 0.2s;
293
+ height: 80%;
294
+ }
295
+
296
+ .equalizer-bar:nth-child(4) {
297
+ animation-delay: 0.3s;
298
+ height: 50%;
299
+ }
300
+
301
+ @keyframes equalize {
302
+ 0% {
303
+ transform: scaleY(0.5);
304
+ }
305
+ 100% {
306
+ transform: scaleY(1);
307
+ }
308
+ }
309
+
310
+ .favorite-btn {
311
+ background: none;
312
+ border: none;
313
+ color: var(--text-secondary);
314
+ font-size: 1.2rem;
315
+ cursor: pointer;
316
+ transition: var(--transition);
317
+ padding: 0.5rem;
318
+ }
319
+
320
+ .favorite-btn:hover {
321
+ color: var(--accent-color);
322
+ transform: scale(1.1);
323
+ }
324
+
325
+ .favorite-btn.active {
326
+ color: #ff6b6b;
327
+ }
328
+
329
+ /* Player Bar */
330
+ .player-bar {
331
+ position: fixed;
332
+ bottom: 0;
333
+ left: 0;
334
+ right: 0;
335
+ background: var(--glass-bg);
336
+ backdrop-filter: blur(20px);
337
+ -webkit-backdrop-filter: blur(20px);
338
+ border-top: 1px solid var(--glass-border);
339
+ padding: 1rem 2rem;
340
+ transform: translateY(100%);
341
+ transition: var(--transition);
342
+ z-index: 200;
343
+ box-shadow: var(--glass-shadow);
344
+ }
345
+
346
+ .player-bar.active {
347
+ transform: translateY(0);
348
+ }
349
+
350
+ .player-content {
351
+ max-width: 1400px;
352
+ margin: 0 auto;
353
+ display: grid;
354
+ grid-template-columns: 1fr auto 1fr;
355
+ gap: 2rem;
356
+ align-items: center;
357
+ }
358
+
359
+ .now-playing {
360
+ display: flex;
361
+ align-items: center;
362
+ gap: 1rem;
363
+ }
364
+
365
+ .now-playing-logo {
366
+ width: 50px;
367
+ height: 50px;
368
+ border-radius: 10px;
369
+ background: var(--secondary-color);
370
+ border: 1px solid var(--glass-border);
371
+ }
372
+
373
+ .now-playing-info h4 {
374
+ font-size: 1rem;
375
+ margin-bottom: 0.25rem;
376
+ }
377
+
378
+ .now-playing-info p {
379
+ font-size: 0.85rem;
380
+ color: var(--text-secondary);
381
+ }
382
+
383
+ .player-controls {
384
+ display: flex;
385
+ align-items: center;
386
+ gap: 1rem;
387
+ }
388
+
389
+ .control-btn {
390
+ background: var(--glass-bg);
391
+ border: 1px solid var(--glass-border);
392
+ color: var(--text-primary);
393
+ width: 50px;
394
+ height: 50px;
395
+ border-radius: 50%;
396
+ cursor: pointer;
397
+ display: flex;
398
+ align-items: center;
399
+ justify-content: center;
400
+ transition: var(--transition);
401
+ font-size: 1.2rem;
402
+ }
403
+
404
+ .control-btn:hover {
405
+ background: var(--accent-color);
406
+ color: var(--primary-color);
407
+ transform: scale(1.1);
408
+ }
409
+
410
+ .volume-control {
411
+ display: flex;
412
+ align-items: center;
413
+ gap: 1rem;
414
+ justify-self: end;
415
+ }
416
+
417
+ .volume-slider {
418
+ width: 120px;
419
+ height: 4px;
420
+ -webkit-appearance: none;
421
+ appearance: none;
422
+ background: var(--glass-bg);
423
+ border-radius: 2px;
424
+ outline: none;
425
+ cursor: pointer;
426
+ }
427
+
428
+ .volume-slider::-webkit-slider-thumb {
429
+ -webkit-appearance: none;
430
+ appearance: none;
431
+ width: 16px;
432
+ height: 16px;
433
+ background: var(--accent-color);
434
+ border-radius: 50%;
435
+ cursor: pointer;
436
+ transition: var(--transition);
437
+ }
438
+
439
+ .volume-slider::-webkit-slider-thumb:hover {
440
+ transform: scale(1.2);
441
+ }
442
+
443
+ .volume-slider::-moz-range-thumb {
444
+ width: 16px;
445
+ height: 16px;
446
+ background: var(--accent-color);
447
+ border-radius: 50%;
448
+ cursor: pointer;
449
+ border: none;
450
+ }
451
+
452
+ /* Loading Spinner */
453
+ .loading-spinner {
454
+ display: none;
455
+ position: fixed;
456
+ top: 50%;
457
+ left: 50%;
458
+ transform: translate(-50%, -50%);
459
+ z-index: 300;
460
+ }
461
+
462
+ .loading-spinner.active {
463
+ display: block;
464
+ }
465
+
466
+ .spinner {
467
+ width: 60px;
468
+ height: 60px;
469
+ border: 3px solid var(--glass-bg);
470
+ border-top: 3px solid var(--accent-color);
471
+ border-radius: 50%;
472
+ animation: spin 1s linear infinite;
473
+ }
474
+
475
+ @keyframes spin {
476
+ 0% {
477
+ transform: rotate(0deg);
478
+ }
479
+ 100% {
480
+ transform: rotate(360deg);
481
+ }
482
+ }
483
+
484
+ /* Toast Notifications */
485
+ .toast {
486
+ position: fixed;
487
+ top: 2rem;
488
+ right: 2rem;
489
+ background: var(--glass-bg);
490
+ border: 1px solid var(--glass-border);
491
+ border-radius: 12px;
492
+ padding: 1rem 1.5rem;
493
+ color: var(--text-primary);
494
+ transform: translateX(400px);
495
+ transition: var(--transition);
496
+ z-index: 400;
497
+ backdrop-filter: blur(10px);
498
+ -webkit-backdrop-filter: blur(10px);
499
+ display: flex;
500
+ align-items: center;
501
+ gap: 0.75rem;
502
+ }
503
+
504
+ .toast.show {
505
+ transform: translateX(0);
506
+ }
507
+
508
+ .toast.error {
509
+ border-color: var(--error-color);
510
+ }
511
+
512
+ .toast.success {
513
+ border-color: var(--success-color);
514
+ }
515
+
516
+ /* Empty State */
517
+ .empty-state {
518
+ text-align: center;
519
+ padding: 4rem 2rem;
520
+ color: var(--text-secondary);
521
+ display: none;
522
+ }
523
+
524
+ .empty-state.show {
525
+ display: block;
526
+ }
527
+
528
+ .empty-state i {
529
+ font-size: 4rem;
530
+ margin-bottom: 1rem;
531
+ opacity: 0.5;
532
+ }
533
+
534
+ /* Responsive Design */
535
+ @media (max-width: 768px) {
536
+ header {
537
+ padding: 1rem;
538
+ }
539
+
540
+ main {
541
+ padding: 1rem;
542
+ }
543
+
544
+ .player-content {
545
+ grid-template-columns: 1fr;
546
+ gap: 1rem;
547
+ text-align: center;
548
+ }
549
+
550
+ .volume-control {
551
+ justify-self: center;
552
+ }
553
+
554
+ .stations-grid {
555
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
556
+ gap: 1rem;
557
+ }
558
+
559
+ .search-input {
560
+ font-size: 0.9rem;
561
+ padding: 0.75rem 1.25rem;
562
+ }
563
+ }
564
+
565
+ @media (max-width: 480px) {
566
+ .stations-grid {
567
+ grid-template-columns: 1fr;
568
+ }
569
+
570
+ .player-bar {
571
+ padding: 1rem;
572
+ }
573
+
574
+ .control-btn {
575
+ width: 40px;
576
+ height: 40px;
577
+ font-size: 1rem;
578
+ }
579
+ }
580
+
581
+ /* Accessibility */
582
+ @media (prefers-reduced-motion: reduce) {
583
+ * {
584
+ animation-duration: 0.01ms !important;
585
+ animation-iteration-count: 1 !important;
586
+ transition-duration: 0.01ms !important;
587
+ }
588
+ }
589
+
590
+ /* Focus styles */
591
+ button:focus,
592
+ input:focus {
593
+ outline: 2px solid var(--accent-color);
594
+ outline-offset: 2px;
595
+ }
596
+
597
+ /* Scrollbar styling */
598
+ ::-webkit-scrollbar {
599
+ width: 8px;
600
+ }
601
+
602
+ ::-webkit-scrollbar-track {
603
+ background: var(--primary-color);
604
+ }
605
+
606
+ ::-webkit-scrollbar-thumb {
607
+ background: var(--accent-color);
608
+ border-radius: 4px;
609
+ }
610
+
611
+ ::-webkit-scrollbar-thumb:hover {
612
+ background: #00a8cc;
613
+ }
614
+ </style>
615
  </head>
616
+
617
  <body>
618
+ <header>
619
+ <h1><i class="fas fa-broadcast-tower"></i> Deutsches Online Radio</h1>
620
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">
621
+ Built with anycoder
622
+ </a>
623
+ </header>
624
+
625
+ <main>
626
+ <section class="search-section">
627
+ <input type="text" class="search-input" id="searchInput" placeholder="🔍 Sender nach Name, Genre oder Region suchen...">
628
+ </section>
629
+
630
+ <section class="filter-buttons" id="filterButtons">
631
+ <button class="filter-btn active" data-filter="all">Alle</button>
632
+ <button class="filter-btn" data-filter="national">National</button>
633
+ <button class="filter-btn" data-filter="regional">Regional</button>
634
+ <button class="filter-btn" data-filter="news">News</button>
635
+ <button class="filter-btn" data-filter="music">Musik</button>
636
+ <button class="filter-btn" data-filter="kultur">Kultur</button>
637
+ </section>
638
+
639
+ <section class="stations-grid" id="stationsGrid"></section>
640
+
641
+ <section class="empty-state" id="emptyState">
642
+ <i class="fas fa-search"></i>
643
+ <h3>Keine Sender gefunden</h3>
644
+ <p>Probiere es mit einem anderen Suchbegriff oder Filter.</p>
645
+ </section>
646
+ </main>
647
+
648
+ <footer class="player-bar" id="playerBar">
649
+ <div class="player-content">
650
+ <div class="now-playing" id="nowPlaying">
651
+ <img src="" alt="" class="now-playing-logo" id="nowPlayingLogo">
652
+ <div class="now-playing-info">
653
+ <h4 id="nowPlayingName">Kein Sender ausgewählt</h4>
654
+ <p id="nowPlayingGenre">Wähle einen Sender zum Abspielen</p>
655
+ </div>
656
+ </div>
657
+
658
+ <div class="player-controls">
659
+ <button class="control-btn" id="prevBtn" title="Vorheriger Sender">
660
  <i class="fas fa-step-backward"></i>
661
  </button>
662
+ <button class="control-btn" id="playPauseBtn" title="Play/Pause">
663
  <i class="fas fa-play" id="playPauseIcon"></i>
664
  </button>
665
+ <button class="control-btn" id="nextBtn" title="Nächster Sender">
666
  <i class="fas fa-step-forward"></i>
667
  </button>
668
+ </div>
669
 
670
+ <div class="volume-control">
671
+ <button class="control-btn" id="muteBtn" title="Stummschalten">
672
  <i class="fas fa-volume-up" id="volumeIcon"></i>
673
  </button>
674
+ <input type="range" class="volume-slider" id="volumeSlider" min="0" max="100" value="70">
675
+ </div>
 
 
 
 
 
676
  </div>
677
+ </footer>
678
 
679
+ <div class="loading-spinner" id="loadingSpinner">
680
+ <div class="spinner"></div>
681
+ </div>
682
 
683
+ <div class="toast" id="toast"></div>
684
 
685
+ <audio id="audioPlayer"></audio>
686
+
687
+ <script>
688
+ // Station Data - Official Public Broadcasters
689
  const stations = [
690
  {
691
  id: 'dlf',
 
911
  <div class="station-card ${currentStation?.id === station.id ? 'playing' : ''}"
912
  data-id="${station.id}">
913
  <div class="station-header">
914
+ <img src="${station.logo}" alt="${station.name}" class="station-logo" loading="lazy" onerror="this.src='data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjAiIGhlaWdodD0iNjAiIHZpZXdCb3g9IjAgMCA2MCA2MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjYwIiBoZWlnaHQ9IjYwIiByeD0iMTIiIGZpbGw9IiMxYTFhMmUiLz4KPHBhdGggZD0iTTMwIDIwQzI2LjY4NjMgMjAgMjQgMjIuNjg2MyAyNCAyNkMyNCAyOS4zMTM3IDI2LjY4NjMgMzIgMzAgMzJDMzMuMzEzNyAzMiAzNiAyOS4zMTM3IDM2IDI2QzM2IDIyLjY4NjMgMzMuMzEz