beatvault-legacy / script.js
fLausch's picture
dont use red and blue for the colores, of player highlights. only one color is enough
ff95034 verified
// 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();
}