196 lines
4.9 KiB
JavaScript
196 lines
4.9 KiB
JavaScript
// 1. Sélection des éléments du 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 reverseInterval = null;
|
||
let reverseAudioLoaded = false;
|
||
|
||
// 3. Variables pour vitesse
|
||
let lastPosition = null;
|
||
let lastTime = null;
|
||
|
||
// 4. Connexion socket
|
||
const socket = io();
|
||
|
||
// 5. Sauvegarder les sources
|
||
const originalSrc = audio.getAttribute('src');
|
||
const reverseSrc = audio.dataset.reverseSrc || null;
|
||
|
||
// 6. SOCKET – gestion de la position envoyée
|
||
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;
|
||
|
||
clearTimeout(idleTimeout);
|
||
|
||
if (Math.abs(speed) < 15) {
|
||
stopPlayback();
|
||
return;
|
||
}
|
||
|
||
const isReverse = speed < 0;
|
||
|
||
if (isReverse) {
|
||
simulateReverse(Math.abs(speed));
|
||
} else {
|
||
stopReverse();
|
||
let playbackSpeed = 1 + speed * 0.0003;
|
||
playbackSpeed = Math.max(0.5, Math.min(1.5, playbackSpeed));
|
||
controlPlayback(playbackSpeed);
|
||
}
|
||
}
|
||
|
||
lastPosition = position;
|
||
lastTime = now;
|
||
|
||
idleTimeout = setTimeout(() => {
|
||
stopPlayback();
|
||
}, 300);
|
||
});
|
||
|
||
// 7. Fonctions
|
||
|
||
function togglePlay() {
|
||
if (isPlaying) {
|
||
audio.pause();
|
||
vinyl.style.animationPlayState = 'paused';
|
||
} else {
|
||
audio.play();
|
||
vinyl.style.animationPlayState = 'running';
|
||
}
|
||
isPlaying = !isPlaying;
|
||
}
|
||
|
||
function updateProgress() {
|
||
const progressPercent = (audio.currentTime / audio.duration) * 100;
|
||
progress.value = progressPercent;
|
||
currentTimeEl.textContent = formatTime(audio.currentTime);
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
function controlPlayback(speed) {
|
||
stopReverse();
|
||
if (speed > 0) {
|
||
if (!isPlaying) {
|
||
audio.play();
|
||
vinyl.style.animationPlayState = 'running';
|
||
isPlaying = true;
|
||
}
|
||
audio.playbackRate = speed;
|
||
}
|
||
}
|
||
|
||
function stopPlayback() {
|
||
audio.pause();
|
||
vinyl.style.animationPlayState = 'paused';
|
||
isPlaying = false;
|
||
}
|
||
|
||
// Simule la lecture en reverse via décrémentation du currentTime
|
||
function simulateReverse(speed) {
|
||
const reverseSpeed = Math.min(speed * 0.01, 2);
|
||
|
||
if (!reverseSrc) return;
|
||
|
||
if (!reverseInterval) {
|
||
reverseInterval = setInterval(() => {
|
||
if (audio.currentTime > 0.05) {
|
||
audio.currentTime -= reverseSpeed * 0.1;
|
||
vinyl.style.animationPlayState = 'running';
|
||
} else {
|
||
stopPlayback();
|
||
}
|
||
}, 100);
|
||
|
||
if (!reverseAudioLoaded) {
|
||
const currentTime = audio.currentTime;
|
||
audio.src = reverseSrc;
|
||
audio.load();
|
||
audio.addEventListener('loadedmetadata', () => {
|
||
const newTime = Math.min(currentTime, audio.duration - 0.1);
|
||
audio.currentTime = newTime;
|
||
}, { once: true });
|
||
reverseAudioLoaded = true;
|
||
}
|
||
|
||
isPlaying = true;
|
||
audio.pause();
|
||
}
|
||
}
|
||
|
||
function stopReverse() {
|
||
if (reverseInterval) {
|
||
clearInterval(reverseInterval);
|
||
reverseInterval = null;
|
||
}
|
||
|
||
if (reverseAudioLoaded) {
|
||
const currentTime = audio.currentTime;
|
||
audio.src = originalSrc;
|
||
audio.load();
|
||
audio.addEventListener('loadedmetadata', () => {
|
||
audio.currentTime = Math.min(currentTime, audio.duration - 0.1);
|
||
}, { once: true });
|
||
reverseAudioLoaded = false;
|
||
}
|
||
}
|
||
|
||
// 8. Événements utilisateur
|
||
|
||
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;
|
||
}
|
||
});
|