From 98c738cf791abc029e5ce008552e4069cbc80ab7 Mon Sep 17 00:00:00 2001 From: Jules Date: Mon, 22 Jun 2026 20:07:52 +0200 Subject: [PATCH] Galerie booth : QR avec auto-login, event_id aleatoire, flyer imprimable - QR code inclut le mot de passe (?p=XXXX) pour acces direct sans saisie - Supprime le code texte visible sur l'ecran d'accueil, QR plus petit - Bouton admin 'Imprimer flyer QR galerie' genere un 10x15 avec nom evenement, QR code, texte invitation, design assorti - Auto-login ajoute cote serveur booth (booth.copydev.fr) Co-Authored-By: Claude Opus 4.6 --- backend/main.py | 71 ++++++++++++++++++++++++++++++++++++++++++ frontend/css/style.css | 13 ++------ frontend/index.html | 4 +-- frontend/js/admin.js | 10 ++++++ frontend/js/app.js | 4 +-- frontend/js/share.js | 5 +-- 6 files changed, 90 insertions(+), 17 deletions(-) diff --git a/backend/main.py b/backend/main.py index 91d1e04..4613765 100644 --- a/backend/main.py +++ b/backend/main.py @@ -819,6 +819,77 @@ async def api_qr_galerie(): return {"chemin": f"/data/exports/{chemin.name}"} +@app.post("/api/booth/imprimer-qr") +async def api_booth_imprimer_qr(): + """Genere et imprime un flyer QR pour la galerie booth.""" + config = charger_config() + booth = config.get("booth", {}) + event = config.get("evenement", {}) + + if not booth.get("actif") or not booth.get("url"): + return JSONResponse({"erreur": "Booth non configure"}, status_code=400) + + info = recuperer_booth_password() + password = info.get("password", "") + event_id = booth.get("event_id", "default") + nom_event = event.get("nom", "Photobooth") + url_galerie = f"{booth['url']}/{event_id}?p={password}" + + from PIL import Image, ImageDraw, ImageFont + import qrcode + + W, H = 1200, 1800 # 10x15 portrait @ 300dpi + img = Image.new("RGB", (W, H), "#0a0a1a") + draw = ImageDraw.Draw(img) + + try: + font_big = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 72) + font_med = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 42) + font_small = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 32) + except OSError: + font_big = ImageFont.load_default() + font_med = font_big + font_small = font_big + + # Titre evenement + draw.text((W // 2, 120), "📸", font=font_big, anchor="mt", fill="#e91e63") + draw.text((W // 2, 220), nom_event, font=font_big, anchor="mt", fill="#ffffff") + + # Ligne decorative + draw.line([(150, 320), (W - 150, 320)], fill="#e91e63", width=3) + + # Texte d'invitation + draw.text((W // 2, 400), "Retrouvez toutes les photos", font=font_med, anchor="mt", fill="#cccccc") + draw.text((W // 2, 460), "en direct sur la galerie !", font=font_med, anchor="mt", fill="#cccccc") + + # QR code + qr = qrcode.QRCode(version=None, error_correction=qrcode.constants.ERROR_CORRECT_M, box_size=12, border=3) + qr.add_data(url_galerie) + qr.make(fit=True) + qr_img = qr.make_image(fill_color="#ffffff", back_color="#0a0a1a").convert("RGB") + qr_size = 700 + qr_img = qr_img.resize((qr_size, qr_size), Image.NEAREST) + img.paste(qr_img, ((W - qr_size) // 2, 540)) + + # Instruction + draw.text((W // 2, 1300), "Scannez ce QR code", font=font_med, anchor="mt", fill="#ffffff") + draw.text((W // 2, 1360), "avec votre téléphone", font=font_med, anchor="mt", fill="#ffffff") + + # Ligne decorative bas + draw.line([(150, 1460), (W - 150, 1460)], fill="#e91e63", width=3) + + draw.text((W // 2, 1520), "Les photos apparaissent en temps réel", font=font_small, anchor="mt", fill="#888888") + draw.text((W // 2, 1570), "Téléchargez-les directement !", font=font_small, anchor="mt", fill="#888888") + + # Sauvegarder + chemin = DOSSIER_EXPORTS / "flyer_qr_booth.jpg" + img.save(str(chemin), "JPEG", quality=95) + + # Imprimer + ok = imprimer(str(chemin), copies=1) + return {"succes": ok, "chemin": f"/data/exports/flyer_qr_booth.jpg"} + + # --- API Galerie --- @app.get("/api/galerie") diff --git a/frontend/css/style.css b/frontend/css/style.css index 2b2f047..64ed904 100644 --- a/frontend/css/style.css +++ b/frontend/css/style.css @@ -299,16 +299,9 @@ html, body { } .booth-qr { - width: 270px; - height: 270px; - border-radius: 10px; -} - -.booth-code { - font-size: 1.4rem; - font-weight: bold; - color: rgba(255,255,255,0.7); - letter-spacing: 4px; + width: 100px; + height: 100px; + border-radius: 8px; } /* === Capture / Preview live === */ diff --git a/frontend/index.html b/frontend/index.html index 83448b9..f85e956 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -44,10 +44,9 @@
400 / 400
- +
-
⚙
@@ -644,6 +643,7 @@ +

Galerie locale

diff --git a/frontend/js/admin.js b/frontend/js/admin.js index 8f08a52..28ff83f 100644 --- a/frontend/js/admin.js +++ b/frontend/js/admin.js @@ -922,6 +922,16 @@ async function chargerInfosSysteme() { } } +async function imprimerFlyerQR() { + afficherStatut('Generation du flyer QR...', 'succes'); + const r = await apiPost('/api/booth/imprimer-qr', {}); + if (r.succes) { + afficherStatut('Flyer QR envoye a l\'imprimante !', 'succes'); + } else { + afficherStatut(r.erreur || 'Erreur impression flyer', 'erreur'); + } +} + async function regenererBoothPassword() { const booth = config.booth || {}; const url = getValue('admin-booth-url').replace(/\/$/, ''); diff --git a/frontend/js/app.js b/frontend/js/app.js index 4829608..9cf2474 100644 --- a/frontend/js/app.js +++ b/frontend/js/app.js @@ -335,10 +335,8 @@ async function chargerBoothInfo() { try { const info = await apiGet('/api/booth/info'); if (info.password) { - document.getElementById('booth-code').textContent = info.password; - // QR code vers le site booth const eventId = booth.event_id || 'default'; - const qrUrl = `${booth.url}/${eventId}`; + const qrUrl = `${booth.url}/${eventId}?p=${info.password}`; document.getElementById('booth-qr').src = `/api/qr?url=${encodeURIComponent(qrUrl)}`; el.classList.remove('cache'); } diff --git a/frontend/js/share.js b/frontend/js/share.js index 32d2e64..9c461ac 100644 --- a/frontend/js/share.js +++ b/frontend/js/share.js @@ -158,9 +158,10 @@ async function afficherQR() { if (!zone.classList.contains('cache')) { const booth = config.booth || {}; if (booth.actif && booth.url) { - // QR vers la galerie live + const info = await apiGet('/api/booth/info').catch(() => null); const eventId = booth.event_id || 'default'; - const qrUrl = `${booth.url}/${eventId}`; + const pw = info?.password || ''; + const qrUrl = `${booth.url}/${eventId}?p=${pw}`; document.getElementById('img-qr').src = `/api/qr?url=${encodeURIComponent(qrUrl)}`; } else { const resultat = await apiGet('/api/qr/galerie');