Bulletproof: messages novices, kiosk port auto, reset compteur, sync booth event_id

- Messages erreur camera/impression: texte novice ("Oups!", "Reessayez") au lieu de jargon technique
- kiosk-session.sh: lit le port depuis config.json (plus de http://localhost en dur), healthcheck /api/config, supprime git pull auto au boot
- Activer evenement: reset automatique du compteur photos
- Activer evenement: sync booth.event_id quand galerie live active
- Port par defaut backend: 80 → 8080

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-09-13 09:17:26 +02:00
parent 96464ccca1
commit f1299fd6a7
5 changed files with 41 additions and 23 deletions

View File

@@ -125,6 +125,10 @@ def activer_evenement(event_id: str) -> dict | None:
event = obtenir_evenement(event_id)
if not event:
return None
config = charger_config()
booth = config.get("booth", {})
if booth.get("actif"):
booth["event_id"] = event_id
mettre_a_jour_config({
"evenement": {
"event_id": event_id,
@@ -134,7 +138,8 @@ def activer_evenement(event_id: str) -> dict | None:
"couleur_secondaire": event.get("couleur_secondaire", "#ffffff"),
"media_accueil": event.get("media_accueil"),
"formats_actifs": event.get("formats_actifs", ["10x15", "15x20", "strip"]),
}
},
"booth": booth,
})
log.info(f"Evenement active : {event['nom']} ({event_id})")
return event

View File

@@ -1736,6 +1736,8 @@ async def api_activer_evenement(event_id: str):
event = activer_evenement(event_id)
if not event:
return JSONResponse({"erreur": "Evenement introuvable"}, status_code=404)
reset_compteur()
log.info(f"Compteur photos remis a zero (activation evenement {event_id})")
await diffuser_ws({"type": "config_maj", "config": charger_config()})
return event
@@ -2587,6 +2589,6 @@ if __name__ == "__main__":
uvicorn.run(
app,
host=conf_srv.get("host", "0.0.0.0"),
port=conf_srv.get("port", 80),
port=conf_srv.get("port", 8080),
log_level="info",
)

View File

