local QBCore = exports['qb-core']:GetCoreObject() local PlayerData = {} local currentDJBooth = nil local isPlaying = false local currentVolume = Config.DefaultVolume local currentSong = nil local playlists = {} local currentPlaylist = nil local currentSongIndex = 1 -- Events RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function() PlayerData = QBCore.Functions.GetPlayerData() end) RegisterNetEvent('QBCore:Client:OnJobUpdate', function(JobInfo) PlayerData.job = JobInfo end) -- Key Mapping RegisterKeyMapping('opendj', 'Open DJ Menu', 'keyboard', Config.OpenMenuKey) RegisterCommand('opendj', function() if not CanUseDJScript() then lib.notify({ title = 'DJ System', description = 'Du hast keine Berechtigung das DJ System zu nutzen!', type = 'error' }) return end local nearbyBooth = GetNearbyDJBooth() if nearbyBooth then currentDJBooth = nearbyBooth OpenDJMenu() else lib.notify({ title = 'DJ System', description = 'Du bist nicht in der Nähe einer DJ Booth!', type = 'error' }) end end) -- Functions function CanUseDJScript() if not Config.UseJobRestriction then return true end if not PlayerData.job then return false end for _, job in pairs(Config.AllowedJobs) do if PlayerData.job.name == job then return true end end return false end function GetNearbyDJBooth() local playerCoords = GetEntityCoords(PlayerPedId()) for i, booth in pairs(Config.DJBooths) do local distance = #(playerCoords - booth.coords) if distance <= 3.0 then return booth end end return nil end function OpenDJMenu() TriggerServerEvent('dj:server:getPlaylists') local options = { { title = 'YouTube Song abspielen', description = 'Spiele einen Song von YouTube ab', icon = 'fab fa-youtube', onSelect = function() OpenYouTubeMenu() end }, { title = 'Direkte URL abspielen', description = 'Spiele einen Song von einer direkten URL ab', icon = 'play', onSelect = function() OpenDirectUrlMenu() end }, { title = 'Musik stoppen', description = 'Stoppe die aktuelle Musik', icon = 'stop', onSelect = function() StopMusic() end }, { title = 'Lautstärke ändern', description = 'Aktuelle Lautstärke: ' .. currentVolume .. '%', icon = 'volume-up', onSelect = function() OpenVolumeMenu() end }, { title = 'Playlists verwalten', description = 'Erstelle und verwalte Playlists', icon = 'list', onSelect = function() OpenPlaylistMenu() end } } if isPlaying and currentSong then table.insert(options, 2, { title = 'Aktueller Song', description = currentSong.title, icon = 'music', disabled = true }) end lib.registerContext({ id = 'dj_main_menu', title = 'DJ System - ' .. currentDJBooth.name, options = options }) lib.showContext('dj_main_menu') end function OpenYouTubeMenu() local input = lib.inputDialog('YouTube Song abspielen', { {type = 'input', label = 'Song Titel', placeholder = 'z.B. Daft Punk - One More Time'}, {type = 'input', label = 'YouTube URL', placeholder = 'https://www.youtube.com/watch?v=...'} }) if input and input[1] and input[2] then if not IsValidYouTubeUrl(input[2]) then lib.notify({ title = 'DJ System', description = 'Ungültige YouTube URL!', type = 'error' }) return end lib.notify({ title = 'DJ System', description = 'YouTube URL wird konvertiert, bitte warten...', type = 'info' }) PlayMusic(input[1], input[2]) end end function OpenDirectUrlMenu() local input = lib.inputDialog('Direkte URL abspielen', { {type = 'input', label = 'Song Titel', placeholder = 'Titel des Songs'}, {type = 'input', label = 'Direkte MP3/Audio URL', placeholder = 'https://example.com/song.mp3'} }) if input and input[1] and input[2] then PlayMusic(input[1], input[2]) end end function IsValidYouTubeUrl(url) local patterns = { "youtube%.com/watch%?v=", "youtu%.be/", "youtube%.com/embed/" } for _, pattern in ipairs(patterns) do if string.match(url, pattern) then return true end end return false end function OpenVolumeMenu() local input = lib.inputDialog('Lautstärke einstellen', { { type = 'slider', label = 'Lautstärke (%)', description = 'Höhere Lautstärke = größere Reichweite', default = currentVolume, min = 0, max = Config.MaxVolume, step = 5 } }) if input and input[1] then SetVolume(input[1]) end end function OpenPlaylistMenu() local options = { { title = 'Neue Playlist erstellen', description = 'Erstelle eine neue Playlist', icon = 'plus', onSelect = function() CreateNewPlaylist() end } } for _, playlist in pairs(playlists) do table.insert(options, { title = playlist.name, description = #playlist.songs .. ' Songs', icon = 'music', onSelect = function() OpenPlaylistOptions(playlist) end }) end lib.registerContext({ id = 'playlist_menu', title = 'Playlist Verwaltung', menu = 'dj_main_menu', options = options }) lib.showContext('playlist_menu') end function CreateNewPlaylist() local input = lib.inputDialog('Neue Playlist', { {type = 'input', label = 'Playlist Name', placeholder = 'Meine YouTube Playlist'} }) if input and input[1] then TriggerServerEvent('dj:server:createPlaylist', input[1]) end end function OpenPlaylistOptions(playlist) local options = { { title = 'Playlist abspielen', description = 'Spiele alle Songs der Playlist ab', icon = 'play', onSelect = function() PlayPlaylist(playlist) end }, { title = 'YouTube Song hinzufügen', description = 'Füge einen YouTube Song zur Playlist hinzu', icon = 'fab fa-youtube', onSelect = function() AddYouTubeSongToPlaylist(playlist) end }, { title = 'Direkten Song hinzufügen', description = 'Füge einen Song per direkter URL hinzu', icon = 'plus', onSelect = function() AddDirectSongToPlaylist(playlist) end }, { title = 'Songs anzeigen', description = 'Zeige alle Songs in der Playlist', icon = 'list', onSelect = function() ShowPlaylistSongs(playlist) end }, { title = 'Playlist löschen', description = 'Lösche diese Playlist', icon = 'trash', onSelect = function() DeletePlaylist(playlist) end } } lib.registerContext({ id = 'playlist_options', title = playlist.name, menu = 'playlist_menu', options = options }) lib.showContext('playlist_options') end function AddYouTubeSongToPlaylist(playlist) local input = lib.inputDialog('YouTube Song hinzufügen', { {type = 'input', label = 'Song Titel', placeholder = 'z.B. Avicii - Levels'}, {type = 'input', label = 'YouTube URL', placeholder = 'https://www.youtube.com/watch?v=...'} }) if input and input[1] and input[2] then if not IsValidYouTubeUrl(input[2]) then lib.notify({ title = 'DJ System', description = 'Ungültige YouTube URL!', type = 'error' }) return end TriggerServerEvent('dj:server:addSongToPlaylist', playlist.id, { title = input[1], url = input[2] }) end end function AddDirectSongToPlaylist(playlist) local input = lib.inputDialog('Direkten Song hinzufügen', { {type = 'input', label = 'Song Titel', placeholder = 'Titel des Songs'}, {type = 'input', label = 'Direkte URL', placeholder = 'https://example.com/song.mp3'} }) if input and input[1] and input[2] then TriggerServerEvent('dj:server:addSongToPlaylist', playlist.id, { title = input[1], url = input[2] }) end end function PlayMusic(title, url) if not currentDJBooth then return end currentSong = {title = title, url = url} isPlaying = true TriggerServerEvent('dj:server:playMusic', { title = title, url = url, volume = currentVolume, booth = currentDJBooth, range = CalculateRange() }) lib.notify({ title = 'DJ System', description = 'Lade: ' .. title, type = 'info' }) end function StopMusic() if not isPlaying then lib.notify({ title = 'DJ System', description = 'Es läuft gerade keine Musik!', type = 'error' }) return end isPlaying = false currentSong = nil currentPlaylist = nil TriggerServerEvent('dj:server:stopMusic', currentDJBooth) lib.notify({ title = 'DJ System', description = 'Musik gestoppt', type = 'success' }) end function SetVolume(volume) currentVolume = volume if isPlaying then TriggerServerEvent('dj:server:setVolume', { volume = volume, booth = currentDJBooth, range = CalculateRange() }) end lib.notify({ title = 'DJ System', description = 'Lautstärke auf ' .. volume .. '% gesetzt', type = 'success' }) end function CalculateRange() local baseRange = currentDJBooth.range local maxRange = currentDJBooth.maxRange local volumePercent = currentVolume / 100 return baseRange + ((maxRange - baseRange) * volumePercent) end function PlayPlaylist(playlist) if #playlist.songs == 0 then lib.notify({ title = 'DJ System', description = 'Playlist ist leer!', type = 'error' }) return end currentPlaylist = playlist currentSongIndex = 1 local firstSong = playlist.songs[1] PlayMusic(firstSong.title, firstSong.url) lib.notify({ title = 'DJ System', description = 'Playlist "' .. playlist.name .. '" wird abgespielt', type = 'success' }) end function ShowPlaylistSongs(playlist) local options = {} for i, song in pairs(playlist.songs) do local songType = IsValidYouTubeUrl(song.url) and "YouTube" or "Direkt" table.insert(options, { title = song.title, description = 'Typ: ' .. songType .. ' | Klicken zum Abspielen', icon = IsValidYouTubeUrl(song.url) and 'fab fa-youtube' or 'music', onSelect = function() PlayMusic(song.title, song.url) end }) end if #options == 0 then table.insert(options, { title = 'Keine Songs', description = 'Diese Playlist ist leer', icon = 'exclamation' }) end lib.registerContext({ id = 'playlist_songs', title = playlist.name .. ' - Songs', menu = 'playlist_options', options = options }) lib.showContext('playlist_songs') end function DeletePlaylist(playlist) local confirm = lib.alertDialog({ header = 'Playlist löschen', content = 'Möchtest du die Playlist "' .. playlist.name .. '" wirklich löschen?', centered = true, cancel = true }) if confirm == 'confirm' then TriggerServerEvent('dj:server:deletePlaylist', playlist.id) end end -- Server Events RegisterNetEvent('dj:client:playMusic', function(data) SendNUIMessage({ type = 'playMusic', url = data.url, volume = data.volume, title = data.title }) lib.notify({ title = 'DJ System', description = 'Spielt ab: ' .. data.title, type = 'success' }) end) RegisterNetEvent('dj:client:stopMusic', function() SendNUIMessage({ type = 'stopMusic' }) end) RegisterNetEvent('dj:client:setVolume', function(volume) SendNUIMessage({ type = 'setVolume', volume = volume }) end) RegisterNetEvent('dj:client:updatePlaylists', function(data) playlists = data end) RegisterNetEvent('dj:client:notify', function(message, type) lib.notify({ title = 'DJ System', description = message, type = type or 'info' }) end) -- Music Range Check CreateThread(function() while true do Wait(1000) if isPlaying and currentDJBooth then local playerCoords = GetEntityCoords(PlayerPedId()) local distance = #(playerCoords - currentDJBooth.coords) local range = CalculateRange() if distance > range then SendNUIMessage({ type = 'fadeOut' }) else SendNUIMessage({ type = 'fadeIn' }) end end end end) -- Song End Handler RegisterNUICallback('songEnded', function(data, cb) if currentPlaylist and currentSongIndex < #currentPlaylist.songs then -- Nächster Song in Playlist currentSongIndex = currentSongIndex + 1 local nextSong = currentPlaylist.songs[currentSongIndex] PlayMusic(nextSong.title, nextSong.url) else -- Playlist beendet isPlaying = false currentSong = nil currentPlaylist = nil currentSongIndex = 1 end cb('ok') end) -- Audio Error Handler RegisterNUICallback('audioError', function(data, cb) lib.notify({ title = 'DJ System', description = 'Audio Fehler: ' .. (data.error or 'Unbekannter Fehler'), type = 'error' }) -- Reset playing state isPlaying = false currentSong = nil cb('ok') end) -- Song Progress (optional) RegisterNUICallback('songProgress', function(data, cb) -- Du kannst hier Song-Progress verarbeiten -- z.B. für eine Progress-Bar im Menü cb('ok') end) -- Füge diese Funktionen zu deiner client/main.lua hinzu: local function cleanYouTubeUrl(url) -- Entferne Playlist-Parameter und andere Parameter local patterns = { "(https://www%.youtube%.com/watch%?v=[^&]+)", "(https://youtu%.be/[^?]+)" } for _, pattern in ipairs(patterns) do local cleanUrl = string.match(url, pattern) if cleanUrl then return cleanUrl end end return url end local function extractVideoId(url) local patterns = { "youtube%.com/watch%?v=([^&]+)", "youtu%.be/([^?]+)", "youtube%.com/embed/([^?]+)" } for _, pattern in ipairs(patterns) do local videoId = string.match(url, pattern) if videoId then return videoId end end return nil end -- Aktualisierte PlayMusic Funktion function PlayMusic(title, url, volume) if not title or not url then lib.notify({ title = 'DJ System', description = 'Titel und URL sind erforderlich', type = 'error' }) return end -- 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 gestreamt: ' .. title, type = 'info' }) end print('[DJ System] Streaming: ' .. title .. ' | Clean URL: ' .. cleanUrl) -- Sende an Server TriggerServerEvent('dj:playMusic', title, cleanUrl, volume or 50) -- Update lokale Variablen isPlaying = true currentSong = { title = title, url = cleanUrl, volume = volume or 50 } end