Spaces:
Running
Running
| // Main Application Script for BeatVault Legacy | |
| document.addEventListener('DOMContentLoaded', function() { | |
| console.log('BeatVault Legacy initialized 🎵'); | |
| // Initialize audio player functionality | |
| initializeAudioPlayer(); | |
| // Load sets from API | |
| loadSetsFromAPI(); | |
| // Setup event listeners | |
| setupEventListeners(); | |
| // Initialize theme | |
| initializeTheme(); | |
| // Initialize Mandelbrot background | |
| initializeMandelbrotBackground(); | |
| }); | |
| function initializeAudioPlayer() { | |
| const playBtn = document.querySelector('.player-control-btn:nth-child(2)'); | |
| const audio = new Audio(); | |
| // Simulate audio playback | |
| playBtn.addEventListener('click', function() { | |
| const icon = this.querySelector('i'); | |
| const isPlaying = icon.getAttribute('data-feather') === 'pause'; | |
| if (isPlaying) { | |
| icon.setAttribute('data-feather', 'play'); | |
| this.classList.remove('bg-gradient-to-r', 'from-primary-700', 'to-secondary-700'); | |
| this.classList.add('bg-gradient-to-r', 'from-primary-600', 'to-secondary-600'); | |
| } else { | |
| icon.setAttribute('data-feather', 'pause'); | |
| this.classList.remove('bg-gradient-to-r', 'from-primary-600', 'to-secondary-600'); | |
| this.classList.add('bg-gradient-to-r', 'from-primary-700', 'to-secondary-700'); | |
| } | |
| feather.replace(); | |
| }); | |
| } | |
| async function loadSetsFromAPI() { | |
| const container = document.getElementById('sets-container'); | |
| try { | |
| // For demo purposes, using mock data | |
| // In production, replace with actual API call | |
| const mockSets = [ | |
| { | |
| id: 1, | |
| title: "Neon Dreamscape", | |
| artist: "fLau & blauschig", | |
| event: "Summer Solstice '23", | |
| duration: "67:42", | |
| genre: "Deep House", | |
| bpm: 128, | |
| date: "2023-06-21", | |
| plays: 1245, | |
| cover: "http://static.photos/abstract/640x360/7" | |
| }, | |
| { | |
| id: 2, | |
| title: "Midnight Mirage", | |
| artist: "fLau & blauschig", | |
| event: "Winter Edition '22", | |
| duration: "72:18", | |
| genre: "Melodic Techno", | |
| bpm: 124, | |
| date: "2022-12-15", | |
| plays: 987, | |
| cover: "http://static.photos/blue/200x200/42" | |
| }, | |
| { | |
| id: 3, | |
| title: "Cosmic Dawn", | |
| artist: "fLau & blauschig", | |
| event: "Spring Awakening", | |
| duration: "81:05", | |
| genre: "Progressive House", | |
| bpm: 126, | |
| date: "2023-03-20", | |
| plays: 1567, | |
| cover: "http://static.photos/purple/200x200/88" | |
| }, | |
| { | |
| id: 4, | |
| title: "Ethereal Echoes", | |
| artist: "fLau & blauschig", | |
| event: "Autumn Equinox", | |
| duration: "75:30", | |
| genre: "Ambient House", | |
| bpm: 118, | |
| date: "2022-09-22", | |
| plays: 843, | |
| cover: "http://static.photos/gradient/200x200/23" | |
| }, | |
| { | |
| id: 5, | |
| title: "Digital Oasis", | |
| artist: "fLau & blauschig", | |
| event: "New Year's Eve '22", | |
| duration: "89:15", | |
| genre: "Tech House", | |
| bpm: 127, | |
| date: "2022-12-31", | |
| plays: 2103, | |
| cover: "http://static.photos/technology/200x200/56" | |
| }, | |
| { | |
| id: 6, | |
| title: "Lunar Tides", | |
| artist: "fLau & blauschig", | |
| event: "Full Moon Session", | |
| duration: "65:48", | |
| genre: "Deep Techno", | |
| bpm: 122, | |
| date: "2023-02-05", | |
| plays: 732, | |
| cover: "http://static.photos/monochrome/200x200/91" | |
| } | |
| ]; | |
| // Clear loading state | |
| container.innerHTML = ''; | |
| // Render sets | |
| mockSets.forEach(set => { | |
| const setElement = createSetElement(set); | |
| container.appendChild(setElement); | |
| }); | |
| // Add animation to newly created elements | |
| animateSetItems(); | |
| } catch (error) { | |
| console.error('Error loading sets:', error); | |
| container.innerHTML = ` | |
| <div class="text-center py-12"> | |
| <div class="inline-block p-4 rounded-2xl bg-gradient-to-r from-red-900/20 to-secondary-900/20"> | |
| <i data-feather="alert-circle" class="w-8 h-8 text-red-400"></i> | |
| </div> | |
| <p class="text-red-400 mt-4">Unable to load sets. Please check your connection.</p> | |
| </div>`; | |
| feather.replace(); | |
| } | |
| } | |
| function createSetElement(set) { | |
| const div = document.createElement('div'); | |
| div.className = 'set-item animate-slide-up'; | |
| div.innerHTML = ` | |
| <div class="flex items-start gap-4"> | |
| <div class="relative group flex-shrink-0"> | |
| <div class="w-16 h-16 rounded-xl overflow-hidden"> | |
| <img src="${set.cover}" alt="${set.title}" class="w-full h-full object-cover group-hover:scale-110 transition-transform duration-500"> | |
| </div> | |
| <div class="absolute inset-0 bg-gradient-to-t from-primary-900/40 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-xl"></div> | |
| </div> | |
| <div class="flex-1 min-w-0"> | |
| <h4 class="font-bold text-lg truncate">${set.title}</h4> | |
| <p class="text-sm text-gray-400 truncate">${set.event} • ${set.genre}</p> | |
| <div class="flex items-center justify-between mt-2"> | |
| <div class="flex items-center gap-4"> | |
| <span class="text-xs text-gray-500">${set.duration}</span> | |
| <span class="text-xs text-gray-500">• ${set.bpm} BPM</span> | |
| </div> | |
| <div class="flex items-center gap-2"> | |
| <i data-feather="play" class="w-3 h-3"></i> | |
| <span class="text-xs text-gray-400">${set.plays.toLocaleString()}</span> | |
| </div> | |
| </div> | |
| <button class="play-set-btn ml-2 p-2 rounded-lg hover:bg-primary-900/30 transition-colors"> | |
| <i data-feather="play" class="w-5 h-5 text-primary-400"></i> | |
| </button> | |
| </div> | |
| </div> | |
| `; | |
| // Add click event to play set | |
| const playBtn = div.querySelector('.play-set-btn'); | |
| playBtn.addEventListener('click', function(e) { | |
| e.stopPropagation(); | |
| playSet(set); | |
| }); | |
| div.addEventListener('click', function() { | |
| viewSetDetails(set); | |
| }); | |
| return div; | |
| } | |
| function playSet(set) { | |
| console.log('Playing set:', set.title); | |
| // Update "Now Playing" section | |
| const nowPlayingTitle = document.querySelector('.now-playing-section h3'); | |
| const nowPlayingArtist = document.querySelector('.now-playing-section p'); | |
| const nowPlayingImg = document.querySelector('.now-playing-section img'); | |
| if (nowPlayingTitle) nowPlayingTitle.textContent = set.title; | |
| if (nowPlayingArtist) nowPlayingArtist.textContent = `${set.artist} • ${set.event}`; | |
| if (nowPlayingImg) nowPlayingImg.src = set.cover; | |
| // Show notification | |
| showNotification(`Now playing: ${set.title}`); | |
| } | |
| function viewSetDetails(set) { | |
| console.log('Viewing set details:', set); | |
| // In a real app, you would navigate to a details page or show a modal | |
| showNotification(`Loading ${set.title} details...`); | |
| // For demo, just update the main player | |
| playSet(set); | |
| } | |
| function setupEventListeners() { | |
| // Search functionality | |
| const searchInput = document.querySelector('input[type="text"]'); | |
| if (searchInput) { | |
| searchInput.addEventListener('input', debounce(function(e) { | |
| filterSets(e.target.value); | |
| }, 300)); | |
| } | |
| // Filter buttons | |
| const filterBtns = document.querySelectorAll('.filter-btn'); | |
| filterBtns.forEach(btn => { | |
| btn.addEventListener('click', function() { | |
| filterBtns.forEach(b => b.classList.remove('active')); | |
| this.classList.add('active'); | |
| const filter = this.textContent.toLowerCase(); | |
| applyFilter(filter); | |
| }); | |
| }); | |
| // Volume control | |
| const volumeBar = document.querySelector('.volume-bar'); | |
| if (volumeBar) { | |
| volumeBar.addEventListener('click', function(e) { | |
| const rect = this.getBoundingClientRect(); | |
| const volume = (e.clientX - rect.left) / rect.width; | |
| updateVolume(volume); | |
| }); | |
| } | |
| } | |
| function filterSets(query) { | |
| const setItems = document.querySelectorAll('.set-item'); | |
| const lowerQuery = query.toLowerCase(); | |
| setItems.forEach(item => { | |
| const title = item.querySelector('h4').textContent.toLowerCase(); | |
| const artist = item.querySelector('p').textContent.toLowerCase(); | |
| if (title.includes(lowerQuery) || artist.includes(lowerQuery)) { | |
| item.style.display = 'block'; | |
| setTimeout(() => { | |
| item.style.opacity = '1'; | |
| item.style.transform = 'translateX(0)'; | |
| }, 10); | |
| } else { | |
| item.style.opacity = '0.5'; | |
| item.style.transform = 'translateX(-10px)'; | |
| setTimeout(() => { | |
| item.style.display = 'none'; | |
| }, 300); | |
| } | |
| }); | |
| } | |
| function applyFilter(filter) { | |
| console.log('Applying filter:', filter); | |
| // Implement filter logic based on your data structure | |
| showNotification(`Filtered by: ${filter}`); | |
| } | |
| function updateVolume(volume) { | |
| console.log('Volume updated to:', Math.round(volume * 100) + '%'); | |
| } | |
| function showNotification(message) { | |
| // Create notification element | |
| const notification = document.createElement('div'); | |
| notification.className = 'fixed bottom-4 right-4 bg-gradient-to-r from-primary-800 to-primary-900 text-white px-6 py-3 rounded-xl shadow-2xl transform transition-all duration-300'; | |
| notification.style.zIndex = '1000'; | |
| notification.innerHTML = ` | |
| <div class="flex items-center gap-3"> | |
| <i data-feather="bell" class="w-5 h-5"></i> | |
| <span>${message}</span> | |
| </div> | |
| `; | |
| document.body.appendChild(notification); | |
| // Animate in | |
| setTimeout(() => { | |
| notification.classList.add('translate-y-0', 'opacity-100'); | |
| notification.classList.remove('translate-y-10', 'opacity-0'); | |
| }, 10); | |
| // Remove after 3 seconds | |
| setTimeout(() => { | |
| notification.classList.add('translate-y-10', 'opacity-0'); | |
| setTimeout(() => { | |
| document.body.removeChild(notification); | |
| }, 300); | |
| }, 3000); | |
| } | |
| function animateSetItems() { | |
| const setItems = document.querySelectorAll('.set-item'); | |
| setItems.forEach((item, index) => { | |
| item.style.animationDelay = `${index * 0.1}s`; | |
| } | |
| } | |
| function initializeTheme() { | |
| // Set dark mode by default | |
| if (!document.documentElement.classList.contains('dark')) { | |
| document.documentElement.classList.add('dark'); | |
| } | |
| } | |
| // Utility function for debouncing | |
| function debounce(func, wait) { | |
| let timeout; | |
| return function executedFunction(...args) { | |
| const later = () => { | |
| clearTimeout(timeout); | |
| func(...args); | |
| }; | |
| clearTimeout(timeout); | |
| timeout = setTimeout(later, wait); | |
| }; | |
| } | |
| // Mandelbrot Background Animation | |
| // Extracted from fLau & blauschig V4.20 | |
| // This script creates a animated Mandelbrot fractal background | |
| function initializeMandelbrotBackground() { | |
| // Create canvas element | |
| const canvas = document.createElement('canvas'); | |
| canvas.style.position = 'fixed'; | |
| canvas.style.top = '0'; | |
| canvas.style.left = '0'; | |
| canvas.style.width = '100%'; | |
| canvas.style.height = '100%'; | |
| canvas.style.zIndex = '-1'; // Make sure it's behind content | |
| canvas.style.pointerEvents = 'none'; // No interaction | |
| // Append to psychedelic-bg container or body | |
| const psychedelicBg = document.getElementById('psychedelic-bg'); | |
| if (psychedelicBg) { | |
| psychedelicBg.appendChild(canvas); | |
| } else { | |
| document.body.appendChild(canvas); | |
| } | |
| const ctx = canvas.getContext('2d'); | |
| function resizeCanvas() { | |
| canvas.width = window.innerWidth; | |
| canvas.height = window.innerHeight; | |
| } | |
| resizeCanvas(); | |
| window.addEventListener('resize', resizeCanvas); | |
| // Mandelbrot parameters | |
| const chars = ['f', 'l', 'a', 'u', '&', 'b', 'l', 'a', 'u', 's', 'c', 'h', 'i', 'g']; | |
| let time = 0; | |
| let zoom = 1; | |
| function animate() { | |
| time += 0.0005; // EXTREM LANGSAM: 10x langsamer als Original | |
| zoom += 0.0001; // EXTREM LANGSAM: 10x langsamer als Original | |
| ctx.fillStyle = 'rgba(0,0,0,0.05)'; | |
| ctx.fillRect(0, 0, canvas.width, canvas.height); | |
| ctx.font = '12px monospace'; | |
| ctx.textAlign = 'center'; | |
| ctx.textBaseline = 'middle'; | |
| const density = 20; | |
| for(let x = density; x < canvas.width; x += density) { | |
| for(let y = density; y < canvas.height; y += density) { | |
| let zx = (x - canvas.width/2) * 4.0/(canvas.width * zoom); | |
| let zy = (y - canvas.height/2) * 4.0/(canvas.height * zoom); | |
| let cx = zx; | |
| let cy = zy; | |
| let i = 0; | |
| let zx2 = zx; | |
| let zy2 = zy; | |
| while(i < 20 && zx2*zx2 + zy2*zy2 < 4) { | |
| const tmp = zx2*zx2 - zy2*zy2 + cx; | |
| zy2 = 2.0*zx2*zy2 + cy; | |
| zx2 = tmp; | |
| i++; | |
| } | |
| if(i < 20) { | |
| const hue = 200 + (i * 5 + time * 20) % 40; // Blue range only | |
| const textPosition = Math.floor((x + y + time * 100) / density) % chars.length; | |
| const char = chars[textPosition]; | |
| ctx.fillStyle = `hsla(${hue}, 100%, 70%, ${i/20 * 0.8})`; | |
| ctx.fillText(char, x, y); | |
| } | |
| } | |
| } | |
| if(zoom > 30) { // EXTREM LANG: 15x länger als Original | |
| zoom = 1; | |
| } | |
| requestAnimationFrame(animate); | |
| } | |
| animate(); | |
| } | |