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 <noreply@anthropic.com>
This commit is contained in:
@@ -819,6 +819,77 @@ async def api_qr_galerie():
|
|||||||
return {"chemin": f"/data/exports/{chemin.name}"}
|
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 ---
|
# --- API Galerie ---
|
||||||
|
|
||||||
@app.get("/api/galerie")
|
@app.get("/api/galerie")
|
||||||
|
|||||||
@@ -299,16 +299,9 @@ html, body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.booth-qr {
|
.booth-qr {
|
||||||
width: 270px;
|
width: 100px;
|
||||||
height: 270px;
|
height: 100px;
|
||||||
border-radius: 10px;
|
border-radius: 8px;
|
||||||
}
|
|
||||||
|
|
||||||
.booth-code {
|
|
||||||
font-size: 1.4rem;
|
|
||||||
font-weight: bold;
|
|
||||||
color: rgba(255,255,255,0.7);
|
|
||||||
letter-spacing: 4px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* === Capture / Preview live === */
|
/* === Capture / Preview live === */
|
||||||
|
|||||||
@@ -44,10 +44,9 @@
|
|||||||
<div id="compteur-accueil" class="compteur-accueil cache">
|
<div id="compteur-accueil" class="compteur-accueil cache">
|
||||||
<span id="compteur-restant">400</span> / <span id="compteur-limite">400</span>
|
<span id="compteur-restant">400</span> / <span id="compteur-limite">400</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- Booth : code + QR en bas a gauche -->
|
<!-- Booth : QR discret en bas a gauche -->
|
||||||
<div id="booth-info" class="booth-info cache">
|
<div id="booth-info" class="booth-info cache">
|
||||||
<img id="booth-qr" class="booth-qr" src="">
|
<img id="booth-qr" class="booth-qr" src="">
|
||||||
<span id="booth-code" class="booth-code"></span>
|
|
||||||
</div>
|
</div>
|
||||||
<!-- Icone admin coin bas-droit -->
|
<!-- Icone admin coin bas-droit -->
|
||||||
<div id="btn-admin" class="btn-admin" onclick="ouvrirAdmin()">⚙</div>
|
<div id="btn-admin" class="btn-admin" onclick="ouvrirAdmin()">⚙</div>
|
||||||
@@ -644,6 +643,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<button class="btn-action" onclick="sauvegarderBooth()">Sauvegarder booth</button>
|
<button class="btn-action" onclick="sauvegarderBooth()">Sauvegarder booth</button>
|
||||||
<button class="btn-secondaire" onclick="regenererBoothPassword()">Nouveau code</button>
|
<button class="btn-secondaire" onclick="regenererBoothPassword()">Nouveau code</button>
|
||||||
|
<button class="btn-secondaire" onclick="imprimerFlyerQR()">🖨 Imprimer flyer QR galerie</button>
|
||||||
|
|
||||||
<h3>Galerie locale</h3>
|
<h3>Galerie locale</h3>
|
||||||
<div class="champ">
|
<div class="champ">
|
||||||
|
|||||||
@@ -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() {
|
async function regenererBoothPassword() {
|
||||||
const booth = config.booth || {};
|
const booth = config.booth || {};
|
||||||
const url = getValue('admin-booth-url').replace(/\/$/, '');
|
const url = getValue('admin-booth-url').replace(/\/$/, '');
|
||||||
|
|||||||
@@ -335,10 +335,8 @@ async function chargerBoothInfo() {
|
|||||||
try {
|
try {
|
||||||
const info = await apiGet('/api/booth/info');
|
const info = await apiGet('/api/booth/info');
|
||||||
if (info.password) {
|
if (info.password) {
|
||||||
document.getElementById('booth-code').textContent = info.password;
|
|
||||||
// QR code vers le site booth
|
|
||||||
const eventId = booth.event_id || 'default';
|
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)}`;
|
document.getElementById('booth-qr').src = `/api/qr?url=${encodeURIComponent(qrUrl)}`;
|
||||||
el.classList.remove('cache');
|
el.classList.remove('cache');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,9 +158,10 @@ async function afficherQR() {
|
|||||||
if (!zone.classList.contains('cache')) {
|
if (!zone.classList.contains('cache')) {
|
||||||
const booth = config.booth || {};
|
const booth = config.booth || {};
|
||||||
if (booth.actif && booth.url) {
|
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 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)}`;
|
document.getElementById('img-qr').src = `/api/qr?url=${encodeURIComponent(qrUrl)}`;
|
||||||
} else {
|
} else {
|
||||||
const resultat = await apiGet('/api/qr/galerie');
|
const resultat = await apiGet('/api/qr/galerie');
|
||||||
|
|||||||
Reference in New Issue
Block a user