forked from Simnation/Main
ed
This commit is contained in:
parent
9a29e35e64
commit
cac2b97954
4 changed files with 338 additions and 360 deletions
|
@ -622,7 +622,7 @@ local function extractVideoId(url)
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Aktualisiere die PlayMusic Funktion
|
-- Aktualisierte PlayMusic Funktion
|
||||||
function PlayMusic(title, url, volume)
|
function PlayMusic(title, url, volume)
|
||||||
if not title or not url then
|
if not title or not url then
|
||||||
lib.notify({
|
lib.notify({
|
||||||
|
@ -633,29 +633,31 @@ function PlayMusic(title, url, volume)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Bereinige YouTube URL
|
-- Bereinige URL von Playlist-Parametern
|
||||||
local cleanedUrl = cleanYouTubeUrl(url)
|
local cleanUrl = url
|
||||||
local videoId = extractVideoId(cleanedUrl)
|
if string.find(url, "youtube") then
|
||||||
|
cleanUrl = string.gsub(url, "&list=.-$", "")
|
||||||
|
cleanUrl = string.gsub(cleanUrl, "&start_radio=.-$", "")
|
||||||
|
cleanUrl = string.gsub(cleanUrl, "&index=.-$", "")
|
||||||
|
|
||||||
if videoId then
|
|
||||||
lib.notify({
|
lib.notify({
|
||||||
title = 'DJ System',
|
title = 'DJ System',
|
||||||
description = 'YouTube Video wird geladen: ' .. title,
|
description = 'YouTube Video wird gestreamt: ' .. title,
|
||||||
type = 'info'
|
type = 'info'
|
||||||
})
|
})
|
||||||
|
|
||||||
print('[DJ System] YouTube Video ID: ' .. videoId)
|
|
||||||
print('[DJ System] Bereinigte URL: ' .. cleanedUrl)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
print('[DJ System] Streaming: ' .. title .. ' | Clean URL: ' .. cleanUrl)
|
||||||
|
|
||||||
-- Sende an Server
|
-- Sende an Server
|
||||||
TriggerServerEvent('dj:playMusic', title, cleanedUrl, volume or 50)
|
TriggerServerEvent('dj:playMusic', title, cleanUrl, volume or 50)
|
||||||
|
|
||||||
-- Update lokale Variablen
|
-- Update lokale Variablen
|
||||||
isPlaying = true
|
isPlaying = true
|
||||||
currentSong = {
|
currentSong = {
|
||||||
title = title,
|
title = title,
|
||||||
url = cleanedUrl,
|
url = cleanUrl,
|
||||||
volume = volume or 50
|
volume = volume or 50
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -3,30 +3,21 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>DJ System</title>
|
<title>DJ System - YouTube Streaming</title>
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="music-player">
|
<div id="music-player">
|
||||||
|
<!-- Normaler Audio Player für direkte URLs -->
|
||||||
<audio
|
<audio
|
||||||
id="audio-player"
|
id="audio-player"
|
||||||
preload="auto"
|
preload="auto"
|
||||||
crossorigin="anonymous"
|
crossorigin="anonymous"
|
||||||
controls="false"
|
|
||||||
style="display: none;">
|
style="display: none;">
|
||||||
Dein Browser unterstützt das Audio-Element nicht.
|
|
||||||
</audio>
|
</audio>
|
||||||
|
|
||||||
<!-- Optional: Progress indicator (hidden by default) -->
|
<!-- YouTube Player wird dynamisch erstellt -->
|
||||||
<div id="progress-container" style="display: none;">
|
<!-- Container wird von JavaScript erstellt -->
|
||||||
<div id="progress-bar"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Optional: Song info display (hidden by default) -->
|
|
||||||
<div id="song-info" style="display: none;">
|
|
||||||
<span id="song-title"></span>
|
|
||||||
<span id="song-time"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="script.js"></script>
|
<script src="script.js"></script>
|
||||||
|
|
|
@ -4,78 +4,152 @@ let isPlaying = false;
|
||||||
let currentSong = null;
|
let currentSong = null;
|
||||||
let fadeInterval = null;
|
let fadeInterval = null;
|
||||||
|
|
||||||
|
// YouTube Player API laden
|
||||||
|
let youtubeAPIReady = false;
|
||||||
|
let youtubePlayer = null;
|
||||||
|
|
||||||
// Initialize when page loads
|
// Initialize when page loads
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
audioPlayer = document.getElementById('audio-player');
|
loadYouTubeAPI();
|
||||||
setupAudioPlayer();
|
setupAudioPlayer();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Setup audio player with event listeners
|
// YouTube API laden
|
||||||
function setupAudioPlayer() {
|
function loadYouTubeAPI() {
|
||||||
if (!audioPlayer) return;
|
// YouTube IFrame API Script laden
|
||||||
|
const tag = document.createElement('script');
|
||||||
|
tag.src = 'https://www.youtube.com/iframe_api';
|
||||||
|
const firstScriptTag = document.getElementsByTagName('script')[0];
|
||||||
|
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
|
||||||
|
}
|
||||||
|
|
||||||
// Audio Events
|
// YouTube API Ready Callback
|
||||||
audioPlayer.addEventListener('loadstart', function() {
|
window.onYouTubeIframeAPIReady = function() {
|
||||||
console.log('DJ System: Loading started');
|
console.log('DJ System: YouTube API ready');
|
||||||
});
|
youtubeAPIReady = true;
|
||||||
|
|
||||||
audioPlayer.addEventListener('canplay', function() {
|
// Erstelle versteckten YouTube Player
|
||||||
console.log('DJ System: Can start playing');
|
createYouTubePlayer();
|
||||||
});
|
};
|
||||||
|
|
||||||
audioPlayer.addEventListener('play', function() {
|
// YouTube Player erstellen
|
||||||
console.log('DJ System: Playback started');
|
function createYouTubePlayer() {
|
||||||
isPlaying = true;
|
// Container für YouTube Player
|
||||||
});
|
const playerContainer = document.createElement('div');
|
||||||
|
playerContainer.id = 'youtube-player-container';
|
||||||
|
playerContainer.style.position = 'absolute';
|
||||||
|
playerContainer.style.top = '-9999px';
|
||||||
|
playerContainer.style.left = '-9999px';
|
||||||
|
playerContainer.style.width = '1px';
|
||||||
|
playerContainer.style.height = '1px';
|
||||||
|
playerContainer.style.opacity = '0';
|
||||||
|
document.body.appendChild(playerContainer);
|
||||||
|
|
||||||
audioPlayer.addEventListener('pause', function() {
|
youtubePlayer = new YT.Player('youtube-player-container', {
|
||||||
console.log('DJ System: Playback paused');
|
height: '1',
|
||||||
isPlaying = false;
|
width: '1',
|
||||||
});
|
playerVars: {
|
||||||
|
'autoplay': 0,
|
||||||
audioPlayer.addEventListener('ended', function() {
|
'controls': 0,
|
||||||
console.log('DJ System: Song ended');
|
'disablekb': 1,
|
||||||
isPlaying = false;
|
'fs': 0,
|
||||||
// Notify FiveM that song ended (for playlist functionality)
|
'modestbranding': 1,
|
||||||
fetch(`https://${GetParentResourceName()}/songEnded`, {
|
'playsinline': 1,
|
||||||
method: 'POST',
|
'rel': 0,
|
||||||
headers: {
|
'showinfo': 0,
|
||||||
'Content-Type': 'application/json; charset=UTF-8',
|
'iv_load_policy': 3,
|
||||||
},
|
'cc_load_policy': 0,
|
||||||
body: JSON.stringify({})
|
'start': 0,
|
||||||
});
|
'origin': window.location.origin
|
||||||
});
|
},
|
||||||
|
events: {
|
||||||
audioPlayer.addEventListener('error', function(e) {
|
'onReady': onYouTubePlayerReady,
|
||||||
console.error('DJ System: Audio error', e);
|
'onStateChange': onYouTubePlayerStateChange,
|
||||||
isPlaying = false;
|
'onError': onYouTubePlayerError
|
||||||
// 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
|
function onYouTubePlayerReady(event) {
|
||||||
|
console.log('DJ System: YouTube Player ready');
|
||||||
|
}
|
||||||
|
|
||||||
|
function onYouTubePlayerStateChange(event) {
|
||||||
|
switch(event.data) {
|
||||||
|
case YT.PlayerState.PLAYING:
|
||||||
|
console.log('DJ System: YouTube playing');
|
||||||
|
isPlaying = true;
|
||||||
|
break;
|
||||||
|
case YT.PlayerState.PAUSED:
|
||||||
|
console.log('DJ System: YouTube paused');
|
||||||
|
isPlaying = false;
|
||||||
|
break;
|
||||||
|
case YT.PlayerState.ENDED:
|
||||||
|
console.log('DJ System: YouTube ended');
|
||||||
|
isPlaying = false;
|
||||||
|
notifyFiveM('songEnded', {});
|
||||||
|
break;
|
||||||
|
case YT.PlayerState.BUFFERING:
|
||||||
|
console.log('DJ System: YouTube buffering');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onYouTubePlayerError(event) {
|
||||||
|
console.error('DJ System: YouTube error:', event.data);
|
||||||
|
let errorMessage = 'YouTube Fehler';
|
||||||
|
|
||||||
|
switch(event.data) {
|
||||||
|
case 2:
|
||||||
|
errorMessage = 'Ungültige Video ID';
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
errorMessage = 'HTML5 Player Fehler';
|
||||||
|
break;
|
||||||
|
case 100:
|
||||||
|
errorMessage = 'Video nicht gefunden';
|
||||||
|
break;
|
||||||
|
case 101:
|
||||||
|
case 150:
|
||||||
|
errorMessage = 'Video nicht verfügbar (Einbettung deaktiviert)';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyFiveM('audioError', { error: errorMessage });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup normaler Audio Player für direkte URLs
|
||||||
|
function setupAudioPlayer() {
|
||||||
|
audioPlayer = document.getElementById('audio-player');
|
||||||
|
if (!audioPlayer) {
|
||||||
|
audioPlayer = document.createElement('audio');
|
||||||
|
audioPlayer.id = 'audio-player';
|
||||||
|
audioPlayer.style.display = 'none';
|
||||||
|
document.body.appendChild(audioPlayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
audioPlayer.addEventListener('play', () => {
|
||||||
|
isPlaying = true;
|
||||||
|
console.log('DJ System: Audio playing');
|
||||||
|
});
|
||||||
|
|
||||||
|
audioPlayer.addEventListener('pause', () => {
|
||||||
|
isPlaying = false;
|
||||||
|
console.log('DJ System: Audio paused');
|
||||||
|
});
|
||||||
|
|
||||||
|
audioPlayer.addEventListener('ended', () => {
|
||||||
|
isPlaying = false;
|
||||||
|
notifyFiveM('songEnded', {});
|
||||||
|
});
|
||||||
|
|
||||||
|
audioPlayer.addEventListener('error', (e) => {
|
||||||
|
console.error('DJ System: Audio error', e);
|
||||||
|
notifyFiveM('audioError', { error: 'Audio playback error' });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main message handler
|
||||||
window.addEventListener('message', function(event) {
|
window.addEventListener('message', function(event) {
|
||||||
const data = event.data;
|
const data = event.data;
|
||||||
|
|
||||||
|
@ -89,12 +163,6 @@ window.addEventListener('message', function(event) {
|
||||||
case 'setVolume':
|
case 'setVolume':
|
||||||
setVolume(data.volume);
|
setVolume(data.volume);
|
||||||
break;
|
break;
|
||||||
case 'fadeOut':
|
|
||||||
fadeOut();
|
|
||||||
break;
|
|
||||||
case 'fadeIn':
|
|
||||||
fadeIn();
|
|
||||||
break;
|
|
||||||
case 'pauseMusic':
|
case 'pauseMusic':
|
||||||
pauseMusic();
|
pauseMusic();
|
||||||
break;
|
break;
|
||||||
|
@ -104,90 +172,158 @@ window.addEventListener('message', function(event) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Play music function with YouTube support
|
// Musik abspielen - YouTube oder direkte URL
|
||||||
async function playMusic(url, volume, title = 'Unknown') {
|
async function playMusic(url, volume, title = 'Unknown') {
|
||||||
if (!audioPlayer) {
|
|
||||||
console.error('DJ System: Audio player not initialized');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Stop current music first
|
console.log('DJ System: Playing:', title, url);
|
||||||
|
|
||||||
|
// Stoppe aktuelle Musik
|
||||||
stopMusic();
|
stopMusic();
|
||||||
|
|
||||||
console.log('DJ System: Attempting to play:', title, url);
|
// Setze Lautstärke
|
||||||
|
|
||||||
// Set volume
|
|
||||||
currentVolume = volume || 50;
|
currentVolume = volume || 50;
|
||||||
audioPlayer.volume = currentVolume / 100;
|
|
||||||
|
|
||||||
// Handle different URL types
|
// Prüfe ob YouTube URL
|
||||||
let playableUrl = await processUrl(url);
|
if (isYouTubeUrl(url)) {
|
||||||
|
await playYouTubeMusic(url, volume, title);
|
||||||
if (!playableUrl) {
|
} else {
|
||||||
console.error('DJ System: Could not process URL:', url);
|
await playDirectMusic(url, volume, title);
|
||||||
notifyError('Could not process audio URL');
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set source and play
|
// Speichere aktuelle Song Info
|
||||||
audioPlayer.src = playableUrl;
|
|
||||||
audioPlayer.load();
|
|
||||||
|
|
||||||
// Store current song info
|
|
||||||
currentSong = {
|
currentSong = {
|
||||||
title: title,
|
title: title,
|
||||||
url: url,
|
url: url,
|
||||||
playableUrl: playableUrl
|
volume: volume
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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) {
|
} catch (error) {
|
||||||
console.error('DJ System: Error in playMusic:', error);
|
console.error('DJ System: Error playing music:', error);
|
||||||
notifyError('Error playing music: ' + error.message);
|
notifyFiveM('audioError', { error: error.message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process different URL types
|
// YouTube Musik abspielen
|
||||||
async function processUrl(url) {
|
async function playYouTubeMusic(url, volume, title) {
|
||||||
|
if (!youtubeAPIReady || !youtubePlayer) {
|
||||||
|
throw new Error('YouTube Player nicht bereit');
|
||||||
|
}
|
||||||
|
|
||||||
|
const videoId = extractYouTubeVideoId(url);
|
||||||
|
if (!videoId) {
|
||||||
|
throw new Error('Ungültige YouTube URL');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('DJ System: Playing YouTube video:', videoId);
|
||||||
|
|
||||||
|
// Lade und spiele YouTube Video
|
||||||
|
youtubePlayer.loadVideoById({
|
||||||
|
videoId: videoId,
|
||||||
|
startSeconds: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
// Setze Lautstärke
|
||||||
|
youtubePlayer.setVolume(volume || 50);
|
||||||
|
|
||||||
|
// Warte kurz und starte
|
||||||
|
setTimeout(() => {
|
||||||
|
youtubePlayer.playVideo();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Direkte Audio URL abspielen
|
||||||
|
async function playDirectMusic(url, volume, title) {
|
||||||
|
if (!audioPlayer) {
|
||||||
|
throw new Error('Audio Player nicht verfügbar');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('DJ System: Playing direct audio:', url);
|
||||||
|
|
||||||
|
audioPlayer.src = url;
|
||||||
|
audioPlayer.volume = (volume || 50) / 100;
|
||||||
|
audioPlayer.load();
|
||||||
|
|
||||||
|
const playPromise = audioPlayer.play();
|
||||||
|
if (playPromise !== undefined) {
|
||||||
|
await playPromise;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Musik stoppen
|
||||||
|
function stopMusic() {
|
||||||
try {
|
try {
|
||||||
// Check if it's a YouTube URL
|
// Stoppe YouTube Player
|
||||||
if (isYouTubeUrl(url)) {
|
if (youtubePlayer && youtubeAPIReady) {
|
||||||
console.log('DJ System: Processing YouTube URL');
|
youtubePlayer.stopVideo();
|
||||||
return await convertYouTubeUrl(url);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if it's a direct audio URL
|
// Stoppe Audio Player
|
||||||
if (isDirectAudioUrl(url)) {
|
if (audioPlayer) {
|
||||||
console.log('DJ System: Direct audio URL detected');
|
audioPlayer.pause();
|
||||||
return url;
|
audioPlayer.currentTime = 0;
|
||||||
|
audioPlayer.src = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to use URL as-is (might be pre-converted)
|
isPlaying = false;
|
||||||
console.log('DJ System: Using URL as-is');
|
currentSong = null;
|
||||||
return url;
|
|
||||||
|
console.log('DJ System: Music stopped');
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('DJ System: Error processing URL:', error);
|
console.error('DJ System: Error stopping music:', error);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if URL is YouTube
|
// Musik pausieren
|
||||||
|
function pauseMusic() {
|
||||||
|
try {
|
||||||
|
if (currentSong && isYouTubeUrl(currentSong.url)) {
|
||||||
|
if (youtubePlayer && youtubeAPIReady) {
|
||||||
|
youtubePlayer.pauseVideo();
|
||||||
|
}
|
||||||
|
} else if (audioPlayer) {
|
||||||
|
audioPlayer.pause();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('DJ System: Error pausing music:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Musik fortsetzen
|
||||||
|
function resumeMusic() {
|
||||||
|
try {
|
||||||
|
if (currentSong && isYouTubeUrl(currentSong.url)) {
|
||||||
|
if (youtubePlayer && youtubeAPIReady) {
|
||||||
|
youtubePlayer.playVideo();
|
||||||
|
}
|
||||||
|
} else if (audioPlayer) {
|
||||||
|
audioPlayer.play();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('DJ System: Error resuming music:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lautstärke setzen
|
||||||
|
function setVolume(volume) {
|
||||||
|
try {
|
||||||
|
currentVolume = Math.max(0, Math.min(100, volume));
|
||||||
|
|
||||||
|
if (currentSong && isYouTubeUrl(currentSong.url)) {
|
||||||
|
if (youtubePlayer && youtubeAPIReady) {
|
||||||
|
youtubePlayer.setVolume(currentVolume);
|
||||||
|
}
|
||||||
|
} else if (audioPlayer) {
|
||||||
|
audioPlayer.volume = currentVolume / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('DJ System: Volume set to', currentVolume + '%');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('DJ System: Error setting volume:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// YouTube URL prüfen
|
||||||
function isYouTubeUrl(url) {
|
function isYouTubeUrl(url) {
|
||||||
const youtubePatterns = [
|
const youtubePatterns = [
|
||||||
/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\n?#]+)/,
|
/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\n?#]+)/,
|
||||||
|
@ -197,38 +333,7 @@ function isYouTubeUrl(url) {
|
||||||
return youtubePatterns.some(pattern => pattern.test(url));
|
return youtubePatterns.some(pattern => pattern.test(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if URL is direct audio
|
// YouTube Video ID extrahieren
|
||||||
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) {
|
function extractYouTubeVideoId(url) {
|
||||||
const patterns = [
|
const patterns = [
|
||||||
/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\n?#]+)/,
|
/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\n?#]+)/,
|
||||||
|
@ -245,157 +350,30 @@ function extractYouTubeVideoId(url) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop music
|
// FiveM benachrichtigen
|
||||||
function stopMusic() {
|
function notifyFiveM(event, data) {
|
||||||
if (!audioPlayer) return;
|
fetch(`https://${GetParentResourceName()}/` + event, {
|
||||||
|
|
||||||
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',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json; charset=UTF-8',
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify(data)
|
||||||
error: message
|
|
||||||
})
|
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
console.error('DJ System: Failed to notify error:', err);
|
console.error('DJ System: Failed to notify FiveM:', err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get current resource name
|
|
||||||
function GetParentResourceName() {
|
function GetParentResourceName() {
|
||||||
return window.location.hostname;
|
return window.location.hostname;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug functions (can be called from browser console)
|
// Debug
|
||||||
window.djDebug = {
|
window.djDebug = {
|
||||||
getCurrentSong: () => currentSong,
|
getCurrentSong: () => currentSong,
|
||||||
getVolume: () => currentVolume,
|
getVolume: () => currentVolume,
|
||||||
isPlaying: () => isPlaying,
|
isPlaying: () => isPlaying,
|
||||||
getAudioPlayer: () => audioPlayer,
|
youtubeReady: () => youtubeAPIReady,
|
||||||
testPlay: (url) => playMusic(url, 50, 'Test Song'),
|
testYouTube: (videoId) => playYouTubeMusic('https://www.youtube.com/watch?v=' + videoId, 50, 'Test')
|
||||||
testStop: () => stopMusic(),
|
|
||||||
testVolume: (vol) => setVolume(vol)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Log when script is loaded
|
console.log('DJ System: YouTube streaming system loaded');
|
||||||
console.log('DJ System: Script loaded and ready');
|
|
||||||
|
|
|
@ -360,54 +360,61 @@ local function useAlternativeConversion(originalUrl, videoId, callback)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Aktualisiere die PlayMusic Funktion
|
-- Aktualisierte PlayMusic Funktion
|
||||||
RegisterServerEvent('dj:playMusic')
|
RegisterServerEvent('dj:playMusic')
|
||||||
AddEventHandler('dj:playMusic', function(title, url, volume)
|
AddEventHandler('dj:playMusic', function(title, url, volume)
|
||||||
local src = source
|
local src = source
|
||||||
local Player = QBCore.Functions.GetPlayer(src)
|
local Player = QBCore.Functions.GetPlayer(src)
|
||||||
if not Player then return end
|
if not Player then return end
|
||||||
|
|
||||||
print('[DJ System] Spiele Musik ab: ' .. title .. ' | URL: ' .. url)
|
-- Bereinige YouTube URL (entferne Playlist-Parameter)
|
||||||
|
local cleanUrl = url
|
||||||
|
if string.find(url, "youtube%.com") or string.find(url, "youtu%.be") then
|
||||||
|
-- Entferne &list= und andere Parameter
|
||||||
|
cleanUrl = string.gsub(url, "&list=.-$", "")
|
||||||
|
cleanUrl = string.gsub(cleanUrl, "&start_radio=.-$", "")
|
||||||
|
cleanUrl = string.gsub(cleanUrl, "&index=.-$", "")
|
||||||
|
|
||||||
-- Prüfe ob es eine YouTube URL ist
|
print('[DJ System] Bereinigte YouTube URL: ' .. cleanUrl)
|
||||||
if string.match(url, "youtube%.com") or string.match(url, "youtu%.be") then
|
|
||||||
convertYouTubeUrl(url, function(convertedUrl, error)
|
|
||||||
if error then
|
|
||||||
TriggerClientEvent('QBCore:Notify', src, 'Fehler beim Konvertieren: ' .. error, 'error')
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if convertedUrl then
|
|
||||||
-- Sende konvertierte URL an alle Clients
|
|
||||||
TriggerClientEvent('dj:playMusicClient', -1, title, convertedUrl, volume)
|
|
||||||
|
|
||||||
-- Speichere in Session History
|
|
||||||
MySQL.Async.execute('INSERT INTO dj_session_history (dj_citizenid, dj_name, booth_name, song_title, song_url, song_type, volume, session_start) VALUES (?, ?, ?, ?, ?, ?, ?, NOW())', {
|
|
||||||
Player.PlayerData.citizenid,
|
|
||||||
Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname,
|
|
||||||
'Unknown', -- Du kannst hier den Booth-Namen hinzufügen
|
|
||||||
title,
|
|
||||||
url,
|
|
||||||
'youtube',
|
|
||||||
volume
|
|
||||||
})
|
|
||||||
else
|
|
||||||
TriggerClientEvent('QBCore:Notify', src, 'Konvertierung fehlgeschlagen', 'error')
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
else
|
|
||||||
-- Direkte Audio URL
|
|
||||||
TriggerClientEvent('dj:playMusicClient', -1, title, url, volume)
|
|
||||||
|
|
||||||
-- Speichere in Session History
|
|
||||||
MySQL.Async.execute('INSERT INTO dj_session_history (dj_citizenid, dj_name, booth_name, song_title, song_url, song_type, volume, session_start) VALUES (?, ?, ?, ?, ?, ?, ?, NOW())', {
|
|
||||||
Player.PlayerData.citizenid,
|
|
||||||
Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname,
|
|
||||||
'Unknown',
|
|
||||||
title,
|
|
||||||
url,
|
|
||||||
'direct',
|
|
||||||
volume
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
print('[DJ System] Streaming: ' .. title .. ' | URL: ' .. cleanUrl)
|
||||||
|
|
||||||
|
-- Sende direkt an alle Clients zum Streamen
|
||||||
|
TriggerClientEvent('dj:playMusicClient', -1, title, cleanUrl, volume)
|
||||||
|
|
||||||
|
-- Speichere in Datenbank
|
||||||
|
local songType = 'direct'
|
||||||
|
if string.find(cleanUrl, "youtube") then
|
||||||
|
songType = 'youtube'
|
||||||
|
end
|
||||||
|
|
||||||
|
MySQL.Async.execute('INSERT INTO dj_session_history (dj_citizenid, dj_name, booth_name, song_title, song_url, song_type, volume, session_start) VALUES (?, ?, ?, ?, ?, ?, ?, NOW())', {
|
||||||
|
Player.PlayerData.citizenid,
|
||||||
|
Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname,
|
||||||
|
'DJ Booth',
|
||||||
|
title,
|
||||||
|
cleanUrl,
|
||||||
|
songType,
|
||||||
|
volume
|
||||||
|
})
|
||||||
|
|
||||||
|
TriggerClientEvent('QBCore:Notify', src, 'Streaming: ' .. title, 'success')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
-- Audio Error Handler
|
||||||
|
RegisterNUICallback('audioError', function(data, cb)
|
||||||
|
local src = source
|
||||||
|
print('[DJ System] Audio Error: ' .. (data.error or 'Unknown'))
|
||||||
|
|
||||||
|
TriggerClientEvent('QBCore:Notify', src, 'Audio Fehler: ' .. (data.error or 'Unbekannt'), 'error')
|
||||||
|
cb('ok')
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Song Ended Handler
|
||||||
|
RegisterNUICallback('songEnded', function(data, cb)
|
||||||
|
print('[DJ System] Song ended')
|
||||||
|
-- Hier könntest du Playlist-Logik hinzufügen
|
||||||
|
cb('ok')
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue