Multimedix commited on
Commit
726b5bb
·
verified ·
1 Parent(s): d6316e8

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +203 -226
index.html CHANGED
@@ -25,6 +25,7 @@
25
  --glass-border: rgba(148, 163, 184, 0.2);
26
  --success-color: #10b981;
27
  --error-color: #ef4444;
 
28
  }
29
 
30
  body {
@@ -62,7 +63,6 @@
62
  0% {
63
  transform: rotate(0deg);
64
  }
65
-
66
  100% {
67
  transform: rotate(360deg);
68
  }
@@ -107,12 +107,9 @@
107
  }
108
 
109
  @keyframes pulse {
110
-
111
- 0%,
112
- 100% {
113
  transform: scale(1);
114
  }
115
-
116
  50% {
117
  transform: scale(1.1);
118
  }
@@ -155,37 +152,31 @@
155
  }
156
 
157
  @keyframes glow-green {
158
-
159
- 0%,
160
- 100% {
161
  box-shadow: 0 0 10px rgba(16, 185, 129, 0.5);
162
  }
163
-
164
  50% {
165
  box-shadow: 0 0 20px rgba(16, 185, 129, 0.8);
166
  }
167
  }
168
 
169
  @keyframes blink {
170
-
171
- 0%,
172
- 100% {
173
  opacity: 1;
174
  }
175
-
176
  50% {
177
  opacity: 0.5;
178
  }
179
  }
180
 
181
- /* Main Container - Single Column */
182
  .container {
183
  max-width: 800px;
184
  margin: 2rem auto;
185
  padding: 0 2rem;
186
  }
187
 
188
- /* Player Section - Full Width */
189
  .player-section {
190
  background: var(--glass-bg);
191
  backdrop-filter: blur(10px);
@@ -232,19 +223,15 @@
232
  0% {
233
  transform: rotate(0deg);
234
  }
235
-
236
  100% {
237
  transform: rotate(360deg);
238
  }
239
  }
240
 
241
  @keyframes float {
242
-
243
- 0%,
244
- 100% {
245
  transform: translateY(0);
246
  }
247
-
248
  50% {
249
  transform: translateY(-10px);
250
  }
@@ -323,45 +310,14 @@
323
  transform-origin: bottom;
324
  }
325
 
326
- .bar:nth-child(1) {
327
- animation-delay: 0s;
328
- height: 25px;
329
- }
330
-
331
- .bar:nth-child(2) {
332
- animation-delay: 0.1s;
333
- height: 35px;
334
- }
335
-
336
- .bar:nth-child(3) {
337
- animation-delay: 0.2s;
338
- height: 30px;
339
- }
340
-
341
- .bar:nth-child(4) {
342
- animation-delay: 0.3s;
343
- height: 40px;
344
- }
345
-
346
- .bar:nth-child(5) {
347
- animation-delay: 0.4s;
348
- height: 25px;
349
- }
350
-
351
- .bar:nth-child(6) {
352
- animation-delay: 0.5s;
353
- height: 35px;
354
- }
355
-
356
- .bar:nth-child(7) {
357
- animation-delay: 0.6s;
358
- height: 30px;
359
- }
360
-
361
- .bar:nth-child(8) {
362
- animation-delay: 0.7s;
363
- height: 40px;
364
- }
365
 
366
  .visualizer.paused .bar {
367
  animation-play-state: paused;
@@ -369,12 +325,9 @@
369
  }
370
 
371
  @keyframes wave {
372
-
373
- 0%,
374
- 100% {
375
  transform: scaleY(1);
376
  }
377
-
378
  50% {
379
  transform: scaleY(1.5);
380
  }
@@ -492,7 +445,7 @@
492
  box-shadow: 0 2px 10px rgba(59, 130, 246, 0.3);
493
  }
494
 
495
- /* Station Selector (Compact) */
496
  .station-selector {
497
  background: var(--glass-bg);
498
  border: 1px solid var(--glass-border);
@@ -559,11 +512,33 @@
559
  animation: blink 1s infinite;
560
  }
561
 
 
 
 
 
 
562
  .bitrate {
563
  color: var(--secondary-color);
564
  font-weight: 600;
565
  }
566
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
567
  /* Toast Notification */
568
  .toast {
569
  position: fixed;
@@ -600,10 +575,53 @@
600
  color: var(--error-color);
601
  }
