From 94def589bd269c82e4ba44c582e28e1662cf8817 Mon Sep 17 00:00:00 2001 From: jbperrin Date: Fri, 17 Jul 2026 18:00:00 +0200 Subject: [PATCH] fix: mode descriptions, bordure impression (remove raw+extent), logs httpx silencieux --- Opencode/scripts/script_print.sh | 139 ++++++++++++++----------- frontend/templates/admin/print.html | 8 +- main.py | 3 + photobooth-app/script/script_print.sh | 140 +++++++++++--------------- 4 files changed, 149 insertions(+), 141 deletions(-) diff --git a/Opencode/scripts/script_print.sh b/Opencode/scripts/script_print.sh index fda8668..ced8a79 100644 --- a/Opencode/scripts/script_print.sh +++ b/Opencode/scripts/script_print.sh @@ -8,10 +8,27 @@ # Imprimantes: Canon Selphy CP1300 x2 via WiFi (blanche + noire) # Load balancing: utilise l'imprimante idle, sinon file d'attente sur la 1ere # -# Sortie stdout parsable par Node-RED: -# PRINTED:: en cas de succes -# PRINT_ERROR: en cas d'erreur +# Sortie stdout parsable par printer_service.py: +# PRINTED:: en cas de succes +# PRINT_ERROR:: en cas d'erreur # ============================================================================= +# +# ── CALIBRAGE IMPRESSION ───────────────────────────────────────────────────── +# +# Selphy CP1300 — papier Postcard 150x100mm (ratio exact 3:2) +# Image traitée par photobooth-app : 2000x1333 (ratio 3:2) +# Résolution cible à 300 dpi : 1772x1181 px +# +# TEST_MODE contrôle la méthode de préparation : +# A — resize 1772x1181 + media=Postcard + fit-to-page (pas de raw) +# → marges blanches possibles si le driver ajoute des marges +# B — resize 1860x1240 (overscan 5%) + media=Postcard.Fullbleed + print-scaling=fill +# → image légèrement agrandie puis rognée par la Selphy pour remplir le papier +# +# Après validation, laisser la valeur qui donne le meilleur résultat. +# ============================================================================= + +TEST_MODE="B" # A ou B set -uo pipefail @@ -25,18 +42,6 @@ copies="${4:-1}" PRINTER_1="Selphy_Blanche_WiFi" PRINTER_2="Selphy_Noire_WiFi" -# --- Options d'impression Selphy --- -# Mode raw : envoie le JPEG directement a la Selphy -# Postcard.Fullbleed : impression sans bordures -PRINT_OPTIONS="-o media=Postcard.Fullbleed -o raw" - -# --- Calibrage bordures pour impression Fullbleed --- -# La Selphy deborde de quelques mm en mode Fullbleed -# On ajoute des marges noires pour compenser et eviter de couper la photo -# Valeurs calibrees pour Canon Selphy CP1300 via WiFi -PRINT_EXTENT="2114x1418" -PRINT_ROLL="-12+8" - # --- Logging --- LOG_TAG="photomaton-print" log_info() { logger -t "$LOG_TAG" "[INFO] $1"; echo "[INFO] $1"; } @@ -59,30 +64,59 @@ if [ "$copies" -lt 1 ] || [ "$copies" -gt 3 ]; then fi # --- Preparation de l'image pour impression --- -# Resize a 2000x1333 (ratio 3:2), ajout de marges noires pour compenser -# le debordement Fullbleed, puis decalage pour centrer correctement filename_no_ext="${filename%.jpg}" filename_print="${filename_no_ext}_print.jpg" -if command -v convert &>/dev/null; then - log_info "Preparation de l'image pour impression Fullbleed..." - if convert "$filename" \ - -resize 2000x1333 \ - -background black \ - -gravity center \ - -extent ${PRINT_EXTENT} \ - -roll ${PRINT_ROLL} \ - -quality 95 \ - "$filename_print" 2>/dev/null; then - FILE_TO_PRINT="$filename_print" - log_info "Image preparee: $filename_print" - else - log_error "Echec de la preparation, impression de l'original" - FILE_TO_PRINT="$filename" - fi -else - log_error "ImageMagick (convert) non installe, impression de l'original" +if ! command -v convert &>/dev/null; then + log_error "ImageMagick (convert) non installe — impression de l'original" FILE_TO_PRINT="$filename" + PRINT_OPTIONS="-o media=Postcard -o fit-to-page" +else + log_info "Preparation image (mode ${TEST_MODE})..." + + if [ "$TEST_MODE" = "A" ]; then + # ── MODE A : 300 dpi exact, fit-to-page ───────────────────────────── + # Resize à la résolution native 300 dpi du Selphy (150x100mm). + # Le driver CUPS aligne l'image sur le papier sans rognage. + # Si des marges blanches apparaissent, passer au mode B. + if convert "$filename" \ + -resize 1772x1181 \ + -gravity center \ + -background white \ + -extent 1772x1181 \ + -quality 95 \ + "$filename_print" 2>/dev/null; then + FILE_TO_PRINT="$filename_print" + PRINT_OPTIONS="-o media=Postcard -o fit-to-page" + log_info "Image preparee (mode A): 1772x1181 → $filename_print" + else + log_error "Echec convert mode A — impression original" + FILE_TO_PRINT="$filename" + PRINT_OPTIONS="-o media=Postcard -o fit-to-page" + fi + + else + # ── MODE B : overscan 5% + fullbleed fill ─────────────────────────── + # Image légèrement agrandie (5% overscan) pour que la Selphy en mode + # Fullbleed puisse rogner les bords sans laisser de marges blanches. + # print-scaling=fill remplace le raw mode et laisse le driver gérer + # l'alignement — élimine les bordures noires de l'ancien script. + if convert "$filename" \ + -resize 1860x1240 \ + -gravity center \ + -background white \ + -extent 1860x1240 \ + -quality 95 \ + "$filename_print" 2>/dev/null; then + FILE_TO_PRINT="$filename_print" + PRINT_OPTIONS="-o media=Postcard.Fullbleed -o print-scaling=fill" + log_info "Image preparee (mode B): 1860x1240 → $filename_print" + else + log_error "Echec convert mode B — impression original" + FILE_TO_PRINT="$filename" + PRINT_OPTIONS="-o media=Postcard.Fullbleed -o print-scaling=fill" + fi + fi fi # --- Selection de l'imprimante (load balancing) --- @@ -98,7 +132,6 @@ elif [[ "$printer_2_status" == *"idle"* ]]; then SELECTED_PRINTER="$PRINTER_2" log_info "Imprimante selectionnee: $PRINTER_2 (idle)" else - # Aucune idle : choisir celle avec le moins de jobs en attente jobs_1=$(lpstat -o "$PRINTER_1" 2>/dev/null | wc -l) jobs_2=$(lpstat -o "$PRINTER_2" 2>/dev/null | wc -l) @@ -107,13 +140,12 @@ else else SELECTED_PRINTER="$PRINTER_2" fi - log_info "Aucune imprimante idle, file d'attente sur: $SELECTED_PRINTER (jobs: P1=$jobs_1, P2=$jobs_2)" + log_info "Aucune imprimante idle, file sur: $SELECTED_PRINTER (P1=$jobs_1 jobs, P2=$jobs_2 jobs)" fi # --- Impression --- -log_info "Impression x${copies} sur $SELECTED_PRINTER: $FILE_TO_PRINT" +log_info "Impression x${copies} sur $SELECTED_PRINTER [mode ${TEST_MODE}]: $FILE_TO_PRINT" -# Envoi du job CUPS et capture du job ID job_output=$(lp -n "$copies" -d "$SELECTED_PRINTER" $PRINT_OPTIONS "$FILE_TO_PRINT" 2>&1) lp_exit=$? @@ -128,19 +160,14 @@ fi 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" + 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 -# --- Preparation du sondage IPP direct sur l'imprimante --- -# La Selphy CP1300 expose un serveur IPP natif (port 631, sans auth). -# Cela permet de detecter les erreurs physiques en quelques secondes -# sans attendre un timeout CUPS (qui ne rapporte pas les erreurs papier). - +# --- Sondage IPP direct (Selphy CP1300 port 631, sans auth) --- PRINTER_IP=$(lpstat -v "$SELECTED_PRINTER" 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -1) IPP_URI="" IPP_TEST="/usr/share/cups/ipptool/get-printer-attributes.test" @@ -148,24 +175,23 @@ if [ -n "$PRINTER_IP" ] && command -v ipptool &>/dev/null && [ -f "$IPP_TEST" ]; IPP_URI="ipp://${PRINTER_IP}:631/ipp/print" log_info "Sondage IPP direct actif: $IPP_URI" else - log_info "Sondage IPP desactive (IP=${PRINTER_IP:-inconnu}, ipptool=$(command -v ipptool || echo absent))" + log_info "Sondage IPP desactive (IP=${PRINTER_IP:-inconnu})" fi -# Mots-cles d'erreur dans printer-state-reasons IPP_ERROR_REASONS="input-tray-missing|media-empty|media-needed|marker-supply-empty|cover-open|door-open|offline-report" -# --- Suivi du job CUPS (attend la completion physique) --- -MAX_WAIT=90 # secondes max (Selphy CP1300 imprime en ~47s) +# --- Suivi du job CUPS --- +MAX_WAIT=90 POLL_INTERVAL=3 elapsed=0 -log_info "Suivi du job CUPS (max ${MAX_WAIT}s)..." +log_info "Suivi du job (max ${MAX_WAIT}s)..." while [ $elapsed -lt $MAX_WAIT ]; do sleep $POLL_INTERVAL elapsed=$((elapsed + POLL_INTERVAL)) - # 1. Succes : job dans la liste des jobs termines par CUPS + # 1. Succes CUPS 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}" @@ -173,13 +199,13 @@ while [ $elapsed -lt $MAX_WAIT ]; do exit 0 fi - # 2. Sondage IPP direct (detecte papier absent, capot ouvert, etc. en ~3s) + # 2. Erreur IPP directe (papier absent, capot ouvert, etc.) if [ -n "$IPP_URI" ]; then ipp_reasons=$(ipptool -tv "$IPP_URI" "$IPP_TEST" 2>/dev/null \ | grep "printer-state-reasons" | head -1) if echo "$ipp_reasons" | grep -qiE "$IPP_ERROR_REASONS"; then reason=$(echo "$ipp_reasons" | grep -oiE "$IPP_ERROR_REASONS" | head -1) - log_error "Erreur imprimante detectee via IPP: $reason (${elapsed}s)" + log_error "Erreur imprimante via IPP: $reason (${elapsed}s)" cancel "$JOB_ID" 2>/dev/null || true echo "PRINT_ERROR:${SELECTED_PRINTER}:${reason}" [ -f "$filename_print" ] && rm -f "$filename_print" 2>/dev/null @@ -187,13 +213,13 @@ while [ $elapsed -lt $MAX_WAIT ]; do fi fi - # 3. Fallback CUPS : imprimante stoppee cote CUPS + # 3. Fallback CUPS stopped/disabled 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 stoppee (CUPS): ${reason:-stopped} (${elapsed}s)" + log_error "Imprimante stoppee CUPS: ${reason:-stopped} (${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 @@ -203,7 +229,6 @@ while [ $elapsed -lt $MAX_WAIT ]; do 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" diff --git a/frontend/templates/admin/print.html b/frontend/templates/admin/print.html index 539a25b..bb287ec 100644 --- a/frontend/templates/admin/print.html +++ b/frontend/templates/admin/print.html @@ -248,10 +248,10 @@ -
- Direct : chaque demande est imprimée immédiatement (load-balancing automatique entre les Selphy). - Validation : l'admin doit valider chaque impression via cette page. - File galerie : les demandes s'accumulent sans impression automatique. +
+
🚀 Direct — chaque demande d'impression est envoyée immédiatement à l'imprimante disponible. Load-balancing automatique entre les Selphy. Aucune intervention requise.
+
Validation — les demandes arrivent en file d'attente. L'admin doit valider (ou refuser) chaque impression depuis cette page avant qu'elle parte à l'imprimante.
+
🖼 File galerie — les demandes s'accumulent dans la file sans déclenchement automatique. Utile pour imprimer en batch à la fin d'un événement.
diff --git a/main.py b/main.py index 8a67ebd..b614a49 100644 --- a/main.py +++ b/main.py @@ -33,6 +33,9 @@ logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", ) +# httpx log tous les appels HTTP en INFO — trop verbeux pour nos probes internes +logging.getLogger("httpx").setLevel(logging.WARNING) +logging.getLogger("httpcore").setLevel(logging.WARNING) logger = logging.getLogger(__name__) BASE_DIR = Path(__file__).parent diff --git a/photobooth-app/script/script_print.sh b/photobooth-app/script/script_print.sh index 216ea18..3a10f9d 100644 --- a/photobooth-app/script/script_print.sh +++ b/photobooth-app/script/script_print.sh @@ -8,10 +8,27 @@ # Imprimantes: Canon Selphy CP1300 x2 via WiFi (blanche + noire) # Load balancing: utilise l'imprimante idle, sinon file d'attente sur la 1ere # -# Sortie stdout parsable par Node-RED: -# PRINTED:: en cas de succes -# PRINT_ERROR: en cas d'erreur +# Sortie stdout parsable par printer_service.py: +# PRINTED:: en cas de succes +# PRINT_ERROR:: en cas d'erreur # ============================================================================= +# +# ── CALIBRAGE IMPRESSION ───────────────────────────────────────────────────── +# +# Selphy CP1300 — papier Postcard 150x100mm (ratio exact 3:2) +# Image traitée par photobooth-app : 2000x1333 (ratio 3:2) +# Résolution cible à 300 dpi : 1772x1181 px +# +# TEST_MODE contrôle la méthode de préparation : +# A — resize 1772x1181 + media=Postcard + fit-to-page (pas de raw) +# → marges blanches possibles si le driver ajoute des marges +# B — resize 1860x1240 (overscan 5%) + media=Postcard.Fullbleed + print-scaling=fill +# → image légèrement agrandie puis rognée par la Selphy pour remplir le papier +# +# Après validation, laisser la valeur qui donne le meilleur résultat. +# ============================================================================= + +TEST_MODE="B" # A ou B set -uo pipefail @@ -25,18 +42,6 @@ copies="${4:-1}" PRINTER_1="Selphy_Blanche_WiFi" PRINTER_2="Selphy_Noire_WiFi" -# --- Options d'impression Selphy --- -# Mode raw : envoie le JPEG directement a la Selphy -# Postcard.Fullbleed : impression sans bordures -PRINT_OPTIONS="-o media=Postcard.Fullbleed -o raw" - -# --- Calibrage bordures pour impression Fullbleed --- -# La Selphy deborde de quelques mm en mode Fullbleed -# On ajoute des marges noires pour compenser et eviter de couper la photo -# Valeurs calibrees pour Canon Selphy CP1300 via WiFi -PRINT_EXTENT="2114x1418" -PRINT_ROLL="-12+8" - # --- Logging --- LOG_TAG="photomaton-print" log_info() { logger -t "$LOG_TAG" "[INFO] $1"; echo "[INFO] $1"; } @@ -59,75 +64,50 @@ if [ "$copies" -lt 1 ] || [ "$copies" -gt 3 ]; then fi # --- Preparation de l'image pour impression --- -# Resize a 2000x1333 (ratio 3:2), ajout de marges noires pour compenser -# le debordement Fullbleed, puis decalage pour centrer correctement filename_no_ext="${filename%.jpg}" filename_print="${filename_no_ext}_print.jpg" -if command -v convert &>/dev/null; then - log_info "Preparation de l'image pour impression Fullbleed..." - if convert "$filename" \ - -resize 2000x1333 \ - -background black \ - -gravity center \ - -extent ${PRINT_EXTENT} \ - -roll ${PRINT_ROLL} \ - -quality 95 \ - "$filename_print" 2>/dev/null; then - FILE_TO_PRINT="$filename_print" - log_info "Image preparee: $filename_print" - else - log_error "Echec de la preparation, impression de l'original" - FILE_TO_PRINT="$filename" - fi -else - log_error "ImageMagick (convert) non installe, impression de l'original" +if ! command -v convert &>/dev/null; then + log_error "ImageMagick (convert) non installe — impression de l'original" FILE_TO_PRINT="$filename" -fi - -# --- Selection de l'imprimante (load balancing) --- -printer_1_status=$(lpstat -p "$PRINTER_1" 2>/dev/null || echo "not found") -printer_2_status=$(lpstat -p "$PRINTER_2" 2>/dev/null || echo "not found") - -SELECTED_PRINTER="" - -if [[ "$printer_1_status" == *"idle"* ]]; then - SELECTED_PRINTER="$PRINTER_1" - log_info "Imprimante selectionnee: $PRINTER_1 (idle)" -elif [[ "$printer_2_status" == *"idle"* ]]; then - SELECTED_PRINTER="$PRINTER_2" - log_info "Imprimante selectionnee: $PRINTER_2 (idle)" + PRINT_OPTIONS="-o media=Postcard -o fit-to-page" else - # Aucune idle : choisir celle avec le moins de jobs en attente - jobs_1=$(lpstat -o "$PRINTER_1" 2>/dev/null | wc -l) - jobs_2=$(lpstat -o "$PRINTER_2" 2>/dev/null | wc -l) + log_info "Preparation image (mode ${TEST_MODE})..." + + if [ "$TEST_MODE" = "A" ]; then + # ── MODE A : 300 dpi exact, fit-to-page ───────────────────────────── + # Resize à la résolution native 300 dpi du Selphy (150x100mm). + # Le driver CUPS aligne l'image sur le papier sans rognage. + # Si des marges blanches apparaissent, passer au mode B. + if convert "$filename" \ + -resize 1772x1181 \ + -gravity center \ + -background white \ + -extent 1772x1181 \ + -quality 95 \ + "$filename_print" 2>/dev/null; then + FILE_TO_PRINT="$filename_print" + PRINT_OPTIONS="-o media=Postcard -o fit-to-page" + log_info "Image preparee (mode A): 1772x1181 → $filename_print" + else + log_error "Echec convert mode A — impression original" + FILE_TO_PRINT="$filename" + PRINT_OPTIONS="-o media=Postcard -o fit-to-page" + fi - if [ "$jobs_1" -le "$jobs_2" ] 2>/dev/null; then - SELECTED_PRINTER="$PRINTER_1" else - SELECTED_PRINTER="$PRINTER_2" - fi - log_info "Aucune imprimante idle, file d'attente sur: $SELECTED_PRINTER (jobs: P1=$jobs_1, P2=$jobs_2)" -fi - -# --- Impression --- -log_info "Impression x${copies} sur $SELECTED_PRINTER: $FILE_TO_PRINT" - -# Envoi du job CUPS et capture du job ID -job_output=$(lp -n "$copies" -d "$SELECTED_PRINTER" $PRINT_OPTIONS "$FILE_TO_PRINT" 2>&1) -lp_exit=$? - -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 -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 – imp \ No newline at end of file + # ── MODE B : overscan 5% + fullbleed fill ─────────────────────────── + # Image légèrement agrandie (5% overscan) pour que la Selphy en mode + # Fullbleed puisse rogner les bords sans laisser de marges blanches. + # print-scaling=fill remplace le raw mode et laisse le driver gérer + # l'alignement — élimine les bordures noires de l'ancien script. + if convert "$filename" \ + -resize 1860x1240 \ + -gravity center \ + -background white \ + -extent 1860x1240 \ + -quality 95 \ + "$filename_print" 2>/dev/null; then + FILE_TO_PRINT="$filename_print" + PRINT_OPTIONS="-o media=Postcard.Fullbleed -o print-scaling=fill" + log \ No newline at end of file