forked from Simnation/Main
401 lines
11 KiB
JavaScript
401 lines
11 KiB
JavaScript
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');
|