602
 
 
 
 
 
603
  .toast.info i {
604
  color: var(--secondary-color);
605
  }
606
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
607
  /* Hidden Stations Section */
608
  .stations-section {
609
  display: none;
@@ -739,9 +757,20 @@
739
  <div class="status-text">
740
  <span class="status-indicator" id="statusIndicator"></span>
741
  <span id="statusText">Bereit</span>
 
 
 
742
  </div>
743
  <div class="bitrate" id="bitrate">128 kbps</div>
744
  </div>
 
 
 
 
 
 
 
 
745
  </section>
746
 
747
  <section class="stations-section">
@@ -758,7 +787,7 @@
758
  <audio id="audioPlayer" crossorigin="anonymous"></audio>
759
 
760
  <script>
761
- // German Radio Stations with real streaming URLs
762
  const radioStations = [
763
  {
764
  id: 1,
@@ -766,7 +795,11 @@
766
  genre: "Pop",
767
  category: "pop",
768
  icon: "fa-broadcast-tower",
769
- url: "https://mp3channels.webradio.antenne.de/antenne-bayern",
 
 
 
 
770
  bitrate: "128"
771
  },
772
  {
@@ -775,7 +808,10 @@
775
  genre: "Pop & Rock",
776
  category: "pop",
777
  icon: "fa-music",
778
- url: "https://br-br3-live.cast.addradio.de/br/br3/live/mp3/128/stream.mp3",
 
 
 
779
  bitrate: "128"
780
  },
781
  {
@@ -784,7 +820,10 @@
784
  genre: "Nachrichten & Info",
785
  category: "news",
786
  icon: "fa-newspaper",
787
- url: "https://st01.dlf.de/dlf/01/128/mp3/stream.mp3",
 
 
 
788
  bitrate: "128"
789
  },
790
  {
@@ -793,7 +832,10 @@
793
  genre: "Pop",
794
  category: "pop",
795
  icon: "fa-radio",
796
- url: "https://hr-hr3-live.cast.addradio.de/hr/hr3/live/mp3/128/stream.mp3",
 
 
 
797
  bitrate: "128"
798
  },
799
  {
@@ -802,7 +844,10 @@
802
  genre: "Pop & News",
803
  category: "pop",
804
  icon: "fa-microphone",
805
- url: "https://wdr-wdr2-live.icecast.wdr.de/wdr/wdr2/live/mp3/128/stream.mp3",
 
 
 
806
  bitrate: "128"
807
  },
808
  {
@@ -811,7 +856,10 @@
811
  genre: "Pop",
812
  category: "pop",
813
  icon: "fa-headphones",
814
- url: "https://swr-swr3-live.cast.addradio.de/swr/swr3/live/mp3/128/stream.mp3",
 
 
 
815
  bitrate: "128"
816
  },
817
  {
@@ -820,7 +868,10 @@
820
  genre: "Pop & Rock",
821
  category: "rock",
822
  icon: "fa-guitar",
823
- url: "https://ndr-ndr2-niedersachsen.cast.addradio.de/ndr/ndr2/niedersachsen/mp3/128/stream.mp3",
 
 
 
824
  bitrate: "128"
825
  },
826
  {
@@ -829,7 +880,10 @@
829
  genre: "Pop & Rock",
830
  category: "rock",
831
  icon: "fa-compact-disc",
832
- url: "https://mdr-mdr-jump-sachsen.cast.addradio.de/mdr/mdr-jump/sachsen/mp3/128/stream.mp3",
 
 
 
833
  bitrate: "128"
834
  },
835
  {
@@ -838,7 +892,10 @@
838
  genre: "Pop",
839
  category: "pop",
840
  icon: "fa-broadcast-tower",
841
- url: "https://rbb-88-8-live.cast.addradio.de/rbb/rbb888/live/mp3/128/stream.mp3",
 
 
 
842
  bitrate: "128"
843
  },
844
  {
@@ -847,7 +904,10 @@
847
  genre: "Klassik",
848
  category: "classic",
849
  icon: "fa-violin",
850
- url: "https://br-brklassik-live.cast.addradio.de/br/brklassik/live/mp3/128/stream.mp3",
 
 
 
851
  bitrate: "128"
852
  },
853
  {
@@ -856,7 +916,10 @@
856
  genre: "Pop & Electro",
857
  category: "pop",
858
  icon: "fa-bolt",
859
- url: "https://swr-dasding-live.cast.addradio.de/swr/dasding/live/mp3/128/stream.mp3",
 
 
 
860
  bitrate: "128"
861
  },
862
  {
@@ -865,7 +928,10 @@
865
  genre: "Pop & Electro",
866
  category: "pop",
867
  icon: "fa-star",
868
- url: "https://hr-youfm-live.cast.addradio.de/hr/youfm/live/mp3/128/stream.mp3",
 
 
 
869
  bitrate: "128"
870
  },
871
  {
@@ -874,7 +940,10 @@
874
  genre: "Pop & Electro",
875
  category: "pop",
876
  icon: "fa-broadcast-tower",
877
- url: "https://wdr-1live-live.icecast.wdr.de/wdr/1live/live/mp3/128/stream.mp3",
 
 
 
878
  bitrate: "128"
879
  },
880
  {
@@ -883,7 +952,10 @@
883
  genre: "Pop & Electro",
884
  category: "pop",
885
  icon: "fa-headphones",
886
- url: "https://ndr-n-joy-niedersachsen.cast.addradio.de/ndr/njoy/niedersachsen/mp3/128/stream.mp3",
 
 
 
887
  bitrate: "128"
888
  },
889
  {
@@ -892,7 +964,10 @@
892
  genre: "Nachrichten",
893
  category: "news",
894
  icon: "fa-newspaper",
895
- url: "https://br-br24-live.cast.addradio.de/br/br24/live/mp3/128/stream.mp3",
 
 
 
896
  bitrate: "128"
897
  }
898
  ];
@@ -901,6 +976,9 @@
901
  let isPlaying = false;
902
  let currentStationIndex = -1;
903
  let audioPlayer;
 
 
 
904
 
905
  // DOM Elements
906
  const playBtn = document.getElementById('playBtn');
@@ -919,11 +997,15 @@
919
  const statusIndicator = document.getElementById('statusIndicator');
920
  const statusText = document.getElementById('statusText');
921
  const bitrate = document.getElementById('bitrate');
 
 
 
922
 
923
  // Initialize
924
  function init() {
925
  audioPlayer = document.getElementById('audioPlayer');
926
  audioPlayer.volume = 0.7;
 
927
 
928
  populateStationSelect();
929
  setupEventListeners();
@@ -941,17 +1023,19 @@
941
  });
