forked from Simnation/Main
587 lines
No EOL
15 KiB
Lua
587 lines
No EOL
15 KiB
Lua
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) |