68b0a36031
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/
180 lines
6.4 KiB
Bash
180 lines
6.4 KiB
Bash
#!/bin/bash
|
||
# =============================================================================
|
||
# script_print.sh - Impression photo pour photomaton
|
||
# =============================================================================
|
||
# Usage: script_print.sh "<filename>" "<media_type>" "<action_config_name>" "<copies>"
|
||
# 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:<printer_name>:<copies> en cas de succes
|
||
# PRINT_ERROR:<printer_name> 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
|