942
  }
943
 
944
- // Setup audio event listeners
945
  function setupAudioEventListeners() {
946
  audioPlayer.addEventListener('loadstart', () => {
947
  loadingSpinner.classList.add('active');
948
  updateStatus('Laden...', 'loading');
949
- showToast('Verbindung wird hergestellt...', 'info');
950
  });
951
 
952
  audioPlayer.addEventListener('canplay', () => {
953
  loadingSpinner.classList.remove('active');
954
  updateStatus('Verbunden', 'connected');
 
 
955
  showToast('Stream erfolgreich geladen!', 'success');
956
  });
957
 
@@ -980,10 +1064,22 @@
980
  });
981
 
982
  audioPlayer.addEventListener('error', (e) => {
983
- loadingSpinner.classList.remove('active');
984
- updateStatus('Fehler', 'error');
985
- showToast('Fehler beim Laden des Streams', 'error');
986
- console.error('Audio error:', e);
 
 
 
 
 
 
 
 
 
 
 
 
987
  });
988
 
989
  // Simulate track info updates
@@ -991,148 +1087,29 @@
991
  if (isPlaying && currentStation) {
992
  updateTrackInfo();
993
  }
994
- }, 30000); // Update every 30 seconds
995
- }
996
-
997
- // Update track info (simulation)
998
- function updateTrackInfo() {
999
- if (!currentStation) return;
1000
-
1001
- const trackTitle = document.querySelector('.track-title');
1002
- const trackArtist = document.querySelector('.track-artist');
1003
-
1004
- // Simulate changing track info
1005
- const titles = [
1006
- 'Aktueller Titel',
1007
- 'Live von der Station',
1008
- 'Musikprogramm läuft',
1009
- 'Unterhaltung pur'
1010
- ];
1011
-
1012
- const randomTitle = titles[Math.floor(Math.random() * titles.length)];
1013
- trackTitle.textContent = randomTitle;
1014
- trackArtist.textContent = `via ${currentStation.name}`;
1015
- }
1016
-
1017
- // Update status
1018
- function updateStatus(text, state) {
1019
- statusText.textContent = text;
1020
- statusIndicator.className = 'status-indicator';
1021
-
1022
- switch(state) {
1023
- case 'connected':
1024
- statusIndicator.classList.add('connected');
1025
- break;
1026
- case 'error':
1027
- statusIndicator.classList.add('error');
1028
- break;
1029
- default:
1030
- break;
1031
- }
1032
  }
