Multimedix commited on
Commit
bccc2d4
·
verified ·
1 Parent(s): 5da5f18

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +555 -189
index.html CHANGED
@@ -1,9 +1,10 @@
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>Deutsche Radio Stream - Live Radio aus Deutschland (Demo)</title>
7
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
8
  <style>
9
  * {
@@ -22,6 +23,8 @@
22
  --text-secondary: #94a3b8;
23
  --glass-bg: rgba(30, 41, 59, 0.5);
24
  --glass-border: rgba(148, 163, 184, 0.2);
 
 
25
  }
26
 
27
  body {
@@ -56,8 +59,12 @@
56
  }
57
 
58
  @keyframes rotate {
59
- 0% { transform: rotate(0deg); }
60
- 100% { transform: rotate(360deg); }
 
 
 
 
61
  }
62
 
63
  /* Header */
@@ -99,8 +106,12 @@
99
  }
100
 
101
  @keyframes pulse {
102
- 0%, 100% { transform: scale(1); }
103
- 50% { transform: scale(1.1); }
 
 
 
 
104
  }
105
 
106
  .attribution {
@@ -118,19 +129,43 @@
118
  color: var(--accent-color);
119
  }
120
 
121
- .demo-badge {
122
- background: linear-gradient(135deg, var(--accent-color), #dc2626);
123
  color: white;
124
  padding: 0.25rem 0.75rem;
125
  border-radius: 20px;
126
  font-size: 0.75rem;
127
  font-weight: bold;
128
- animation: blink 2s infinite;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  }
130
 
131
  @keyframes blink {
132
- 0%, 100% { opacity: 1; }
133
- 50% { opacity: 0.7; }
 
 
 
 
134
  }
135
 
136
  /* Main Container */
@@ -170,11 +205,37 @@
170
  color: white;
171
  box-shadow: 0 10px 30px rgba(59, 130, 246, 0.3);
172
  animation: float 3s ease-in-out infinite;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
  }
174
 
175
  @keyframes float {
176
- 0%, 100% { transform: translateY(0); }
177
- 50% { transform: translateY(-10px); }
 
 
 
 
178
  }
179
 
180
  .station-name {
@@ -191,6 +252,46 @@
191
  font-size: 1.1rem;
192
  margin-bottom: 1rem;
193
  min-height: 30px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
  }
195
 
196
  .visualizer {
@@ -207,6 +308,7 @@
207
  background: linear-gradient(to top, var(--secondary-color), var(--accent-color));
208
  border-radius: 2px;
209
  animation: wave 1s ease-in-out infinite;
 
210
  }
211
 
212
  .bar:nth-child(1) { animation-delay: 0s; height: 20px; }
@@ -220,12 +322,16 @@
220
 
221
  .visualizer.paused .bar {
222
  animation-play-state: paused;
223
- opacity: 0.5;
224
  }
225
 
226
  @keyframes wave {
227
- 0%, 100% { transform: scaleY(1); }
228
- 50% { transform: scaleY(1.5); }
 
 
 
 
229
  }
230
 
231
  /* Player Controls */
@@ -250,6 +356,26 @@
250
  cursor: pointer;
251
  transition: all 0.3s;
252
  font-size: 1.2rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
  }
254
 
255
  .control-btn:hover {
@@ -289,6 +415,7 @@
289
  background: var(--light-bg);
290
  border-radius: 3px;
291
  outline: none;
 
292
  }
293
 
294
  .volume-slider::-webkit-slider-thumb {
@@ -300,6 +427,7 @@
300
  border-radius: 50%;
301
  cursor: pointer;
302
  transition: all 0.3s;
 
303
  }
304
 
305
  .volume-slider::-webkit-slider-thumb:hover {
@@ -307,6 +435,16 @@
307
  transform: scale(1.2);
308
  }
309
 
 
 
 
 
 
 
 
 
 
 
310
  /* Stations List */
311
  .stations-section {
312
  background: var(--glass-bg);
@@ -351,6 +489,24 @@
351
  margin-bottom: 0.75rem;
352
  cursor: pointer;
353
  transition: all 0.3s;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
354
  }
355
 
356
  .station-item:hover {
@@ -364,6 +520,11 @@
364
  border-color: var(--accent-color);
365
  }
366
 
 
 
 
 
 
367
  .station-icon {
368
  width: 50px;
369
  height: 50px;
@@ -374,6 +535,11 @@
374
  justify-content: center;
375
  color: white;
376
  font-size: 1.2rem;
 
 
 
 
 
377
  }
378
 
