diff --git a/resources/[tools]/nordi_dj/html/script.js b/resources/[tools]/nordi_dj/html/script.js index fb8458458..ed3464921 100644 --- a/resources/[tools]/nordi_dj/html/script.js +++ b/resources/[tools]/nordi_dj/html/script.js @@ -1,377 +1,3 @@ -let audioPlayer = null; -let currentVolume = 50; -let isPlaying = false; -let currentSong = null; -let fadeInterval = null; - -// YouTube Player API laden -let youtubeAPIReady = false; -let youtubePlayer = null; - -// Initialize when page loads -document.addEventListener('DOMContentLoaded', function() { - loadYouTubeAPI(); - setupAudioPlayer(); -}); - -// 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'); - youtubeAPIReady = true; - - // Erstelle versteckten YouTube Player - createYouTubePlayer(); -}; - -// YouTube Player erstellen -function createYouTubePlayer() { - // Container für YouTube Player - const playerContainer = document.createElement('div'); - playerContainer.id = 'youtube-player-container'; - playerContainer.style.position = 'absolute'; - playerContainer.style.top = '-9999px'; - playerContainer.style.left = '-9999px'; - playerContainer.style.width = '1px'; - playerContainer.style.height = '1px'; - playerContainer.style.opacity = '0'; - document.body.appendChild(playerContainer); - - youtubePlayer = new YT.Player('youtube-player-container', { - height: '1', - width: '1', - 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, - 'start': 0, - 'origin': window.location.origin - }, - events: { - 'onReady': onYouTubePlayerReady, - 'onStateChange': onYouTubePlayerStateChange, - 'onError': onYouTubePlayerError - } - }); -} - -function onYouTubePlayerReady(event) { - console.log('DJ System: YouTube Player ready'); -} - -function onYouTubePlayerStateChange(event) { - switch(event.data) { - case YT.PlayerState.PLAYING: - console.log('DJ System: YouTube playing'); - isPlaying = true; - break; - case YT.PlayerState.PAUSED: - console.log('DJ System: YouTube paused'); - isPlaying = false; - break; - case YT.PlayerState.ENDED: - console.log('DJ System: YouTube ended'); - isPlaying = false; - notifyFiveM('songEnded', {}); - break; - case YT.PlayerState.BUFFERING: - console.log('DJ System: YouTube buffering'); - break; - } -} - -function onYouTubePlayerError(event) { - console.error('DJ System: YouTube error:', 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; - } - - notifyFiveM('audioError', { error: errorMessage }); -} - -// Setup normaler Audio Player für direkte URLs -function setupAudioPlayer() { - audioPlayer = document.getElementById('audio-player'); - if (!audioPlayer) { - audioPlayer = document.createElement('audio'); - audioPlayer.id = 'audio-player'; - audioPlayer.style.display = 'none'; - document.body.appendChild(audioPlayer); - } - - audioPlayer.addEventListener('play', () => { - isPlaying = true; - console.log('DJ System: Audio playing'); - }); - - audioPlayer.addEventListener('pause', () => { - isPlaying = false; - console.log('DJ System: Audio paused'); - }); - - audioPlayer.addEventListener('ended', () => { - isPlaying = false; - notifyFiveM('songEnded', {}); - }); - - audioPlayer.addEventListener('error', (e) => { - console.error('DJ System: Audio error', e); - notifyFiveM('audioError', { error: 'Audio playback error' }); - }); -} - -// Main message handler -window.addEventListener('message', function(event) { - const data = event.data; - - switch(data.type) { - case 'playMusic': - playMusic(data.url, data.volume, data.title); - break; - case 'stopMusic': - stopMusic(); - break; - case 'setVolume': - setVolume(data.volume); - break; - } -}); - -// Musik abspielen - YouTube oder direkte URL -async function playMusic(url, volume, title = 'Unknown') { - try { - console.log('DJ System: Playing:', title, url); - - // Stoppe aktuelle Musik - stopMusic(); - - // Setze Lautstärke - currentVolume = volume || 50; - - // Prüfe ob YouTube URL - if (isYouTubeUrl(url)) { - await playYouTubeMusic(url, volume, title); - } else { - await playDirectMusic(url, volume, title); - } - - // Speichere aktuelle Song Info - currentSong = { - title: title, - url: url, - volume: volume - }; - - } catch (error) { - console.error('DJ System: Error playing music:', error); - notifyFiveM('audioError', { error: error.message }); - } -} - -// YouTube Musik abspielen -async function playYouTubeMusic(url, volume, title) { - if (!youtubeAPIReady || !youtubePlayer) { - throw new Error('YouTube Player nicht bereit'); - } - - const videoId = extractYouTubeVideoId(url); - if (!videoId) { - throw new Error('Ungültige YouTube URL'); - } - - console.log('DJ System: Playing YouTube video:', videoId); - - // Lade und spiele YouTube Video - youtubePlayer.loadVideoById({ - videoId: videoId, - startSeconds: 0 - }); - - // Setze Lautstärke - youtubePlayer.setVolume(volume || 50); - - // Warte kurz und starte - setTimeout(() => { - youtubePlayer.playVideo(); - }, 1000); -} - -// Direkte Audio URL abspielen -async function playDirectMusic(url, volume, title) { - if (!audioPlayer) { - throw new Error('Audio Player nicht verfügbar'); - } - - console.log('DJ System: Playing direct audio:', url); - - audioPlayer.src = url; - audioPlayer.volume = (volume || 50) / 100; - audioPlayer.load(); - - const playPromise = audioPlayer.play(); - if (playPromise !== undefined) { - await playPromise; - } -} - -// Musik stoppen -function stopMusic() { - try { - // Stoppe YouTube Player - if (youtubePlayer && youtubeAPIReady) { - youtubePlayer.stopVideo(); - } - - // Stoppe Audio Player - if (audioPlayer) { - audioPlayer.pause(); - audioPlayer.currentTime = 0; - audioPlayer.src = ''; - } - - isPlaying = false; - currentSong = null; - - console.log('DJ System: Music stopped'); - - } catch (error) { - console.error('DJ System: Error stopping music:', error); - } -} - -// Musik pausieren -function pauseMusic() { - try { - if (currentSong && isYouTubeUrl(currentSong.url)) { - if (youtubePlayer && youtubeAPIReady) { - youtubePlayer.pauseVideo(); - } - } else if (audioPlayer) { - audioPlayer.pause(); - } - } catch (error) { - console.error('DJ System: Error pausing music:', error); - } -} - -// Musik fortsetzen -function resumeMusic() { - try { - if (currentSong && isYouTubeUrl(currentSong.url)) { - if (youtubePlayer && youtubeAPIReady) { - youtubePlayer.playVideo(); - } - } else if (audioPlayer) { - audioPlayer.play(); - } - } catch (error) { - console.error('DJ System: Error resuming music:', error); - } -} - -// Lautstärke setzen -function setVolume(volume) { - try { - currentVolume = Math.max(0, Math.min(100, volume)); - - if (currentSong && isYouTubeUrl(currentSong.url)) { - if (youtubePlayer && youtubeAPIReady) { - youtubePlayer.setVolume(currentVolume); - } - } else if (audioPlayer) { - audioPlayer.volume = currentVolume / 100; - } - - console.log('DJ System: Volume set to', currentVolume + '%'); - } catch (error) { - console.error('DJ System: Error setting volume:', error); - } -} - -// YouTube URL prüfen -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)); -} - -// YouTube Video ID extrahieren -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; -} - -// FiveM benachrichtigen -function notifyFiveM(event, data) { - fetch(`https://${GetParentResourceName()}/${event}`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(data) - }).catch(err => { - console.error('DJ System: Failed to notify FiveM:', err); - }); -} - -function GetParentResourceName() { - return window.location.hostname; -} - -// Debug -window.djDebug = { - getCurrentSong: () => currentSong, - getVolume: () => currentVolume, - isPlaying: () => isPlaying, - youtubeReady: () => youtubeAPIReady, - testYouTube: (videoId) => playYouTubeMusic('https://www.youtube.com/watch?v=' + videoId, 50, 'Test') -}; - -console.log('DJ System: YouTube streaming system loaded'); - // DJ System - Professional Interface let djInterface = { decks: { @@ -381,7 +7,8 @@ let djInterface = { volume: 75, pitch: 0, cuePoint: 0, - player: null + player: null, + youtubePlayer: null }, B: { track: null, @@ -389,7 +16,8 @@ let djInterface = { volume: 75, pitch: 0, cuePoint: 0, - player: null + player: null, + youtubePlayer: null } }, mixer: { @@ -408,9 +36,198 @@ let djInterface = { wetDry: 0 }, currentDeck: null, - isRecording: false + isRecording: false, + youtubeAPIReady: false }; +// 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; +}; + +// Hier ist die fehlende Funktion +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 = ''; + 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 = ''; + 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 = ''; + 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 + }); +} + +// Füge den Rest deines bestehenden script.js-Codes hier ein +// ... + // Initialize DJ Interface document.addEventListener('DOMContentLoaded', function() { initializeDJSystem(); @@ -419,767 +236,34 @@ document.addEventListener('DOMContentLoaded', function() { loadYouTubeAPI(); }); -function initializeDJSystem() { - console.log('DJ System: Initializing professional interface...'); - - // 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(); - - 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 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', () => { - isDragging = false; - document.body.style.cursor = 'default'; - }); - }); -} - -function getCurrentRotation(element) { - 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; -} - -// 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 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 = ''; - playBtn.classList.remove('playing'); - vinyl.classList.remove('spinning'); - deckData.isPlaying = false; - } else { - // Play - playTrack(deck); - playBtn.innerHTML = ''; - 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); -} - -// Crossfader (Fortsetzung) -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) { - 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()}`); - 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); - - document.getElementById(`time-elapsed-${deck.toLowerCase()}`).textContent = - formatTime(currentTime); - document.getElementById(`time-total-${deck.toLowerCase()}`).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 = 'STOP'; - startRecordingTimer(); - showNotification('Recording started', 'success'); - } else { - recordBtn.classList.remove('recording'); - recordBtn.innerHTML = 'REC'; - 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' } +// Funktion zum Extrahieren der YouTube Video ID +function extractYouTubeVideoId(url) { + const patterns = [ + /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\n?#]+)/, + /youtube\.com\/watch\?.*v=([^&\n?#]+)/ ]; - 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'); - trackList.innerHTML = ''; - - tracks.forEach(track => { - const trackItem = document.createElement('div'); - trackItem.className = 'track-item'; - trackItem.innerHTML = ` -
${track.title}
-
${track.artist}
- `; - - 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()}`); - 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'); -} - -// Interface Control -function closeDJInterface() { - document.getElementById('dj-interface').classList.add('hidden'); - - // Stop all playback - ['A', 'B'].forEach(deck => { - if (djInterface.decks[deck].isPlaying) { - togglePlay(deck); + for (let pattern of patterns) { + const match = url.match(pattern); + if (match && match[1]) { + return match[1]; } - }); + } - // Notify FiveM - notifyFiveM('djInterfaceClosed', {}); + return null; } -function showDJInterface() { - document.getElementById('dj-interface').classList.remove('hidden'); +// Prüfe ob es eine YouTube URL ist +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)); } -// Utility Functions +// Benachrichtigungen anzeigen function showNotification(message, type = 'info') { console.log(`DJ System [${type.toUpperCase()}]: ${message}`); @@ -1218,43 +302,7 @@ function showNotification(message, type = 'info') { }, 3000); } -function handleResize() { - // Handle responsive design - const djInterface = document.getElementById('dj-interface'); - if (window.innerWidth < 1200) { - djInterface.classList.add('mobile-layout'); - } else { - djInterface.classList.remove('mobile-layout'); - } -} - -// YouTube Integration (from previous code) -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; -} - -// FiveM Integration +// FiveM benachrichtigen function notifyFiveM(event, data) { fetch(`https://${GetParentResourceName()}/${event}`, { method: 'POST', @@ -1270,43 +318,3 @@ function notifyFiveM(event, data) { function GetParentResourceName() { return window.location.hostname; } - -// 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; - } -}); - -// 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'); -