1033
 
1034
- // Select station from dropdown
1035
- function selectStationFromDropdown() {
1036
- const stationId = parseInt(stationSelect.value);
1037
- if (!stationId) return;
1038
 
1039
- const stationIndex = radioStations.findIndex(s => s.id === stationId);
1040
- if (stationIndex !== -1) {
1041
- selectStation(stationIndex);
1042
- }
1043
- }
1044
-
1045
- // Select station
1046
- function selectStation(index) {
1047
- if (index < 0 || index >= radioStations.length) return;
1048
-
1049
- currentStationIndex = index;
1050
- currentStation = radioStations[index];
1051
-
1052
- // Update UI
1053
- stationName.textContent = currentStation.name;
1054
- stationLogo.innerHTML = `<i class="fas ${currentStation.icon}"></i>`;
1055
- bitrate.textContent = `${currentStation.bitrate} kbps`;
1056
-
1057
- // Update dropdown
1058
- stationSelect.value = currentStation.id;
1059
-
1060
- // Update track info
1061
- document.querySelector('.track-title').textContent = 'Lade...';
1062
- document.querySelector('.track-artist').textContent = '';
1063
-
1064
- // Load and play
1065
- audioPlayer.src = currentStation.url;
1066
- audioPlayer.play();
1067
 
1068
- showToast(`Station gewechselt: ${currentStation.name}`, 'info');
1069
- }
1070
-
1071
- // Setup event listeners
1072
- function setupEventListeners() {
1073
- playBtn.addEventListener('click', togglePlay);
1074
- prevBtn.addEventListener('click', playPrevious);
1075
- nextBtn.addEventListener('click', playNext);
1076
-
1077
- stationSelect.addEventListener('change', selectStationFromDropdown);
1078
-
1079
- volumeSlider.addEventListener('input', (e) => {
1080
- const volume = e.target.value / 100;
1081
- audioPlayer.volume = volume;
1082
- volumeValue.textContent = `${e.target.value}%`;
1083
- });
1084
- }
1085
-
1086
- // Toggle play/pause
1087
- function togglePlay() {
1088
- if (!currentStation) {
1089
- showToast('Bitte wähle zuerst eine Station aus', 'info');
1090
- return;
1091
- }
1092
-
1093
- if (isPlaying) {
1094
- audioPlayer.pause();
1095
- } else {
1096
  audioPlayer.play();
1097
- }
1098
- }
1099
-
1100
- // Play previous station
1101
- function playPrevious() {
1102
- if (currentStationIndex <= 0) {
1103
- selectStation(radioStations.length - 1);
1104
- } else {
1105
- selectStation(currentStationIndex - 1);
1106
- }
1107
- }
1108
-
1109
- // Play next station
1110
- function playNext() {
1111
- if (currentStationIndex >= radioStations.length - 1) {
1112
- selectStation(0);
1113
- } else {
1114
- selectStation(currentStationIndex + 1);
1115
- }
1116
- }
1117
-
1118
- // Show toast notification
1119
- function showToast(message, type = 'info') {
1120
- toastMessage.textContent = message;
1121
- toast.className = `toast ${type} show`;
1122
-
1123
- // Update icon based on type
1124
- const icon = toast.querySelector('i');
1125
- icon.className = type === 'success' ? 'fas fa-check-circle' :
1126
- type === 'error' ? 'fas fa-exclamation-circle' :
1127
- 'fas fa-info-circle';
1128
-
1129
- setTimeout(() => {
1130
- toast.classList.remove('show');
1131
- }, 3000);
1132
- }
1133
-
1134
- // Initialize the app
1135
- init();
1136
- </script>
1137
- </body>
1138
- </html>
 
25
  --glass-border: rgba(148, 163, 184, 0.2);
26
  --success-color: #10b981;
27
  --error-color: #ef4444;
