let audioPlayer = null; let currentVolume = 50; let isPlaying = false; let currentSong = null; let fadeInterval = null; // Initialize when page loads document.addEventListener('DOMContentLoaded', function() { audioPlayer = document.getElementById('audio-player'); setupAudioPlayer(); }); // Setup audio player with event listeners function setupAudioPlayer() { if (!audioPlayer) return; // Audio Events audioPlayer.addEventListener('loadstart', function() { console.log('DJ System: Loading started'); }); audioPlayer.addEventListener('canplay', function() { console.log('DJ System: Can start playing'); }); audioPlayer.addEventListener('play', function() { console.log('DJ System: Playback started'); isPlaying = true; }); audioPlayer.addEventListener('pause', function() { console.log('DJ System: Playback paused'); isPlaying = false; }); audioPlayer.addEventListener('ended', function() { console.log('DJ System: Song ended'); isPlaying = false; // Notify FiveM that song ended (for playlist functionality) fetch(`https://${GetParentResourceName()}/songEnded`, { method: 'POST', headers: { 'Content-Type': 'application/json; charset=UTF-8', }, body: JSON.stringify({}) }); }); audioPlayer.addEventListener('error', function(e) { console.error('DJ System: Audio error', e); isPlaying = false; // Notify FiveM about the error fetch(`https://${GetParentResourceName()}/audioError`, { method: 'POST', headers: { 'Content-Type': 'application/json; charset=UTF-8', }, body: JSON.stringify({ error: 'Audio playback error', code: audioPlayer.error ? audioPlayer.error.code : 'unknown' }) }); }); audioPlayer.addEventListener('loadedmetadata', function() { console.log('DJ System: Metadata loaded, duration:', audioPlayer.duration); }); audioPlayer.addEventListener('timeupdate', function() { // Optional: Send progress updates if (isPlaying && audioPlayer.duration) { const progress = (audioPlayer.currentTime / audioPlayer.duration) * 100; // You can use this for progress bars if needed } }); } // Main message handler from FiveM window.addEventListener('message', function(event) { const data = event.data; switch(data.type) { case 'playMusic': playMusic(data.url, data.volume, data.title); break; case 'stopMusic': stopMusic(); break; case 'setVolume': setVolume(data.volume); break; case 'fadeOut': fadeOut(); break; case 'fadeIn': fadeIn(); break; case 'pauseMusic': pauseMusic(); break; case 'resumeMusic': resumeMusic(); break; } }); // Play music function with YouTube support async function playMusic(url, volume, title = 'Unknown') { if (!audioPlayer) { console.error('DJ System: Audio player not initialized'); return; } try { // Stop current music first stopMusic(); console.log('DJ System: Attempting to play:', title, url); // Set volume currentVolume = volume || 50; audioPlayer.volume = currentVolume / 100; // Handle different URL types let playableUrl = await processUrl(url); if (!playableUrl) { console.error('DJ System: Could not process URL:', url); notifyError('Could not process audio URL'); return; } // Set source and play audioPlayer.src = playableUrl; audioPlayer.load(); // Store current song info currentSong = { title: title, url: url, playableUrl: playableUrl }; // Attempt to play const playPromise = audioPlayer.play(); if (playPromise !== undefined) { playPromise .then(() => { console.log('DJ System: Successfully started playing:', title); isPlaying = true; }) .catch(error => { console.error('DJ System: Play failed:', error); notifyError('Playback failed: ' + error.message); }); } } catch (error) { console.error('DJ System: Error in playMusic:', error); notifyError('Error playing music: ' + error.message); } } // Process different URL types async function processUrl(url) { try { // Check if it's a YouTube URL if (isYouTubeUrl(url)) { console.log('DJ System: Processing YouTube URL'); return await convertYouTubeUrl(url); } // Check if it's a direct audio URL if (isDirectAudioUrl(url)) { console.log('DJ System: Direct audio URL detected'); return url; } // Try to use URL as-is (might be pre-converted) console.log('DJ System: Using URL as-is'); return url; } catch (error) { console.error('DJ System: Error processing URL:', error); return null; } } // Check if URL is YouTube function isYouTubeUrl(url) { const youtubePatterns = [ /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\n?#]+)/, /youtube\.com\/watch\?.*v=([^&\n?#]+)/ ]; return youtubePatterns.some(pattern => pattern.test(url)); } // Check if URL is direct audio function isDirectAudioUrl(url) { const audioExtensions = ['.mp3', '.wav', '.ogg', '.m4a', '.aac', '.flac']; const lowerUrl = url.toLowerCase(); return audioExtensions.some(ext => lowerUrl.includes(ext)) || lowerUrl.includes('audio/') || lowerUrl.includes('stream'); } // Convert YouTube URL (this would be handled server-side in real implementation) async function convertYouTubeUrl(url) { try { // Extract video ID const videoId = extractYouTubeVideoId(url); if (!videoId) { throw new Error('Could not extract YouTube video ID'); } console.log('DJ System: YouTube Video ID:', videoId); // In a real implementation, this would call your server-side converter // For now, we'll return the original URL and let the server handle it return url; } catch (error) { console.error('DJ System: YouTube conversion error:', error); return null; } } // Extract YouTube video ID function extractYouTubeVideoId(url) { const patterns = [ /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\n?#]+)/, /youtube\.com\/watch\?.*v=([^&\n?#]+)/ ]; for (let pattern of patterns) { const match = url.match(pattern); if (match && match[1]) { return match[1]; } } return null; } // Stop music function stopMusic() { if (!audioPlayer) return; try { audioPlayer.pause(); audioPlayer.currentTime = 0; audioPlayer.src = ''; isPlaying = false; currentSong = null; // Clear any fade effects if (fadeInterval) { clearInterval(fadeInterval); fadeInterval = null; } console.log('DJ System: Music stopped'); } catch (error) { console.error('DJ System: Error stopping music:', error); } } // Pause music function pauseMusic() { if (!audioPlayer || !isPlaying) return; try { audioPlayer.pause(); isPlaying = false; console.log('DJ System: Music paused'); } catch (error) { console.error('DJ System: Error pausing music:', error); } } // Resume music function resumeMusic() { if (!audioPlayer || isPlaying) return; try { const playPromise = audioPlayer.play(); if (playPromise !== undefined) { playPromise .then(() => { isPlaying = true; console.log('DJ System: Music resumed'); }) .catch(error => { console.error('DJ System: Resume failed:', error); }); } } catch (error) { console.error('DJ System: Error resuming music:', error); } } // Set volume function setVolume(volume) { if (!audioPlayer) return; try { currentVolume = Math.max(0, Math.min(100, volume)); audioPlayer.volume = currentVolume / 100; console.log('DJ System: Volume set to', currentVolume + '%'); } catch (error) { console.error('DJ System: Error setting volume:', error); } } // Fade out effect (when player moves away) function fadeOut() { if (!audioPlayer || !isPlaying) return; if (fadeInterval) { clearInterval(fadeInterval); } let currentVol = audioPlayer.volume; const targetVol = 0; const fadeStep = 0.05; fadeInterval = setInterval(() => { currentVol -= fadeStep; if (currentVol <= targetVol) { currentVol = targetVol; audioPlayer.volume = currentVol; clearInterval(fadeInterval); fadeInterval = null; } else { audioPlayer.volume = currentVol; } }, 50); } // Fade in effect (when player moves closer) function fadeIn() { if (!audioPlayer || !isPlaying) return; if (fadeInterval) { clearInterval(fadeInterval); } let currentVol = audioPlayer.volume; const targetVol = currentVolume / 100; const fadeStep = 0.05; fadeInterval = setInterval(() => { currentVol += fadeStep; if (currentVol >= targetVol) { currentVol = targetVol; audioPlayer.volume = currentVol; clearInterval(fadeInterval); fadeInterval = null; } else { audioPlayer.volume = currentVol; } }, 50); } // Notify FiveM about errors function notifyError(message) { fetch(`https://${GetParentResourceName()}/audioError`, { method: 'POST', headers: { 'Content-Type': 'application/json; charset=UTF-8', }, body: JSON.stringify({ error: message }) }).catch(err => { console.error('DJ System: Failed to notify error:', err); }); } // Get current resource name function GetParentResourceName() { return window.location.hostname; } // Debug functions (can be called from browser console) window.djDebug = { getCurrentSong: () => currentSong, getVolume: () => currentVolume, isPlaying: () => isPlaying, getAudioPlayer: () => audioPlayer, testPlay: (url) => playMusic(url, 50, 'Test Song'), testStop: () => stopMusic(), testVolume: (vol) => setVolume(vol) }; // Log when script is loaded console.log('DJ System: Script loaded and ready');