| document.addEventListener("DOMContentLoaded", () => { |
| |
| const fileInput = document.getElementById('file-input'); |
| const runBtn = document.getElementById('btn-run'); |
| const nameInput = document.getElementById('recording-name'); |
| const preview = document.getElementById('video-preview'); |
| const placeholder = document.getElementById('video-placeholder'); |
| const localVideo = document.getElementById('local-video'); |
| const youtubeVideo = document.getElementById('youtube-video'); |
| const videoLink = document.getElementById('video-link'); |
| const loadLocal = document.getElementById('load-local'); |
| const loadYoutube = document.getElementById('load-youtube'); |
| let selectedFile = null; |
|
|
| |
| chargerBibliotheque(); |
|
|
| |
| localVideo.addEventListener('click', () =>{ |
| videoLink.style.display = "none"; |
| loadLocal.style.display = "block"; |
| loadYoutube.style.display = "none"; |
| }); |
|
|
| youtubeVideo.addEventListener('click', () =>{ |
| videoLink.style.display = "block"; |
| loadLocal.style.display = "none"; |
| loadYoutube.style.display = "block"; |
| }); |
|
|
| |
| loadYoutube.addEventListener('click', async () => { |
| const link = videoLink.value.trim(); |
| if (!link) return updateStatus("Entrez le lien d'une vidéo youtube", 'error'); |
|
|
| updateStatus("Téléchargement en cours...", 'active'); |
| setProgress(20); |
| loadYoutube.disabled = true; |
|
|
| try { |
| |
| const response = await fetch('/telecharger', { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ url: link }) |
| }); |
| const data = await response.json(); |
| if (data.succes) { |
| setProgress(60); |
| updateStatus("Vidéo téléchargée — donnez un nom et lancez le pipeline", 'done'); |
| runBtn.disabled = false; |
| } else { |
| setProgress(0); |
| updateStatus(`Erreur téléchargement : ${data.message}`, 'error'); |
| } |
| } catch (err) { |
| setProgress(0); |
| updateStatus(`Erreur : ${err.message}`, 'error'); |
| } finally { |
| loadYoutube.disabled = false; |
| } |
| }); |
|
|
| |
| fileInput.addEventListener('change', (e) => { |
| const file = e.target.files[0]; |
| if (!file) return; |
| selectedFile = file; |
| |
| preview.src = URL.createObjectURL(file); |
| preview.style.display = 'block'; |
| placeholder.style.display = 'none'; |
| runBtn.disabled = false; |
| updateStatus(`Vidéo prête : ${file.name}`, 'active'); |
| }); |
|
|
| |
| runBtn.addEventListener('click', async () => { |
| |
| const nom = nameInput.value.trim(); |
| if (!nom) return updateStatus("Donnez un nom à l'enregistrement", 'error'); |
|
|
| const isYoutube = loadYoutube.style.display === "block"; |
| if (!isYoutube && !selectedFile) return updateStatus("Chargez une vidéo d'abord", 'error'); |
|
|
| try { |
| const responsescv = await fetch('/liste'); |
| if (!responsescv.ok) throw new Error(); |
| const listescv = await responsescv.json(); |
| if (listescv.noms.includes(nom)) { |
| return updateStatus("Ce nom est déjà dans la bibliothèque", 'error'); |
| } |
| } catch (err) { |
| console.error("Erreur de vérification de la bibliothèque:", err); |
| } |
|
|
| runBtn.disabled = true; |
| updateStatus("Extraction des landmarks et génération des mouvements...", 'active'); |
| setProgress(30); |
|
|
| let response; |
| |
| if (isYoutube) { |
| |
| response = await fetch('/lancer-youtube', { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ nom }) |
| }); |
| } else { |
| |
| const formData = new FormData(); |
| formData.append('video', selectedFile); |
| formData.append('nom', nom); |
| response = await fetch('/charger', { method: 'POST', body: formData }); |
| } |
|
|
| try { |
| if (!response.ok) { |
| const text = await response.text(); |
| updateStatus(`Erreur serveur (${response.status}): ${text}`, 'error'); |
| runBtn.disabled = false; |
| return; |
| } |
|
|
| const data = await response.json(); |
|
|
| if (data.succes) { |
| setProgress(100); |
| updateStatus(`Enregistrement "${nom}" ajouté ! Lancement de la vidéo...`, 'done'); |
|
|
| |
| setTimeout(() => { |
| preview.src = `/video/${encodeURIComponent(nom)}?t=${Date.now()}`; |
| preview.style.display = 'block'; |
| placeholder.style.display = 'none'; |
|
|
| const playVideo = () => { |
| preview.play().catch(() => { preview.controls = true; }); |
| }; |
|
|
| if (preview.readyState >= 3) { |
| playVideo(); |
| } else { |
| preview.addEventListener('canplay', playVideo, { once: true }); |
| } |
|
|
| nameInput.value = ''; |
| selectedFile = null; |
| chargerBibliotheque(); |
| }, 500); |
| } else { |
| setProgress(0); |
| updateStatus(`Erreur : ${data.message}`, 'error'); |
| } |
| } catch (err) { |
| setProgress(0); |
| updateStatus(`Erreur: ${err.message}`, 'error'); |
| } finally { |
| runBtn.disabled = false; |
| } |
| }); |
| }); |
|
|
| |
|
|
| |
| async function chargerBibliotheque() { |
| try { |
| const response = await fetch('/liste'); |
| if (!response.ok) throw new Error(); |
| const data = await response.json(); |
|
|
| const liste = document.getElementById('library-list'); |
| liste.innerHTML = ''; |
|
|
| if (data && data.noms) { |
| data.noms.forEach((nom, i) => { |
| const item = document.createElement('li'); |
| item.className = 'library-item'; |
| item.innerHTML = ` |
| <span class="item-index">${String(i + 1).padStart(2, '0')}</span> |
| <span class="item-name">${nom}</span> |
| <button class="btn btn-play button-in-list" onclick="playRecording('${nom}', this)">play</button> |
| <button class="btn btn-delete button-in-list" onclick="deleteRecording('${nom}')">delete</button> |
| `; |
| liste.appendChild(item); |
| }); |
| } |
| } catch (err) { |
| console.error("Erreur de chargement de la bibliothèque", err); |
| } |
| } |
|
|
|
|
| |
| async function playRecording(nom, btn) { |
| document.querySelectorAll('.library-item').forEach(el => el.classList.remove('playing')); |
| document.querySelectorAll('.btn-play').forEach(b => { |
| b.classList.remove('active'); |
| b.textContent = 'play'; |
| }); |
|
|
| btn.classList.add('active'); |
| btn.textContent = '...'; |
| btn.closest('.library-item').classList.add('playing'); |
| updateStatus(`Chargement pour : ${nom}`, 'active'); |
| setProgress(50); |
|
|
| const preview = document.getElementById('video-preview'); |
| const placeholder = document.getElementById('video-placeholder'); |
|
|
| preview.pause(); |
| preview.removeAttribute('src'); |
| preview.load(); |
|
|
| |
| try { |
| const simResp = await fetch('/play', { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ nom }) |
| }); |
| const simData = await simResp.json(); |
| if (!simData.succes) { |
| setProgress(0); |
| updateStatus(`Erreur simulation : ${simData.message}`, 'error'); |
| btn.classList.remove('active'); |
| btn.textContent = 'play'; |
| btn.closest('.library-item').classList.remove('playing'); |
| return; |
| } |
| } catch (err) { |
| setProgress(0); |
| updateStatus(`Erreur : ${err.message}`, 'error'); |
| btn.classList.remove('active'); |
| btn.textContent = 'play'; |
| btn.closest('.library-item').classList.remove('playing'); |
| return; |
| } |
|
|
| updateStatus(`En attente du signal robot...`, 'active'); |
|
|
| |
| const POLL_INTERVAL = 250; |
| const POLL_TIMEOUT = 60000; |
| const pollStart = Date.now(); |
|
|
| try { |
| await new Promise((resolve, reject) => { |
| const poll = async () => { |
| if (Date.now() - pollStart > POLL_TIMEOUT) { |
| return reject(new Error("Timeout : le robot n'a pas répondu dans les 60s")); |
| } |
| try { |
| const r = await fetch('/is-ready'); |
| const d = await r.json(); |
| const elapsed = ((Date.now() - pollStart) / 1000).toFixed(1); |
| updateStatus(`En attente du signal robot… ${elapsed}s`, 'active'); |
| if (d.ready) resolve(); |
| else setTimeout(poll, POLL_INTERVAL); |
| } catch (e) { |
| setTimeout(poll, POLL_INTERVAL); |
| } |
| }; |
| poll(); |
| }); |
| } catch (err) { |
| setProgress(0); |
| updateStatus(`Erreur : ${err.message}`, 'error'); |
| btn.classList.remove('active'); |
| btn.textContent = 'play'; |
| btn.closest('.library-item').classList.remove('playing'); |
| return; |
| } |
|
|
| |
| preview.src = `/video/${encodeURIComponent(nom)}`; |
| preview.style.display = 'block'; |
| placeholder.style.display = 'none'; |
|
|
| await new Promise((resolve, reject) => { |
| preview.addEventListener('canplay', resolve, { once: true }); |
| preview.addEventListener('error', () => reject(new Error('Erreur chargement vidéo')), { once: true }); |
| preview.load(); |
| }); |
|
|
| preview.play().catch(e => { preview.controls = true; }); |
| setProgress(100); |
| updateStatus(`Lecture en cours...`, 'done'); |
|
|
| |
| preview.onended = () => { |
| btn.classList.remove('active'); |
| btn.textContent = 'play'; |
| btn.closest('.library-item').classList.remove('playing'); |
| updateStatus(`Séquence "${nom}" terminée`, 'done'); |
| fetch('/stop', { method: 'POST' }).catch(() => {}); |
| }; |
| } |
|
|
|
|
| |
| function updateStatus(msg, type) { |
| const el = document.getElementById('status-line'); |
| if (el) { el.textContent = msg; el.className = `status-line ${type}`; } |
| } |
|
|
| function setProgress(pct) { |
| const bar = document.getElementById('progress-bar'); |
| if (bar) { |
| bar.style.width = `${pct}%`; |
| if (pct === 100) setTimeout(() => { bar.style.width = '0%'; }, 1200); |
| } |
| } |
|
|
| |
| async function deleteRecording(nom) { |
| const preview = document.getElementById('video-preview'); |
| const placeholder = document.getElementById('video-placeholder'); |
|
|
| if (preview.src.includes(encodeURIComponent(nom))) { |
| preview.pause(); |
| preview.src = ''; |
| preview.load(); |
| preview.style.display = 'none'; |
| placeholder.style.display = 'block'; |
| } |
|
|
| await new Promise(r => setTimeout(r, 300)); |
|
|
| try { |
| const response = await fetch('/supprimer', { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ nom }) |
| }); |
| const data = await response.json(); |
| if (data.succes) { |
| updateStatus(`"${nom}" supprimé.`, 'done'); |
| chargerBibliotheque(); |
| } else { |
| updateStatus(`Erreur suppression : ${data.message}`, 'error'); |
| } |
| } catch (err) { |
| updateStatus(`Erreur : ${err.message}`, 'error'); |
| } |
| } |