379
  .station-info {
@@ -393,11 +559,16 @@
393
  .station-live {
394
  width: 8px;
395
  height: 8px;
396
- background: #ef4444;
397
  border-radius: 50%;
398
  animation: blink 2s infinite;
399
  }
400
 
 
 
 
 
 
401
  /* Categories */
402
  .categories {
403
  display: flex;
@@ -415,6 +586,20 @@
415
  cursor: pointer;
416
  transition: all 0.3s;
417
  font-size: 0.875rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
418
  }
419
 
420
  .category-btn:hover {
@@ -423,36 +608,94 @@
423
  transform: translateY(-2px);
424
  }
425
 
 
 
 
 
426
  .category-btn.active {
427
  background: var(--secondary-color);
428
  color: white;
429
  }
430
 
431
- /* Demo Notice */
432
- .demo-notice {
433
- background: linear-gradient(135deg, rgba(245, 158, 11, 0.2), rgba(220, 38, 38, 0.2));
434
- border: 1px solid rgba(245, 158, 11, 0.5);
435
- border-radius: 15px;
436
- padding: 1rem;
437
- margin-bottom: 2rem;
438
- text-align: center;
439
- animation: glow 2s ease-in-out infinite;
 
 
440
  }
441
 
442
- @keyframes glow {
443
- 0%, 100% { box-shadow: 0 0 20px rgba(245, 158, 11, 0.3); }
444
- 50% { box-shadow: 0 0 30px rgba(245, 158, 11, 0.5); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
445
  }
446
 
447
- .demo-notice i {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
448
  font-size: 1.5rem;
449
- color: var(--accent-color);
450
- margin-bottom: 0.5rem;
451
  }
452
 
453
- .demo-notice p {
454
- color: var(--text-secondary);
455
- font-size: 0.9rem;
 
 
 
 
 
 
 
456
  }
457
 
458
  /* Responsive Design */
@@ -460,6 +703,7 @@
460
  .container {
461
  grid-template-columns: 1fr;
462
  }
 
463
  .stations-section {
464
  max-height: 400px;
465
  }
@@ -469,61 +713,47 @@
469
  header {
470
  padding: 1rem;
471
  }
 
472
  .header-content {
473
  flex-direction: column;
474
  text-align: center;
475
  }
 
476
  .container {
477
  padding: 0 1rem;
478
  margin: 1rem auto;
479
  }
480
- .player-section, .stations-section {
 
 
481
  padding: 1.5rem;
482
  }
 
483
  .station-name {
484
  font-size: 1.5rem;
485
  }
 
486
  .play-btn {
487
  width: 60px;
488
  height: 60px;
489
  font-size: 1.2rem;
490
  }
 
491
  .control-btn {
492
  width: 40px;
493
  height: 40px;
494
  font-size: 1rem;
495
  }
496
- }
497
 
498
- /* Toast Notification */
499
- .toast {
500
- position: fixed;
501
- bottom: 2rem;
502
- left: 50%;
503
- transform: translateX(-50%) translateY(100px);
504
- background: var(--light-bg);
505
- border: 1px solid var(--glass-border);
506
- border-radius: 10px;
507
- padding: 1rem 2rem;
508
- display: flex;
509
- align-items: center;
510
- gap: 1rem;
511
- opacity: 0;
512
- transition: all 0.3s;
513
- z-index: 1000;
514
- }
515
-
516
- .toast.show {
517
- transform: translateX(-50%) translateY(0);
518
- opacity: 1;
519
- }
520
-
521
- .toast i {
522
- font-size: 1.5rem;
523
- color: var(--accent-color);
524
  }
525
  </style>
526
  </head>
 
527
  <body>
528
  <div class="bg-animation"></div>
529
 
@@ -532,7 +762,10 @@
532
  <div class="logo">
533
  <i class="fas fa-radio"></i>
534
  <span>Deutsche Radio Stream</span>
535
- <span class="demo-badge">DEMO</span>
 
 
 
536
  </div>
537
  <div class="attribution">
538
  Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank">anycoder</a>
@@ -542,17 +775,18 @@
542
 
543
  <main class="container">
544
  <section class="player-section">
545
- <div class="demo-notice">
546
- <i class="fas fa-info-circle"></i>
547
- <p><strong>Visuelle Demo</strong> - Dies ist eine Design-Demonstration ohne Audio-Funktion</p>
548
- </div>
549
-
550
  <div class="now-playing">
551
  <div class="station-logo" id="stationLogo">
552
  <i class="fas fa-broadcast-tower"></i>
553
  </div>
554
  <h2 class="station-name" id="stationName">Wähle eine Station</h2>
555
- <p class="current-track" id="currentTrack">---</p>
 
 
 
 
 
 
556
 
557
  <div class="visualizer paused" id="visualizer">
558
  <div class="bar"></div>
@@ -584,6 +818,14 @@
584
  <i class="fas fa-volume-up"></i>
585
  <span id="volumeValue">70%</span>
586
  </div>
 
 
 
 
 
 
 
 
587
  </section>
588
 
589
  <section class="stations-section">
@@ -607,31 +849,158 @@
607
  </main>
608
 
609
  <div class="toast" id="toast">
610
- <i class="fas fa-eye"></i>
611
- <span id="toastMessage">Visuelle Demo</span>
612
  </div>
613
 
 
 
 
614
  <script>
615
- // German Radio Stations (Demo Data)
616
  const radioStations = [
617
- { id: 1, name: "ANTENNE BAYERN", genre: "Pop", category: "pop", icon: "fa-broadcast-tower" },
618
- { id: 2, name: "BAYERN 3", genre: "Pop & Rock", category: "pop", icon: "fa-music" },
619
- { id: 3, name: "DEUTSCHLANDFUNK", genre: "Nachrichten & Info", category: "news", icon: "fa-newspaper" },
620
- { id: 4, name: "HR3", genre: "Pop", category: "pop", icon: "fa-radio" },
621
- { id: 5, name: "WDR 2", genre: "Pop & News", category: "pop", icon: "fa-microphone" },
622
- { id: 6, name: "SWR3", genre: "Pop", category: "pop", icon: "fa-headphones" },
623
- { id: 7, name: "NDR 2", genre: "Pop & Rock", category: "rock", icon: "fa-guitar" },
624
- { id: 8, name: "MDR JUMP", genre: "Pop & Rock", category: "rock", icon: "fa-compact-disc" },
625
- { id: 9, name: "RBB 88.8", genre: "Pop", category: "pop", icon: "fa-broadcast-tower" },
626
- { id: 10, name: "BR-KLASSIK", genre: "Klassik", category: "classic", icon: "fa-violin" },
627
- { id: 11, name: "DASDING", genre: "Pop & Electro", category: "pop", icon: "fa-bolt" },
628
- { id: 12, name: "YOU FM", genre: "Pop & Electro", category: "pop", icon: "fa-star" }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
629
  ];
630
 
631
  let currentStation = null;
632
  let isPlaying = false;
633
  let currentStationIndex = -1;
634
  let filteredStations = [...radioStations];
 
635
 
636
  // DOM Elements
637
  const playBtn = document.getElementById('playBtn');
@@ -646,11 +1015,110 @@
646
  const stationsList = document.getElementById('stationsList');
647
  const toast = document.getElementById('toast');
648
  const toastMessage = document.getElementById('toastMessage');
 
 
 
 
649
 
650
  // Initialize
651
  function init() {
 
 
 
652
  renderStations();
653
  setupEventListeners();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
654
  }
655
 
656
  // Render stations list
@@ -686,8 +1154,9 @@
686
  nextBtn.addEventListener('click', playNext);
687
 
688
  volumeSlider.addEventListener('input', (e) => {
 
 
689
  volumeValue.textContent = `${e.target.value}%`;
690
- showToast(`Lautstärke: ${e.target.value}%`);
691
  });
692
 
693
  // Category filters
@@ -698,107 +1167,4 @@
698
 
699
  const category = e.target.dataset.category;
700
  if (category === 'all') {
701
- filteredStations = [...radioStations];
702
- } else {
703
- filteredStations = radioStations.filter(s => s.category === category);
704
- }
705
- renderStations();
706
- showToast(`Kategorie: ${category === 'all' ? 'Alle' : category.charAt(0).toUpperCase() + category.slice(1)}`);
707
- });
708
- });
709
- }
710
-
711
- // Select station
712
- function selectStation(index) {
713
- if (index < 0 || index >= radioStations.length) return;
714
-
715
- currentStationIndex = index;
716
- currentStation = radioStations[index];
717
-
718
- // Update UI
719
- stationName.textContent = currentStation.name;
720
- stationLogo.innerHTML = `<i class="fas ${currentStation.icon}"></i>`;
721
- currentTrack.textContent = 'Demo-Modus - Kein Stream';
722
-
723
- // Update active state in list
724
- document.querySelectorAll('.station-item').forEach(item => {
725
- item.classList.remove('active');
726
- if (parseInt(item.dataset.stationId) === currentStation.id) {
727
- item.classList.add('active');
728
- }
729
- });
730
-
731
- showToast(`Station ausgewählt: ${currentStation.name}`);
732
- }
733
-
734
- // Toggle play/pause
735
- function togglePlay() {
736
- if (!currentStation && currentStationIndex === -1) {
737
- selectStation(0);
738
- }
739
-
740
- if (isPlaying) {
741
- pausePlayback();
742
- } else {
743
- startPlayback();
744
- }
745
- }
746
-
747
- // Start playback (Demo)
748
- function startPlayback() {
749
- if (!currentStation) return;
750
-
751
- isPlaying = true;
752
- playBtn.innerHTML = '<i class="fas fa-pause"></i>';
753
- visualizer.classList.remove('paused');
754
- currentTrack.textContent = 'Demo - Wiedergabe simuliert';
755
- showToast(`Wiedergabe: ${currentStation.name}`);
756
- }
757
-
758
- // Pause playback (Demo)
759
- function pausePlayback() {
760
- isPlaying = false;
761
- playBtn.innerHTML = '<i class="fas fa-play"></i>';
762
- visualizer.classList.add('paused');
763
- currentTrack.textContent = 'Demo - Pausiert';
764
- showToast('Wiedergabe pausiert');
765
- }
766
-
767
- // Play previous station
768
- function playPrevious() {
769
- if (currentStationIndex > 0) {
770
- selectStation(currentStationIndex - 1);
771
- if (!isPlaying) {
772
- togglePlay();
773
- }
774
- } else {
775
- showToast('Bereits bei der ersten Station');
776
- }
777
- }
778
-
779
- // Play next station
780
- function playNext() {
781
- if (currentStationIndex < radioStations.length - 1) {
782
- selectStation(currentStationIndex + 1);
783
- if (!isPlaying) {
784
- togglePlay();
785
- }
786
- } else {
787
- showToast('Bereits bei der letzten Station');
788
- }
789
- }
790
-
791
- // Show toast notification
792
- function showToast(message) {
793
- toastMessage.textContent = message;
794
- toast.classList.add('show');
795
- setTimeout(() => {
796
- toast.classList.remove('show');
797
- }, 2000);
798
- }
799
-
800
- // Initialize app
801
- init();
802
- </script>
803
- </body>
804
- </html>
 
1
  <!DOCTYPE html>
2
  <html lang="de">
3
+
4
  <head>
5
  <meta charset="UTF-8">
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Deutsche Radio Stream - Live Radio aus Deutschland</title>
8
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
  <style>
10
  * {
 
23
  --text-secondary: #94a3b8;
24
  --glass-bg: rgba(30, 41, 59, 0.5);
25
  --glass-border: rgba(148, 163, 184, 0.2);
26
+ --success-color: #10b981;
27
+ --error-color: #ef4444;
28
  }
29
 
30
  body {
 
59
  }
60
 
61
  @keyframes rotate {
62
+ 0% {
63
+ transform: rotate(0deg);
64
+ }
65
+ 100% {
66
+ transform: rotate(360deg);
67
+ }
68
  }
69
 
70
  /* Header */
 
106
  }
107
 
108
  @keyframes pulse {
109
+ 0%, 100% {
110
+ transform: scale(1);
111
+ }
112
+ 50% {
113
+ transform: scale(1.1);
114
+ }
115
  }
116
 
117
  .attribution {
 
129
  color: var(--accent-color);
130
  }
131
 
132
+ .live-indicator {
133
+ background: linear-gradient(135deg, var(--success-color), #059669);
134
  color: white;
135
  padding: 0.25rem 0.75rem;
136
  border-radius: 20px;
137
  font-size: 0.75rem;
138
  font-weight: bold;
139
+ display: flex;
140
+ align-items: center;
141
+ gap: 0.5rem;
142
+ animation: glow-green 2s ease-in-out infinite;
143
+ }
144
+
145
+ .live-dot {
146
+ width: 8px;
147
+ height: 8px;
148
+ background: white;
149
+ border-radius: 50%;
150
+ animation: blink 1s infinite;
151
+ }
152
+
153
+ @keyframes glow-green {
154
+ 0%, 100% {
155
+ box-shadow: 0 0 10px rgba(16, 185, 129, 0.5);
156
+ }
157
+ 50% {
158
+ box-shadow: 0 0 20px rgba(16, 185, 129, 0.8);
159
+ }
160
  }
161
 
162
  @keyframes blink {
163
+ 0%, 100% {
164
+ opacity: 1;
165
+ }
166
+ 50% {
167
+ opacity: 0.5;
168
+ }
169
  }
170
 
171
  /* Main Container */
 
205
  color: white;
206
  box-shadow: 0 10px 30px rgba(59, 130, 246, 0.3);
207
  animation: float 3s ease-in-out infinite;
208
+ position: relative;
209
+ overflow: hidden;
210
+ }
211
+
212
+ .station-logo.playing::after {
213
+ content: '';
214
+ position: absolute;
215
+ top: -50%;
216
+ left: -50%;
217
+ width: 200%;
218
+ height: 200%;
219
+ background: linear-gradient(45deg, transparent, rgba(255, 255, 255, 0.1), transparent);
220
+ animation: shine 3s infinite;
221
+ }
222
+
223
+ @keyframes shine {
224
+ 0% {
225
+ transform: rotate(0deg);
226
+ }
227
+ 100% {
228
+ transform: rotate(360deg);
229
+ }
230
  }
