forked from Simnation/Main
ed
This commit is contained in:
parent
585ee069c2
commit
b0f9fbee65
3 changed files with 620 additions and 6 deletions
|
@ -123,6 +123,20 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="playlist-browser">
|
||||
<div class="browser-header">
|
||||
<h4>PLAYLISTS</h4>
|
||||
<button id="create-playlist-btn" class="create-playlist-btn">
|
||||
<i class="fas fa-plus"></i> Neue Playlist
|
||||
</button>
|
||||
</div>
|
||||
<div id="playlist-container" class="playlist-container">
|
||||
<!-- Playlists werden hier dynamisch eingefügt -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Channel Controls -->
|
||||
<div class="channel-controls">
|
||||
<!-- Channel A -->
|
||||
|
|
|
@ -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 = '<div class="no-playlists">Keine Playlists gefunden</div>';
|
||||
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 = `
|
||||
<div class="playlist-header">
|
||||
<span class="playlist-name">${playlist.name}</span>
|
||||
<span class="playlist-count">${playlist.songs.length} Songs</span>
|
||||
</div>
|
||||
<div class="playlist-actions">
|
||||
<button class="playlist-play-btn" title="Play"><i class="fas fa-play"></i></button>
|
||||
<button class="playlist-edit-btn" title="Edit"><i class="fas fa-edit"></i></button>
|
||||
${playlist.isOwner ? '<button class="playlist-delete-btn" title="Delete"><i class="fas fa-trash"></i></button>' : ''}
|
||||
</div>
|
||||
`;
|
||||
|
||||
// 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 = `
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3>Playlist bearbeiten: ${playlist.name}</h3>
|
||||
<button class="close-modal">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="playlist-songs">
|
||||
${playlist.songs.map((song, idx) => `
|
||||
<div class="playlist-song">
|
||||
<span>${idx + 1}. ${song.title}</span>
|
||||
<button class="remove-song" data-index="${idx}">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
<div class="add-song-form">
|
||||
<h4>Song hinzufügen</h4>
|
||||
<div class="input-group">
|
||||
<input type="text" id="song-title" placeholder="Titel">
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input type="text" id="song-artist" placeholder="Künstler (optional)">
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input type="text" id="song-url" placeholder="URL (YouTube oder direkt)">
|
||||
</div>
|
||||
<button id="add-song-btn">Song hinzufügen</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue