1855 lines
		
	
	
	
		
			55 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			1855 lines
		
	
	
	
		
			55 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
// DJ System - Professional Interface
 | 
						|
let djInterface = {
 | 
						|
    decks: {
 | 
						|
        A: {
 | 
						|
            track: null,
 | 
						|
            isPlaying: false,
 | 
						|
            volume: 75,
 | 
						|
            pitch: 0,
 | 
						|
            cuePoint: 0,
 | 
						|
            player: null,
 | 
						|
            youtubePlayer: null
 | 
						|
        },
 | 
						|
        B: {
 | 
						|
            track: null,
 | 
						|
            isPlaying: false,
 | 
						|
            volume: 75,
 | 
						|
            pitch: 0,
 | 
						|
            cuePoint: 0,
 | 
						|
            player: null,
 | 
						|
            youtubePlayer: null
 | 
						|
        }
 | 
						|
    },
 | 
						|
    mixer: {
 | 
						|
        crossfader: 50,
 | 
						|
        masterVolume: 80,
 | 
						|
        eq: {
 | 
						|
            A: { high: 0, mid: 0, low: 0 },
 | 
						|
            B: { high: 0, mid: 0, low: 0 }
 | 
						|
        }
 | 
						|
    },
 | 
						|
    effects: {
 | 
						|
        reverb: false,
 | 
						|
        delay: false,
 | 
						|
        filter: false,
 | 
						|
        flanger: false,
 | 
						|
        wetDry: 0
 | 
						|
    },
 | 
						|
    currentDeck: null,
 | 
						|
    isRecording: false,
 | 
						|
    youtubeAPIReady: false
 | 
						|
};
 | 
						|
 | 
						|
// Interface Drag & Resize Variablen
 | 
						|
let isDragging = false;
 | 
						|
let isResizing = false;
 | 
						|
let dragStartX = 0;
 | 
						|
let dragStartY = 0;
 | 
						|
let resizeStartWidth = 0;
 | 
						|
let resizeStartHeight = 0;
 | 
						|
let interfacePosition = { x: 0, y: 0 };
 | 
						|
let interfaceSize = { width: 1000, height: 700 }; // Standard-Größe
 | 
						|
 | 
						|
// Initialize DJ Interface
 | 
						|
document.addEventListener('DOMContentLoaded', function() {
 | 
						|
    initializeDJSystem();
 | 
						|
    setupEventListeners();
 | 
						|
    startAnimations();
 | 
						|
    loadYouTubeAPI();
 | 
						|
});
 | 
						|
 | 
						|
function initializeDJSystem() {
 | 
						|
    console.log('DJ System: Initializing professional interface...');
 | 
						|
 | 
						|
    // Lösche gespeicherte Einstellungen, um die neue Position zu erzwingen
 | 
						|
    localStorage.removeItem('djInterfacePosition');
 | 
						|
    localStorage.removeItem('djInterfaceSize');
 | 
						|
    
 | 
						|
    // 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();
 | 
						|
    
 | 
						|
    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();
 | 
						|
    
 | 
						|
    // Keyboard shortcuts
 | 
						|
    document.addEventListener('keydown', handleKeyboardShortcuts);
 | 
						|
    
 | 
						|
    // Window resize
 | 
						|
    window.addEventListener('resize', handleResize);
 | 
						|
}
 | 
						|
 | 
						|
function setupAudioEventListeners() {
 | 
						|
    // Setup event listeners for audio players
 | 
						|
    if (djInterface.decks.A.player) {
 | 
						|
        djInterface.decks.A.player.addEventListener('ended', function() {
 | 
						|
            notifyFiveM('songEnded', { deck: 'A' });
 | 
						|
        });
 | 
						|
        
 | 
						|
        djInterface.decks.A.player.addEventListener('error', function(e) {
 | 
						|
            notifyFiveM('audioError', { 
 | 
						|
                deck: 'A', 
 | 
						|
                error: 'Audio error: ' + (e.target.error ? e.target.error.message : 'Unknown error') 
 | 
						|
            });
 | 
						|
        });
 | 
						|
    }
 | 
						|
    
 | 
						|
    if (djInterface.decks.B.player) {
 | 
						|
        djInterface.decks.B.player.addEventListener('ended', function() {
 | 
						|
            notifyFiveM('songEnded', { deck: 'B' });
 | 
						|
        });
 | 
						|
        
 | 
						|
        djInterface.decks.B.player.addEventListener('error', function(e) {
 | 
						|
            notifyFiveM('audioError', { 
 | 
						|
                deck: 'B', 
 | 
						|
                error: 'Audio error: ' + (e.target.error ? e.target.error.message : 'Unknown error') 
 | 
						|
            });
 | 
						|
        });
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function setupKnobControls() {
 | 
						|
    const knobs = document.querySelectorAll('.knob');
 | 
						|
    
 | 
						|
    knobs.forEach(knob => {
 | 
						|
        let isDragging = false;
 | 
						|
        let startY = 0;
 | 
						|
        let startRotation = 0;
 | 
						|
        
 | 
						|
        knob.addEventListener('mousedown', (e) => {
 | 
						|
            isDragging = true;
 | 
						|
            startY = e.clientY;
 | 
						|
            startRotation = getCurrentRotation(knob.querySelector('.knob-indicator'));
 | 
						|
            document.body.style.cursor = 'grabbing';
 | 
						|
            e.preventDefault();
 | 
						|
        });
 | 
						|
        
 | 
						|
        document.addEventListener('mousemove', (e) => {
 | 
						|
            if (!isDragging) return;
 | 
						|
            
 | 
						|
            const deltaY = startY - e.clientY;
 | 
						|
            const rotation = Math.max(-150, Math.min(150, startRotation + deltaY * 2));
 | 
						|
            
 | 
						|
            const indicator = knob.querySelector('.knob-indicator');
 | 
						|
            indicator.style.transform = `translateX(-50%) rotate(${rotation}deg)`;
 | 
						|
            
 | 
						|
            // Update EQ values
 | 
						|
            const channel = knob.dataset.channel;
 | 
						|
            const eqType = knob.dataset.eq;
 | 
						|
            const effect = knob.dataset.effect;
 | 
						|
            
 | 
						|
            if (channel && eqType) {
 | 
						|
                const value = Math.round((rotation / 150) * 100);
 | 
						|
                djInterface.mixer.eq[channel][eqType] = value;
 | 
						|
                
 | 
						|
                const valueSpan = knob.parentElement.querySelector('.eq-value');
 | 
						|
                if (valueSpan) {
 | 
						|
                    valueSpan.textContent = value > 0 ? `+${value}` : value;
 | 
						|
                }
 | 
						|
                
 | 
						|
                applyEQ(channel, eqType, value);
 | 
						|
            }
 | 
						|
            
 | 
						|
            if (effect) {
 | 
						|
                const value = Math.round(((rotation + 150) / 300) * 100);
 | 
						|
                djInterface.effects[effect] = value;
 | 
						|
                applyEffect(effect, value);
 | 
						|
            }
 | 
						|
        });
 | 
						|
        
 | 
						|
        document.addEventListener('mouseup', () => {
 | 
						|
            if (isDragging) {
 | 
						|
                isDragging = false;
 | 
						|
                document.body.style.cursor = 'default';
 | 
						|
            }
 | 
						|
        });
 | 
						|
    });
 | 
						|
}
 | 
						|
 | 
						|
function getCurrentRotation(element) {
 | 
						|
    if (!element) return 0;
 | 
						|
    
 | 
						|
    const transform = window.getComputedStyle(element).transform;
 | 
						|
    if (transform === 'none') return 0;
 | 
						|
    
 | 
						|
    const matrix = transform.match(/matrix\(([^)]+)\)/);
 | 
						|
    if (!matrix) return 0;
 | 
						|
    
 | 
						|
    const values = matrix[1].split(',').map(parseFloat);
 | 
						|
    const angle = Math.atan2(values[1], values[0]) * (180 / Math.PI);
 | 
						|
    return angle;
 | 
						|
}
 | 
						|
 | 
						|
// 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');
 | 
						|
    djInterface.youtubeAPIReady = true;
 | 
						|
};
 | 
						|
 | 
						|
// Deck Functions
 | 
						|
function loadTrackToDeck(deck) {
 | 
						|
    djInterface.currentDeck = deck;
 | 
						|
    document.getElementById('track-loader').classList.remove('hidden');
 | 
						|
}
 | 
						|
 | 
						|
function confirmLoadTrack() {
 | 
						|
    const title = document.getElementById('track-title').value;
 | 
						|
    const artist = document.getElementById('track-artist').value;
 | 
						|
    const url = document.getElementById('track-url').value;
 | 
						|
    
 | 
						|
    if (!title || !url) {
 | 
						|
        showNotification('Please fill in title and URL', 'error');
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    
 | 
						|
    const deck = djInterface.currentDeck;
 | 
						|
    const trackData = { title, artist, url };
 | 
						|
    
 | 
						|
    loadTrackToPlayer(deck, trackData);
 | 
						|
    closeTrackLoader();
 | 
						|
    
 | 
						|
    showNotification(`Track loaded to Deck ${deck}: ${title}`, 'success');
 | 
						|
}
 | 
						|
 | 
						|
function loadTrackToPlayer(deck, trackData) {
 | 
						|
    const deckData = djInterface.decks[deck];
 | 
						|
    deckData.track = trackData;
 | 
						|
    
 | 
						|
    // Update UI
 | 
						|
    document.getElementById(`track-name-${deck.toLowerCase()}`).textContent = trackData.title.substring(0, 15);
 | 
						|
    document.getElementById(`title-${deck.toLowerCase()}`).textContent = trackData.title;
 | 
						|
    document.getElementById(`artist-${deck.toLowerCase()}`).textContent = trackData.artist || 'Unknown Artist';
 | 
						|
    
 | 
						|
    // Load audio
 | 
						|
    if (isYouTubeUrl(trackData.url)) {
 | 
						|
        loadYouTubeTrack(deck, trackData);
 | 
						|
    } else {
 | 
						|
        loadDirectTrack(deck, trackData);
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Generate waveform
 | 
						|
    generateWaveform(deck, trackData.url);
 | 
						|
}
 | 
						|
 | 
						|
function loadYouTubeTrack(deck, trackData) {
 | 
						|
    const videoId = extractYouTubeVideoId(trackData.url);
 | 
						|
    if (!videoId) {
 | 
						|
        showNotification('Invalid YouTube URL', 'error');
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Create hidden YouTube player for this deck
 | 
						|
    createYouTubePlayerForDeck(deck, videoId);
 | 
						|
}
 | 
						|
 | 
						|
function createYouTubePlayerForDeck(deck, videoId) {
 | 
						|
    if (!djInterface.youtubeAPIReady) {
 | 
						|
        console.error('DJ System: YouTube API not ready');
 | 
						|
        showNotification('YouTube API not ready', 'error');
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Container für YouTube Player erstellen
 | 
						|
    const containerId = `youtube-player-${deck.toLowerCase()}`;
 | 
						|
    let container = document.getElementById(containerId);
 | 
						|
    
 | 
						|
    if (!container) {
 | 
						|
        container = document.createElement('div');
 | 
						|
        container.id = containerId;
 | 
						|
        container.style.position = 'absolute';
 | 
						|
        container.style.top = '-9999px';
 | 
						|
        container.style.left = '-9999px';
 | 
						|
        container.style.width = '1px';
 | 
						|
        container.style.height = '1px';
 | 
						|
        container.style.opacity = '0';
 | 
						|
        document.body.appendChild(container);
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Bestehenden Player zerstören, falls vorhanden
 | 
						|
    if (djInterface.decks[deck].youtubePlayer) {
 | 
						|
        try {
 | 
						|
            djInterface.decks[deck].youtubePlayer.destroy();
 | 
						|
        } catch (e) {
 | 
						|
            console.error('DJ System: Error destroying YouTube player', e);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Neuen Player erstellen
 | 
						|
    djInterface.decks[deck].youtubePlayer = new YT.Player(containerId, {
 | 
						|
        height: '1',
 | 
						|
        width: '1',
 | 
						|
        videoId: videoId,
 | 
						|
        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,
 | 
						|
            'origin': window.location.origin
 | 
						|
        },
 | 
						|
        events: {
 | 
						|
            'onReady': function(event) {
 | 
						|
                console.log(`DJ System: YouTube player ready for Deck ${deck}`);
 | 
						|
                // Setze Lautstärke
 | 
						|
                event.target.setVolume(djInterface.decks[deck].volume);
 | 
						|
                
 | 
						|
                // Generiere Waveform
 | 
						|
                generateWaveform(deck, videoId);
 | 
						|
                
 | 
						|
                // Aktualisiere UI
 | 
						|
                const vinyl = document.getElementById(`vinyl-${deck.toLowerCase()}`);
 | 
						|
                if (vinyl) {
 | 
						|
                    vinyl.classList.add('loaded');
 | 
						|
                }
 | 
						|
            },
 | 
						|
            'onStateChange': function(event) {
 | 
						|
                handleYouTubeStateChange(deck, event);
 | 
						|
            },
 | 
						|
            'onError': function(event) {
 | 
						|
                handleYouTubeError(deck, event);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    });
 | 
						|
}
 | 
						|
 | 
						|
// YouTube State Change Handler
 | 
						|
function handleYouTubeStateChange(deck, event) {
 | 
						|
    const deckData = djInterface.decks[deck];
 | 
						|
    const playBtn = document.getElementById(`play-${deck.toLowerCase()}`);
 | 
						|
    const vinyl = document.getElementById(`vinyl-${deck.toLowerCase()}`);
 | 
						|
    
 | 
						|
    switch(event.data) {
 | 
						|
        case YT.PlayerState.PLAYING:
 | 
						|
            console.log(`DJ System: YouTube playing on Deck ${deck}`);
 | 
						|
            deckData.isPlaying = true;
 | 
						|
            if (playBtn) {
 | 
						|
                playBtn.innerHTML = '<i class="fas fa-pause"></i>';
 | 
						|
                playBtn.classList.add('playing');
 | 
						|
            }
 | 
						|
            if (vinyl) {
 | 
						|
                vinyl.classList.add('spinning');
 | 
						|
            }
 | 
						|
            
 | 
						|
            // Notify FiveM
 | 
						|
            notifyFiveM('deckStateChanged', {
 | 
						|
                deck: deck,
 | 
						|
                isPlaying: true,
 | 
						|
                track: deckData.track
 | 
						|
            });
 | 
						|
            break;
 | 
						|
            
 | 
						|
        case YT.PlayerState.PAUSED:
 | 
						|
            console.log(`DJ System: YouTube paused on Deck ${deck}`);
 | 
						|
            deckData.isPlaying = false;
 | 
						|
            if (playBtn) {
 | 
						|
                playBtn.innerHTML = '<i class="fas fa-play"></i>';
 | 
						|
                playBtn.classList.remove('playing');
 | 
						|
            }
 | 
						|
            if (vinyl) {
 | 
						|
                vinyl.classList.remove('spinning');
 | 
						|
            }
 | 
						|
            
 | 
						|
            // Notify FiveM
 | 
						|
            notifyFiveM('deckStateChanged', {
 | 
						|
                deck: deck,
 | 
						|
                isPlaying: false,
 | 
						|
                track: deckData.track
 | 
						|
            });
 | 
						|
            break;
 | 
						|
            
 | 
						|
        case YT.PlayerState.ENDED:
 | 
						|
            console.log(`DJ System: YouTube ended on Deck ${deck}`);
 | 
						|
            deckData.isPlaying = false;
 | 
						|
            if (playBtn) {
 | 
						|
                playBtn.innerHTML = '<i class="fas fa-play"></i>';
 | 
						|
                playBtn.classList.remove('playing');
 | 
						|
            }
 | 
						|
            if (vinyl) {
 | 
						|
                vinyl.classList.remove('spinning');
 | 
						|
            }
 | 
						|
            
 | 
						|
            // Notify FiveM
 | 
						|
            notifyFiveM('songEnded', {
 | 
						|
                deck: deck
 | 
						|
            });
 | 
						|
            break;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// YouTube Error Handler
 | 
						|
function handleYouTubeError(deck, event) {
 | 
						|
    console.error(`DJ System: YouTube error on Deck ${deck}:`, 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;
 | 
						|
    }
 | 
						|
    
 | 
						|
    showNotification(errorMessage, 'error');
 | 
						|
    
 | 
						|
    // Notify FiveM
 | 
						|
    notifyFiveM('audioError', {
 | 
						|
        deck: deck,
 | 
						|
        error: errorMessage
 | 
						|
    });
 | 
						|
}
 | 
						|
 | 
						|
function loadDirectTrack(deck, trackData) {
 | 
						|
    const player = djInterface.decks[deck].player;
 | 
						|
    player.src = trackData.url;
 | 
						|
    player.load();
 | 
						|
}
 | 
						|
 | 
						|
function togglePlay(deck) {
 | 
						|
    const deckData = djInterface.decks[deck];
 | 
						|
    const playBtn = document.getElementById(`play-${deck.toLowerCase()}`);
 | 
						|
    const vinyl = document.getElementById(`vinyl-${deck.toLowerCase()}`);
 | 
						|
    
 | 
						|
    if (!deckData.track) {
 | 
						|
        showNotification(`No track loaded in Deck ${deck}`, 'warning');
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    
 | 
						|
    if (deckData.isPlaying) {
 | 
						|
        // Pause
 | 
						|
        pauseTrack(deck);
 | 
						|
        playBtn.innerHTML = '<i class="fas fa-play"></i>';
 | 
						|
        playBtn.classList.remove('playing');
 | 
						|
        vinyl.classList.remove('spinning');
 | 
						|
        deckData.isPlaying = false;
 | 
						|
    } else {
 | 
						|
        // Play
 | 
						|
        playTrack(deck);
 | 
						|
        playBtn.innerHTML = '<i class="fas fa-pause"></i>';
 | 
						|
        playBtn.classList.add('playing');
 | 
						|
        vinyl.classList.add('spinning');
 | 
						|
        deckData.isPlaying = true;
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Send to FiveM
 | 
						|
    notifyFiveM('deckStateChanged', {
 | 
						|
        deck: deck,
 | 
						|
        isPlaying: deckData.isPlaying,
 | 
						|
        track: deckData.track
 | 
						|
    });
 | 
						|
}
 | 
						|
 | 
						|
function playTrack(deck) {
 | 
						|
    const deckData = djInterface.decks[deck];
 | 
						|
    
 | 
						|
    if (deckData.youtubePlayer) {
 | 
						|
        deckData.youtubePlayer.playVideo();
 | 
						|
    } else if (deckData.player) {
 | 
						|
        deckData.player.play();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function pauseTrack(deck) {
 | 
						|
    const deckData = djInterface.decks[deck];
 | 
						|
    
 | 
						|
    if (deckData.youtubePlayer) {
 | 
						|
        deckData.youtubePlayer.pauseVideo();
 | 
						|
    } else if (deckData.player) {
 | 
						|
        deckData.player.pause();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function cue(deck) {
 | 
						|
    const deckData = djInterface.decks[deck];
 | 
						|
    
 | 
						|
    if (!deckData.track) {
 | 
						|
        showNotification(`No track loaded in Deck ${deck}`, 'warning');
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Set cue point to current position or go to cue point
 | 
						|
    if (deckData.isPlaying) {
 | 
						|
        deckData.cuePoint = getCurrentTime(deck);
 | 
						|
        showNotification(`Cue point set in Deck ${deck}`, 'info');
 | 
						|
    } else {
 | 
						|
        seekTo(deck, deckData.cuePoint);
 | 
						|
        showNotification(`Jumped to cue point in Deck ${deck}`, 'info');
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function adjustPitch(deck, value) {
 | 
						|
    const deckData = djInterface.decks[deck];
 | 
						|
    deckData.pitch = parseFloat(value);
 | 
						|
    
 | 
						|
    document.getElementById(`pitch-value-${deck.toLowerCase()}`).textContent = `${value}%`;
 | 
						|
    
 | 
						|
    // Apply pitch change (this would need audio processing)
 | 
						|
    applyPitchChange(deck, value);
 | 
						|
}
 | 
						|
 | 
						|
function adjustVolume(deck, value) {
 | 
						|
    const deckData = djInterface.decks[deck];
 | 
						|
    deckData.volume = parseInt(value);
 | 
						|
    
 | 
						|
    if (deckData.youtubePlayer) {
 | 
						|
        deckData.youtubePlayer.setVolume(value);
 | 
						|
    } else if (deckData.player) {
 | 
						|
        deckData.player.volume = value / 100;
 | 
						|
    }
 | 
						|
    
 | 
						|
    updateVUMeter(deck, value);
 | 
						|
    
 | 
						|
    // Notify FiveM about volume change
 | 
						|
    notifyFiveM('volumeChanged', {
 | 
						|
        deck: deck,
 | 
						|
        volume: value
 | 
						|
    });
 | 
						|
}
 | 
						|
 | 
						|
function adjustCrossfader(value) {
 | 
						|
    djInterface.mixer.crossfader = parseInt(value);
 | 
						|
    
 | 
						|
    const volumeA = value <= 50 ? 100 : (100 - (value - 50) * 2);
 | 
						|
    const volumeB = value >= 50 ? 100 : (value * 2);
 | 
						|
    
 | 
						|
    // Apply crossfader mixing
 | 
						|
    applyCrossfaderMix(volumeA, volumeB);
 | 
						|
    
 | 
						|
    // Visual feedback
 | 
						|
    const crossfader = document.getElementById('crossfader');
 | 
						|
    crossfader.style.background = `linear-gradient(90deg, 
 | 
						|
        rgba(78, 205, 196, ${volumeA/100}) 0%, 
 | 
						|
        rgba(255, 107, 107, 0.5) 50%, 
 | 
						|
        rgba(78, 205, 196, ${volumeB/100}) 100%)`;
 | 
						|
}
 | 
						|
 | 
						|
function applyCrossfaderMix(volumeA, volumeB) {
 | 
						|
    const deckA = djInterface.decks.A;
 | 
						|
    const deckB = djInterface.decks.B;
 | 
						|
    
 | 
						|
    const finalVolumeA = (deckA.volume * volumeA / 100) / 100;
 | 
						|
    const finalVolumeB = (deckB.volume * volumeB / 100) / 100;
 | 
						|
    
 | 
						|
    if (deckA.youtubePlayer) {
 | 
						|
        deckA.youtubePlayer.setVolume(finalVolumeA * 100);
 | 
						|
    } else if (deckA.player) {
 | 
						|
        deckA.player.volume = finalVolumeA;
 | 
						|
    }
 | 
						|
    
 | 
						|
    if (deckB.youtubePlayer) {
 | 
						|
        deckB.youtubePlayer.setVolume(finalVolumeB * 100);
 | 
						|
    } else if (deckB.player) {
 | 
						|
        deckB.player.volume = finalVolumeB;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function adjustMasterVolume(value) {
 | 
						|
    djInterface.mixer.masterVolume = parseInt(value);
 | 
						|
    
 | 
						|
    // Apply master volume to both decks
 | 
						|
    const masterMultiplier = value / 100;
 | 
						|
    
 | 
						|
    Object.keys(djInterface.decks).forEach(deck => {
 | 
						|
        const deckData = djInterface.decks[deck];
 | 
						|
        const finalVolume = (deckData.volume * masterMultiplier) / 100;
 | 
						|
        
 | 
						|
        if (deckData.youtubePlayer) {
 | 
						|
            deckData.youtubePlayer.setVolume(finalVolume * 100);
 | 
						|
        } else if (deckData.player) {
 | 
						|
            deckData.player.volume = finalVolume;
 | 
						|
        }
 | 
						|
    });
 | 
						|
    
 | 
						|
    updateMasterVU(value);
 | 
						|
}
 | 
						|
 | 
						|
function setCrossfaderCurve(type) {
 | 
						|
    document.querySelectorAll('.curve-btn').forEach(btn => btn.classList.remove('active'));
 | 
						|
    event.target.classList.add('active');
 | 
						|
    
 | 
						|
    djInterface.mixer.crossfaderCurve = type;
 | 
						|
    showNotification(`Crossfader curve set to ${type}`, 'info');
 | 
						|
}
 | 
						|
 | 
						|
// EQ Functions
 | 
						|
function applyEQ(channel, eqType, value) {
 | 
						|
    const deckData = djInterface.decks[channel];
 | 
						|
    
 | 
						|
    if (!deckData.audioContext) {
 | 
						|
        setupAudioContext(channel);
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Apply EQ filter (simplified)
 | 
						|
    console.log(`Applying ${eqType} EQ: ${value}% to Deck ${channel}`);
 | 
						|
    
 | 
						|
    // Visual feedback
 | 
						|
    const knob = document.querySelector(`[data-channel="${channel}"][data-eq="${eqType}"] .knob-indicator`);
 | 
						|
    if (knob) {
 | 
						|
        const hue = value > 0 ? 120 : value < 0 ? 0 : 200;
 | 
						|
        knob.style.boxShadow = `0 0 8px hsl(${hue}, 70%, 50%)`;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Effects Functions
 | 
						|
function toggleEffect(effectType) {
 | 
						|
    const isActive = djInterface.effects[effectType];
 | 
						|
    djInterface.effects[effectType] = !isActive;
 | 
						|
    
 | 
						|
    const btn = event.target;
 | 
						|
    btn.classList.toggle('active');
 | 
						|
    
 | 
						|
    if (djInterface.effects[effectType]) {
 | 
						|
        applyEffect(effectType, 50);
 | 
						|
        showNotification(`${effectType.toUpperCase()} ON`, 'success');
 | 
						|
    } else {
 | 
						|
        removeEffect(effectType);
 | 
						|
        showNotification(`${effectType.toUpperCase()} OFF`, 'info');
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function applyEffect(effectType, value) {
 | 
						|
    console.log(`Applying ${effectType} effect: ${value}%`);
 | 
						|
    
 | 
						|
    // Effect processing would go here
 | 
						|
    switch(effectType) {
 | 
						|
        case 'reverb':
 | 
						|
            applyReverb(value);
 | 
						|
            break;
 | 
						|
        case 'delay':
 | 
						|
            applyDelay(value);
 | 
						|
            break;
 | 
						|
        case 'filter':
 | 
						|
            applyFilter(value);
 | 
						|
            break;
 | 
						|
        case 'flanger':
 | 
						|
            applyFlanger(value);
 | 
						|
            break;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function removeEffect(effectType) {
 | 
						|
    console.log(`Removing ${effectType} effect`);
 | 
						|
    // Remove effect processing
 | 
						|
}
 | 
						|
 | 
						|
// Waveform Functions
 | 
						|
function initializeWaveforms() {
 | 
						|
    const canvasA = document.getElementById('waveform-a');
 | 
						|
    const canvasB = document.getElementById('waveform-b');
 | 
						|
    
 | 
						|
    if (canvasA && canvasB) {
 | 
						|
        drawEmptyWaveform(canvasA);
 | 
						|
        drawEmptyWaveform(canvasB);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function drawEmptyWaveform(canvas) {
 | 
						|
    if (!canvas) return;
 | 
						|
    
 | 
						|
    const ctx = canvas.getContext('2d');
 | 
						|
    const width = canvas.width;
 | 
						|
    const height = canvas.height;
 | 
						|
    
 | 
						|
    ctx.clearRect(0, 0, width, height);
 | 
						|
    
 | 
						|
    // Draw center line
 | 
						|
    ctx.strokeStyle = 'rgba(78, 205, 196, 0.3)';
 | 
						|
    ctx.lineWidth = 1;
 | 
						|
    ctx.beginPath();
 | 
						|
    ctx.moveTo(0, height / 2);
 | 
						|
    ctx.lineTo(width, height / 2);
 | 
						|
    ctx.stroke();
 | 
						|
    
 | 
						|
    // Draw grid
 | 
						|
    ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)';
 | 
						|
    for (let i = 0; i < width; i += 20) {
 | 
						|
        ctx.beginPath();
 | 
						|
        ctx.moveTo(i, 0);
 | 
						|
        ctx.lineTo(i, height);
 | 
						|
        ctx.stroke();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function generateWaveform(deck, url) {
 | 
						|
    const canvas = document.getElementById(`waveform-${deck.toLowerCase()}`);
 | 
						|
    if (!canvas) return;
 | 
						|
    
 | 
						|
    const ctx = canvas.getContext('2d');
 | 
						|
    
 | 
						|
    // Simulate waveform generation
 | 
						|
    drawSimulatedWaveform(ctx, canvas.width, canvas.height);
 | 
						|
}
 | 
						|
 | 
						|
function drawSimulatedWaveform(ctx, width, height) {
 | 
						|
    ctx.clearRect(0, 0, width, height);
 | 
						|
    
 | 
						|
    const centerY = height / 2;
 | 
						|
    const gradient = ctx.createLinearGradient(0, 0, 0, height);
 | 
						|
    gradient.addColorStop(0, 'rgba(255, 107, 107, 0.8)');
 | 
						|
    gradient.addColorStop(0.5, 'rgba(78, 205, 196, 0.8)');
 | 
						|
    gradient.addColorStop(1, 'rgba(255, 107, 107, 0.8)');
 | 
						|
    
 | 
						|
    ctx.fillStyle = gradient;
 | 
						|
    
 | 
						|
    for (let x = 0; x < width; x += 2) {
 | 
						|
        const amplitude = Math.random() * (height / 2) * (0.3 + Math.sin(x * 0.01) * 0.7);
 | 
						|
        ctx.fillRect(x, centerY - amplitude, 2, amplitude * 2);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// VU Meters
 | 
						|
function startVUMeters() {
 | 
						|
    setInterval(() => {
 | 
						|
        updateVUMeter('A', djInterface.decks.A.isPlaying ? Math.random() * 100 : 0);
 | 
						|
        updateVUMeter('B', djInterface.decks.B.isPlaying ? Math.random() * 100 : 0);
 | 
						|
        updateMasterVU(djInterface.mixer.masterVolume);
 | 
						|
    }, 100);
 | 
						|
}
 | 
						|
 | 
						|
function updateVUMeter(deck, level) {
 | 
						|
    const vuBar = document.getElementById(`vu-${deck.toLowerCase()}`);
 | 
						|
    if (vuBar) {
 | 
						|
        vuBar.style.height = `${level}%`;
 | 
						|
        
 | 
						|
        // Color based on level
 | 
						|
        if (level > 80) {
 | 
						|
            vuBar.style.background = 'linear-gradient(180deg, #ff0000, #ff6b6b)';
 | 
						|
        } else if (level > 60) {
 | 
						|
            vuBar.style.background = 'linear-gradient(180deg, #feca57, #ff6b6b)';
 | 
						|
        } else {
 | 
						|
            vuBar.style.background = 'linear-gradient(180deg, #4ecdc4, #feca57)';
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function updateMasterVU(level) {
 | 
						|
    const masterVU = document.getElementById('master-vu');
 | 
						|
    if (masterVU) {
 | 
						|
        masterVU.style.height = `${level}%`;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Time and BPM
 | 
						|
function updateTimeDisplay() {
 | 
						|
    const now = new Date();
 | 
						|
    const timeString = now.toLocaleTimeString('de-DE', { 
 | 
						|
        hour: '2-digit', 
 | 
						|
        minute: '2-digit' 
 | 
						|
    });
 | 
						|
    
 | 
						|
    const timeDisplay = document.getElementById('current-time');
 | 
						|
    if (timeDisplay) {
 | 
						|
        timeDisplay.textContent = timeString;
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Update track times
 | 
						|
    updateTrackTimes();
 | 
						|
    
 | 
						|
    // Update BPM (simulated)
 | 
						|
    updateBPMDisplay();
 | 
						|
}
 | 
						|
 | 
						|
function updateTrackTimes() {
 | 
						|
    ['A', 'B'].forEach(deck => {
 | 
						|
        const deckData = djInterface.decks[deck];
 | 
						|
        if (deckData.track && deckData.isPlaying) {
 | 
						|
            const currentTime = getCurrentTime(deck);
 | 
						|
            const duration = getDuration(deck);
 | 
						|
            
 | 
						|
            const elapsedElement = document.getElementById(`time-elapsed-${deck.toLowerCase()}`);
 | 
						|
            const totalElement = document.getElementById(`time-total-${deck.toLowerCase()}`);
 | 
						|
            
 | 
						|
            if (elapsedElement) {
 | 
						|
                elapsedElement.textContent = formatTime(currentTime);
 | 
						|
            }
 | 
						|
            
 | 
						|
            if (totalElement) {
 | 
						|
                totalElement.textContent = formatTime(duration);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    });
 | 
						|
}
 | 
						|
 | 
						|
function getCurrentTime(deck) {
 | 
						|
    const deckData = djInterface.decks[deck];
 | 
						|
    
 | 
						|
    if (deckData.youtubePlayer) {
 | 
						|
        return deckData.youtubePlayer.getCurrentTime() || 0;
 | 
						|
    } else if (deckData.player) {
 | 
						|
        return deckData.player.currentTime || 0;
 | 
						|
    }
 | 
						|
    
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
function getDuration(deck) {
 | 
						|
    const deckData = djInterface.decks[deck];
 | 
						|
    
 | 
						|
    if (deckData.youtubePlayer) {
 | 
						|
        return deckData.youtubePlayer.getDuration() || 0;
 | 
						|
    } else if (deckData.player) {
 | 
						|
        return deckData.player.duration || 0;
 | 
						|
    }
 | 
						|
    
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
function formatTime(seconds) {
 | 
						|
    const mins = Math.floor(seconds / 60);
 | 
						|
    const secs = Math.floor(seconds % 60);
 | 
						|
    return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
 | 
						|
}
 | 
						|
 | 
						|
function updateBPMDisplay() {
 | 
						|
    // Simulate BPM detection
 | 
						|
    const bpm = 120 + Math.floor(Math.random() * 40);
 | 
						|
    const bpmDisplay = document.getElementById('bpm-display');
 | 
						|
    if (bpmDisplay) {
 | 
						|
        bpmDisplay.textContent = bpm;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Recording Functions
 | 
						|
function toggleRecording() {
 | 
						|
    djInterface.isRecording = !djInterface.isRecording;
 | 
						|
    
 | 
						|
    const recordBtn = document.querySelector('.record-btn');
 | 
						|
    const recordTime = document.querySelector('.recording-time');
 | 
						|
    
 | 
						|
    if (djInterface.isRecording) {
 | 
						|
        recordBtn.classList.add('recording');
 | 
						|
        recordBtn.innerHTML = '<i class="fas fa-stop"></i><span>STOP</span>';
 | 
						|
        startRecordingTimer();
 | 
						|
        showNotification('Recording started', 'success');
 | 
						|
    } else {
 | 
						|
        recordBtn.classList.remove('recording');
 | 
						|
        recordBtn.innerHTML = '<i class="fas fa-circle"></i><span>REC</span>';
 | 
						|
        stopRecordingTimer();
 | 
						|
        showNotification('Recording stopped', 'info');
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
let recordingStartTime = 0;
 | 
						|
let recordingTimer = null;
 | 
						|
 | 
						|
function startRecordingTimer() {
 | 
						|
    recordingStartTime = Date.now();
 | 
						|
    recordingTimer = setInterval(() => {
 | 
						|
        const elapsed = Math.floor((Date.now() - recordingStartTime) / 1000);
 | 
						|
        const recordTime = document.querySelector('.recording-time');
 | 
						|
        if (recordTime) {
 | 
						|
            recordTime.textContent = formatTime(elapsed);
 | 
						|
        }
 | 
						|
    }, 1000);
 | 
						|
}
 | 
						|
 | 
						|
function stopRecordingTimer() {
 | 
						|
    if (recordingTimer) {
 | 
						|
        clearInterval(recordingTimer);
 | 
						|
        recordingTimer = null;
 | 
						|
    }
 | 
						|
    
 | 
						|
    const recordTime = document.querySelector('.recording-time');
 | 
						|
    if (recordTime) {
 | 
						|
        recordTime.textContent = '00:00';
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Search and Library Functions
 | 
						|
function searchTracks(query) {
 | 
						|
    console.log('Searching tracks:', query);
 | 
						|
    
 | 
						|
    // Simulate track search
 | 
						|
    const mockTracks = [
 | 
						|
        { title: 'Summer Vibes', artist: 'DJ Cool', url: 'https://example.com/track1' },
 | 
						|
        { title: 'Night Drive', artist: 'Electronic Dreams', url: 'https://example.com/track2' },
 | 
						|
        { title: 'Bass Drop', artist: 'Heavy Beats', url: 'https://example.com/track3' },
 | 
						|
        { title: 'Chill Out', artist: 'Ambient Sounds', url: 'https://example.com/track4' }
 | 
						|
    ];
 | 
						|
    
 | 
						|
    const filteredTracks = mockTracks.filter(track => 
 | 
						|
        track.title.toLowerCase().includes(query.toLowerCase()) ||
 | 
						|
        track.artist.toLowerCase().includes(query.toLowerCase())
 | 
						|
    );
 | 
						|
    
 | 
						|
    displayTrackList(filteredTracks);
 | 
						|
}
 | 
						|
 | 
						|
function displayTrackList(tracks) {
 | 
						|
    const trackList = document.getElementById('track-list');
 | 
						|
    if (!trackList) return;
 | 
						|
    
 | 
						|
    trackList.innerHTML = '';
 | 
						|
    
 | 
						|
    tracks.forEach(track => {
 | 
						|
        const trackItem = document.createElement('div');
 | 
						|
        trackItem.className = 'track-item';
 | 
						|
        trackItem.innerHTML = `
 | 
						|
            <div class="track-item-title">${track.title}</div>
 | 
						|
            <div class="track-item-artist">${track.artist}</div>
 | 
						|
        `;
 | 
						|
        
 | 
						|
        trackItem.addEventListener('dblclick', () => {
 | 
						|
            if (djInterface.currentDeck) {
 | 
						|
                loadTrackToPlayer(djInterface.currentDeck, track);
 | 
						|
                showNotification(`${track.title} loaded to Deck ${djInterface.currentDeck}`, 'success');
 | 
						|
            }
 | 
						|
        });
 | 
						|
        
 | 
						|
        trackList.appendChild(trackItem);
 | 
						|
    });
 | 
						|
}
 | 
						|
 | 
						|
// Keyboard Shortcuts
 | 
						|
function handleKeyboardShortcuts(event) {
 | 
						|
    if (event.target.tagName === 'INPUT') return;
 | 
						|
    
 | 
						|
    switch(event.code) {
 | 
						|
        case 'Space':
 | 
						|
            event.preventDefault();
 | 
						|
            if (djInterface.currentDeck) {
 | 
						|
                togglePlay(djInterface.currentDeck);
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        case 'KeyQ':
 | 
						|
            togglePlay('A');
 | 
						|
            break;
 | 
						|
        case 'KeyW':
 | 
						|
            togglePlay('B');
 | 
						|
            break;
 | 
						|
        case 'KeyA':
 | 
						|
            cue('A');
 | 
						|
            break;
 | 
						|
        case 'KeyS':
 | 
						|
            cue('B');
 | 
						|
            break;
 | 
						|
        case 'KeyZ':
 | 
						|
            adjustCrossfader(0); // Full A
 | 
						|
            break;
 | 
						|
        case 'KeyX':
 | 
						|
            adjustCrossfader(50); // Center
 | 
						|
            break;
 | 
						|
        case 'KeyC':
 | 
						|
            adjustCrossfader(100); // Full B
 | 
						|
            break;
 | 
						|
        case 'KeyR':
 | 
						|
            toggleRecording();
 | 
						|
            break;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Animations
 | 
						|
function startAnimations() {
 | 
						|
    // Vinyl spinning animation is handled by CSS
 | 
						|
    
 | 
						|
    // Playhead animation
 | 
						|
    setInterval(() => {
 | 
						|
        ['A', 'B'].forEach(deck => {
 | 
						|
            const deckData = djInterface.decks[deck];
 | 
						|
            if (deckData.isPlaying && deckData.track) {
 | 
						|
                updatePlayhead(deck);
 | 
						|
            }
 | 
						|
        });
 | 
						|
    }, 100);
 | 
						|
}
 | 
						|
 | 
						|
function updatePlayhead(deck) {
 | 
						|
    const playhead = document.getElementById(`playhead-${deck.toLowerCase()}`);
 | 
						|
    if (!playhead) return;
 | 
						|
    
 | 
						|
    const currentTime = getCurrentTime(deck);
 | 
						|
    const duration = getDuration(deck);
 | 
						|
    
 | 
						|
    if (duration > 0) {
 | 
						|
        const progress = (currentTime / duration) * 100;
 | 
						|
        playhead.style.left = `${progress}%`;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Modal Functions
 | 
						|
function closeTrackLoader() {
 | 
						|
    document.getElementById('track-loader').classList.add('hidden');
 | 
						|
    
 | 
						|
    // Clear form
 | 
						|
    document.getElementById('track-title').value = '';
 | 
						|
    document.getElementById('track-artist').value = '';
 | 
						|
    document.getElementById('track-url').value = '';
 | 
						|
}
 | 
						|
 | 
						|
function ejectDeck(deck) {
 | 
						|
    const deckData = djInterface.decks[deck];
 | 
						|
    
 | 
						|
    if (deckData.isPlaying) {
 | 
						|
        togglePlay(deck);
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Clear deck
 | 
						|
    deckData.track = null;
 | 
						|
    deckData.cuePoint = 0;
 | 
						|
    
 | 
						|
    // Clear UI
 | 
						|
    document.getElementById(`track-name-${deck.toLowerCase()}`).textContent = 'NO TRACK';
 | 
						|
    document.getElementById(`title-${deck.toLowerCase()}`).textContent = 'No Track Loaded';
 | 
						|
    document.getElementById(`artist-${deck.toLowerCase()}`).textContent = '-';
 | 
						|
    document.getElementById(`time-elapsed-${deck.toLowerCase()}`).textContent = '00:00';
 | 
						|
    document.getElementById(`time-total-${deck.toLowerCase()}`).textContent = '00:00';
 | 
						|
    
 | 
						|
    // Clear waveform
 | 
						|
    const canvas = document.getElementById(`waveform-${deck.toLowerCase()}`);
 | 
						|
    drawEmptyWaveform(canvas);
 | 
						|
    
 | 
						|
    showNotification(`Deck ${deck} ejected`, 'info');
 | 
						|
}
 | 
						|
 | 
						|
// Initialisiere Interface-Einstellungen
 | 
						|
function initializeInterfaceSettings() {
 | 
						|
    console.log('Initializing interface settings');
 | 
						|
    
 | 
						|
    // Setze Standardwerte
 | 
						|
    interfacePosition = {
 | 
						|
        x: (window.innerWidth - 1000) / 2,
 | 
						|
        y: (window.innerHeight - 700) / 2
 | 
						|
    };
 | 
						|
    
 | 
						|
    interfaceSize = { width: 1000, height: 700 };
 | 
						|
    
 | 
						|
    // Versuche gespeicherte Einstellungen zu laden
 | 
						|
    try {
 | 
						|
        const savedPosition = localStorage.getItem('djInterfacePosition');
 | 
						|
        const savedSize = localStorage.getItem('djInterfaceSize');
 | 
						|
        
 | 
						|
        if (savedPosition) {
 | 
						|
            const parsedPosition = JSON.parse(savedPosition);
 | 
						|
            // Prüfe ob die Position gültig ist
 | 
						|
            if (parsedPosition && typeof parsedPosition.x === 'number' && typeof parsedPosition.y === 'number') {
 | 
						|
                interfacePosition = parsedPosition;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        
 | 
						|
        if (savedSize) {
 | 
						|
            const parsedSize = JSON.parse(savedSize);
 | 
						|
            // Prüfe ob die Größe gültig ist
 | 
						|
            if (parsedSize && typeof parsedSize.width === 'number' && typeof parsedSize.height === 'number') {
 | 
						|
                interfaceSize = parsedSize;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    } catch (e) {
 | 
						|
        console.error('DJ System: Error loading saved settings', e);
 | 
						|
        // Lösche ungültige Einstellungen
 | 
						|
        localStorage.removeItem('djInterfacePosition');
 | 
						|
        localStorage.removeItem('djInterfaceSize');
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Wende Einstellungen an
 | 
						|
    applyInterfaceSettings();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
function applyInterfaceSettings() {
 | 
						|
    const djInterface = document.getElementById('dj-interface');
 | 
						|
    if (!djInterface) return;
 | 
						|
    
 | 
						|
    // Setze Position
 | 
						|
    djInterface.style.position = 'absolute';
 | 
						|
    djInterface.style.left = interfacePosition.x + 'px';
 | 
						|
    djInterface.style.top = interfacePosition.y + 'px';
 | 
						|
    
 | 
						|
    // Setze Größe
 | 
						|
    djInterface.style.width = interfaceSize.width + 'px';
 | 
						|
    djInterface.style.height = interfaceSize.height + 'px';
 | 
						|
    
 | 
						|
    // Speichere Einstellungen
 | 
						|
    localStorage.setItem('djInterfacePosition', JSON.stringify(interfacePosition));
 | 
						|
    localStorage.setItem('djInterfaceSize', JSON.stringify(interfaceSize));
 | 
						|
}
 | 
						|
 | 
						|
function setupInterfaceControls() {
 | 
						|
    const djInterfaceElement = document.getElementById('dj-interface');
 | 
						|
    const header = document.querySelector('.dj-header');
 | 
						|
    const resizeHandle = document.createElement('div');
 | 
						|
    
 | 
						|
    // Verbesserte Schließen-Funktion
 | 
						|
    const closeBtn = document.querySelector('.close-btn');
 | 
						|
    if (closeBtn) {
 | 
						|
        closeBtn.addEventListener('click', function(e) {
 | 
						|
            // Nur Interface schließen, Musik weiterlaufen lassen
 | 
						|
            closeDJInterface();
 | 
						|
            e.preventDefault();
 | 
						|
            e.stopPropagation();
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    // Erstelle Resize-Handle
 | 
						|
    resizeHandle.className = 'resize-handle';
 | 
						|
    resizeHandle.innerHTML = '<i class="fas fa-grip-lines-diagonal"></i>';
 | 
						|
    resizeHandle.style.cssText = `
 | 
						|
        position: absolute;
 | 
						|
        bottom: 0;
 | 
						|
        right: 0;
 | 
						|
        width: 20px;
 | 
						|
        height: 20px;
 | 
						|
        cursor: nwse-resize;
 | 
						|
        color: rgba(255, 255, 255, 0.5);
 | 
						|
        display: flex;
 | 
						|
        align-items: center;
 | 
						|
        justify-content: center;
 | 
						|
        z-index: 1000;
 | 
						|
    `;
 | 
						|
    djInterfaceElement.appendChild(resizeHandle);
 | 
						|
    
 | 
						|
    // Header Drag-Funktionalität
 | 
						|
    if (header) {
 | 
						|
        header.style.cursor = 'move';
 | 
						|
        
 | 
						|
        header.addEventListener('mousedown', function(e) {
 | 
						|
            // Ignoriere Klicks auf den Schließen-Button
 | 
						|
            if (e.target.closest('.close-btn')) return;
 | 
						|
            
 | 
						|
            isDragging = true;
 | 
						|
            dragStartX = e.clientX - interfacePosition.x;
 | 
						|
            dragStartY = e.clientY - interfacePosition.y;
 | 
						|
            
 | 
						|
            document.addEventListener('mousemove', handleDrag);
 | 
						|
            document.addEventListener('mouseup', stopDrag);
 | 
						|
        });
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Resize-Funktionalität
 | 
						|
    resizeHandle.addEventListener('mousedown', function(e) {
 | 
						|
        isResizing = true;
 | 
						|
        resizeStartWidth = interfaceSize.width;
 | 
						|
        resizeStartHeight = interfaceSize.height;
 | 
						|
        dragStartX = e.clientX;
 | 
						|
        dragStartY = e.clientY;
 | 
						|
        
 | 
						|
        document.addEventListener('mousemove', handleResize);
 | 
						|
        document.addEventListener('mouseup', stopResize);
 | 
						|
        
 | 
						|
        e.preventDefault();
 | 
						|
        e.stopPropagation();
 | 
						|
    });
 | 
						|
    
 | 
						|
    // Verbesserte Schließen-Funktion
 | 
						|
    // Korrigierte Schreibweise: close-btn ist die CSS-Klasse
 | 
						|
    const closeButton = document.querySelector('.close-btn');
 | 
						|
    if (closeButton) {
 | 
						|
        closeButton.addEventListener('click', function(e) {
 | 
						|
            closeDJInterface();
 | 
						|
            e.preventDefault();
 | 
						|
            e.stopPropagation();
 | 
						|
        });
 | 
						|
    }
 | 
						|
}
 | 
						|
function handleDrag(e) {
 | 
						|
    if (!isDragging) return;
 | 
						|
    
 | 
						|
    interfacePosition.x = e.clientX - dragStartX;
 | 
						|
    interfacePosition.y = e.clientY - dragStartY;
 | 
						|
    
 | 
						|
    // Begrenze Position auf Bildschirm
 | 
						|
    interfacePosition.x = Math.max(0, Math.min(window.innerWidth - interfaceSize.width, interfacePosition.x));
 | 
						|
    interfacePosition.y = Math.max(0, Math.min(window.innerHeight - interfaceSize.height, interfacePosition.y));
 | 
						|
    
 | 
						|
    const djInterfaceElement = document.getElementById('dj-interface');
 | 
						|
    if (djInterfaceElement) {
 | 
						|
        djInterfaceElement.style.left = interfacePosition.x + 'px';
 | 
						|
        djInterfaceElement.style.top = interfacePosition.y + 'px';
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function stopDrag() {
 | 
						|
    if (isDragging) {
 | 
						|
        isDragging = false;
 | 
						|
        document.removeEventListener('mousemove', handleDrag);
 | 
						|
        document.removeEventListener('mouseup', stopDrag);
 | 
						|
        
 | 
						|
        // Speichere Position
 | 
						|
        localStorage.setItem('djInterfacePosition', JSON.stringify(interfacePosition));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function handleResize(e) {
 | 
						|
    if (!isResizing) return;
 | 
						|
    
 | 
						|
    const deltaX = e.clientX - dragStartX;
 | 
						|
    const deltaY = e.clientY - dragStartY;
 | 
						|
    
 | 
						|
    interfaceSize.width = Math.max(800, resizeStartWidth + deltaX);
 | 
						|
    interfaceSize.height = Math.max(600, resizeStartHeight + deltaY);
 | 
						|
    
 | 
						|
    const djInterfaceElement = document.getElementById('dj-interface');
 | 
						|
    if (djInterfaceElement) {
 | 
						|
        djInterfaceElement.style.width = interfaceSize.width + 'px';
 | 
						|
        djInterfaceElement.style.height = interfaceSize.height + 'px';
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function stopResize() {
 | 
						|
    if (isResizing) {
 | 
						|
        isResizing = false;
 | 
						|
        document.removeEventListener('mousemove', handleResize);
 | 
						|
        document.removeEventListener('mouseup', stopResize);
 | 
						|
        
 | 
						|
        // Speichere Größe
 | 
						|
        localStorage.setItem('djInterfaceSize', JSON.stringify(interfaceSize));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function closeDJInterface() {
 | 
						|
    console.log('DJ System: Closing interface...');
 | 
						|
    
 | 
						|
    // Entferne Event-Listener für Drag und Resize
 | 
						|
    document.removeEventListener('mousemove', handleDrag);
 | 
						|
    document.removeEventListener('mouseup', stopDrag);
 | 
						|
    document.removeEventListener('mousemove', handleResize);
 | 
						|
    document.removeEventListener('mouseup', stopResize);
 | 
						|
    
 | 
						|
    // Verstecke Interface
 | 
						|
    const djInterfaceElement = document.getElementById('dj-interface');
 | 
						|
    if (djInterfaceElement) {
 | 
						|
        djInterfaceElement.classList.add('hidden');
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Wichtig: Benachrichtige FiveM BEVOR wir irgendwelche lokalen Variablen zurücksetzen
 | 
						|
    notifyFiveM('djInterfaceClosed', {
 | 
						|
        stopMusic: false // Musik weiterlaufen lassen
 | 
						|
    });
 | 
						|
    
 | 
						|
    // Setze Drag & Resize Status zurück
 | 
						|
    isDragging = false;
 | 
						|
    isResizing = false;
 | 
						|
    
 | 
						|
    console.log('DJ System: Interface closed, music continues playing');
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
function stopAllAudio() {
 | 
						|
    // Stoppe YouTube-Player
 | 
						|
    for (const deck of ['A', 'B']) {
 | 
						|
        if (djInterface.decks[deck].youtubePlayer) {
 | 
						|
            try {
 | 
						|
                djInterface.decks[deck].youtubePlayer.stopVideo();
 | 
						|
            } catch (e) {
 | 
						|
                console.error(`DJ System: Error stopping YouTube player for Deck ${deck}`, e);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        
 | 
						|
        // Stoppe Audio-Player
 | 
						|
        if (djInterface.decks[deck].player) {
 | 
						|
            try {
 | 
						|
                djInterface.decks[deck].player.pause();
 | 
						|
                djInterface.decks[deck].player.currentTime = 0;
 | 
						|
            } catch (e) {
 | 
						|
                console.error(`DJ System: Error stopping audio player for Deck ${deck}`, e);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        
 | 
						|
        // Reset Deck-Status
 | 
						|
        djInterface.decks[deck].isPlaying = false;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function showDJInterface() {
 | 
						|
    console.log('DJ System: Showing interface');
 | 
						|
    
 | 
						|
    const djInterfaceElement = document.getElementById('dj-interface');
 | 
						|
    if (!djInterfaceElement) {
 | 
						|
        console.error('DJ System: Interface element not found!');
 | 
						|
        notifyFiveM('error', {error: 'Interface element not found'});
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Entferne hidden-Klasse
 | 
						|
    djInterfaceElement.classList.remove('hidden');
 | 
						|
    
 | 
						|
    // Position weiter rechts und unten, falls keine gespeicherte Position
 | 
						|
    if (!localStorage.getItem('djInterfacePosition')) {
 | 
						|
        interfacePosition = {
 | 
						|
            x: (window.innerWidth - interfaceSize.width) / 2,
 | 
						|
            y: (window.innerHeight - interfaceSize.height) / 2
 | 
						|
        };
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Wende gespeicherte Einstellungen an
 | 
						|
    applyInterfaceSettings();
 | 
						|
    
 | 
						|
    console.log('DJ System: Interface shown');
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
// Aktualisiere den Message Handler
 | 
						|
window.addEventListener('message', function(event) {
 | 
						|
    const data = event.data;
 | 
						|
    
 | 
						|
    switch(data.type) {
 | 
						|
        case 'showDJInterface':
 | 
						|
            if (data.center) {
 | 
						|
                // Zentriere das Interface, aber weiter rechts und unten
 | 
						|
                interfacePosition = {
 | 
						|
                    x: (window.innerWidth - interfaceSize.width) / 2 + 200, // 200px weiter rechts
 | 
						|
                    y: (window.innerHeight - interfaceSize.height) / 2 + 150 // 150px weiter unten
 | 
						|
                };
 | 
						|
            }
 | 
						|
            
 | 
						|
            if (data.reset) {
 | 
						|
                // Setze Größe zurück
 | 
						|
                interfaceSize = { width: 1000, height: 700 };
 | 
						|
            }
 | 
						|
            
 | 
						|
            showDJInterface();
 | 
						|
            break;
 | 
						|
        // ... andere cases ...
 | 
						|
    }
 | 
						|
});
 | 
						|
 | 
						|
 | 
						|
 | 
						|
// Utility Functions
 | 
						|
function showNotification(message, type = 'info') {
 | 
						|
    console.log(`DJ System [${type.toUpperCase()}]: ${message}`);
 | 
						|
    
 | 
						|
    // Create notification element
 | 
						|
    const notification = document.createElement('div');
 | 
						|
    notification.className = `notification notification-${type}`;
 | 
						|
    notification.textContent = message;
 | 
						|
    notification.style.cssText = `
 | 
						|
        position: fixed;
 | 
						|
        top: 80px;
 | 
						|
        right: 20px;
 | 
						|
        padding: 15px 20px;
 | 
						|
        border-radius: 10px;
 | 
						|
        color: white;
 | 
						|
        font-weight: 600;
 | 
						|
        z-index: 10000;
 | 
						|
        transform: translateX(100%);
 | 
						|
        transition: transform 0.3s ease;
 | 
						|
        background: ${type === 'error' ? '#ff6b6b' : type === 'success' ? '#4ecdc4' : type === 'warning' ? '#feca57' : '#45b7d1'};
 | 
						|
        box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
 | 
						|
    `;
 | 
						|
    
 | 
						|
    document.body.appendChild(notification);
 | 
						|
    
 | 
						|
    // Animate in
 | 
						|
    setTimeout(() => {
 | 
						|
        notification.style.transform = 'translateX(0)';
 | 
						|
    }, 100);
 | 
						|
    
 | 
						|
    // Remove after 3 seconds
 | 
						|
    setTimeout(() => {
 | 
						|
        notification.style.transform = 'translateX(100%)';
 | 
						|
        setTimeout(() => {
 | 
						|
            document.body.removeChild(notification);
 | 
						|
        }, 300);
 | 
						|
    }, 3000);
 | 
						|
}
 | 
						|
 | 
						|
function handleResize() {
 | 
						|
    // Handle responsive design
 | 
						|
    const djInterfaceElement = document.getElementById('dj-interface');
 | 
						|
    if (window.innerWidth < 1200) {
 | 
						|
        djInterfaceElement.classList.add('mobile-layout');
 | 
						|
    } else {
 | 
						|
        djInterfaceElement.classList.remove('mobile-layout');
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// YouTube Integration
 | 
						|
function isYouTubeUrl(url) {
 | 
						|
    const youtubePatterns = [
 | 
						|
        /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\n?#]+)/,
 | 
						|
        /youtube\.com\/watch\?.*v=([^&\n?#]+)/
 | 
						|
    ];
 | 
						|
    
 | 
						|
    return youtubePatterns.some(pattern => pattern.test(url));
 | 
						|
}
 | 
						|
 | 
						|
function extractYouTubeVideoId(url) {
 | 
						|
    const patterns = [
 | 
						|
        /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\n?#]+)/,
 | 
						|
        /youtube\.com\/watch\?.*v=([^&\n?#]+)/
 | 
						|
    ];
 | 
						|
    
 | 
						|
    for (let pattern of patterns) {
 | 
						|
        const match = url.match(pattern);
 | 
						|
        if (match && match[1]) {
 | 
						|
            return match[1];
 | 
						|
        }
 | 
						|
    }
 | 
						|
    
 | 
						|
    return null;
 | 
						|
}
 | 
						|
 | 
						|
// Verbesserte notifyFiveM-Funktion mit Fehlerbehandlung
 | 
						|
function notifyFiveM(event, data, callback) {
 | 
						|
    try {
 | 
						|
        const callbackId = callback ? Date.now().toString() + Math.random().toString(36).substr(2, 5) : null;
 | 
						|
        
 | 
						|
        if (callback) {
 | 
						|
            window.callbacks = window.callbacks || {};
 | 
						|
            window.callbacks[callbackId] = callback;
 | 
						|
        }
 | 
						|
        
 | 
						|
        const payload = {
 | 
						|
            ...data,
 | 
						|
            callbackId: callbackId
 | 
						|
        };
 | 
						|
        
 | 
						|
        console.log(`DJ System: Sending ${event} to FiveM`, payload);
 | 
						|
        
 | 
						|
        fetch(`https://${GetParentResourceName()}/${event}`, {
 | 
						|
            method: 'POST',
 | 
						|
            headers: {
 | 
						|
                'Content-Type': 'application/json'
 | 
						|
            },
 | 
						|
            body: JSON.stringify(payload)
 | 
						|
        }).catch(err => {
 | 
						|
            console.error('DJ System: Failed to notify FiveM:', err);
 | 
						|
            if (callback) {
 | 
						|
                delete window.callbacks[callbackId];
 | 
						|
                callback({success: false, error: 'Failed to communicate with FiveM'});
 | 
						|
            }
 | 
						|
        });
 | 
						|
    } catch (error) {
 | 
						|
        console.error('DJ System: Error in notifyFiveM:', error);
 | 
						|
        if (callback) {
 | 
						|
            callback({success: false, error: 'Internal error'});
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// 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) {
 | 
						|
    const data = event.data;
 | 
						|
    
 | 
						|
    switch(data.type) {
 | 
						|
        case 'showDJInterface':
 | 
						|
            showDJInterface();
 | 
						|
            break;
 | 
						|
        case 'hideDJInterface':
 | 
						|
            closeDJInterface();
 | 
						|
            break;
 | 
						|
        case 'loadTrack':
 | 
						|
            if (data.deck && data.track) {
 | 
						|
                loadTrackToPlayer(data.deck, data.track);
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        case 'setVolume':
 | 
						|
            if (data.deck && data.volume !== undefined) {
 | 
						|
                adjustVolume(data.deck, data.volume);
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        case 'playMusic':
 | 
						|
            playMusic(data.url, data.volume, data.title);
 | 
						|
            break;
 | 
						|
        case 'stopMusic':
 | 
						|
            stopMusic();
 | 
						|
            break;
 | 
						|
    }
 | 
						|
});
 | 
						|
 | 
						|
// Direct music playback for FiveM integration
 | 
						|
function playMusic(url, volume, title) {
 | 
						|
    // This function is used when FiveM sends a direct playMusic command
 | 
						|
    // It will play through the main audio system, not through the decks
 | 
						|
    
 | 
						|
    console.log('DJ System: Playing direct music:', title, url);
 | 
						|
    
 | 
						|
    // If YouTube URL, load into deck A
 | 
						|
    if (isYouTubeUrl(url)) {
 | 
						|
        const videoId = extractYouTubeVideoId(url);
 | 
						|
        if (videoId) {
 | 
						|
            // Create track data
 | 
						|
            const trackData = {
 | 
						|
                title: title || 'Unknown Track',
 | 
						|
                artist: '',
 | 
						|
                url: url
 | 
						|
            };
 | 
						|
            
 | 
						|
            // Load into deck A and play
 | 
						|
            loadTrackToPlayer('A', trackData);
 | 
						|
            
 | 
						|
            // Set volume
 | 
						|
            adjustVolume('A', volume || 50);
 | 
						|
            
 | 
						|
            // Play
 | 
						|
            if (!djInterface.decks.A.isPlaying) {
 | 
						|
                togglePlay('A');
 | 
						|
            }
 | 
						|
            
 | 
						|
            return;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    
 | 
						|
    // For direct audio URLs, use a separate audio element
 | 
						|
    const audioElement = document.createElement('audio');
 | 
						|
    audioElement.src = url;
 | 
						|
    audioElement.volume = (volume || 50) / 100;
 | 
						|
    
 | 
						|
    audioElement.addEventListener('canplay', () => {
 | 
						|
        audioElement.play().catch(e => {
 | 
						|
            console.error('DJ System: Error playing direct audio:', e);
 | 
						|
            showNotification('Error playing audio', 'error');
 | 
						|
        });
 | 
						|
    });
 | 
						|
    
 | 
						|
    audioElement.addEventListener('error', (e) => {
 | 
						|
        console.error('DJ System: Direct audio error:', e);
 | 
						|
        showNotification('Audio error: ' + (e.target.error ? e.target.error.message : 'Unknown error'), 'error');
 | 
						|
    });
 | 
						|
    
 | 
						|
    // Store for later reference
 | 
						|
    window.currentDirectAudio = audioElement;
 | 
						|
}
 | 
						|
 | 
						|
function stopMusic() {
 | 
						|
    // Stop any direct audio
 | 
						|
    if (window.currentDirectAudio) {
 | 
						|
        window.currentDirectAudio.pause();
 | 
						|
        window.currentDirectAudio.src = '';
 | 
						|
        window.currentDirectAudio = null;
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Also stop deck A if it's playing
 | 
						|
    if (djInterface.decks.A.isPlaying) {
 | 
						|
        togglePlay('A');
 | 
						|
    }
 | 
						|
    
 | 
						|
    // And deck B
 | 
						|
    if (djInterface.decks.B.isPlaying) {
 | 
						|
        togglePlay('B');
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function setVolume(volume) {
 | 
						|
    // Set volume for direct audio
 | 
						|
    if (window.currentDirectAudio) {
 | 
						|
        window.currentDirectAudio.volume = volume / 100;
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Also set master volume
 | 
						|
    adjustMasterVolume(volume);
 | 
						|
}
 | 
						|
 | 
						|
// Initialize search with default tracks
 | 
						|
document.addEventListener('DOMContentLoaded', function() {
 | 
						|
    setTimeout(() => {
 | 
						|
        searchTracks(''); // Load default tracks
 | 
						|
    }, 1000);
 | 
						|
});
 | 
						|
 | 
						|
console.log('DJ System: Professional interface loaded! 🎛️');
 | 
						|
console.log('Keyboard shortcuts:');
 | 
						|
console.log('Q/W - Play/Pause Deck A/B');
 | 
						|
console.log('A/S - Cue Deck A/B');
 | 
						|
console.log('Z/X/C - Crossfader positions');
 | 
						|
console.log('R - Toggle recording');
 | 
						|
console.log('Space - Play/Pause current deck');
 | 
						|
 | 
						|
// In script.js - Separate Funktion zum Stoppen der Musik
 | 
						|
function stopAllAudioAndCloseInterface() {
 | 
						|
    // Stoppe YouTube-Player
 | 
						|
    for (const deck of ['A', 'B']) {
 | 
						|
        if (djInterface.decks[deck].youtubePlayer) {
 | 
						|
            try {
 | 
						|
                djInterface.decks[deck].youtubePlayer.stopVideo();
 | 
						|
            } catch (e) {
 | 
						|
                console.error(`DJ System: Error stopping YouTube player for Deck ${deck}`, e);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        
 | 
						|
        // Stoppe Audio-Player
 | 
						|
        if (djInterface.decks[deck].player) {
 | 
						|
            try {
 | 
						|
                djInterface.decks[deck].player.pause();
 | 
						|
                djInterface.decks[deck].player.currentTime = 0;
 | 
						|
            } catch (e) {
 | 
						|
                console.error(`DJ System: Error stopping audio player for Deck ${deck}`, e);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        
 | 
						|
        // Reset Deck-Status
 | 
						|
        djInterface.decks[deck].isPlaying = false;
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Schließe Interface
 | 
						|
    closeDJInterface();
 | 
						|
    
 | 
						|
    // Benachrichtige FiveM, dass die Musik gestoppt werden soll
 | 
						|
    notifyFiveM('djInterfaceClosed', {
 | 
						|
        stopMusic: true
 | 
						|
    });
 | 
						|
}
 | 
						|
 | 
						|
function showDJInterface() {
 | 
						|
    console.log('DJ System: Showing interface');
 | 
						|
    
 | 
						|
    const djInterfaceElement = document.getElementById('dj-interface');
 | 
						|
    if (!djInterfaceElement) {
 | 
						|
        console.error('DJ System: Interface element not found!');
 | 
						|
        notifyFiveM('error', {error: 'Interface element not found'});
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Entferne hidden-Klasse
 | 
						|
    djInterfaceElement.classList.remove('hidden');
 | 
						|
    
 | 
						|
    // Position weiter rechts und unten, falls keine gespeicherte Position
 | 
						|
    if (!localStorage.getItem('djInterfacePosition')) {
 | 
						|
        interfacePosition = {
 | 
						|
            x: (window.innerWidth - interfaceSize.width) / 2,
 | 
						|
            y: (window.innerHeight - interfaceSize.height) / 2
 | 
						|
        };
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Wende gespeicherte Einstellungen an
 | 
						|
    applyInterfaceSettings();
 | 
						|
    
 | 
						|
    console.log('DJ System: Interface shown');
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// Vereinfachte Playlist-Verwaltung
 | 
						|
let playlists = [];
 | 
						|
let currentPlaylist = null;
 | 
						|
let currentPlaylistIndex = 0;
 | 
						|
 | 
						|
// Playlist laden - vereinfacht
 | 
						|
function loadPlaylists() {
 | 
						|
    console.log('DJ System: Loading playlists');
 | 
						|
    
 | 
						|
    // Simuliere Playlists für den Anfang
 | 
						|
    playlists = [
 | 
						|
        {
 | 
						|
            id: 1,
 | 
						|
            name: "Party Hits",
 | 
						|
            isOwner: true,
 | 
						|
            songs: [
 | 
						|
                { id: 1, title: "Song 1", artist: "Artist 1", url: "https://example.com/song1.mp3" },
 | 
						|
                { id: 2, title: "Song 2", artist: "Artist 2", url: "https://example.com/song2.mp3" }
 | 
						|
            ]
 | 
						|
        },
 | 
						|
        {
 | 
						|
            id: 2,
 | 
						|
            name: "Chill Vibes",
 | 
						|
            isOwner: true,
 | 
						|
            songs: [
 | 
						|
                { id: 3, title: "Chill Song 1", artist: "Chill Artist 1", url: "https://example.com/chill1.mp3" },
 | 
						|
                { id: 4, title: "Chill Song 2", artist: "Chill Artist 2", url: "https://example.com/chill2.mp3" }
 | 
						|
            ]
 | 
						|
        }
 | 
						|
    ];
 | 
						|
    
 | 
						|
    // Aktualisiere Anzeige
 | 
						|
    updatePlaylistDisplay();
 | 
						|
}
 | 
						|
 | 
						|
// Playlist-Anzeige aktualisieren - vereinfacht
 | 
						|
function updatePlaylistDisplay() {
 | 
						|
    console.log('DJ System: Updating playlist display');
 | 
						|
    
 | 
						|
    const playlistContainer = document.getElementById('playlist-container');
 | 
						|
    if (!playlistContainer) {
 | 
						|
        console.error('DJ System: Playlist container not found');
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    
 | 
						|
    playlistContainer.innerHTML = '';
 | 
						|
    
 | 
						|
    if (playlists.length === 0) {
 | 
						|
        playlistContainer.innerHTML = '<div class="no-playlists">Keine Playlists gefunden</div>';
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Erstelle Playlist-Elemente
 | 
						|
    playlists.forEach((playlist) => {
 | 
						|
        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>
 | 
						|
            </div>
 | 
						|
        `;
 | 
						|
        
 | 
						|
        // Event-Listener für Play-Button
 | 
						|
        const playBtn = playlistElement.querySelector('.playlist-play-btn');
 | 
						|
        if (playBtn) {
 | 
						|
            playBtn.onclick = function(e) {
 | 
						|
                e.preventDefault();
 | 
						|
                e.stopPropagation();
 | 
						|
                playPlaylist(playlist);
 | 
						|
            };
 | 
						|
        }
 | 
						|
        
 | 
						|
        playlistContainer.appendChild(playlistElement);
 | 
						|
    });
 | 
						|
}
 | 
						|
 | 
						|
// Playlist abspielen - vereinfacht
 | 
						|
function playPlaylist(playlist) {
 | 
						|
    console.log('DJ System: Playing playlist', playlist.name);
 | 
						|
    
 | 
						|
    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 - vereinfacht
 | 
						|
function playPlaylistSong(index) {
 | 
						|
    if (!currentPlaylist || !currentPlaylist.songs || index >= currentPlaylist.songs.length) {
 | 
						|
        showNotification('Playlist beendet', 'info');
 | 
						|
        currentPlaylist = null;
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    
 | 
						|
    const song = currentPlaylist.songs[index];
 | 
						|
    console.log('DJ System: Playing playlist song', song.title);
 | 
						|
    
 | 
						|
    // Spiele den Song direkt ab
 | 
						|
    PlayMusicAsDJ(song.title, song.url, currentVolume || 50);
 | 
						|
    
 | 
						|
    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;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Funktion zum Abspielen von Musik als DJ (vereinfacht)
 | 
						|
function PlayMusicAsDJ(title, url, volume) {
 | 
						|
    console.log('DJ System: Playing music as DJ', title, url);
 | 
						|
    
 | 
						|
    // Sende an FiveM
 | 
						|
    notifyFiveM('playTrack', {
 | 
						|
        title: title,
 | 
						|
        url: url,
 | 
						|
        volume: volume || 50
 | 
						|
    });
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 |