231
 
232
  @keyframes float {
233
+ 0%, 100% {
234
+ transform: translateY(0);
235
+ }
236
+ 50% {
237
+ transform: translateY(-10px);
238
+ }
239
  }
240
 
241
  .station-name {
 
252
  font-size: 1.1rem;
253
  margin-bottom: 1rem;
254
  min-height: 30px;
255
+ display: flex;
256
+ align-items: center;
257
+ justify-content: center;
258
+ gap: 0.5rem;
259
+ }
260
+
261
+ .track-info {
262
+ display: flex;
263
+ flex-direction: column;
264
+ gap: 0.25rem;
265
+ }
266
+
267
+ .track-title {
268
+ font-size: 1rem;
269
+ color: var(--text-primary);
270
+ }
271
+
272
+ .track-artist {
273
+ font-size: 0.875rem;
274
+ color: var(--text-secondary);
275
+ }
276
+
277
+ .loading-spinner {
278
+ display: none;
279
+ width: 20px;
280
+ height: 20px;
281
+ border: 2px solid var(--text-secondary);
282
+ border-top-color: var(--secondary-color);
283
+ border-radius: 50%;
284
+ animation: spin 1s linear infinite;
285
+ }
286
+
287
+ .loading-spinner.active {
288
+ display: inline-block;
289
+ }
290
+
291
+ @keyframes spin {
292
+ to {
293
+ transform: rotate(360deg);
294
+ }
295
  }
296
 
297
  .visualizer {
 
308
  background: linear-gradient(to top, var(--secondary-color), var(--accent-color));
309
  border-radius: 2px;
310
  animation: wave 1s ease-in-out infinite;
311
+ transform-origin: bottom;
312
  }
313
 
314
  .bar:nth-child(1) { animation-delay: 0s; height: 20px; }
 
