Init photobooth - borne photo evenementielle RPi4

Backend FastAPI complet : camera gphoto2, filtres/overlays Pillow,
chroma key OpenCV, multi-shot/collage, GIF, impression CUPS, email SMTP,
QR code. Frontend web vanilla (HTML/CSS/JS) pour Chromium kiosk.
Menu admin cache (appui long coin ecran). Toutes les fonctionnalites
activables/desactivables. Scripts install + systemd pour RPi4.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-21 21:46:31 +01:00
commit c8e4ec80c1
29 changed files with 2854 additions and 0 deletions

257
frontend/index.html Normal file
View File

@@ -0,0 +1,257 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Photobooth</title>
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<div id="app">
<!-- Ecran d'accueil -->
<section id="ecran-accueil" class="ecran actif">
<div class="accueil-contenu">
<h1 id="nom-evenement">Photobooth</h1>
<p class="accueil-instruction">Touchez pour commencer</p>
<div class="accueil-animation">
<div class="cercle-pulse"></div>
</div>
</div>
<!-- Zone admin : coin bas-droit, appui long 3s -->
<div id="zone-admin" class="zone-admin"></div>
</section>
<!-- Choix du mode -->
<section id="ecran-mode" class="ecran">
<h2>Choisissez votre mode</h2>
<div class="grille-modes">
<button class="btn-mode" data-mode="simple">
<div class="mode-icone">&#128247;</div>
<span>Photo</span>
</button>
<button class="btn-mode" data-mode="multi" id="btn-multi">
<div class="mode-icone">&#127924;</div>
<span>Multi-shot</span>
</button>
<button class="btn-mode" data-mode="gif" id="btn-gif">
<div class="mode-icone">&#127910;</div>
<span>GIF</span>
</button>
</div>
<button class="btn-retour" onclick="allerA('accueil')">Retour</button>
</section>
<!-- Compte a rebours + Capture -->
<section id="ecran-capture" class="ecran">
<div id="preview-live" class="preview-live">
<img id="img-preview" src="" alt="Preview">
</div>
<div id="compte-a-rebours" class="compte-a-rebours cache">
<span id="chiffre-car">3</span>
</div>
<div id="flash-blanc" class="flash-blanc cache"></div>
<div class="capture-info">
<span id="capture-compteur"></span>
</div>
</section>
<!-- Preview photo + Filtres -->
<section id="ecran-preview" class="ecran">
<div class="preview-photo-container">
<img id="photo-resultat" src="" alt="Photo">
</div>
<div id="barre-filtres" class="barre-filtres">
<!-- Rempli dynamiquement -->
</div>
<div id="barre-overlays" class="barre-overlays cache">
<!-- Rempli dynamiquement -->
</div>
<div class="preview-actions">
<button class="btn-action" onclick="allerA('partage')">Valider</button>
<button class="btn-secondaire" onclick="recommencer()">Reprendre</button>
</div>
</section>
<!-- Partage -->
<section id="ecran-partage" class="ecran">
<h2>Partagez votre photo !</h2>
<div class="photo-miniature">
<img id="photo-partage" src="" alt="Photo">
</div>
<div class="grille-partage">
<button class="btn-partage" id="btn-imprimer" onclick="lancerImpression()">
<div class="partage-icone">&#128424;</div>
<span>Imprimer</span>
</button>
<button class="btn-partage" id="btn-email" onclick="ouvrirEmail()">
<div class="partage-icone">&#128231;</div>
<span>Email</span>
</button>
<button class="btn-partage" id="btn-qr" onclick="afficherQR()">
<div class="partage-icone">&#128274;</div>
<span>QR Code</span>
</button>
</div>
<!-- Formulaire email -->
<div id="form-email" class="form-email cache">
<input type="email" id="input-email" placeholder="votre@email.com" autocomplete="off">
<button class="btn-action" onclick="envoyerEmail()">Envoyer</button>
<button class="btn-secondaire" onclick="fermerEmail()">Annuler</button>
</div>
<!-- QR Code -->
<div id="zone-qr" class="zone-qr cache">
<img id="img-qr" src="" alt="QR Code">
</div>
<!-- Message statut -->
<div id="statut-partage" class="statut-partage cache"></div>
<div class="partage-bas">
<button class="btn-action" onclick="allerA('accueil')">Terminer</button>
<button class="btn-secondaire" onclick="recommencer()">Nouvelle photo</button>
</div>
</section>
<!-- Galerie -->
<section id="ecran-galerie" class="ecran">
<h2>Galerie</h2>
<div id="grille-galerie" class="grille-galerie">
<!-- Rempli dynamiquement -->
</div>
<button class="btn-retour" onclick="allerA('accueil')">Retour</button>
</section>
<!-- Menu Admin -->
<section id="ecran-admin" class="ecran">
<div class="admin-header">
<h2>Administration</h2>
<button class="btn-fermer" onclick="allerA('accueil')">&times;</button>
</div>
<div class="admin-contenu">
<!-- Onglets -->
<div class="admin-onglets">
<button class="onglet actif" data-onglet="fonctionnalites">Fonctions</button>
<button class="onglet" data-onglet="evenement">Evenement</button>
<button class="onglet" data-onglet="camera-admin">Camera</button>
<button class="onglet" data-onglet="impression-admin">Impression</button>
<button class="onglet" data-onglet="email-admin">Email</button>
<button class="onglet" data-onglet="galerie-admin">Galerie</button>
<button class="onglet" data-onglet="systeme">Systeme</button>
</div>
<!-- Panneau Fonctionnalites -->
<div class="admin-panneau actif" id="panneau-fonctionnalites">
<div class="toggle-groupe">
<label class="toggle"><input type="checkbox" id="tog-photo-simple" checked><span class="toggle-slider"></span> Photo simple</label>
<label class="toggle"><input type="checkbox" id="tog-multi-shot"><span class="toggle-slider"></span> Multi-shot</label>
<label class="toggle"><input type="checkbox" id="tog-gif"><span class="toggle-slider"></span> GIF anime</label>
<label class="toggle"><input type="checkbox" id="tog-filtres"><span class="toggle-slider"></span> Filtres</label>
<label class="toggle"><input type="checkbox" id="tog-overlays"><span class="toggle-slider"></span> Overlays / Cadres</label>
<label class="toggle"><input type="checkbox" id="tog-chroma-key"><span class="toggle-slider"></span> Chroma key (fond vert)</label>
<label class="toggle"><input type="checkbox" id="tog-impression"><span class="toggle-slider"></span> Impression</label>
<label class="toggle"><input type="checkbox" id="tog-email"><span class="toggle-slider"></span> Email</label>
<label class="toggle"><input type="checkbox" id="tog-qr-code"><span class="toggle-slider"></span> QR Code</label>
<label class="toggle"><input type="checkbox" id="tog-galerie"><span class="toggle-slider"></span> Galerie</label>
<label class="toggle"><input type="checkbox" id="tog-compteur"><span class="toggle-slider"></span> Compteur</label>
</div>
</div>
<!-- Panneau Evenement -->
<div class="admin-panneau" id="panneau-evenement">
<div class="champ">
<label>Nom de l'evenement</label>
<input type="text" id="admin-nom-event" placeholder="Mon Evenement">
</div>
<div class="champ">
<label>Couleur primaire</label>
<input type="color" id="admin-couleur-primaire" value="#e91e63">
</div>
<div class="champ">
<label>Couleur secondaire</label>
<input type="color" id="admin-couleur-secondaire" value="#ffffff">
</div>
<button class="btn-action" onclick="sauvegarderEvenement()">Sauvegarder</button>
</div>
<!-- Panneau Camera -->
<div class="admin-panneau" id="panneau-camera-admin">
<div class="champ">
<label>Statut</label>
<span id="admin-camera-statut">--</span>
<button class="btn-secondaire" onclick="reconnecterCamera()">Reconnecter</button>
</div>
<div class="champ">
<label>Compte a rebours (secondes)</label>
<input type="number" id="admin-car" min="1" max="10" value="3">
</div>
</div>
<!-- Panneau Impression -->
<div class="admin-panneau" id="panneau-impression-admin">
<div class="champ">
<label>Imprimante</label>
<select id="admin-imprimante"><option value="">Chargement...</option></select>
</div>
<div class="champ">
<label>Copies</label>
<input type="number" id="admin-copies" min="1" max="5" value="1">
</div>
</div>
<!-- Panneau Email -->
<div class="admin-panneau" id="panneau-email-admin">
<div class="champ">
<label>Serveur SMTP</label>
<input type="text" id="admin-smtp-host" placeholder="smtp.gmail.com">
</div>
<div class="champ">
<label>Port</label>
<input type="number" id="admin-smtp-port" value="587">
</div>
<div class="champ">
<label>Utilisateur</label>
<input type="text" id="admin-smtp-user" placeholder="user@gmail.com">
</div>
<div class="champ">
<label>Mot de passe</label>
<input type="password" id="admin-smtp-pass">
</div>
<div class="champ">
<label>Expediteur</label>
<input type="text" id="admin-expediteur" placeholder="photobooth@event.com">
</div>
<button class="btn-action" onclick="sauvegarderEmail()">Sauvegarder</button>
</div>
<!-- Panneau Galerie -->
<div class="admin-panneau" id="panneau-galerie-admin">
<div class="champ">
<label>Photos prises : <span id="admin-nb-photos">0</span></label>
<label>Exports : <span id="admin-nb-exports">0</span></label>
</div>
<div class="champ">
<label>URL galerie en ligne</label>
<input type="text" id="admin-url-galerie" placeholder="https://...">
</div>
<button class="btn-action" onclick="sauvegarderGalerie()">Sauvegarder URL</button>
<button class="btn-danger" onclick="confirmerViderGalerie()">Vider toute la galerie</button>
</div>
<!-- Panneau Systeme -->
<div class="admin-panneau" id="panneau-systeme">
<button class="btn-danger" onclick="confirmerRedemarrage()">Redemarrer le systeme</button>
<button class="btn-danger" onclick="confirmerExtinction()">Eteindre le systeme</button>
</div>
</div>
</section>
</div>
<script src="/js/websocket.js"></script>
<script src="/js/app.js"></script>
<script src="/js/camera.js"></script>
<script src="/js/effects.js"></script>
<script src="/js/gallery.js"></script>
<script src="/js/share.js"></script>
<script src="/js/admin.js"></script>
</body>
</html>