1
0
Fork 0
forked from Simnation/Main
This commit is contained in:
Nordi98 2025-08-03 17:09:00 +02:00
parent 9a29e35e64
commit cac2b97954
4 changed files with 338 additions and 360 deletions

View file

@ -622,7 +622,7 @@ local function extractVideoId(url)
return nil
end
-- Aktualisiere die PlayMusic Funktion
-- Aktualisierte PlayMusic Funktion
function PlayMusic(title, url, volume)
if not title or not url then
lib.notify({
@ -633,29 +633,31 @@ function PlayMusic(title, url, volume)
return
end
-- Bereinige YouTube URL
local cleanedUrl = cleanYouTubeUrl(url)
local videoId = extractVideoId(cleanedUrl)
if videoId then
-- Bereinige URL von Playlist-Parametern
local cleanUrl = url
if string.find(url, "youtube") then
cleanUrl = string.gsub(url, "&list=.-$", "")
cleanUrl = string.gsub(cleanUrl, "&start_radio=.-$", "")
cleanUrl = string.gsub(cleanUrl, "&index=.-$", "")
lib.notify({
title = 'DJ System',
description = 'YouTube Video wird geladen: ' .. title,
description = 'YouTube Video wird gestreamt: ' .. title,
type = 'info'
})
print('[DJ System] YouTube Video ID: ' .. videoId)
print('[DJ System] Bereinigte URL: ' .. cleanedUrl)
end
print('[DJ System] Streaming: ' .. title .. ' | Clean URL: ' .. cleanUrl)
-- Sende an Server
TriggerServerEvent('dj:playMusic', title, cleanedUrl, volume or 50)
TriggerServerEvent('dj:playMusic', title, cleanUrl, volume or 50)
-- Update lokale Variablen
isPlaying = true
currentSong = {
title = title,
url = cleanedUrl,
url = cleanUrl,
volume = volume or 50
}
end

View file

@ -3,30 +3,21 @@
<head>
<meta charset="utf-8">
<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">
</head>
<body>
<div id="music-player">
<!-- Normaler Audio Player für direkte URLs -->
<audio
id="audio-player"
preload="auto"
crossorigin="anonymous"
controls="false"
style="display: none;">
Dein Browser unterstützt das Audio-Element nicht.
</audio>
<!-- Optional: Progress indicator (hidden by default) -->
<div id="progress-container" style="display: none;">
<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>
<!-- YouTube Player wird dynamisch erstellt -->
<!-- Container wird von JavaScript erstellt -->
</div>
<script src="script.js"></script>

View file

@ -4,78 +4,152 @@ let isPlaying = false;
let currentSong = null;
let fadeInterval = null;
// YouTube Player API laden
let youtubeAPIReady = false;
let youtubePlayer = null;
// Initialize when page loads
document.addEventListener('DOMContentLoaded', function() {
audioPlayer = document.getElementById('audio-player');
loadYouTubeAPI();
setupAudioPlayer();
});
// Setup audio player with event listeners
function setupAudioPlayer() {
if (!audioPlayer) return;
// YouTube API laden
function loadYouTubeAPI() {
// 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);
}
// YouTube API Ready Callback
window.onYouTubeIframeAPIReady = function() {
console.log('DJ System: YouTube API ready');
youtubeAPIReady = true;
// Audio Events
audioPlayer.addEventListener('loadstart', function() {
console.log('DJ System: Loading started');
});
// Erstelle versteckten YouTube Player
createYouTubePlayer();
};
// YouTube Player erstellen
function createYouTubePlayer() {
// 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('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
youtubePlayer = new YT.Player('youtube-player-container', {
height: '1',
width: '1',
playerVars: {
'autoplay': 0,
'controls': 0,
'disablekb': 1,
'fs': 0,
'modestbranding': 1,
'playsinline': 1,
'rel': 0,
'showinfo': 0,
'iv_load_policy': 3,
'cc_load_policy': 0,
'start': 0,
'origin': window.location.origin
},
events: {
'onReady': onYouTubePlayerReady,
'onStateChange': onYouTubePlayerStateChange,
'onError': onYouTubePlayerError
}
});
}
// 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) {
const data = event.data;
@ -89,12 +163,6 @@ window.addEventListener('message', function(event) {
case 'setVolume':
setVolume(data.volume);
break;
case 'fadeOut':
fadeOut();
break;
case 'fadeIn':
fadeIn();
break;
case 'pauseMusic':
pauseMusic();
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') {
if (!audioPlayer) {
console.error('DJ System: Audio player not initialized');
return;
}
try {
// Stop current music first
console.log('DJ System: Playing:', title, url);
// Stoppe aktuelle Musik
stopMusic();
console.log('DJ System: Attempting to play:', title, url);
// Set volume
// Setze Lautstärke
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;
// Prüfe ob YouTube URL
if (isYouTubeUrl(url)) {
await playYouTubeMusic(url, volume, title);
} else {
await playDirectMusic(url, volume, title);
}
// Set source and play
audioPlayer.src = playableUrl;
audioPlayer.load();
// Store current song info
// Speichere aktuelle Song Info
currentSong = {
title: title,
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) {
console.error('DJ System: Error in playMusic:', error);
notifyError('Error playing music: ' + error.message);
console.error('DJ System: Error playing music:', error);
notifyFiveM('audioError', { error: error.message });
}
}
// Process different URL types
async function processUrl(url) {
// YouTube Musik abspielen
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 {
// Check if it's a YouTube URL
if (isYouTubeUrl(url)) {
console.log('DJ System: Processing YouTube URL');
return await convertYouTubeUrl(url);
// Stoppe YouTube Player
if (youtubePlayer && youtubeAPIReady) {
youtubePlayer.stopVideo();
}
// Check if it's a direct audio URL
if (isDirectAudioUrl(url)) {
console.log('DJ System: Direct audio URL detected');
return url;
// Stoppe Audio Player
if (audioPlayer) {
audioPlayer.pause();
audioPlayer.currentTime = 0;
audioPlayer.src = '';
}
// Try to use URL as-is (might be pre-converted)
console.log('DJ System: Using URL as-is');
return url;
isPlaying = false;
currentSong = null;
console.log('DJ System: Music stopped');
} catch (error) {
console.error('DJ System: Error processing URL:', error);
return null;
console.error('DJ System: Error stopping music:', error);
}
}
// 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) {
const youtubePatterns = [
/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\n?#]+)/,
@ -197,38 +333,7 @@ function isYouTubeUrl(url) {
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
// YouTube Video ID extrahieren
function extractYouTubeVideoId(url) {
const patterns = [
/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\n?#]+)/,
@ -245,157 +350,30 @@ function extractYouTubeVideoId(url) {
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`, {
// FiveM benachrichtigen
function notifyFiveM(event, data) {
fetch(`https://${GetParentResourceName()}/` + event, {
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=UTF-8',
'Content-Type': 'application/json'
},
body: JSON.stringify({
error: message
})
body: JSON.stringify(data)
}).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() {
return window.location.hostname;
}
// Debug functions (can be called from browser console)
// Debug
window.djDebug = {
getCurrentSong: () => currentSong,
getVolume: () => currentVolume,
isPlaying: () => isPlaying,
getAudioPlayer: () => audioPlayer,
testPlay: (url) => playMusic(url, 50, 'Test Song'),
testStop: () => stopMusic(),
testVolume: (vol) => setVolume(vol)
youtubeReady: () => youtubeAPIReady,
testYouTube: (videoId) => playYouTubeMusic('https://www.youtube.com/watch?v=' + videoId, 50, 'Test')
};
// Log when script is loaded
console.log('DJ System: Script loaded and ready');
console.log('DJ System: YouTube streaming system loaded');

View file

@ -360,54 +360,61 @@ local function useAlternativeConversion(originalUrl, videoId, callback)
end)
end
-- Aktualisiere die PlayMusic Funktion
-- Aktualisierte PlayMusic Funktion
RegisterServerEvent('dj:playMusic')
AddEventHandler('dj:playMusic', function(title, url, volume)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if not Player then return end
print('[DJ System] Spiele Musik ab: ' .. title .. ' | URL: ' .. url)
-- Prüfe ob es eine YouTube URL ist
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)
-- 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=.-$", "")
-- 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
})
print('[DJ System] Bereinigte YouTube URL: ' .. cleanUrl)
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)
-- 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)