Changes to be committed:
🚀 Deploy — JH Photomaton / 🔍 Vérification (push) Has been cancelled
🚀 Deploy — JH Photomaton / 🍓 Deploy sur le Pi (push) Has been cancelled

modified:   Opencode/scripts/script_print.sh
	modified:   backend/services/printer_service.py
	modified:   frontend/templates/admin/actions.html
	modified:   frontend/templates/admin/events.html
	modified:   frontend/templates/admin/gallery.html
	modified:   frontend/templates/admin/settings.html
	modified:   frontend/templates/base.html
	modified:   frontend/templates/public/gallery.html
 Untracked files:
	photobooth-app/script/
	photobooth-app/userdata/
This commit is contained in:
2026-07-17 17:02:57 +02:00
parent c32b3ba345
commit 68b0a36031
8 changed files with 92 additions and 68 deletions
+61 -20
View File
@@ -113,26 +113,67 @@ fi
# --- Impression --- # --- Impression ---
log_info "Impression x${copies} sur $SELECTED_PRINTER: $FILE_TO_PRINT" log_info "Impression x${copies} sur $SELECTED_PRINTER: $FILE_TO_PRINT"
if lp -n "$copies" -d "$SELECTED_PRINTER" $PRINT_OPTIONS "$FILE_TO_PRINT"; then # Envoi du job CUPS et capture du job ID
log_info "Job d'impression envoye avec succes" job_output=$(lp -n "$copies" -d "$SELECTED_PRINTER" $PRINT_OPTIONS "$FILE_TO_PRINT" 2>&1)
lp_exit=$?
# Sortie parsable par Node-RED pour les compteurs
echo "PRINTED:${SELECTED_PRINTER}:${copies}"
# Nettoyage du fichier temporaire (apres que CUPS l'ait lu)
if [ "$FILE_TO_PRINT" = "$filename_print" ] && [ -f "$filename_print" ]; then
(sleep 30 && rm -f "$filename_print" 2>/dev/null) &
fi
exit 0
else
log_error "Echec de l'envoi du job d'impression"
echo "PRINT_ERROR:${SELECTED_PRINTER}"
# Nettoyage meme en cas d'erreur
if [ -f "$filename_print" ]; then
rm -f "$filename_print" 2>/dev/null
fi
if [ $lp_exit -ne 0 ]; then
log_error "lp a echoue (exit $lp_exit): $job_output"
echo "PRINT_ERROR:${SELECTED_PRINTER}:lp_failed"
[ -f "$filename_print" ] && rm -f "$filename_print" 2>/dev/null
exit 1 exit 1
fi fi
# Extraction du job ID (ex: "request id is Selphy_Blanche_WiFi-42 (1 file(s))")
JOB_ID=$(echo "$job_output" | grep -oE '[A-Za-z0-9_-]+-[0-9]+' | head -1)
log_info "Job CUPS accepte: ${JOB_ID:-inconnu}"
# Si le job ID est introuvable, comportement legacy (retour immediat)
if [ -z "$JOB_ID" ]; then
log_info "Job ID non extrait impression reputee reussie"
echo "PRINTED:${SELECTED_PRINTER}:${copies}"
[ "$FILE_TO_PRINT" = "$filename_print" ] && (sleep 30 && rm -f "$filename_print" 2>/dev/null) &
exit 0
fi
# --- Suivi du job CUPS (attend la completion physique) ---
MAX_WAIT=90 # secondes max (Selphy CP1300 ~ 60s)
POLL_INTERVAL=3
elapsed=0
log_info "Suivi du job CUPS (max ${MAX_WAIT}s)..."
while [ $elapsed -lt $MAX_WAIT ]; do
sleep $POLL_INTERVAL
elapsed=$((elapsed + POLL_INTERVAL))
# Succes : job dans la liste des jobs termines
if lpstat -W completed 2>/dev/null | grep -q "^${JOB_ID} "; then
log_info "Job $JOB_ID termine avec succes (${elapsed}s)"
echo "PRINTED:${SELECTED_PRINTER}:${copies}"
[ "$FILE_TO_PRINT" = "$filename_print" ] && rm -f "$filename_print" 2>/dev/null
exit 0
fi
# Erreur : imprimante stoppee (papier absent, capot ouvert, etc.)
printer_state=$(lpstat -p "$SELECTED_PRINTER" 2>/dev/null || true)
if echo "$printer_state" | grep -qiE "stopped|disabled"; then
reason=$(lpstat -l -p "$SELECTED_PRINTER" 2>/dev/null \
| grep -i "Reason:" | head -1 \
| sed 's/.*Reason:[[:space:]]*//' | xargs)
log_error "Imprimante $SELECTED_PRINTER en erreur: ${reason:-stopped} (apres ${elapsed}s)"
cancel "$JOB_ID" 2>/dev/null || true
echo "PRINT_ERROR:${SELECTED_PRINTER}:${reason:-stopped}"
[ -f "$filename_print" ] && rm -f "$filename_print" 2>/dev/null
exit 1
fi
log_info "Impression en cours... (${elapsed}s / ${MAX_WAIT}s)"
done
# Timeout : annule le job CUPS
log_error "Timeout impression apres ${MAX_WAIT}s (job: $JOB_ID)"
cancel "$JOB_ID" 2>/dev/null || true
echo "PRINT_ERROR:${SELECTED_PRINTER}:timeout_${MAX_WAIT}s"
[ -f "$filename_print" ] && rm -f "$filename_print" 2>/dev/null
exit 1
+15 -6
View File
@@ -235,17 +235,26 @@ class PrinterService:
cmd = ["/bin/bash", script, filename, "image", "default", str(copies)] cmd = ["/bin/bash", script, filename, "image", "default", str(copies)]
try: try:
proc = subprocess.run( proc = subprocess.run(
cmd, capture_output=True, text=True, timeout=60 cmd, capture_output=True, text=True, timeout=120
) )
stdout = proc.stdout.strip() stdout = proc.stdout.strip()
if proc.returncode == 0 and "PRINTED:" in stdout: # Cherche PRINTED: ou PRINT_ERROR: dans la dernière ligne significative
parts = stdout.split(":") for line in reversed(stdout.splitlines()):
line = line.strip()
if line.startswith("PRINTED:"):
parts = line.split(":")
printer = parts[1] if len(parts) > 1 else "" printer = parts[1] if len(parts) > 1 else ""
return {"success": True, "printer": printer, "output": stdout} return {"success": True, "printer": printer, "output": stdout}
else: if line.startswith("PRINT_ERROR:"):
return {"success": False, "error": proc.stderr.strip() or stdout, "printer": ""} parts = line.split(":", 2)
printer = parts[1] if len(parts) > 1 else ""
reason = parts[2] if len(parts) > 2 else ""
err_msg = f"Erreur imprimante {printer}: {reason}" if reason else f"Erreur imprimante {printer}"
return {"success": False, "error": err_msg, "printer": printer}
# Aucun marqueur reconnu
return {"success": False, "error": proc.stderr.strip() or stdout or "Script sans sortie reconnue", "printer": ""}
except subprocess.TimeoutExpired: except subprocess.TimeoutExpired:
return {"success": False, "error": "Timeout impression (60s)"} return {"success": False, "error": "Timeout impression (120s)"}
except Exception as e: except Exception as e:
return {"success": False, "error": str(e)} return {"success": False, "error": str(e)}
-9
View File
@@ -1,15 +1,6 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}Actions — JH Photomaton{% endblock %} {% block title %}Actions — JH Photomaton{% endblock %}
{% block nav_links %}
<a href="/admin">Dashboard</a>
<a href="/admin/gallery">Galerie</a>
<a href="/admin/actions" class="active">Actions</a>
<a href="/admin/print">Impression</a>
<a href="/admin/settings">Réglages</a>
<a href="/admin/events">Événements</a>
<a href="/admin/logout">Déconnexion</a>
{% endblock %}
{% block content %} {% block content %}
<div class="container"> <div class="container">
-9
View File
@@ -1,15 +1,6 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}Événements — JH Photomaton{% endblock %} {% block title %}Événements — JH Photomaton{% endblock %}
{% block nav_links %}
<a href="/admin">Dashboard</a>
<a href="/admin/gallery">Galerie</a>
<a href="/admin/actions">Actions</a>
<a href="/admin/print">Impression</a>
<a href="/admin/settings">Réglages</a>
<a href="/admin/events" class="active">Événements</a>
<a href="/admin/logout">Déconnexion</a>
{% endblock %}
{% block head %} {% block head %}
<style> <style>
-9
View File
@@ -1,15 +1,6 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}Galerie admin — JH Photomaton{% endblock %} {% block title %}Galerie admin — JH Photomaton{% endblock %}
{% block nav_links %}
<a href="/admin">Dashboard</a>
<a href="/admin/gallery" class="active">Galerie</a>
<a href="/admin/actions">Actions</a>
<a href="/admin/print">Impression</a>
<a href="/admin/settings">Réglages</a>
<a href="/admin/events">Événements</a>
<a href="/admin/logout">Déconnexion</a>
{% endblock %}
{% block head %} {% block head %}
<style> <style>
-8
View File
@@ -2,14 +2,6 @@
{% block title %}Réglages bouton — JH Photomaton{% endblock %} {% block title %}Réglages bouton — JH Photomaton{% endblock %}
{% block nav_links %}
<a href="/admin">Dashboard</a>
<a href="/admin/gallery">Galerie</a>
<a href="/admin/actions">Actions</a>
<a href="/admin/print">Impression</a>
<a href="/admin/settings" class="active">Réglages</a>
<a href="/admin/logout">Déconnexion</a>
{% endblock %}
{% block head %} {% block head %}
<style> <style>
+13
View File
@@ -13,7 +13,20 @@
<nav class="navbar"> <nav class="navbar">
<div class="navbar-brand">📷 <span>JH</span> Photomaton</div> <div class="navbar-brand">📷 <span>JH</span> Photomaton</div>
<div class="navbar-links"> <div class="navbar-links">
{% set p = request.url.path %}
{% if p.startswith('/admin') %}
<a href="/admin" class="{{ 'active' if p == '/admin' or p == '/admin/' else '' }}">Dashboard</a>
<a href="/admin/gallery" class="{{ 'active' if p.startswith('/admin/gallery') else '' }}">Galerie</a>
<a href="/admin/actions" class="{{ 'active' if p.startswith('/admin/actions') else '' }}">Actions</a>
<a href="/admin/print" class="{{ 'active' if p.startswith('/admin/print') else '' }}">Impression</a>
<a href="/admin/settings" class="{{ 'active' if p.startswith('/admin/settings') else '' }}">Réglages</a>
<a href="/admin/events" class="{{ 'active' if p.startswith('/admin/events') else '' }}">Événements</a>
<a href="/admin/logout">Déconnexion</a>
{% elif p.startswith('/gallery') or p == '/' %}
<a href="/gallery" class="{{ 'active' if p.startswith('/gallery') else '' }}">📷 Galerie photos</a>
{% else %}
{% block nav_links %}{% endblock %} {% block nav_links %}{% endblock %}
{% endif %}
</div> </div>
<div class="navbar-status" id="ws-status"> <div class="navbar-status" id="ws-status">
<span class="dot dot-gray" id="ws-dot"></span> <span class="dot dot-gray" id="ws-dot"></span>
-4
View File
@@ -1,10 +1,6 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}Galerie photos — Photomaton LSDW{% endblock %} {% block title %}Galerie photos — Photomaton LSDW{% endblock %}
{% block nav_links %}
<a href="/gallery" class="active">📷 Galerie photos</a>
{% endblock %}
{% block head %} {% block head %}
<style> <style>
.pub-print-badge { .pub-print-badge {