diff --git a/resources/[tools]/nordi_dj/html/index.html b/resources/[tools]/nordi_dj/html/index.html
index 6e7538855..50c3faf3b 100644
--- a/resources/[tools]/nordi_dj/html/index.html
+++ b/resources/[tools]/nordi_dj/html/index.html
@@ -123,6 +123,20 @@
+
+
diff --git a/resources/[tools]/nordi_dj/html/script.js b/resources/[tools]/nordi_dj/html/script.js
index 9d2d94831..bdb97306d 100644
--- a/resources/[tools]/nordi_dj/html/script.js
+++ b/resources/[tools]/nordi_dj/html/script.js
@@ -91,6 +91,49 @@ function initializeDJSystem() {
console.log('DJ System: Professional interface ready!');
}
+function initializeDJSystem() {
+ console.log('DJ System: Initializing professional interface...');
+
+ // Initialisiere Interface-Einstellungen
+ initializeInterfaceSettings();
+
+ // Initialize audio players
+ djInterface.decks.A.player = document.getElementById('audio-player-a');
+ djInterface.decks.B.player = document.getElementById('audio-player-b');
+
+ // Setup audio event listeners
+ setupAudioEventListeners();
+
+ // Initialize waveforms
+ initializeWaveforms();
+
+ // Start time display
+ updateTimeDisplay();
+ setInterval(updateTimeDisplay, 1000);
+
+ // Start VU meters animation
+ startVUMeters();
+
+ // Setup Interface-Kontrollen
+ setupInterfaceControls();
+
+ // Lade Playlists
+ loadPlaylists();
+
+ // Setup Playlist Event Listeners
+ setupPlaylistEventListeners();
+
+ // Event-Listener für "Neue Playlist"-Button
+ const createPlaylistBtn = document.getElementById('create-playlist-btn');
+ if (createPlaylistBtn) {
+ createPlaylistBtn.addEventListener('click', createNewPlaylist);
+ }
+
+ console.log('DJ System: Professional interface ready!');
+}
+
+
+
function setupEventListeners() {
// Knob interactions
setupKnobControls();
@@ -1410,22 +1453,40 @@ function extractYouTubeVideoId(url) {
return null;
}
-// FiveM Integration
-function notifyFiveM(event, data) {
+// Verbesserte notifyFiveM-Funktion mit Callback-Unterstützung
+function notifyFiveM(event, data, callback) {
+ const callbackId = callback ? Date.now().toString() + Math.random().toString(36).substr(2, 5) : null;
+
+ if (callback) {
+ window.callbacks = window.callbacks || {};
+ window.callbacks[callbackId] = callback;
+ }
+
fetch(`https://${GetParentResourceName()}/${event}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
- body: JSON.stringify(data)
+ body: JSON.stringify({
+ ...data,
+ callbackId: callbackId
+ })
}).catch(err => {
console.error('DJ System: Failed to notify FiveM:', err);
+ if (callback) {
+ delete window.callbacks[callbackId];
+ }
});
}
-function GetParentResourceName() {
- return window.location.hostname;
-}
+// Callback-Handler
+window.handleCallback = function(callbackId, data) {
+ if (window.callbacks && window.callbacks[callbackId]) {
+ window.callbacks[callbackId](data);
+ delete window.callbacks[callbackId];
+ }
+};
+
// Message Handler for FiveM
window.addEventListener('message', function(event) {
@@ -1589,3 +1650,290 @@ function stopAllAudioAndCloseInterface() {
stopMusic: true
});
}
+
+
+// Playlist-Verwaltung
+let playlists = [];
+let currentPlaylist = null;
+let currentPlaylistIndex = 0;
+
+// Playlist laden
+function loadPlaylists() {
+ // Lade Playlists vom Server
+ notifyFiveM('getPlaylists', {}, function(response) {
+ if (response.success && response.playlists) {
+ playlists = response.playlists;
+ updatePlaylistDisplay();
+ }
+ });
+}
+
+// Playlist-Anzeige aktualisieren
+function updatePlaylistDisplay() {
+ const playlistContainer = document.getElementById('playlist-container');
+ if (!playlistContainer) return;
+
+ playlistContainer.innerHTML = '';
+
+ if (playlists.length === 0) {
+ playlistContainer.innerHTML = '
Keine Playlists gefunden
';
+ return;
+ }
+
+ // Erstelle Playlist-Elemente
+ playlists.forEach((playlist, index) => {
+ const playlistElement = document.createElement('div');
+ playlistElement.className = 'playlist-item';
+ if (currentPlaylist && currentPlaylist.id === playlist.id) {
+ playlistElement.classList.add('active');
+ }
+
+ playlistElement.innerHTML = `
+
+
+
+
+ ${playlist.isOwner ? '' : ''}
+
+ `;
+
+ // Event-Listener für Playlist-Aktionen
+ const playBtn = playlistElement.querySelector('.playlist-play-btn');
+ if (playBtn) {
+ playBtn.addEventListener('click', () => playPlaylist(playlist));
+ }
+
+ const editBtn = playlistElement.querySelector('.playlist-edit-btn');
+ if (editBtn) {
+ editBtn.addEventListener('click', () => editPlaylist(playlist));
+ }
+
+ const deleteBtn = playlistElement.querySelector('.playlist-delete-btn');
+ if (deleteBtn) {
+ deleteBtn.addEventListener('click', () => deletePlaylist(playlist));
+ }
+
+ playlistContainer.appendChild(playlistElement);
+ });
+}
+
+// Playlist abspielen
+function playPlaylist(playlist) {
+ if (!playlist || !playlist.songs || playlist.songs.length === 0) {
+ showNotification('Diese Playlist enthält keine Songs', 'warning');
+ return;
+ }
+
+ currentPlaylist = playlist;
+ currentPlaylistIndex = 0;
+
+ // Ersten Song abspielen
+ playPlaylistSong(currentPlaylistIndex);
+}
+
+// Playlist-Song abspielen
+function playPlaylistSong(index) {
+ if (!currentPlaylist || !currentPlaylist.songs || index >= currentPlaylist.songs.length) {
+ showNotification('Playlist beendet', 'info');
+ currentPlaylist = null;
+ return;
+ }
+
+ const song = currentPlaylist.songs[index];
+
+ // Lade Song in Deck A
+ const trackData = {
+ title: song.title,
+ artist: song.artist || '',
+ url: song.url
+ };
+
+ loadTrackToPlayer('A', trackData);
+
+ // Starte Wiedergabe
+ if (!djInterface.decks.A.isPlaying) {
+ togglePlay('A');
+ }
+
+ showNotification(`Playlist: ${currentPlaylist.name} - Song ${index + 1}/${currentPlaylist.songs.length}`, 'info');
+}
+
+// Nächster Song in Playlist
+function playNextSong() {
+ if (!currentPlaylist) return;
+
+ currentPlaylistIndex++;
+ if (currentPlaylistIndex < currentPlaylist.songs.length) {
+ playPlaylistSong(currentPlaylistIndex);
+ } else {
+ showNotification('Playlist beendet', 'info');
+ currentPlaylist = null;
+ }
+}
+
+// Playlist bearbeiten
+function editPlaylist(playlist) {
+ openPlaylistEditor(playlist);
+}
+
+// Playlist löschen
+function deletePlaylist(playlist) {
+ if (confirm(`Möchtest du die Playlist "${playlist.name}" wirklich löschen?`)) {
+ notifyFiveM('deletePlaylist', { playlistId: playlist.id }, function(response) {
+ if (response.success) {
+ showNotification('Playlist gelöscht', 'success');
+ loadPlaylists(); // Playlists neu laden
+ } else {
+ showNotification('Fehler beim Löschen der Playlist', 'error');
+ }
+ });
+ }
+}
+
+// Neue Playlist erstellen
+function createNewPlaylist() {
+ const playlistName = prompt('Name der neuen Playlist:');
+ if (!playlistName) return;
+
+ notifyFiveM('createPlaylist', {
+ name: playlistName,
+ isPublic: confirm('Soll die Playlist öffentlich sein?')
+ }, function(response) {
+ if (response.success) {
+ showNotification('Playlist erstellt', 'success');
+ loadPlaylists(); // Playlists neu laden
+ } else {
+ showNotification('Fehler beim Erstellen der Playlist', 'error');
+ }
+ });
+}
+
+// Song zu Playlist hinzufügen
+function addSongToPlaylist(song, playlistId) {
+ notifyFiveM('addToPlaylist', {
+ playlistId: playlistId,
+ track: song
+ }, function(response) {
+ if (response.success) {
+ showNotification('Song zur Playlist hinzugefügt', 'success');
+ loadPlaylists(); // Playlists neu laden
+ } else {
+ showNotification('Fehler beim Hinzufügen des Songs', 'error');
+ }
+ });
+}
+
+// Playlist-Editor öffnen
+function openPlaylistEditor(playlist) {
+ // Implementiere einen Playlist-Editor
+ // Dies könnte ein Modal sein, in dem Songs hinzugefügt/entfernt werden können
+ console.log('Playlist Editor für:', playlist);
+
+ // Beispiel für ein einfaches Modal
+ const modal = document.createElement('div');
+ modal.className = 'modal';
+ modal.innerHTML = `
+
+
+
+
+ ${playlist.songs.map((song, idx) => `
+
+ ${idx + 1}. ${song.title}
+
+
+ `).join('')}
+
+
+
+
+ `;
+
+ document.body.appendChild(modal);
+
+ // Event-Listener für Modal
+ const closeBtn = modal.querySelector('.close-modal');
+ if (closeBtn) {
+ closeBtn.addEventListener('click', () => {
+ document.body.removeChild(modal);
+ });
+ }
+
+ // Event-Listener für Song-Entfernung
+ const removeBtns = modal.querySelectorAll('.remove-song');
+ removeBtns.forEach(btn => {
+ btn.addEventListener('click', () => {
+ const index = parseInt(btn.dataset.index);
+ removeSongFromPlaylist(playlist.id, playlist.songs[index].id);
+ });
+ });
+
+ // Event-Listener für Song-Hinzufügung
+ const addBtn = modal.querySelector('#add-song-btn');
+ if (addBtn) {
+ addBtn.addEventListener('click', () => {
+ const title = modal.querySelector('#song-title').value;
+ const artist = modal.querySelector('#song-artist').value;
+ const url = modal.querySelector('#song-url').value;
+
+ if (!title || !url) {
+ showNotification('Titel und URL sind erforderlich', 'error');
+ return;
+ }
+
+ addSongToPlaylist({
+ title: title,
+ artist: artist,
+ url: url
+ }, playlist.id);
+
+ // Modal schließen
+ document.body.removeChild(modal);
+ });
+ }
+}
+
+// Song aus Playlist entfernen
+function removeSongFromPlaylist(playlistId, songId) {
+ notifyFiveM('removeSongFromPlaylist', {
+ playlistId: playlistId,
+ songId: songId
+ }, function(response) {
+ if (response.success) {
+ showNotification('Song aus Playlist entfernt', 'success');
+ loadPlaylists(); // Playlists neu laden
+ } else {
+ showNotification('Fehler beim Entfernen des Songs', 'error');
+ }
+ });
+}
+
+// Event-Listener für Song-Ende (für Playlist-Fortsetzung)
+function setupPlaylistEventListeners() {
+ // Wenn ein Song endet, spiele den nächsten in der Playlist
+ document.addEventListener('songEnded', function(e) {
+ if (currentPlaylist) {
+ playNextSong();
+ }
+ });
+}
diff --git a/resources/[tools]/nordi_dj/html/style.css b/resources/[tools]/nordi_dj/html/style.css
index 5b0dd6264..75bf194b0 100644
--- a/resources/[tools]/nordi_dj/html/style.css
+++ b/resources/[tools]/nordi_dj/html/style.css
@@ -1454,3 +1454,255 @@ body {
.p-15 { padding: 15px; }
.p-20 { padding: 20px; }
+/* Playlist Styles */
+.playlist-browser {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ background: linear-gradient(145deg, #0f0f23, #1a1a2e);
+ border-radius: 15px;
+ padding: 15px;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ overflow: hidden;
+}
+
+.browser-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 15px;
+ padding-bottom: 10px;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+}
+
+.create-playlist-btn {
+ padding: 8px 12px;
+ border: none;
+ border-radius: 8px;
+ background: linear-gradient(145deg, #4ecdc4, #45b7d1);
+ color: #ffffff;
+ font-size: 12px;
+ font-weight: 600;
+ cursor: pointer;
+ transition: all 0.3s ease;
+}
+
+.create-playlist-btn:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(78, 205, 196, 0.4);
+}
+
+.playlist-container {
+ flex: 1;
+ overflow-y: auto;
+ scrollbar-width: thin;
+ scrollbar-color: #4ecdc4 transparent;
+}
+
+.playlist-container::-webkit-scrollbar {
+ width: 6px;
+}
+
+.playlist-container::-webkit-scrollbar-track {
+ background: transparent;
+}
+
+.playlist-container::-webkit-scrollbar-thumb {
+ background: #4ecdc4;
+ border-radius: 3px;
+}
+
+.playlist-item {
+ padding: 10px;
+ margin-bottom: 8px;
+ background: rgba(0, 0, 0, 0.3);
+ border-radius: 8px;
+ border: 1px solid rgba(255, 255, 255, 0.05);
+ transition: all 0.3s ease;
+}
+
+.playlist-item:hover {
+ background: rgba(78, 205, 196, 0.1);
+ border-color: rgba(78, 205, 196, 0.3);
+}
+
+.playlist-item.active {
+ background: rgba(78, 205, 196, 0.2);
+ border-color: rgba(78, 205, 196, 0.5);
+}
+
+.playlist-header {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 8px;
+}
+
+.playlist-name {
+ font-weight: 600;
+ color: #ffffff;
+}
+
+.playlist-count {
+ font-size: 12px;
+ color: #888888;
+}
+
+.playlist-actions {
+ display: flex;
+ gap: 8px;
+}
+
+.playlist-actions button {
+ width: 30px;
+ height: 30px;
+ border: none;
+ border-radius: 50%;
+ background: rgba(0, 0, 0, 0.3);
+ color: #ffffff;
+ cursor: pointer;
+ transition: all 0.3s ease;
+}
+
+.playlist-play-btn {
+ background: linear-gradient(145deg, #4ecdc4, #45b7d1) !important;
+}
+
+.playlist-edit-btn {
+ background: linear-gradient(145deg, #feca57, #ff9ff3) !important;
+}
+
+.playlist-delete-btn {
+ background: linear-gradient(145deg, #ff6b6b, #ff5722) !important;
+}
+
+.playlist-actions button:hover {
+ transform: scale(1.1);
+}
+
+.no-playlists {
+ text-align: center;
+ color: #888888;
+ padding: 20px;
+}
+
+/* Playlist Editor Modal */
+.modal {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.8);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 2000;
+}
+
+.modal-content {
+ background: linear-gradient(145deg, #1e1e2e, #2a2a3e);
+ border-radius: 20px;
+ padding: 20px;
+ width: 500px;
+ max-width: 90%;
+ max-height: 90%;
+ overflow-y: auto;
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
+}
+
+.modal-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 15px;
+ padding-bottom: 10px;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+}
+
+.modal-header h3 {
+ color: #4ecdc4;
+ font-size: 18px;
+}
+
+.close-modal {
+ width: 30px;
+ height: 30px;
+ border: none;
+ border-radius: 50%;
+ background: rgba(255, 0, 0, 0.7);
+ color: #ffffff;
+ font-size: 18px;
+ cursor: pointer;
+ transition: all 0.3s ease;
+}
+
+.close-modal:hover {
+ background: rgba(255, 0, 0, 0.9);
+ transform: scale(1.1);
+}
+
+.playlist-songs {
+ margin-bottom: 20px;
+ max-height: 200px;
+ overflow-y: auto;
+}
+
+.playlist-song {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 8px;
+ margin-bottom: 5px;
+ background: rgba(0, 0, 0, 0.2);
+ border-radius: 5px;
+}
+
+.remove-song {
+ width: 24px;
+ height: 24px;
+ border: none;
+ border-radius: 50%;
+ background: rgba(255, 0, 0, 0.7);
+ color: #ffffff;
+ cursor: pointer;
+}
+
+.add-song-form {
+ padding: 15px;
+ background: rgba(0, 0, 0, 0.2);
+ border-radius: 10px;
+}
+
+.add-song-form h4 {
+ margin-bottom: 10px;
+ color: #4ecdc4;
+}
+
+.input-group {
+ margin-bottom: 10px;
+}
+
+.input-group input {
+ width: 100%;
+ padding: 8px 12px;
+ border: none;
+ border-radius: 5px;
+ background: rgba(0, 0, 0, 0.3);
+ color: #ffffff;
+ outline: none;
+}
+
+#add-song-btn {
+ padding: 8px 15px;
+ border: none;
+ border-radius: 5px;
+ background: linear-gradient(145deg, #4ecdc4, #45b7d1);
+ color: #ffffff;
+ cursor: pointer;
+ transition: all 0.3s ease;
+}
+
+#add-song-btn:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(78, 205, 196, 0.4);
+}