28
+ --warning-color: #f59e0b;
29
  }
30
 
31
  body {
 
63
  0% {
64
  transform: rotate(0deg);
65
  }
 
66
  100% {
67
  transform: rotate(360deg);
68
  }
 
107
  }
108
 
109
  @keyframes pulse {
110
+ 0%, 100% {
 
 
111
  transform: scale(1);
112
  }
 
113
  50% {
114
  transform: scale(1.1);
115
  }
 
152
  }
153
 
154
  @keyframes glow-green {
155
+ 0%, 100% {
 
 
156
  box-shadow: 0 0 10px rgba(16, 185, 129, 0.5);
157
  }
 
158
  50% {
159
  box-shadow: 0 0 20px rgba(16, 185, 129, 0.8);
160
  }
161
  }
162
 
163
  @keyframes blink {
164
+ 0%, 100% {
 
 
165
  opacity: 1;
166
  }
 
167
  50% {
168
  opacity: 0.5;
169
  }
170
  }
171
 
172
+ /* Main Container */
173
  .container {
174
  max-width: 800px;
175
  margin: 2rem auto;
176
  padding: 0 2rem;
177
  }
178
 
179
+ /* Player Section */
180
  .player-section {
181
  background: var(--glass-bg);
182
  backdrop-filter: blur(10px);
 
223
  0% {
224
  transform: rotate(0deg);
225
  }
 
226
  100% {
227
  transform: rotate(360deg);
228
  }
229
  }
230
 
231
  @keyframes float {
232
+ 0%, 100% {
 
 
233
  transform: translateY(0);
234
  }
 
235
  50% {
236
  transform: translateY(-10px);
237
  }
 
310
  transform-origin: bottom;
311
  }
312
 
