/* Module capture photo - Preview live, compte a rebours, declenchement */ let previewActif = false; let previewInterval = null; const EMOJI_CAR = { 3: '🤪', 2: '😱', 1: '🔥' }; // --- Preview live --- function lancerPreview() { if (previewActif) return; previewActif = true; previewInterval = setInterval(() => { if (previewActif) wsEnvoyer({ type: 'preview' }); }, 100); } function arreterPreview() { previewActif = false; if (previewInterval) { clearInterval(previewInterval); previewInterval = null; } } wsOnMessage('preview', (msg) => { if (!previewActif) return; const img = document.getElementById('img-preview'); if (img) img.src = msg.image; }); // --- Capture --- async function lancerCapture() { photosSession = []; lancerPreview(); majCompteurAccueil(); const nbPhotos = getNombrePhotos(); const compteurEl = document.getElementById('capture-compteur'); for (let i = 0; i < nbPhotos; i++) { if (nbPhotos > 1) { compteurEl.textContent = `Photo ${i + 1} / ${nbPhotos}`; } await compteARebours(); arreterPreview(); await pause(300); // Laisser le DSLR se liberer du preview afficherFlash(); // Tenter la capture avec retry let resultat = null; for (let r = 0; r < 3; r++) { resultat = await apiPost('/api/capturer'); if (!resultat.erreur) break; if (resultat.erreur === 'Limite de photos atteinte') { allerA('accueil'); return; } await pause(500); } if (resultat.erreur) { allerA('accueil'); return; } if (resultat.nom) { photosSession.push(resultat.nom); } if (i < nbPhotos - 1) { lancerPreview(); await pause(500); } } await traiterCapture(); } function getNombrePhotos() { if (modeActuel === 'simple') return 1; if (modeActuel === 'multi') return config.multi_shot?.nombre_photos || 4; return 1; } function choisirAnimationAleatoire() { // Construire la liste de toutes les animations actives (CSS + custom) const cssActives = config.camera?.animations_css_actives || ['classique']; const customActives = config.animations_custom?.actives || []; const pool = [ ...cssActives.map(id => ({ type: 'css', id })), ...customActives.map(nom => ({ type: 'custom', nom })), ]; if (pool.length === 0) return { type: 'css', id: 'classique' }; return pool[Math.floor(Math.random() * pool.length)]; } async function compteARebours() { const conteneur = document.getElementById('compte-a-rebours'); const chiffre = document.getElementById('chiffre-car'); const duree = config.camera?.compte_a_rebours || 3; // Choisir une animation au hasard parmi les actives const anim = choisirAnimationAleatoire(); if (anim.type === 'custom') { // Jouer la video/gif en plein ecran, shoot des qu'elle finit await jouerAnimationCustom(anim.nom); return; // pas de compte a rebours CSS } else { // Animation CSS classique conteneur.classList.remove('cache'); for (let i = duree; i > 0; i--) { if (anim.id === 'emoji') { chiffre.textContent = EMOJI_CAR[i] || i; } else { chiffre.textContent = i; } chiffre.className = 'anim-chiffre'; void chiffre.offsetWidth; chiffre.classList.add('anim-' + anim.id); await pause(1000); } conteneur.classList.add('cache'); } } async function jouerAnimationCustom(nom) { // Creer un overlay plein ecran pour la video/gif const overlay = document.createElement('div'); overlay.className = 'video-countdown-overlay'; const ext = nom.split('.').pop().toLowerCase(); if (ext === 'gif') { const img = document.createElement('img'); // Ajouter un timestamp pour forcer le rechargement (relancer le GIF depuis le debut) img.src = `/assets/animations/${nom}?t=${Date.now()}`; overlay.appendChild(img); document.body.appendChild(overlay); // Pour un GIF, on attend sa duree naturelle // Comme on ne peut pas detecter la fin d'un GIF, on utilise un fallback de 3s // L'utilisateur peut configurer la duree dans le nom du fichier (ex: countdown_5s.gif) const match = nom.match(/(\d+)s/); const dureeGif = match ? parseInt(match[1]) * 1000 : 3000; await pause(dureeGif); } else { // Video MP4/WebM : on attend la fin exacte de la video const video = document.createElement('video'); video.src = `/assets/animations/${nom}`; video.muted = true; video.playsInline = true; overlay.appendChild(video); document.body.appendChild(overlay); await new Promise(resolve => { video.onended = resolve; video.onerror = resolve; video.play().catch(resolve); }); } overlay.remove(); } function afficherFlash() { const flash = document.getElementById('flash-blanc'); flash.classList.remove('cache'); flash.style.animation = 'none'; flash.offsetHeight; flash.style.animation = 'flash 0.3s ease-out forwards'; setTimeout(() => flash.classList.add('cache'), 400); } let photoImpression = null; // Nom du fichier d'impression (2 bandes sur 10x15) async function traiterCapture() { if (photosSession.length === 0) { allerA('accueil'); return; } photoImpression = null; if (modeActuel === 'multi') { const mode = config.multi_shot?.mode || 'strip'; let resultat; if (mode === 'strip') { resultat = await apiPost('/api/strip', { photos: photosSession }); if (resultat.impression) { photoImpression = resultat.impression; } } else { resultat = await apiPost('/api/collage', { photos: photosSession }); } if (resultat.nom) { photoFinale = resultat.nom; afficherPreviewPhoto('/data/exports/' + resultat.nom); } } else { photoFinale = photosSession[0]; afficherPreviewPhoto('/data/photos/' + photosSession[0]); } allerA('preview'); } function afficherPreviewPhoto(chemin) { document.getElementById('photo-resultat').src = chemin; document.getElementById('photo-partage').src = chemin; if (modeActuel === 'simple' && config.fonctionnalites?.filtres) { chargerFiltres(); } if (config.fonctionnalites?.overlays) { chargerOverlays(); } if (config.fonctionnalites?.chroma_key) { chargerFonds(); } } // --- Compteur accueil --- async function majCompteurAccueil() { const etat = await apiGet('/api/compteur'); const el = document.getElementById('compteur-accueil'); if (etat.actif) { el.classList.remove('cache'); document.getElementById('compteur-restant').textContent = etat.restantes; document.getElementById('compteur-limite').textContent = etat.limite; } else { el.classList.add('cache'); } } function pause(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }