Changes to be committed:
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:
@@ -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
|
||||||
|
|||||||
@@ -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()):
|
||||||
printer = parts[1] if len(parts) > 1 else ""
|
line = line.strip()
|
||||||
return {"success": True, "printer": printer, "output": stdout}
|
if line.startswith("PRINTED:"):
|
||||||
else:
|
parts = line.split(":")
|
||||||
return {"success": False, "error": proc.stderr.strip() or stdout, "printer": ""}
|
printer = parts[1] if len(parts) > 1 else ""
|
||||||
|
return {"success": True, "printer": printer, "output": stdout}
|
||||||
|
if line.startswith("PRINT_ERROR:"):
|
||||||
|
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)}
|
||||||
|
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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,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">
|
||||||
{% block nav_links %}{% endblock %}
|
{% 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 %}
|
||||||
|
{% 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>
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user