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

59
backend/gallery.py Normal file
View File

@@ -0,0 +1,59 @@
import logging
import os
from datetime import datetime
from pathlib import Path
from backend.config import DOSSIER_PHOTOS, DOSSIER_EXPORTS
log = logging.getLogger("photobooth.gallery")
def lister_photos(dossier: str = "exports") -> list[dict]:
"""Liste les photos d'un dossier avec leurs metadonnees."""
chemin = DOSSIER_EXPORTS if dossier == "exports" else DOSSIER_PHOTOS
photos = []
extensions = {".jpg", ".jpeg", ".png", ".gif"}
if not chemin.exists():
return photos
for fichier in sorted(chemin.iterdir(), key=lambda f: f.stat().st_mtime, reverse=True):
if fichier.suffix.lower() in extensions:
stat = fichier.stat()
photos.append({
"nom": fichier.name,
"chemin": str(fichier.relative_to(chemin.parent.parent)),
"taille": stat.st_size,
"date": datetime.fromtimestamp(stat.st_mtime).isoformat(),
})
return photos
def compter_photos() -> dict:
"""Retourne le compteur de photos pour l'evenement."""
nb_photos = len(list(DOSSIER_PHOTOS.glob("*.jpg"))) + len(list(DOSSIER_PHOTOS.glob("*.jpeg")))
nb_exports = len(list(DOSSIER_EXPORTS.glob("*"))) - len(list(DOSSIER_EXPORTS.glob(".gitkeep")))
return {
"photos_prises": nb_photos,
"exports": max(0, nb_exports),
}
def supprimer_photo(nom: str, dossier: str = "exports") -> bool:
"""Supprime une photo par son nom."""
chemin = DOSSIER_EXPORTS if dossier == "exports" else DOSSIER_PHOTOS
fichier = chemin / nom
if fichier.exists() and fichier.parent == chemin:
fichier.unlink()
log.info(f"Photo supprimee : {fichier}")
return True
return False
def vider_galerie():
"""Supprime toutes les photos (reset evenement)."""
for dossier in [DOSSIER_PHOTOS, DOSSIER_EXPORTS]:
for fichier in dossier.iterdir():
if fichier.name != ".gitkeep":
fichier.unlink()
log.info("Galerie videe")