322
 
323
  .visualizer.paused .bar {
324
  animation-play-state: paused;
325
+ opacity: 0.3;
326
  }
327
 
328
  @keyframes wave {
329
+ 0%, 100% {
330
+ transform: scaleY(1);
331
+ }
332
+ 50% {
333
+ transform: scaleY(1.5);
334
+ }
335
  }
336
 
337
  /* Player Controls */
 
356
  cursor: pointer;
357
  transition: all 0.3s;
358
  font-size: 1.2rem;
359
+ position: relative;
360
+ overflow: hidden;
361
+ }
362
+
363
+ .control-btn::before {
364
+ content: '';
365
+ position: absolute;
366
+ top: 50%;
367
+ left: 50%;
368
+ width: 0;
369
+ height: 0;
370
+ background: rgba(59, 130, 246, 0.3);
371
+ border-radius: 50%;
372
+ transform: translate(-50%, -50%);
373
+ transition: width 0.6s, height 0.6s;
374
+ }
375
+
376
+ .control-btn:hover::before {
377
+ width: 100%;
378
+ height: 100%;
379
  }
380
 
381
  .control-btn:hover {
 
415
  background: var(--light-bg);
416
  border-radius: 3px;
417
  outline: none;
418
+ position: relative;
419
  }
420
 
421
  .volume-slider::-webkit-slider-thumb {
 
427
  border-radius: 50%;
428
  cursor: pointer;
429
  transition: all 0.3s;
430
+ box-shadow: 0 2px 10px rgba(59, 130, 246, 0.3);
431
  }
432
 
433
  .volume-slider::-webkit-slider-thumb:hover {
 
435
  transform: scale(1.2);
436
  }
437
 
438
+ .volume-slider::-moz-range-thumb {
439
+ width: 18px;
440
+ height: 18px;
441
+ background: var(--secondary-color);
442
+ border-radius: 50%;
443
+ cursor: pointer;
444
+ transition: all 0.3s;
445
+ box-shadow: 0 2px 10px rgba(59, 130, 246, 0.3);
446
+ }
447
+
448
  /* Stations List */
449
  .stations-section {
450
  background: var(--glass-bg);
 
489
  margin-bottom: 0.75rem;
490
  cursor: pointer;
491
  transition: all 0.3s;
492
+ position: relative;
493
+ overflow: hidden;
494
+ }
495
+
496
+ .station-item::before {
497
+ content: '';
498
+ position: absolute;
499
+ left: 0;
500
+ top: 0;
501
+ height: 100%;
502
+ width: 3px;
503
+ background: var(--secondary-color);
504
+ transform: translateX(-100%);
505
+ transition: transform 0.3s;
506
+ }
507
+
508
+ .station-item:hover::before {
509
+ transform: translateX(0);
510
  }
511
 
512
  .station-item:hover {
 
520
  border-color: var(--accent-color);
521
  }
522
 
523
+ .station-item.active::before {
524
+ background: var(--accent-color);
525
+ transform: translateX(0);
526
+ }
527
+
528
  .station-icon {
529
  width: 50px;
530
  height: 50px;
 
535
  justify-content: center;
536
  color: white;
537
  font-size: 1.2rem;
538
+ transition: transform 0.3s;
539
+ }
540
+
541
+ .station-item:hover .station-icon {
542
+ transform: rotate(10deg) scale(1.1);
543
  }
544
 