313
+ .bar:nth-child(1) { animation-delay: 0s; height: 25px; }
314
+ .bar:nth-child(2) { animation-delay: 0.1s; height: 35px; }
315
+ .bar:nth-child(3) { animation-delay: 0.2s; height: 30px; }
316
+ .bar:nth-child(4) { animation-delay: 0.3s; height: 40px; }
317
+ .bar:nth-child(5) { animation-delay: 0.4s; height: 25px; }
318
+ .bar:nth-child(6) { animation-delay: 0.5s; height: 35px; }
319
+ .bar:nth-child(7) { animation-delay: 0.6s; height: 30px; }
320
+ .bar:nth-child(8) { animation-delay: 0.7s; height: 40px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
321
 
322
  .visualizer.paused .bar {
323
  animation-play-state: paused;
 
325
  }
326
 
327
  @keyframes wave {
328
+ 0%, 100% {
 
 
329
  transform: scaleY(1);
330
  }
 
331
  50% {
332
  transform: scaleY(1.5);
333
  }
 
445
  box-shadow: 0 2px 10px rgba(59, 130, 246, 0.3);
446
  }
447
 
448
+ /* Station Selector */
449
  .station-selector {
450
  background: var(--glass-bg);
451
  border: 1px solid var(--glass-border);
 
512
  animation: blink 1s infinite;
513
  }
514
 
515
+ .status-indicator.warning {
516
+ background: var(--warning-color);
517
+ animation: blink 2s infinite;
518
+ }
519
+
520
  .bitrate {
521
  color: var(--secondary-color);
522
  font-weight: 600;
523
  }
524
 
525
+ /* Retry Button */
526
+ .retry-btn {
527
+ background: var(--secondary-color);
528
+ color: white;
529
+ border: none;
530
+ padding: 0.5rem 1rem;
531
+ border-radius: 8px;
532
+ cursor: pointer;
533
+ font-size: 0.9rem;
534
+ transition: all 0.3s;
535
+ }
536
+
537
+ .retry-btn:hover {
538
+ background: var(--accent-color);
539
+ transform: scale(1.05);
540
+ }
541
+
542
  /* Toast Notification */
543
  .toast {
544
  position: fixed;
 
575
  color: var(--error-color);
576
  }
577
 
578
+ .toast.warning i {
579
+ color: var(--warning-color);
580
+ }
581
+
582
  .toast.info i {
583
  color: var(--secondary-color);
584
  }
585
 
586
+ /* Error Panel */
587
+ .error-panel {
588
+ background: rgba(239, 68, 68, 0.1);
589
+ border: 1px solid var(--error-color);
590
+ border-radius: 15px;
591
+ padding: 1.5rem;
592
+ margin-top: 1rem;
593
+ display: none;
594
+ animation: slideIn 0.3s ease-out;
595
+ }
596
+
597
+ .error-panel.show {
598
+ display: block;
599
+ }
600
+
601
+ .error-panel h3 {
602
+ color: var(--error-color);
603
+ margin-bottom: 0.5rem;
604
+ display: flex;
605
+ align-items: center;
606
+ gap: 0.5rem;
607
+ }
608
+
609
+ .error-panel p {
610
+ color: var(--text-secondary);
611
+ margin-bottom: 1rem;
612
+ }
613
+
614
+ @keyframes slideIn {
615
+ from {
616
+ opacity: 0;
617
+ transform: translateY(-10px);
618
+ }
619
+ to {
620
+ opacity: 1;
621
+ transform: translateY(0);
622
+ }
623
+ }
624
+
625
  /* Hidden Stations Section */
626
  .stations-section {
627
  display: none;
 
757
  <div class="status-text">
758
  <span class="status-indicator" id="statusIndicator"></span>
759
  <span id="statusText">Bereit</span>
760
+ <button class="retry-btn" id="retryBtn" style="display: none;">
761
+ <i class="fas fa-redo"></i> Erneut versuchen
762
+ </button>
763
  </div>
764
  <div class="bitrate" id="bitrate">128 kbps</div>
765
  </div>
766
+
767
+ <div class="error-panel" id="errorPanel">
768
+ <h3><i class="fas fa-exclamation-triangle"></i> Stream-Verbindungsfehler</h3>
769
+ <p id="errorMessage">Der Stream konnte nicht geladen werden. Dies kann an Netzwerkproblemen oder daran liegen, dass der Stream derzeit nicht verfügbar ist.</p>
770
+ <button class="retry-btn" onclick="retryCurrentStation()">
771
+ <i class="fas fa-redo"></i> Nochmal versuchen
772
+ </button>
773
+ </div>
774
  </section>
775
 
776
  <section class="stations-section">
 
787
  <audio id="audioPlayer" crossorigin="anonymous"></audio>
788
 
789
  <script>
790
+ // Updated German Radio Stations with working streaming URLs and fallbacks
791
  const radioStations = [
792
  {
793
  id: 1,
 
795
  genre: "Pop",
796
  category: "pop",
797
  icon: "fa-broadcast-tower",
798
+ urls: [
799
+ "https://mp3channels.webradio.antenne.de/antenne-bayern",
800
+ "https://mp3channels.webradio.antenne.de/antenne-bayern-mp3",
801
+ "https://stream.antenne.de/antenne-bayern/live/mp3"
802
+ ],
803
  bitrate: "128"
804
  },
805
  {
 
808
  genre: "Pop & Rock",
809
  category: "pop",
810
  icon: "fa-music",
811
+ urls: [
812
+ "https://br-br3-live.cast.addradio.de/br/br3/live/mp3/128/stream.mp3",
813
+ "https://br-br3-live.cast.addradio.de/br/br3/live/mp3/64/stream.mp3"
814
+ ],
815
  bitrate: "128"
816
  },
817
  {
 
820
  genre: "Nachrichten & Info",
821
  category: "news",
822
  icon: "fa-newspaper",
823
+ urls: [
824
+ "https://st01.dlf.de/dlf/01/128/mp3/stream.mp3",
825
+ "https://st02.dlf.de/dlf/02/128/mp3/stream.mp3"
826
+ ],
827
  bitrate: "128"
828
  },
829
  {
 
832
  genre: "Pop",
833
  category: "pop",
834
  icon: "fa-radio",
835
+ urls: [
836
+ "https://hr-hr3-live.cast.addradio.de/hr/hr3/live/mp3/128/stream.mp3",
837
+ "https://hr-hr3-live.cast.addradio.de/hr/hr3/live/mp3/64/stream.mp3"
838
+ ],
839
  bitrate: "128"
840
  },
841
  {
 
844
  genre: "Pop & News",
845
  category: "pop",
846
  icon: "fa-microphone",
847
+ urls: [
848
+ "https://wdr-wdr2-live.icecast.wdr.de/wdr/wdr2/live/mp3/128/stream.mp3",
849
+ "https://wdr-wdr2-live.icecast.wdr.de/wdr/wdr2/live/mp3/64/stream.mp3"
850
+ ],
851
  bitrate: "128"
852
  },
853
  {
 
856
  genre: "Pop",
857
  category: "pop",
858
  icon: "fa-headphones",
859
+ urls: [
860
+ "https://swr-swr3-live.cast.addradio.de/swr/swr3/live/mp3/128/stream.mp3",
861
+ "https://swr-swr3-live.cast.addradio.de/swr/swr3/live/mp3/64/stream.mp3"
862
+ ],
863
  bitrate: "128"
864
  },
865
  {
 
868
  genre: "Pop & Rock",
869
  category: "rock",
870
  icon: "fa-guitar",
871
+ urls: [
872
+ "https://ndr-ndr2-niedersachsen.cast.addradio.de/ndr/ndr2/niedersachsen/mp3/128/stream.mp3",
873
+ "https://ndr-ndr2-niedersachsen.cast.addradio.de/ndr/ndr2/niedersachsen/mp3/64/stream.mp3"
874
+ ],
875
  bitrate: "128"
876
  },
877
  {
 
880
  genre: "Pop & Rock",
881
  category: "rock",
882
  icon: "fa-compact-disc",
883
+ urls: [
884
+ "https://mdr-mdr-jump-sachsen.cast.addradio.de/mdr/mdr-jump/sachsen/mp3/128/stream.mp3",
885
+ "https://mdr-mdr-jump-sachsen.cast.addradio.de/mdr/mdr-jump/sachsen/mp3/64/stream.mp3"
886
+ ],
887
  bitrate: "128"
888
  },
889
  {
 
892
  genre: "Pop",
893
  category: "pop",
894
  icon: "fa-broadcast-tower",
895
+ urls: [
896
+ "https://rbb-88-8-live.cast.addradio.de/rbb/rbb888/live/mp3/128/stream.mp3",
897
+ "https://rbb-88-8-live.cast.addradio.de/rbb/rbb888/live/mp3/64/stream.mp3"
898
+ ],
899
  bitrate: "128"
900
  },
901
  {
 
904
  genre: "Klassik",
905
  category: "classic",
906
  icon: "fa-violin",
907
+ urls: [
908
+ "https://br-brklassik-live.cast.addradio.de/br/brklassik/live/mp3/128/stream.mp3",
909
+ "https://br-brklassik-live.cast.addradio.de/br/brklassik/live/mp3/64/stream.mp3"
910
+ ],
911
  bitrate: "128"
912
  },
913
  {
 
916
  genre: "Pop & Electro",
917
  category: "pop",
918
  icon: "fa-bolt",
919
+ urls: [
920
+ "https://swr-dasding-live.cast.addradio.de/swr/dasding/live/mp3/128/stream.mp3",
921
+ "https://swr-dasding-live.cast.addradio.de/swr/dasding/live/mp3/64/stream.mp3"
922
+ ],
923
  bitrate: "128"
924
  },
925
  {
 
928
  genre: "Pop & Electro",
929
  category: "pop",
930
  icon: "fa-star",
931
+ urls: [
932
+ "https://hr-youfm-live.cast.addradio.de/hr/youfm/live/mp3/128/stream.mp3",
933
+ "https://hr-youfm-live.cast.addradio.de/hr/youfm/live/mp3/64/stream.mp3"
934
+ ],
935
  bitrate: "128"
936
  },
937
  {
 
940
  genre: "Pop & Electro",
941
  category: "pop",
942
  icon: "fa-broadcast-tower",
943
+ urls: [
944
+ "https://wdr-1live-live.icecast.wdr.de/wdr/1live/live/mp3/128/stream.mp3",
945
+ "https://wdr-1live-live.icecast.wdr.de/wdr/1live/live/mp3/64/stream.mp3"
946
+ ],
947
  bitrate: "128"
948
  },
949
  {
 
952
  genre: "Pop & Electro",
953
  category: "pop",
954
  icon: "fa-headphones",
955
+ urls: [
956
+ "https://ndr-n-joy-niedersachsen.cast.addradio.de/ndr/njoy/niedersachsen/mp3/128/stream.mp3",
957
+ "https://ndr-n-joy-niedersachsen.cast.addradio.de/ndr/njoy/niedersachsen/mp3/64/stream.mp3"
958
+ ],
959
  bitrate: "128"
960
  },
961
  {
 
964
  genre: "Nachrichten",
965
  category: "news",
966
  icon: "fa-newspaper",
967
+ urls: [
968
+ "https://br-br24-live.cast.addradio.de/br/br24/live/mp3/128/stream.mp3",
969
+ "https://br-br24-live.cast.addradio.de/br/br24/live/mp3/64/stream.mp3"
970
+ ],
971
  bitrate: "128"
972
  }
973
  ];
 
976
  let isPlaying = false;
977
  let currentStationIndex = -1;
978
  let audioPlayer;
979
+ let currentUrlIndex = 0;
980
+ let retryCount = 0;
981
+ const maxRetries = 3;
982
 
983
  // DOM Elements
984
  const playBtn = document.getElementById('playBtn');
 
997
  const statusIndicator = document.getElementById('statusIndicator');
998
  const statusText = document.getElementById('statusText');
999
  const bitrate = document.getElementById('bitrate');
1000
+ const retryBtn = document.getElementById('retryBtn');
1001
+ const errorPanel = document.getElementById('errorPanel');
1002
+ const errorMessage = document.getElementById('errorMessage');
1003
 
1004
  // Initialize
1005
  function init() {
1006
  audioPlayer = document.getElementById('audioPlayer');
1007
  audioPlayer.volume = 0.7;
1008
+ audioPlayer.preload = 'none';
1009
 
1010
  populateStationSelect();
1011
  setupEventListeners();
 
1023
  });
1024
  }
