- 1class RadioPlayer {
- 2 constructor(container, config) {
- 3 this.container = container;
- 4 this.config = config;
- 5 this.audio = new Audio();
- 6 this.isPlaying = false;
- 7 this.currentVolume = 1;
- 8 this.elapsed = 0;
- 9 this.metadata = null;
- 10 this.lastFetchTime = 0;
- 11 this.fetchInProgress = false;
- 12 this.retryCount = 0;
- 13 this.maxRetries = 3;
- 14 this.parsedHistory = null;
- 15
- 16 this.init();
- 17 }
- 18
- 19 init() {
- 20 console.log('Initializing Radio Player...');
- 21 this.setupAudio();
- 22 this.setupElements();
- 23 this.attachEventListeners();
- 24 this.setupMediaSession();
- 25 this.setupVisibilityHandling();
- 26 this.setupWebAudio();
- 27 this.loadPlayerState();
- 28 this.registerServiceWorker();
- 29 this.startMetadataPolling();
- 30 this.startTimeTracking();
- 31 }
- 32
- 33 setupAudio() {
- 34 // Configure audio with better error handling
- 35 this.audio.crossOrigin = 'anonymous';
- 36 this.audio.preload = 'none';
- 37
- 38 // Set initial source
- 39 this.audio.src = this.config.streamUrl;
- 40 console.log('Audio source set to:', this.config.streamUrl);
- 41
- 42 // Add comprehensive error handling
- 43 this.audio.addEventListener('error', (e) => this.handleAudioError(e));
- 44 this.audio.addEventListener('canplay', () => console.log('Audio: Ready to play'));
- 45 this.audio.addEventListener('loadstart', () => console.log('Audio: Loading started'));
- 46 this.audio.addEventListener('stalled', () => this.handleStreamStall());
- 47 this.audio.addEventListener('abort', () => console.log('Audio: Load aborted'));
- 48 this.audio.addEventListener('emptied', () => console.log('Audio: Media emptied'));
- 49 }
- 50
- 51 setupElements() {
- 52 // Get DOM elements
- 53 this.artworkImage = this.container.querySelector('.artwork-image');
- 54 this.artworkPlaceholder = this.container.querySelector('.artwork-placeholder');
- 55 this.songNameDiv = this.container.querySelector('.song-name');
- 56 this.artistNameDiv = this.container.querySelector('.artist-name');
- 57 this.radioNameDiv = this.container.querySelector('.radio-name');
- 58 this.liveContainer = this.container.querySelector('.live-container');
- 59 this.playButton = this.container.querySelector('.play-button');
- 60 this.volumeButton = this.container.querySelector('.volume-button');
- 61 this.volumeSlider = this.container.querySelector('.volume-slider');
- 62 this.timeBarProgress = this.container.querySelector('.time-bar-progress');
- 63 this.elapsedTimeSpan = this.container.querySelector('.elapsed-time');
- 64 this.durationTimeSpan = this.container.querySelector('.duration-time');
- 65 this.historyModal = document.querySelector('.history-modal');
- 66 this.historyList = document.querySelector('.history-list');
- 67
- 68 // Set default values
- 69 if (this.songNameDiv) this.songNameDiv.textContent = this.config.stationName || 'Paranormal FM';
- 70 if (this.artistNameDiv) this.artistNameDiv.textContent = 'Click play to start streaming';
- 71 if (this.radioNameDiv) this.radioNameDiv.textContent = this.config.stationName || 'Paranormal FM';
- 72
- 73 // Initialize volume
- 74 if (this.volumeSlider) {
- 75 this.volumeSlider.value = 100;
- 76 this.isMuted = false;
- 77 this.preMuteVolume = 1;
- 78 }
- 79
- 80 this.setupVolumeControls();
- 81 this.attachHistoryEventListeners();
- 82 }
- 83
- 84 setupVolumeControls() {
- 85 if (!this.volumeButton || !this.volumeSlider) return;
- 86
- 87 const volumeContainer = this.container.querySelector('.volume-bar-container');
- 88 let hideTimeout;
- 89
- 90 const showVolume = () => {
- 91 clearTimeout(hideTimeout);
- 92 volumeContainer.classList.add('show');
- 93 };
- 94
- 95 const hideVolume = () => {
- 96 hideTimeout = setTimeout(() => {
- 97 volumeContainer.classList.remove('show');
- 98 }, 200);
- 99 };
- 100
- 101 this.volumeButton.addEventListener('mouseenter', showVolume);
- 102 this.volumeButton.addEventListener('mouseleave', hideVolume);
- 103 volumeContainer.addEventListener('mouseenter', showVolume);
- 104 volumeContainer.addEventListener('mouseleave', hideVolume);
- 105
- 106 this.volumeButton.addEventListener('click', (e) => {
- 107 e.stopPropagation();
- 108 this.toggleMute();
- 109 });
- 110
- 111 this.volumeSlider.addEventListener('input', (e) => {
- 112 const value = e.target.value / 100;
- 113 this.setVolume(value);
- 114 });
- 115 }
- 116
- 117 attachEventListeners() {
- 118 this.audio.addEventListener('play', () => {
- 119 this.isPlaying = true;
- 120 this.updatePlayState();
- 121 this.savePlayerState();
- 122 console.log('Audio: Playing');
- 123 });
- 124
- 125 this.audio.addEventListener('pause', () => {
- 126 this.isPlaying = false;
- 127 this.updatePlayState();
- 128 this.savePlayerState();
- 129 console.log('Audio: Paused');
- 130 });
- 131
- 132 this.audio.addEventListener('ended', () => {
- 133 console.log('Audio: Ended (should not happen for streams)');
- 134 this.isPlaying = false;
- 135 this.updatePlayState();
- 136 });
- 137
- 138 if (this.playButton) {
- 139 this.playButton.addEventListener('click', () => this.togglePlay());
- 140 }
- 141 }
- 142
- 143 setupMediaSession() {
- 144 if ('mediaSession' in navigator) {
- 145 navigator.mediaSession.setActionHandler('play', () => this.startPlayback());
- 146 navigator.mediaSession.setActionHandler('pause', () => this.audio.pause());
- 147 navigator.mediaSession.setActionHandler('stop', () => {
- 148 this.audio.pause();
- 149 this.audio.currentTime = 0;
- 150 });
- 151 }
- 152 }
- 153
- 154 setupVisibilityHandling() {
- 155 document.addEventListener('visibilitychange', () => {
- 156 if (document.hidden) {
- 157 // Page is hidden - save state
- 158 this.savePlayerState();
- 159 console.log('Page hidden, state saved');
- 160 } else {
- 161 // Page is visible again - check if should resume
- 162 console.log('Page visible again');
- 163 if (this.isPlaying && this.audio.paused) {
- 164 console.log('Resuming audio playback');
- 165 this.audio.play().catch(console.error);
- 166 }
- 167 }
- 168 });
- 169
- 170 // Handle page unload
- 171 window.addEventListener('beforeunload', () => {
- 172 this.savePlayerState();
- 173 });
- 174 }
- 175
- 176 setupWebAudio() {
- 177 try {
- 178 this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
- 179 this.source = this.audioContext.createMediaElementSource(this.audio);
- 180 this.gainNode = this.audioContext.createGain();
- 181
- 182 this.source.connect(this.gainNode);
- 183 this.gainNode.connect(this.audioContext.destination);
- 184
- 185 // Keep context alive
- 186 document.addEventListener('visibilitychange', () => {
- 187 if (this.audioContext.state === 'suspended' && this.isPlaying) {
- 188 this.audioContext.resume();
- 189 }
- 190 });
- 191
- 192 console.log('Web Audio API setup complete');
- 193 } catch (error) {
- 194 console.warn('Web Audio API not supported:', error);
- 195 }
- 196 }
- 197
- 198 async registerServiceWorker() {
- 199 if ('serviceWorker' in navigator) {
- 200 try {
- 201 const registration = await navigator.serviceWorker.register(this.config.serviceWorkerUrl || '/wp-content/plugins/persistent-radio-player/sw.js');
- 202 console.log('Service Worker registered:', registration);
- 203 } catch (error) {
- 204 console.error('Service Worker registration failed:', error);
- 205 }
- 206 }
- 207 }
- 208
- 209 savePlayerState() {
- 210 const state = {
- 211 isPlaying: this.isPlaying,
- 212 volume: this.currentVolume,
- 213 elapsed: this.elapsed,
- 214 lastSong: this.metadata?.now_playing?.song,
- 215 timestamp: Date.now()
- 216 };
- 217 try {
- 218 localStorage.setItem('paranormalfm_player_state', JSON.stringify(state));
- 219 } catch (error) {
- 220 console.warn('Failed to save player state:', error);
- 221 }
- 222 }
- 223
- 224 loadPlayerState() {
- 225 try {
- 226 const saved = localStorage.getItem('paranormalfm_player_state');
- 227 if (saved) {
- 228 const state = JSON.parse(saved);
- 229
- 230 // Restore volume
- 231 if (state.volume !== undefined) {
- 232 this.setVolume(state.volume);
- 233 }
- 234
- 235 // Auto-resume if was playing recently (within 30 minutes)
- 236 const timeDiff = Date.now() - state.timestamp;
- 237 if (state.isPlaying && timeDiff < 30 * 60 * 1000) {
- 238 this.showAutoResumePrompt();
- 239 }
- 240 }
- 241 } catch (error) {
- 242 console.warn('Failed to load player state:', error);
- 243 }
- 244 }
- 245
- 246 showAutoResumePrompt() {
- 247 const resume = confirm('Resume playing Paranormal FM from your last session?');
- 248 if (resume) {
- 249 this.startPlayback();
- 250 }
- 251 }
- 252
- 253 async togglePlay() {
- 254 try {
- 255 if (this.isPlaying) {
- 256 console.log('Pausing audio...');
- 257 this.audio.pause();
- 258 } else {
- 259 console.log('Starting audio playback...');
- 260 await this.startPlayback();
- 261 }
- 262 } catch (error) {
- 263 console.error('Error in togglePlay:', error);
- 264 this.showError('Failed to start playback: ' + error.message);
- 265 }
- 266 }
- 267
- 268 async startPlayback() {
- 269 this.showLoadingState();
- 270
- 271 try {
- 272 // Resume audio context if suspended
- 273 if (this.audioContext && this.audioContext.state === 'suspended') {
- 274 await this.audioContext.resume();
- 275 console.log('Audio context resumed');
- 276 }
- 277
- 278 // Reload the audio source to ensure fresh connection
- 279 this.audio.load();
- 280 console.log('Audio loaded');
- 281
- 282 // Wait a moment for the browser to initialize
- 283 await new Promise(resolve => setTimeout(resolve, 1000));
- 284
- 285 // Attempt to play
- 286 console.log('Attempting to play audio...');
- 287 await this.audio.play();
- 288
- 289 console.log('Playback started successfully');
- 290 this.retryCount = 0;
- 291 this.hideLoadingState();
- 292
- 293 } catch (error) {
- 294 console.error('Playback failed:', error);
- 295
- 296 if (this.retryCount < this.maxRetries) {
- 297 this.retryCount++;
- 298 console.log(`Retrying playback (${this.retryCount}/${this.maxRetries})...`);
- 299 setTimeout(() => this.startPlayback(), 2000);
- 300 } else {
- 301 this.hideLoadingState();
- 302 this.handlePlaybackError(error);
- 303 }
- 304 }
- 305 }
- 306
- 307 handlePlaybackError(error) {
- 308 let errorMessage = 'Unable to connect to the radio stream.';
- 309
- 310 if (error.name === 'NotAllowedError') {
- 311 errorMessage = 'Audio playback blocked by browser. Please click the play button again.';
- 312 } else if (error.name === 'NotSupportedError') {
- 313 errorMessage = 'Audio format not supported by your browser.';
- 314 } else if (error.name === 'AbortError') {
- 315 errorMessage = 'Audio loading was interrupted.';
- 316 }
- 317
- 318 this.showError(errorMessage);
- 319 console.error('Audio error details:', error);
- 320 }
- 321
- 322 handleAudioError(error) {
- 323 const target = error.target;
- 324 let errorMessage = 'Stream connection error';
- 325
- 326 if (target && target.error) {
- 327 switch (target.error.code) {
- 328 case target.error.MEDIA_ERR_ABORTED:
- 329 errorMessage = 'Audio loading aborted';
- 330 break;
- 331 case target.error.MEDIA_ERR_NETWORK:
- 332 errorMessage = 'Network error - check your connection';
- 333 break;
- 334 case target.error.MEDIA_ERR_DECODE:
- 335 errorMessage = 'Audio decode error';
- 336 break;
- 337 case target.error.MEDIA_ERR_SRC_NOT_SUPPORTED:
- 338 errorMessage = 'Stream source not supported';
- 339 break;
- 340 default:
- 341 errorMessage = 'Unknown audio error';
- 342 }
- 343 }
- 344
- 345 console.error('Audio error:', target?.error);
- 346 this.hideLoadingState();
- 347 this.showError(errorMessage);
- 348
- 349 // Auto-retry on network errors
- 350 if (target?.error?.code === target.error.MEDIA_ERR_NETWORK && this.retryCount < this.maxRetries) {
- 351 this.retryCount++;
- 352 setTimeout(() => {
- 353 if (this.isPlaying) {
- 354 console.log(`Auto-retrying after network error (${this.retryCount}/${this.maxRetries})`);
- 355 this.startPlayback();
- 356 }
- 357 }, 3000);
- 358 }
- 359 }
- 360
- 361 handleStreamStall() {
- 362 console.log('Stream stalled, attempting recovery...');
- 363 if (this.isPlaying) {
- 364 setTimeout(() => {
- 365 this.audio.load();
- 366 this.audio.play().catch(console.error);
- 367 }, 1000);
- 368 }
- 369 }
- 370
- 371 showLoadingState() {
- 372 if (this.playButton) {
- 373 this.playButton.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
- 374 this.playButton.disabled = true;
- 375 }
- 376 }
- 377
- 378 hideLoadingState() {
- 379 if (this.playButton) {
- 380 this.playButton.disabled = false;
- 381 this.updatePlayState();
- 382 }
- 383 }
- 384
- 385 updatePlayState() {
- 386 if (this.playButton) {
- 387 const icon = this.isPlaying ? 'pause' : 'play';
- 388 this.playButton.innerHTML = `<i class="fas fa-${icon}"></i>`;
- 389 }
- 390 }
- 391
- 392 setVolume(value) {
- 393 this.audio.volume = value;
- 394 this.currentVolume = value;
- 395 if (this.gainNode) {
- 396 this.gainNode.gain.value = value;
- 397 }
- 398 this.updateVolumeState();
- 399 this.savePlayerState();
- 400 }
- 401
- 402 toggleMute() {
- 403 if (this.audio.volume > 0) {
- 404 this.preMuteVolume = this.audio.volume;
- 405 this.setVolume(0);
- 406 } else {
- 407 this.setVolume(this.preMuteVolume || 0.5);
- 408 }
- 409 }
- 410
- 411 updateVolumeState() {
- 412 if (this.volumeButton && this.volumeSlider) {
- 413 const volume = this.audio.volume;
- 414 this.volumeSlider.value = volume * 100;
- 415
- 416 let icon = 'volume-up';
- 417 if (volume === 0) icon = 'volume-mute';
- 418 else if (volume < 0.33) icon = 'volume-off';
- 419 else if (volume < 0.66) icon = 'volume-down';
- 420
- 421 this.volumeButton.innerHTML = `<i class="fas fa-${icon}"></i>`;
- 422 }
- 423 }
- 424
- 425 startMetadataPolling() {
- 426 this.fetchMetadata();
- 427 this.metadataInterval = setInterval(() => this.fetchMetadata(), 15000);
- 428 }
- 429
- 430 startTimeTracking() {
- 431 this.elapsedInterval = setInterval(() => {
- 432 if (this.isPlaying) {
- 433 this.elapsed++;
- 434 this.updateTimeDisplay();
- 435 }
- 436 }, 1000);
- 437 }
- 438
- 439 async fetchMetadata() {
- 440 if (this.fetchInProgress || Date.now() - this.lastFetchTime < 5000) return;
- 441 this.fetchInProgress = true;
- 442
- 443 try {
- 444 const response = await fetch(this.config.directApiUrl);
- 445 const data = await response.json();
- 446 this.metadata = data;
- 447 this.updateMetadata(data);
- 448 this.updateMediaSession();
- 449 this.lastFetchTime = Date.now();
- 450 console.log('Metadata updated successfully');
- 451 } catch (error) {
- 452 console.error('Error fetching metadata:', error);
- 453 } finally {
- 454 this.fetchInProgress = false;
- 455 }
- 456 }
- 457
- 458 updateMetadata(data) {
- 459 try {
- 460 // Parse the nested JSON strings from Radiolize API
- 461 let nowPlaying = null;
- 462 let songHistory = null;
- 463
- 464 if (data.now_playing) {
- 465 if (typeof data.now_playing === 'string') {
- 466 nowPlaying = JSON.parse(data.now_playing);
- 467 } else {
- 468 nowPlaying = data.now_playing;
- 469 }
- 470 }
- 471
- 472 if (data.song_history) {
- 473 if (typeof data.song_history === 'string') {
- 474 songHistory = JSON.parse(data.song_history);
- 475 } else {
- 476 songHistory = data.song_history;
- 477 }
- 478 }
- 479
- 480 if (nowPlaying && nowPlaying.song) {
- 481 // Reset elapsed time for new song
- 482 this.elapsed = Math.max(0, nowPlaying.elapsed || 0);
- 483
- 484 // Update song info
- 485 if (this.songNameDiv) {
- 486 this.songNameDiv.textContent = nowPlaying.song.title || 'Paranormal FM';
- 487 }
- 488 if (this.artistNameDiv) {
- 489 this.artistNameDiv.textContent = nowPlaying.song.artist || 'Now Playing';
- 490 }
- 491
- 492 // Update artwork (only if not generic placeholder)
- 493 if (this.artworkImage && nowPlaying.song.art &&
- 494 nowPlaying.song.art !== 'https://s76.radiolize.com/static/img/generic_song.png') {
- 495 this.artworkImage.src = nowPlaying.song.art;
- 496 this.artworkImage.style.display = 'block';
- 497 if (this.artworkPlaceholder) {
- 498 this.artworkPlaceholder.style.display = 'none';
- 499 }
- 500 } else {
- 501 if (this.artworkImage) this.artworkImage.style.display = 'none';
- 502 if (this.artworkPlaceholder) this.artworkPlaceholder.style.display = 'flex';
- 503 }
- 504 }
- 505
- 506 // Update station name
- 507 if (this.radioNameDiv && data.station?.name) {
- 508 this.radioNameDiv.textContent = data.station.name;
- 509 }
- 510
- 511 // Update live status
- 512 if (this.liveContainer && data.live) {
- 513 const isLive = data.live.is_live;
- 514 this.liveContainer.style.display = isLive ? 'flex' : 'none';
- 515 const liveText = this.liveContainer.querySelector('.live-text');
- 516 if (liveText && isLive && data.live.streamer_name) {
- 517 liveText.textContent = data.live.streamer_name;
- 518 } else if (liveText) {
- 519 liveText.textContent = 'LIVE';
- 520 }
- 521 }
- 522
- 523 // Store parsed history for modal
- 524 if (songHistory) {
- 525 this.parsedHistory = songHistory;
- 526 }
- 527
- 528 // Save state after metadata update
- 529 this.savePlayerState();
- 530
- 531 } catch (error) {
- 532 console.error('Error updating metadata:', error);
- 533 }
- 534 }
- 535
- 536 updateMediaSession() {
- 537 if ('mediaSession' in navigator && this.metadata?.now_playing) {
- 538 let nowPlaying;
- 539 try {
- 540 nowPlaying = typeof this.metadata.now_playing === 'string'
- 541 ? JSON.parse(this.metadata.now_playing)
- 542 : this.metadata.now_playing;
- 543
- 544 const song = nowPlaying.song;
- 545 navigator.mediaSession.metadata = new MediaMetadata({
- 546 title: song.title || 'Paranormal FM',
- 547 artist: song.artist || 'Live Stream',
- 548 album: 'Paranormal FM Radio',
- 549 artwork: [
- 550 {
- 551 src: song.art || 'https://s76.radiolize.com/static/img/generic_song.png',
- 552 sizes: '512x512',
- 553 type: 'image/jpeg'
- 554 }
- 555 ]
- 556 });
- 557 } catch (e) {
- 558 console.error('Error updating media session:', e);
- 559 }
- 560 }
- 561 }
- 562
- 563 formatTime(seconds) {
- 564 const mins = Math.floor(seconds / 60);
- 565 const secs = Math.floor(seconds % 60);
- 566 return `${mins}:${secs.toString().padStart(2, '0')}`;
- 567 }
- 568
- 569 updateTimeDisplay() {
- 570 if (this.elapsedTimeSpan) {
- 571 this.elapsedTimeSpan.textContent = this.formatTime(this.elapsed);
- 572 }
- 573
- 574 if (this.timeBarProgress && this.metadata?.now_playing) {
- 575 let nowPlaying;
- 576 try {
- 577 nowPlaying = typeof this.metadata.now_playing === 'string'
- 578 ? JSON.parse(this.metadata.now_playing)
- 579 : this.metadata.now_playing;
- 580
- 581 const duration = nowPlaying.duration || 0;
- 582 if (duration > 0) {
- 583 const progress = (this.elapsed / duration) * 100;
- 584 this.timeBarProgress.style.width = `${Math.min(progress, 100)}%`;
- 585 }
- 586
- 587 if (this.durationTimeSpan) {
- 588 this.durationTimeSpan.textContent = this.formatTime(duration);
- 589 }
- 590 } catch (e) {
- 591 console.error('Error parsing time data:', e);
- 592 }
- 593 }
- 594 }
- 595
- 596 showError(message) {
- 597 console.error('Radio Player Error:', message);
- 598
- 599 if (this.artistNameDiv) {
- 600 this.artistNameDiv.textContent = message;
- 601 }
- 602
- 603 // Show toast notification
- 604 const toast = document.createElement('div');
- 605 toast.style.cssText = `
- 606 position: fixed;
- 607 bottom: 120px;
- 608 right: 20px;
- 609 background: rgba(255, 0, 0, 0.9);
- 610 color: white;
- 611 padding: 12px 20px;
- 612 border-radius: 8px;
- 613 z-index: 10000;
- 614 font-family: Arial, sans-serif;
- 615 font-size: 14px;
- 616 max-width: 300px;
- 617 `;
- 618 toast.textContent = message;
- 619
- 620 document.body.appendChild(toast);
- 621 setTimeout(() => {
- 622 if (document.body.contains(toast)) {
- 623 document.body.removeChild(toast);
- 624 }
- 625 }, 5000);
- 626 }
- 627
- 628 attachHistoryEventListeners() {
- 629 const historyButton = this.container.querySelector('.history-button');
- 630 const historyModalClose = document.querySelector('.history-modal-close');
- 631 const historyModalOverlay = document.querySelector('.history-modal-overlay');
- 632
- 633 if (historyButton && this.historyModal) {
- 634 historyButton.addEventListener('click', () => this.showHistoryModal());
- 635 historyModalClose?.addEventListener('click', () => this.hideHistoryModal());
- 636 historyModalOverlay?.addEventListener('click', () => this.hideHistoryModal());
- 637 }
- 638 }
- 639
- 640 showHistoryModal() {
- 641 if (this.historyModal && this.parsedHistory) {
- 642 this.updateHistoryList();
- 643 this.historyModal.style.display = 'block';
- 644 document.body.style.overflow = 'hidden';
- 645 }
- 646 }
- 647
- 648 hideHistoryModal() {
- 649 if (this.historyModal) {
- 650 this.historyModal.style.display = 'none';
- 651 document.body.style.overflow = '';
- 652 }
- 653 }
- 654
- 655 updateHistoryList() {
- 656 if (!this.historyList || !this.parsedHistory) return;
- 657
- 658 try {
- 659 const historyArray = Object.values(this.parsedHistory);
- 660 this.historyList.innerHTML = historyArray.map(item => `
- 661 <div class="history-item">
- 662 <div class="history-artwork">
- 663 <img src="${item.song.art || 'https://s76.radiolize.com/static/img/generic_song.png'}"
- 664 alt="${item.song.title}" />
- 665 </div>
- 666 <div class="history-song-info">
- 667 <div class="history-song-title">${item.song.title}</div>
- 668 <div class="history-artist-name">${item.song.artist}</div>
- 669 </div>
- 670 </div>
- 671 `).join('');
- 672 } catch (error) {
- 673 console.error('Error updating history list:', error);
- 674 }
- 675 }
- 676
- 677 cleanup() {
- 678 if (this.elapsedInterval) {
- 679 clearInterval(this.elapsedInterval);
- 680 }
- 681 if (this.metadataInterval) {
- 682 clearInterval(this.metadataInterval);
- 683 }
- 684 if (this.audio) {
- 685 this.audio.pause();
- 686 }
- 687 }
- 688}
- 689
- 690// Initialize the player when DOM is ready
- 691document.addEventListener('DOMContentLoaded', () => {
- 692 const container = document.getElementById('radio-player');
- 693
- 694 // Use config from WordPress localization or fallback
- 695 const config = window.radioPlayerConfig || {
- 696 streamUrl: 'https://s76.radiolize.com:8050/radio.mp3',
- 697 directApiUrl: 'https://s76.radiolize.com/api/nowplaying/18',
- 698 stationName: 'Paranormal FM'
- 699 };
- 700
- 701 if (container) {
- 702 window.radioPlayer = new RadioPlayer(container, config);
- 703 console.log('Radio player initialized successfully');
- 704 } else {
- 705 console.error('Radio player container not found');
- 706 }
- 707});
- 708
- 709// Cleanup on page unload
- 710window.addEventListener('beforeunload', () => {
- 711 if (window.radioPlayer) {
- 712 window.radioPlayer.cleanup();
- 713 }
- 714});
Raw Paste