@@ -50,8 +50,8 @@ function afficherErreurPreview(visible) {
const el = document.getElementById('preview-erreur-camera');
if (!el) return;
if (visible) {
el.querySelector('p').textContent = 'Appareil photo deconnecte';
el.querySelector('span').textContent = 'Reconnexion en cours...';
el.querySelector('p').textContent = 'Un instant...';
el.querySelector('span').textContent = 'L\'appareil photo se reconnecte';
el.classList.remove('cache');
} else {
el.classList.add('cache');
@@ -194,7 +194,7 @@ wsOnMessage('camera_erreur', () => {
if (!captureEnCours) {
captureAbortee = true;
if (ecranActuel === 'capture') {
afficherErreurCapture('Appareil photo déconnecté', 'Reconnexion en cours...');
afficherErreurCapture('Un instant...', 'L\'appareil photo se prepare');
}
}
});
@@ -253,7 +253,7 @@ async function lancerCapture() {
try {
let statut = await apiGet('/api/camera/statut');
if ((!statut.connectee || statut.mode === 'erreur') && statut.reveil_en_cours) {
afficherStatut('Appareil photo en preparation...', 'succes');
afficherStatut('L\'appareil photo se prepare, un instant...', 'succes');
for (let att = 0; att < 30; att++) {
await pause(2000);
statut = await apiGet('/api/camera/statut');
@@ -263,12 +263,12 @@ async function lancerCapture() {
}
if (!statut.connectee || statut.mode === 'erreur') {
captureEnCours = false;
afficherErreurCapture('Appareil photo non disponible', 'Verifiez la connexion USB du DSLR');
afficherErreurCapture('Appareil photo indisponible', 'Reessayez dans quelques instants');
return;
}
} catch (e) {
captureEnCours = false;
afficherErreurCapture('Impossible de joindre le serveur', 'Verifiez que le photobooth est bien demarre');
afficherErreurCapture('Le photobooth demarre', 'Reessayez dans quelques instants');
return;
}
@@ -304,7 +304,7 @@ async function lancerCapture() {
flash.classList.add('cache');
document.getElementById('capture-en-cours').classList.add('cache');
captureEnCours = false;
afficherErreurCapture('Erreur reseau', 'Le serveur ne repond pas — redemarrez le photobooth');
afficherErreurCapture('Oups !', 'La photo n\'a pas pu etre prise, reessayez');
return;
}
@@ -313,7 +313,7 @@ async function lancerCapture() {
document.getElementById('capture-en-cours').classList.add('cache');
captureEnCours = false;
const msg = resultat.erreur;
afficherErreurCapture('Echec de la capture', msg.includes('Echec') ? 'Verifiez le DSLR et reessayez' : msg);
afficherErreurCapture('Oups !', 'La photo n\'a pas pu etre prise, reessayez');
return;
}
@@ -447,7 +447,7 @@ let formatImpression = null; // Format CUPS à utiliser (ex: "10x15-2up")
async function traiterCapture() {
if (photosSession.length === 0) {
afficherErreurCapture('Aucune photo capturee', 'Une erreur inattendue s\'est produite');
afficherErreurCapture('Oups !', 'La photo n\'a pas pu etre prise, reessayez');
return;
}
@@ -461,7 +461,7 @@ async function traiterCapture() {
if (mode === 'strip') {
resultat = await apiPost('/api/strip', { photos: photosSession });
if (resultat.erreur) {
afficherErreurCapture('Erreur creation de la planche', resultat.erreur);
afficherErreurCapture('Oups !', 'La planche n\'a pas pu etre creee, reessayez');
return;
}
if (resultat.impression) photoImpression = resultat.impression;
@@ -469,12 +469,12 @@ async function traiterCapture() {
} else {
resultat = await apiPost('/api/collage', { photos: photosSession });
if (resultat.erreur) {
afficherErreurCapture('Erreur creation du collage', resultat.erreur);
afficherErreurCapture('Oups !', 'Le collage n\'a pas pu etre cree, reessayez');
return;
}
}
if (!resultat.nom) {
afficherErreurCapture('Erreur traitement des photos', 'Le fichier final n\'a pas ete genere');
afficherErreurCapture('Oups !', 'Un probleme est survenu, reessayez');
return;
}
photoFinale = resultat.nom;

View File

@@ -125,9 +125,9 @@ async function lancerImpression() {
} else if (resultat.succes) {
afficherStatut(`${nbExemplaires} exemplaire(s) envoye(s) a l'imprimante !`, 'succes');
} else if (resultat.erreur === 'evenement_termine') {
afficherStatut('Evenement termine — impression indisponible', 'erreur');
afficherStatut('L\'impression n\'est plus disponible pour cet evenement', 'erreur');
} else {
afficherStatut('Erreur d\'impression', 'erreur');
afficherStatut('L\'impression a rencontre un probleme, reessayez', 'erreur');
}
}
@@ -189,7 +189,7 @@ async function envoyerEmail() {
} else if (resultat.succes) {
afficherStatut('Email envoyé !', 'succes');
} else {
afficherStatut('Erreur d\'envoi email', 'erreur');
afficherStatut('L\'email n\'a pas pu etre envoye, reessayez', 'erreur');
}
}

View File

@@ -4,6 +4,18 @@
exec 9>/tmp/photobooth-kiosk.lock
flock -n 9 || { echo "[kiosk] Déjà en cours, abandon."; exit 0; }
PHOTOBOOTH_DIR=/home/jules/photobooth
# Port du backend (lu depuis config.json, fallback 8080)
BACKEND_PORT=$(python3 -c "
import json, sys
try:
d = json.load(open('$PHOTOBOOTH_DIR/data/config.json'))
print(d.get('serveur', {}).get('port', 8080))
except: print(8080)
" 2>/dev/null)
BACKEND_URL="http://localhost:${BACKEND_PORT}"
# Tuer tout backend orphelin d'une session precedente
pkill -f 'python.*backend.main' 2>/dev/null || true
sleep 1
@@ -23,8 +35,7 @@ pkill -f gvfs-gphoto2-volume-monitor 2>/dev/null || true
_backend_loop() {
while true; do
while [ -f /tmp/photobooth-maintenance ]; do sleep 2; done
cd /home/jules/photobooth
git pull --ff-only 2>&1 | logger -t photobooth-git
cd "$PHOTOBOOTH_DIR"
source .venv/bin/activate
authbind --deep python -m backend.main 2>&1 | tee -a /tmp/photobooth-backend.log
echo "[kiosk] backend terminé, redémarrage dans 3s..."
@@ -37,9 +48,9 @@ BACKEND_LOOP_PID=$!
# Nettoyer le backend si le kiosk est tue
trap "kill $BACKEND_LOOP_PID 2>/dev/null; pkill -f 'python.*backend.main' 2>/dev/null" EXIT
# Attendre que le backend soit pret (max 15s)
for i in $(seq 1 15); do
curl -sf http://localhost/ > /dev/null 2>&1 && break
# Attendre que le backend soit pret (max 30s)
for i in $(seq 1 30); do
curl -sf "${BACKEND_URL}/api/config" > /dev/null 2>&1 && break
sleep 1
done
@@ -64,7 +75,7 @@ while true; do
--password-store=basic \
--lang=fr \
--remote-debugging-port=9222 \
http://localhost
"${BACKEND_URL}"
echo "[kiosk] Chromium terminé, redémarrage dans 2s..."
sleep 2
done