1025
 
1026
+ // Setup audio event listeners with enhanced error handling
1027
  function setupAudioEventListeners() {
1028
  audioPlayer.addEventListener('loadstart', () => {
1029
  loadingSpinner.classList.add('active');
1030
  updateStatus('Laden...', 'loading');
1031
+ hideErrorPanel();
1032
  });
1033
 
1034
  audioPlayer.addEventListener('canplay', () => {
1035
  loadingSpinner.classList.remove('active');
1036
  updateStatus('Verbunden', 'connected');
1037
+ retryCount = 0;
1038
+ currentUrlIndex = 0;
1039
  showToast('Stream erfolgreich geladen!', 'success');
1040
  });
1041
 
 
1064
  });
1065
 
1066
  audioPlayer.addEventListener('error', (e) => {
1067
+ handleAudioError(e);
1068
+ });
1069
+
1070
+ // Network status detection
1071
+ audioPlayer.addEventListener('stalled', () => {
1072
+ if (isPlaying) {
1073
+ updateStatus('Verbindung unterbrochen', 'warning');
1074
+ showToast('Verbindung unterbrochen, versuche erneut...', 'warning');
1075
+ }
1076
+ });
1077
+
1078
+ audioPlayer.addEventListener('waiting', () => {
1079
+ if (isPlaying) {
1080
+ loadingSpinner.classList.add('active');
1081
+ updateStatus('Puffern...', 'loading');
1082
+ }
1083
  });