545
  .station-info {
 
559
  .station-live {
560
  width: 8px;
561
  height: 8px;
562
+ background: var(--success-color);
563
  border-radius: 50%;
564
  animation: blink 2s infinite;
565
  }
566
 
567
+ .station-item.active .station-live {
568
+ background: var(--accent-color);
569
+ animation: pulse 1s infinite;
570
+ }
571
+
572
  /* Categories */
573
  .categories {
574
  display: flex;
 
586
  cursor: pointer;
587
  transition: all 0.3s;
588
  font-size: 0.875rem;
589
+ position: relative;
590
+ overflow: hidden;
591
+ }
592
+
593
+ .category-btn::after {
594
+ content: '';
595
+ position: absolute;
596
+ bottom: 0;
597
+ left: 0;
598
+ width: 100%;
599
+ height: 2px;
600
+ background: var(--secondary-color);
601
+ transform: scaleX(0);
602
+ transition: transform 0.3s;
603
  }
604
 
605
  .category-btn:hover {
 
608
  transform: translateY(-2px);
609
  }
610
 
611
+ .category-btn:hover::after {
612
+ transform: scaleX(1);
613
+ }
614
+
615
  .category-btn.active {
616
  background: var(--secondary-color);
617
  color: white;
618
  }
619
 
620
+ /* Status Bar */
621
+ .status-bar {
622
+ background: var(--glass-bg);
623
+ border: 1px solid var(--glass-border);
624
+ border-radius: 12px;
625
+ padding: 0.75rem 1rem;
626
+ margin-top: 1rem;
627
+ display: flex;
628
+ align-items: center;
629
+ justify-content: space-between;
630
+ font-size: 0.875rem;
631
  }
632
 
633
+ .status-text {
634
+ display: flex;
635
+ align-items: center;
636
+ gap: 0.5rem;
637
+ }
638
+
639
+ .status-indicator {
640
+ width: 8px;
641
+ height: 8px;
642
+ border-radius: 50%;
643
+ background: var(--text-secondary);
644
+ }
645
+
646
+ .status-indicator.connected {
647
+ background: var(--success-color);
648
+ animation: pulse 2s infinite;
649
+ }
650
+
651
+ .status-indicator.error {
652
+ background: var(--error-color);
653
+ animation: blink 1s infinite;
654
+ }
655
+
656
+ .bitrate {
657
+ color: var(--secondary-color);
658
+ font-weight: 600;
659
  }
660
 
661
+ /* Toast Notification */
662
+ .toast {
663
+ position: fixed;
664
+ bottom: 2rem;
665
+ left: 50%;
666
+ transform: translateX(-50%) translateY(100px);
667
+ background: var(--light-bg);
668
+ border: 1px solid var(--glass-border);
669
+ border-radius: 10px;
670
+ padding: 1rem 2rem;
671
+ display: flex;
672
+ align-items: center;
673
+ gap: 1rem;
674
+ opacity: 0;
675
+ transition: all 0.3s;
676
+ z-index: 1000;
677
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
678
+ }
679
+
680
+ .toast.show {
681
+ transform: translateX(-50%) translateY(0);
682
+ opacity: 1;
683
+ }
684
+
685
+ .toast i {
686
  font-size: 1.5rem;
 
 
687
  }
688
 
689
+ .toast.success i {
690
+ color: var(--success-color);
691
+ }
692
+
693
+ .toast.error i {
694
+ color: var(--error-color);
695
+ }
696
+
697
+ .toast.info i {
698
+ color: var(--secondary-color);
699
  }
700
 
701
  /* Responsive Design */
 
703
  .container {
704
  grid-template-columns: 1fr;
705
  }
706
+
707
  .stations-section {
708
  max-height: 400px;
709
  }
 
713
  header {
714
  padding: 1rem;
715
  }
716
+
717
  .header-content {
718
  flex-direction: column;
719
  text-align: center;
720
  }
721
+
722
  .container {
723
  padding: 0 1rem;
724
  margin: 1rem auto;
725
  }
726
+
727
+ .player-section,
728
+ .stations-section {
729
  padding: 1.5rem;
730
  }
731
+
732
  .station-name {
733
  font-size: 1.5rem;
734
  }
735
+
736
  .play-btn {
737
  width: 60px;
738
  height: 60px;
739
  font-size: 1.2rem;
740
  }
741
+
742
  .control-btn {
743
  width: 40px;
744
  height: 40px;
745
  font-size: 1rem;
746
  }
 
747
 
748
+ .status-bar {
749
+ flex-direction: column;
750
+ gap: 0.5rem;
751
+ text-align: center;
752
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
753
  }
754
  </style>
755
  </head>
756
+
757
  <body>
758
  <div class="bg-animation"></div>
759
 
 
762
  <div class="logo">
763
  <i class="fas fa-radio"></i>
764
  <span>Deutsche Radio Stream</span>
765
+ <div class="live-indicator">
766
+ <span class="live-dot"></span>
767
+ LIVE
768
+ </div>
769
  </div>
770
  <div class="attribution">
771
  Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank">anycoder</a>
 
775
 
776
  <main class="container">
777
  <section class="player-section">
 
 
 
 
 
778
  <div class="now-playing">
779
  <div class="station-logo" id="stationLogo">
780
  <i class="fas fa-broadcast-tower"></i>
781
  </div>
782
  <h2 class="station-name" id="stationName">Wähle eine Station</h2>
783
+ <div class="current-track" id="currentTrack">
784
+ <div class="track-info">
785
+ <span class="track-title">--- Bitte Station auswählen ---</span>
786
+ <span class="track-artist"></span>
787
+ </div>
788
+ <div class="loading-spinner" id="loadingSpinner"></div>
789
+ </div>
790
 
791
  <div class="visualizer paused" id="visualizer">
792
  <div class="bar"></div>
 
818
  <i class="fas fa-volume-up"></i>
819
  <span id="volumeValue">70%</span>
820
  </div>
821
+
822
+ <div class="status-bar">
823
+ <div class="status-text">
824
+ <span class="status-indicator" id="statusIndicator"></span>
825
+ <span id="statusText">Bereit</span>
826
+ </div>
827
+ <div class="bitrate" id="bitrate">128 kbps</div>
828
+ </div>
829
  </section>
830
 
831
  <section class="stations-section">
 
849
  </main>
850
 
851
  <div class="toast" id="toast">
852
+ <i class="fas fa-info-circle"></i>
853
+ <span id="toastMessage">Nachricht</span>
854
  </div>
855
 
856
+ <!-- Hidden audio element -->
857
+ <audio id="audioPlayer" crossorigin="anonymous"></audio>
858
+
859
  <script>
860
+ // German Radio Stations with real streaming URLs
861
  const radioStations = [
862
+ {
863
+ id: 1,
864
+ name: "ANTENNE BAYERN",
865
+ genre: "Pop",
866
+ category: "pop",
867
+ icon: "fa-broadcast-tower",
868
+ url: "https://mp3channels.webradio.antenne.de/antenne-bayern",
869
+ bitrate: "128"
870
+ },
871
+ {
872
+ id: 2,
873
+ name: "BAYERN 3",
874
+ genre: "Pop & Rock",
875
+ category: "pop",
876
+ icon: "fa-music",
877
+ url: "https://br-br3-live.cast.addradio.de/br/br3/live/mp3/128/stream.mp3",
878
+ bitrate: "128"
879
+ },
880
+ {
881
+ id: 3,
882
+ name: "DEUTSCHLANDFUNK",
883
+ genre: "Nachrichten & Info",
884
+ category: "news",
885
+ icon: "fa-newspaper",
886
+ url: "https://st01.dlf.de/dlf/01/128/mp3/stream.mp3",
887
+ bitrate: "128"
888
+ },
889
+ {
890
+ id: 4,
891
+ name: "HR3",
892
+ genre: "Pop",
893
+ category: "pop",
894
+ icon: "fa-radio",
895
+ url: "https://hr-hr3-live.cast.addradio.de/hr/hr3/live/mp3/128/stream.mp3",
896
+ bitrate: "128"
897
+ },
898
+ {
899
+ id: 5,
900
+ name: "WDR 2",
901
+ genre: "Pop & News",
902
+ category: "pop",
903
+ icon: "fa-microphone",
904
+ url: "https://wdr-wdr2-live.icecast.wdr.de/wdr/wdr2/live/mp3/128/stream.mp3",
905
+ bitrate: "128"
906
+ },
907
+ {
908
+ id: 6,
909
+ name: "SWR3",
910
+ genre: "Pop",
911
+ category: "pop",
912
+ icon: "fa-headphones",
913
+ url: "https://swr-swr3-live.cast.addradio.de/swr/swr3/live/mp3/128/stream.mp3",
914
+ bitrate: "128"
915
+ },
916
+ {
917
+ id: 7,
918
+ name: "NDR 2",
919
+ genre: "Pop & Rock",
920
+ category: "rock",
921
+ icon: "fa-guitar",
922
+ url: "https://ndr-ndr2-niedersachsen.cast.addradio.de/ndr/ndr2/niedersachsen/mp3/128/stream.mp3",
923
+ bitrate: "128"
924
+ },
925
+ {
926
+ id: 8,
927
+ name: "MDR JUMP",
928
+ genre: "Pop & Rock",
929
+ category: "rock",
930
+ icon: "fa-compact-disc",
931
+ url: "https://mdr-mdr-jump-sachsen.cast.addradio.de/mdr/mdr-jump/sachsen/mp3/128/stream.mp3",
932
+ bitrate: "128"
933
+ },
934
+ {
935
+ id: 9,
936
+ name: "RBB 88.8",
937
+ genre: "Pop",
938
+ category: "pop",
939
+ icon: "fa-broadcast-tower",
940
+ url: "https://rbb-88-8-live.cast.addradio.de/rbb/rbb888/live/mp3/128/stream.mp3",
941
+ bitrate: "128"
942
+ },
943
+ {
944
+ id: 10,
945
+ name: "BR-KLASSIK",
946
+ genre: "Klassik",
947
+ category: "classic",
948
+ icon: "fa-violin",
949
+ url: "https://br-brklassik-live.cast.addradio.de/br/brklassik/live/mp3/128/stream.mp3",
950
+ bitrate: "128"
951
+ },
952
+ {
953
+ id: 11,
954
+ name: "DASDING",
955
+ genre: "Pop & Electro",
956
+ category: "pop",
957
+ icon: "fa-bolt",
958
+ url: "https://swr-dasding-live.cast.addradio.de/swr/dasding/live/mp3/128/stream.mp3",
959
+ bitrate: "128"
960
+ },
961
+ {
962
+ id: 12,
963
+ name: "YOU FM",
964
+ genre: "Pop & Electro",
965
+ category: "pop",
966
+ icon: "fa-star",
967
+ url: "https://hr-youfm-live.cast.addradio.de/hr/youfm/live/mp3/128/stream.mp3",
968
+ bitrate: "128"
969
+ },
970
+ {
971
+ id: 13,
972
+ name: "1LIVE",
973
+ genre: "Pop & Electro",
974
+ category: "pop",
975
+ icon: "fa-broadcast-tower",
976
+ url: "https://wdr-1live-live.icecast.wdr.de/wdr/1live/live/mp3/128/stream.mp3",
977
+ bitrate: "128"
978
+ },
979
+ {
980
+ id: 14,
981
+ name: "N-JOY",
982
+ genre: "Pop & Electro",
983
+ category: "pop",
984
+ icon: "fa-headphones",
985
+ url: "https://ndr-n-joy-niedersachsen.cast.addradio.de/ndr/njoy/niedersachsen/mp3/128/stream.mp3",
986
+ bitrate: "128"
987
+ },
988
+ {
989
+ id: 15,
990
+ name: "BR24",
991
+ genre: "Nachrichten",
992
+ category: "news",
993
+ icon: "fa-newspaper",
994
+ url: "https://br-br24-live.cast.addradio.de/br/br24/live/mp3/128/stream.mp3",
995
+ bitrate: "128"
996
+ }
997
  ];
998
 
999
  let currentStation = null;
1000
  let isPlaying = false;
1001
  let currentStationIndex = -1;
1002
  let filteredStations = [...radioStations];
1003
+ let audioPlayer;
1004
 
1005
  // DOM Elements
1006
  const playBtn = document.getElementById('playBtn');
 
1015
  const stationsList = document.getElementById('stationsList');
1016
  const toast = document.getElementById('toast');
1017
  const toastMessage = document.getElementById('toastMessage');
1018
+ const loadingSpinner = document.getElementById('loadingSpinner');
1019
+ const statusIndicator = document.getElementById('statusIndicator');
1020
+ const statusText = document.getElementById('statusText');
1021
+ const bitrate = document.getElementById('bitrate');
1022
 
1023
  // Initialize
1024
  function init() {
1025
+ audioPlayer = document.getElementById('audioPlayer');
1026
+ audioPlayer.volume = 0.7;
1027
+
1028
  renderStations();
1029
  setupEventListeners();
1030
+ setupAudioEventListeners();
1031
+ updateStatus('Bereit', 'idle');
1032
+ }
1033
+
1034
+ // Setup audio event listeners
1035
+ function setupAudioEventListeners() {
1036
+ audioPlayer.addEventListener('loadstart', () => {
1037
+ loadingSpinner.classList.add('active');
1038
+ updateStatus('Laden...', 'loading');
1039
+ showToast('Verbindung wird hergestellt...', 'info');
1040
+ });
1041
+
1042
+ audioPlayer.addEventListener('canplay', () => {
1043
+ loadingSpinner.classList.remove('active');
1044
+ updateStatus('Verbunden', 'connected');
1045
+ showToast('Stream erfolgreich geladen!', 'success');
1046
+ });
1047
+
1048
+ audioPlayer.addEventListener('play', () => {
1049
+ isPlaying = true;
1050
+ playBtn.innerHTML = '<i class="fas fa-pause"></i>';
1051
+ visualizer.classList.remove('paused');
1052
+ stationLogo.classList.add('playing');
1053
+ updateStatus('Spielt', 'connected');
1054
+ });
1055
+
1056
+ audioPlayer.addEventListener('pause', () => {
1057
+ isPlaying = false;
1058
+ playBtn.innerHTML = '<i class="fas fa-play"></i>';
1059
+ visualizer.classList.add('paused');
1060
+ stationLogo.classList.remove('playing');
1061
+ updateStatus('Pausiert', 'idle');
1062
+ });
1063
+
1064
+ audioPlayer.addEventListener('ended', () => {
1065
+ isPlaying = false;
1066
+ playBtn.innerHTML = '<i class="fas fa-play"></i>';
1067
+ visualizer.classList.add('paused');
1068
+ stationLogo.classList.remove('playing');
1069
+ updateStatus('Beendet', 'idle');
1070
+ });
1071
+
1072
+ audioPlayer.addEventListener('error', (e) => {
1073
+ loadingSpinner.classList.remove('active');
1074
+ updateStatus('Fehler', 'error');
1075
+ showToast('Fehler beim Laden des Streams', 'error');
1076
+ console.error('Audio error:', e);
1077
+ });
1078
+
1079
+ // Simulate track info updates
1080
+ setInterval(() => {
1081
+ if (isPlaying && currentStation) {
1082
+ updateTrackInfo();
1083
+ }
1084
+ }, 30000); // Update every 30 seconds
1085
+ }
1086
+
1087
+ // Update track info (simulation)
1088
+ function updateTrackInfo() {
1089
+ if (!currentStation) return;
1090
+
1091
+ const trackTitle = document.querySelector('.track-title');
1092
+ const trackArtist = document.querySelector('.track-artist');
1093
+
1094
+ // Simulate changing track info
1095
+ const titles = [
1096
+ 'Aktueller Titel',
1097
+ 'Live von der Station',
1098
+ 'Musikprogramm läuft',
1099
+ 'Unterhaltung pur'
1100
+ ];
1101
+
1102
+ const randomTitle = titles[Math.floor(Math.random() * titles.length)];
1103
+ trackTitle.textContent = randomTitle;
1104
+ trackArtist.textContent = `via ${currentStation.name}`;
1105
+ }
1106
+
1107
+ // Update status
1108
+ function updateStatus(text, state) {
1109
+ statusText.textContent = text;
1110
+ statusIndicator.className = 'status-indicator';
1111
+
1112
+ switch(state) {
1113
+ case 'connected':
1114
+ statusIndicator.classList.add('connected');
1115
+ break;
1116
+ case 'error':
1117
+ statusIndicator.classList.add('error');
1118
+ break;
1119
+ default:
1120
+ break;
1121
+ }
1122
  }
1123
 
1124
  // Render stations list
 
1154
  nextBtn.addEventListener('click', playNext);
1155
 
1156
  volumeSlider.addEventListener('input', (e) => {
1157
+ const volume = e.target.value / 100;
1158
+ audioPlayer.volume = volume;
1159
  volumeValue.textContent = `${e.target.value}%`;
 
1160
  });
1161
 
1162
  // Category filters
 
1167
 
1168
  const category = e.target.dataset.category;
1169
  if (category === 'all') {
1170
+ filteredStations = [...radioStations];