// 1. Sélection des éléments DOM const vinyl = document.getElementById('vinyl'); const audio = document.getElementById('audio'); const progress = document.getElementById('progress'); const currentTimeEl = document.getElementById('currentTime'); const durationEl = document.getElementById('duration'); const backwardBtn = document.getElementById('backward'); const forwardBtn = document.getElementById('forward'); const loopBtn = document.getElementById('loop'); // 2. Variables utiles let isPlaying = false; let isLooping = false; let idleTimeout = null; let lastPosition = null; let lastTime = null; const rotationsPerTrack = 3; // Nombre de tours complets du vinyle sur toute la durée // 3. Connexion Socket.io const socket = io(); socket.on('position', (position) => { const now = Date.now(); if (lastPosition !== null && lastTime !== null) { const deltaPos = position - lastPosition; const deltaTime = (now - lastTime) / 1000; const speed = deltaPos / deltaTime; // vitesse de rotation clearTimeout(idleTimeout); // Si la vitesse est très faible, on considère que l'encodeur est à l'arrêt if (Math.abs(speed) < 5) { stopPlayback(); return; } if (speed < 0) { // Rotation inverse : on recule la timeline audio.pause(); isPlaying = false; // On recule la timeline proportionnellement à la vitesse inversée, avec un facteur de sensibilité audio.currentTime = Math.max(0, audio.currentTime + speed * 0.03); // speed<0 donc avance négative ici } else { // Rotation avant : on joue la piste et ajuste la vitesse if (!isPlaying) { audio.play(); isPlaying = true; } // Accélération adoucie : vitesse lente plus étendue, max 1.5x let playbackSpeed = 0.5 + speed / 400; playbackSpeed = Math.min(Math.max(playbackSpeed, 0.5), 1.5); audio.playbackRate = playbackSpeed; // On fait avancer le son naturellement } } lastPosition = position; lastTime = now; // Détecter l'inactivité (pas de mouvement > 300ms) : stoppe la lecture idleTimeout = setTimeout(() => { stopPlayback(); }, 300); }); // 4. Fonctions function togglePlay() { if (isPlaying) { audio.pause(); vinyl.style.animationPlayState = 'paused'; } else { audio.play(); vinyl.style.animationPlayState = 'running'; } isPlaying = !isPlaying; } function updateProgress() { if (!isNaN(audio.duration)) { const progressPercent = (audio.currentTime / audio.duration) * 100; progress.value = progressPercent; currentTimeEl.textContent = formatTime(audio.currentTime); durationEl.textContent = formatTime(audio.duration); // Mise à jour rotation vinyle synchronisée au temps courant updateVinylRotation(); } } function formatTime(time) { const minutes = Math.floor(time / 60); const seconds = Math.floor(time % 60); return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`; } function setProgress(e) { const width = progress.clientWidth; const clickX = e.offsetX; const duration = audio.duration; audio.currentTime = (clickX / width) * duration; } // Fonction pour stopper la lecture function stopPlayback() { audio.pause(); vinyl.style.animationPlayState = 'paused'; isPlaying = false; audio.playbackRate = 1; } // Fonction qui fait tourner le vinyle selon la timeline audio function updateVinylRotation() { if (audio.duration && audio.currentTime >= 0) { // Calcul de l'angle en degrés // rotation proportionnelle au temps courant * nombre de rotations sur la piste const rotationDegrees = (audio.currentTime / audio.duration) * 360 * rotationsPerTrack; vinyl.style.transform = `rotate(${rotationDegrees}deg)`; } } // 5. Événements vinyl.addEventListener('click', togglePlay); audio.addEventListener('timeupdate', updateProgress); audio.addEventListener('loadedmetadata', () => { durationEl.textContent = formatTime(audio.duration); }); progress.addEventListener('click', setProgress); backwardBtn.addEventListener('click', () => { audio.currentTime = Math.max(0, audio.currentTime - 10); }); forwardBtn.addEventListener('click', () => { audio.currentTime = Math.min(audio.duration, audio.currentTime + 10); }); loopBtn.addEventListener('click', () => { isLooping = !isLooping; loopBtn.style.backgroundColor = isLooping ? 'white' : 'transparent'; loopBtn.style.color = isLooping ? '#18344b' : 'white'; }); audio.addEventListener('ended', () => { if (isLooping) { audio.currentTime = 0; audio.play(); } else { vinyl.style.animationPlayState = 'paused'; isPlaying = false; } });