1084
 
1085
  // Simulate track info updates
 
1087
  if (isPlaying && currentStation) {
1088
  updateTrackInfo();
1089
  }
1090
+ }, 30000);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1091
  }
1092
 
1093
+ // Handle audio errors with fallback mechanism
1094
+ function handleAudioError(error) {
1095
+ loadingSpinner.classList.remove('active');
 
1096
 
1097
+ console.error('Audio error:', error);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1098
 
1099
+ if (currentStation && currentUrlIndex < currentStation.urls.length - 1) {
1100
+ // Try next URL
1101
+ currentUrlIndex++;
1102
+ console.log(`Trying fallback URL ${currentUrlIndex + 1}/${currentStation.urls.length}`);
1103
+ audioPlayer.src = currentStation.urls[currentUrlIndex];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1104
  audioPlayer.play();
1105
+ showToast(`Versuche alternative Stream-URL...`, 'info');
1106
+ } else if (retryCount < maxRetries) {
1107
+ // Retry with same URL
1108
+ retryCount++;
1109
+ currentUrlIndex = 0;
1110
+ setTimeout(() => {
1111
+ console.log(`Retry attempt ${retryCount}/${maxRetries}`);
1112
+ audioPlayer.src = currentStation.urls[0];
1113
+ audioPlayer.play();
1114
+ showToast(`Versuch ${retryCount}/${maxRetries}...`, 'warning');
1115
+ }, 2000 *