Backoffice complet : materiel, compteur, destinations, cadres, animations

- Onglet Materiel : choix appareil photo + imprimante
- Compteur avec limite configurable (399/400) affiche sur l'accueil
- Destinations multiples : memoire interne, cle USB, FTP, site web, email
- Choix sauvegarder toutes les photos ou seulement les imprimees
- Gestion des cadres : activation/desactivation + import
- 7 animations de compte a rebours : classique, explosion, rebond,
  rotation 3D, fondu, emoji fun, tremblement
- Preview des animations dans le backoffice
- Module destinations.py (copie USB, envoi FTP, compteur)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-21 23:03:30 +01:00
parent 3b3271a32f
commit d9de999d74
8 changed files with 1009 additions and 148 deletions

View File

@@ -3,16 +3,17 @@
let previewActif = false;
let previewInterval = null;
const EMOJI_CAR = { 3: '🤪', 2: '😱', 1: '🔥' };
// --- Preview live ---
function lancerPreview() {
if (previewActif) return;
previewActif = true;
// Demander des previews via WebSocket
previewInterval = setInterval(() => {
if (previewActif) wsEnvoyer({ type: 'preview' });
}, 100); // ~10 fps
}, 100);
}
function arreterPreview() {
@@ -23,7 +24,6 @@ function arreterPreview() {
}
}
// Recevoir les previews
wsOnMessage('preview', (msg) => {
if (!previewActif) return;
const img = document.getElementById('img-preview');
@@ -35,6 +35,7 @@ wsOnMessage('preview', (msg) => {
async function lancerCapture() {
photosSession = [];
lancerPreview();
majCompteurAccueil();
const nbPhotos = getNombrePhotos();
const compteurEl = document.getElementById('capture-compteur');
@@ -44,27 +45,28 @@ async function lancerCapture() {
compteurEl.textContent = `Photo ${i + 1} / ${nbPhotos}`;
}
// Compte a rebours
await compteARebours();
// Flash
afficherFlash();
// Capture
arreterPreview();
const resultat = await apiPost('/api/capturer');
if (resultat.erreur) {
// Limite atteinte ou erreur
allerA('accueil');
return;
}
if (resultat.nom) {
photosSession.push(resultat.nom);
}
// Reprendre le preview si encore des photos a prendre
if (i < nbPhotos - 1) {
lancerPreview();
await pause(500);
}
}
// Traitement selon le mode
await traiterCapture();
}
@@ -78,15 +80,24 @@ async function compteARebours() {
const conteneur = document.getElementById('compte-a-rebours');
const chiffre = document.getElementById('chiffre-car');
const duree = config.camera?.compte_a_rebours || 3;
const anim = config.camera?.animation_compte_a_rebours || 'classique';
conteneur.classList.remove('cache');
for (let i = duree; i > 0; i--) {
chiffre.textContent = i;
// Re-trigger animation
// Contenu selon le type d'animation
if (anim === 'emoji') {
chiffre.textContent = EMOJI_CAR[i] || i;
} else {
chiffre.textContent = i;
}
// Appliquer l'animation
chiffre.className = 'anim-chiffre anim-' + anim;
chiffre.style.animation = 'none';
chiffre.offsetHeight; // force reflow
chiffre.style.animation = 'pop 0.5s ease';
chiffre.offsetHeight;
chiffre.className = 'anim-chiffre anim-' + anim;
await pause(1000);
}
@@ -109,7 +120,6 @@ async function traiterCapture() {
}
if (modeActuel === 'multi') {
// Creer le strip/collage
const mode = config.multi_shot?.mode || 'strip';
let resultat;
if (mode === 'strip') {
@@ -122,7 +132,6 @@ async function traiterCapture() {
afficherPreviewPhoto('/data/exports/' + resultat.nom);
}
} else {
// Photo simple - aller au preview avec filtres
photoFinale = photosSession[0];
afficherPreviewPhoto('/data/photos/' + photosSession[0]);
}
@@ -134,17 +143,28 @@ function afficherPreviewPhoto(chemin) {
document.getElementById('photo-resultat').src = chemin;
document.getElementById('photo-partage').src = chemin;
// Charger les filtres si photo simple
if (modeActuel === 'simple' && config.fonctionnalites?.filtres) {
chargerFiltres();
}
// Charger les overlays
if (config.fonctionnalites?.overlays) {
chargerOverlays();
}
}
// --- 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));
}