#!/bin/bash # ============================================================================= # script_print.sh - Impression photo pour photomaton # ============================================================================= # Usage: script_print.sh "" "" "" "" # Appele par photobooth-app via l'action Share "Impression" # # 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 # ============================================================================= set -uo pipefail # --- Parametres --- filename="${1:-}" media_type="${2:-}" action_config_name="${3:-}" copies="${4:-1}" # --- Noms des imprimantes CUPS (WiFi) --- 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"; } log_error() { logger -t "$LOG_TAG" "[ERROR] $1"; echo "[ERROR] $1"; } # --- Validation --- if [ -z "$filename" ]; then log_error "Aucun fichier specifie" exit 1 fi if [ ! -f "$filename" ]; then log_error "Fichier introuvable: $filename" exit 1 fi if [ "$copies" -lt 1 ] || [ "$copies" -gt 3 ]; then log_error "Nombre de copies invalide: $copies (doit etre entre 1 et 3)" exit 1 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" 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)" 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) 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 – 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