La PageSize CUPS w288h432-div2 declare la meme taille de page que w288h432
(confirme par le PPD Mitsubishi) : le K60 coupe la feuille pleine apres
impression, il ne duplique pas une image fournie a taille reduite. On envoyait
la bande seule (600x1800) sous cette PageSize, donc CUPS l'etirait pour
remplir la feuille 1200x1800 puis le K60 la coupait en 2 — d'ou la photo
etiree et coupee au lieu de 2 bandes identiques.
Ajoute aussi le miroir horizontal sur le preview live (selfie) et un flou de
chargement pendant le bref delai de remontee du miroir DSLR apres capture,
pour eviter l'impression d'image figee sur la photo precedente.
creer_strip() produisait une bande paysage (1800x595) alors que printer.py
('10x15-2up' -> PageSize ...-div2) et tous les cadres existants (assets/cadres/strip/*.png)
attendent une bande portrait fixe 600x1800 (5x15cm), dupliquee et coupee
automatiquement par l'imprimante. Le mismatch faisait pivoter/etirer
l'image a l'impression (StpiShrinkOutput=Crop sur un ratio incompatible),
d'ou le rendu deforme coupe en 2 mauvais morceaux au lieu de 2 pellicules
identiques.
- creer_strip() : bande 600x1800 portrait, photos empilees verticalement,
perforations sur les cotes (au lieu de haut/bas)
- Cadre de visee live (camera.js) : formule de ratio mise a jour en
consequence pour rester fidele au resultat final
L'apercu live montrait le plein cadre camera (ratio ~3:2) alors que le
recadrage final de chaque case (creer_strip/creer_collage) est plus carre
(~1.11:1 pour 3 photos en pellicule) : le cadrage visualise pendant le
compte a rebours ne correspondait pas au resultat imprime.
Ajoute un cadre pointille superpose au live preview, dimensionne en
reproduisant cote JS la meme formule de recadrage que backend/collage.py,
visible uniquement en mode multi-shot (strip ou collage).
Le boitier se mettait en veille apres quelques minutes d'inactivite et
apparaissait deconnecte. Deux causes possibles traitees :
- autopoweroff Canon desactive a la connexion (cote firmware)
- USB autosuspend du host desactive pour les peripheriques Canon (cote noyau)
Le bloc partage (photo + boutons + form impression) pouvait depasser la
hauteur de l'ecran quand le choix du nombre d'exemplaires s'affichait.
La miniature absorbe maintenant l'espace restant (flex + min-height:0)
au lieu d'une hauteur fixe en vh, les autres elements gardent leur taille.
- Flash integre Canon activable/desactivable depuis l'admin (configurer_flash)
- Popup de choix de cadre juste avant impression, layout sans defilement (flex nowrap + aspect-ratio)
- Capture : ignore camera_erreur pendant une capture en cours (laisse l'API trancher)
- surveiller_dslr : grace period preview augmentee a 2s, reapplique la config camera apres reconnexion
- start.sh simplifie (Chromium gere par kiosk-session.sh), remote debugging port 9222 active
Les resets USB ioctl répétés en boucle aggravent l'état des appareils Canon
au lieu de les débloquer. La fonction _usb_reset_canon est conservée pour
usage manuel mais n'est plus appelée automatiquement.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Le reset USB ioctl prend ~10s sur le Canon EOS 6D pour que l'appareil
se ré-énumère sur le bus. Avec 3s on retombait en boucle de resets.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Quand gphoto2 détecte le DSLR mais que camera.init() timeout répétitivement
(USB stall après kill -9 ou déconnexion brutale), on tente un reset USB ioctl
(USBDEVFS_RESET sur /dev/bus/usb/<bus>/<dev>) toutes les 3 tentatives (~15s).
Évite de rester bloqué indéfiniment sur "Timeout reading from or writing to the port".
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- camera.py : désactiver LiveView avant capture (viewfinder=0) → miroir abaissé →
AF phase-détection Canon actif. Remplace _declencher_af_liveview (autofocusdrive=1
pouvait bloquer sur certains corps Canon). activer_viewfinder() après la prise.
- main.py : fix bug global _preview_actif dans api_capturer (était variable locale,
le global restait True). Timeout asyncio 20s sur run_in_executor pour éviter freeze
permanent si capture bloque. Logs INFO avant/après capture.
- camera.js + index.html + style.css : spinner "Capture en cours..." entre le flash
et l'affichage du résultat. Cacher le spinner dans afficherErreurCapture aussi.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
backend:
- surveiller_dslr : fix condition qui court-circuitait le check santé
quand mode==gphoto2 — watchdog surveille maintenant via preview_dslr_ok
et reconnecte automatiquement après 15s d'échec (3x5s)
- _pusher_preview : ne pas envoyer camera_erreur pendant capture_en_cours
(race condition HTTP/WS : le POST /capturer peut arriver avant preview_stop)
- api_capturer : force _preview_actif=False dès le début de la capture
- _capture_gphoto2 : déclenche autofocusdrive avant la prise en LiveView
frontend:
- websocket.js : support multi-callbacks par type (wsOnMessage ne remplace
plus le callback précédent, tous s'exécutent)
- app.js : camera_erreur affiche bannière + reload dernier recours à 2min
(plus 10s), camera_ok cache la bannière automatiquement
- camera.js : camera_erreur abort capture + arreterPreview(); camera_ok
reset captureAbortee
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- camera_erreur pendant la capture : abort immédiat + afficherErreurCapture()
→ retour accueil en 4s (au lieu de rester bloqué sur "Reconnexion en cours...")
- camera_ok : cache l'erreur et reset le flag captureAbortee
- captureAbortee flag : permet à lancerCapture() de sortir proprement
sans déclencher de capture fantôme après une déconnexion
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
La balise <img> avec height:100% ne se propage pas quand le parent est un
flex item positionné avec align-self:center au lieu de stretch. Le fix :
position:absolute top:0 left:0 width/height 100% sur .preview-live img.
Retiré aussi --remote-debugging-port=9222 (diagnostic temporaire).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- kiosk-session.sh : tue gvfsd-gphoto2 au démarrage (démon GNOME qui
monopolisait le DSLR sur USB et bloquait gphoto2)
- camera.py : viewfinder activé AVANT mode="gphoto2" → le thread preview
ne peut plus appeler capture_preview() avant que LiveView soit prêt
- _activer_viewfinder_init() sans lock pour l'init (thread pas encore actif)
- exif_transpose sur les captures DSLR → corrige l'orientation et les
bandes blanches dues aux photos mal orientées
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Strip 10x15 :
- Envoie la strip simple avec format 10x15-2up (w288h432-div2)
le K60 duplique et coupe automatiquement → plus de papier non coupé
- Supprime creer_impression_strip et le 2-up manuel avec marges blanches
- api/imprimer accepte format_papier depuis le frontend
- Frontend stocke formatImpression et le passe à l'impression
Diagnostic preview :
- GET /api/camera/debug : teste capture_preview(), liste les widgets
config gphoto2, état du thread → permet de voir l'erreur exacte
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- _thread_preview tourne TOUJOURS quand DSLR connecté (plus de condition
_preview_actif) → capture_preview() appelé en continu → miroir reste levé
- Grace period 15 frames (~500ms) avant de signaler camera_erreur : évite
la race condition qui tuait le preview avant la 1ère frame
- _preview_gphoto2 protégé par _gp_lock (accès concurrent safe)
- surveiller_dslr ne tourne plus quand le DSLR est en mode gphoto2 connecté
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- activer_viewfinder() à la connexion DSLR (miroir levé dès le démarrage)
- activer_viewfinder() après chaque capture (certains Canon le désactivent)
- Suppression desactiver_viewfinder() — miroir ne redescend plus jamais
- lancerPreview() au début du compte à rebours, arreterPreview() juste avant la capture
- Preview live fonctionnel : l'utilisateur se voit pendant tout le compte à rebours
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Icône discrète coin bas-gauche, visible seulement si photostation installée
- Popup PIN (4 chiffres) avant lancement
- Backend : /api/photostation/disponible + /api/photostation/lancer
- kiosk-session.sh : Chromium attend le flag /tmp/photostation_running avant redémarrage
- Script install-photostation.sh pour déploiement sur la Surface
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Si Chromium ou le backend plantent, ils redémarrent automatiquement.
La session LightDM ne se termine jamais → plus d'écran de login intempestif.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Intégration selphy_print (backend CUPS dyesub Mitsubishi CP-K60DW-S)
- Formats impression : 15x20, 10x15, 10x15 2-strips avec cadre et orientation portrait/paysage
- Cadres d'impression par format (strip/10x15/15x20) : upload, sélection, aperçu live
- Cadres démo générés : pellicule noir/vintage/couleurs + bordures classique/doré/rose gold
- Écran de choix de cadre avant capture (clic direct, aperçu grand format)
- Filtres réduits à 3 (Couleur, N&B, Sépia) et appliqués sur tous les strips
- Port 80 via authbind, route /admin avec redirection auto depuis poste distant
- Autologin LightDM corrigé (pam-autologin-service)
- Bouton évacuer bourrage papier dans admin
- Fix : _gp_lock manquant dans Camera.__init__
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
eosremoterelease='Immediate' envoyait GP_EVENT_FILE_ADDED uniquement
pour la 1ere pose (bug Canon/gphoto2 connu). trigger_capture() genere
l'evenement pour CHAQUE pose. En liveview actif (miroir deja leve),
trigger_capture() ne bouge pas le miroir — uniquement l'obturateur.
Timeout reduit 15s -> 12s, poll 500ms -> 200ms (reaction plus rapide).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Retire eosviewfinder=1 avant/apres chaque prise : relancait le liveview
(4-6s de blocage) → timeout 15s → ch=None → photos=[] → S_ACCUEIL
- Le miroir est deja leve par capture_preview() continu, l'obturateur
seul se declenche via eosremoterelease=Immediate
- Logs detailles : chaque etape imprime son resultat pour debug
- Apres capture echouee : _prev_thumbs.append(None) pour garder index cohérent
- S_PREV 2.5s → 1.5s (plus reactif entre les poses)
- Apres capture : revient en S_PREV 0.6s pour montrer la vignette
- Print "Séquence terminée N/M photos" pour suivi
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- post_capture_grace=10 : ignore les erreurs -52 transitoires juste
apres une prise (Canon EOS a besoin ~1s pour stabiliser le capteur)
- Threshold abandon capture : 3 -> 8 erreurs consecutives
- Threshold reconnexion : 5 -> 10 erreurs consecutives
- Avant chaque prise : eosviewfinder=1 pour garantir miroir leve
- Apres prise : viewfinder relance + sleep 1s + drain 8 evenements
- Sim auto-detect : pas de USB reset (evite boucle -52), verification
fonctionnelle avec un capture_preview() avant de valider la camera
- Sim poll : 15s au lieu de 10s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Supprime sleep(0.3) apres chaque capture_preview() reussi
(limitait le live view a ~3fps, cause principale des saccades)
- Fixe crash silencieux thread camera : _nb_retry non initialise
pour errs 1-4 (NameError) -> restructure en for/else propre
- Pellicule live view : _pfit() au lieu de transform.scale() -> plus de distorsion
- draw_capture() : mode pellicule affiché au lieu de fullscreen black
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Admin onglet Système : bouton Quitter (gris) pour fermer l'appli
- Reconnexion caméra : max 10 tentatives (~30s) puis bascule en mode
simulation ; le loop sim reprend la détection automatique toutes les 10s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- _pfit() : helper scale+crop centré pygame sans déformer
- ImageOps.fit() partout : vignettes, galerie, composite (recadrage centré
au lieu de resize qui écrase)
- eosremoterelease 'Immediate' : déclenche l'obturateur Canon en restant
en LV, évite la levée/baisse de miroir à chaque prise
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- draw_galerie() : rendu pellicule (bande film avec perforations) quand le
mode est actif
- _load_gal() : charge les surfs à la taille des cadres pellicule si actif
- S_PREV : 1.5s → 2.5s pour laisser le DSLR revenir en live view
- _usb_reset_done() : utilise unbind/bind sysfs au lieu de usbreset
(unbind/bind = seule méthode qui libère réellement le claim USB)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- _usb_reset_done() : reset USB via usbreset pour libérer tout claim
obsolète avant d'initialiser libgphoto2 (fix erreur -53)
- Appelé au démarrage et dans _cam_loop() toutes les 10s en simulation
- _cam_loop() : log les erreurs d'auto-detect pour diagnostic
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- draw_compte() pellicule : suppression de l'overlay global qui masquait la
bande film pendant le compte à rebours
- _cam_loop() : ne quitte plus en mode simulation, tente une reconnexion
automatique toutes les 8s si le DSLR est branché après démarrage
- _reconnect_cam() : fonctionne aussi depuis le mode simulation (bouton admin)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Le handler utilisait True comme défaut pour tous les toggles, ce qui faisait
que not f.get("pellicule_mode", True) = False au premier clic (toggle semblait
bloqué). Ajout d'un ensemble _false_defs pour les features à défaut False.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Nouveau toggle "Mode pellicule" dans l'onglet Fonctions de l'admin
- _draw_pellicule() : bande noire cinéma avec perforations haut/bas,
cadres numérotés (01A, 02A...) ; le live view est rendu dans le cadre
courant, les photos prises occupent leur case, les futures sont en gris
- draw_preview() / draw_compte() : utilisent _draw_pellicule() quand activé
- _make_composite() : génère une bande simple (non doublée) en mode pellicule
- Thumbnails chargés à _PELL_FH (748px) pour la qualité native en pellicule
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- _make_composite(): crée une image bande (2 rangées) ou grille 2x2 à partir
des photos prises, sauvée en /tmp/photobooth_composite.jpg
- _upload_booth(): upload le composite unique en mode multi au lieu des photos
individuelles
- _draw_multi_strip(): overlay bas avec les cases numérotées — thumbnails pour
les prises faites, cadre pulsant pour la case courante, placeholder gris pour
les suivantes
- draw_preview() / draw_compte(): utilisent _draw_multi_strip() en mode multi
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ajoute deux routes FastAPI :
- GET /s/{code} → galerie HTML mobile avec auto-refresh 15s
- POST /api/sessions/{code}/upload → stockage photo dans session_{code}/
Corrige layout admin : titre sm au lieu de lg, compteur sans chevauchement.
Wizard step 2 : code affiché en lg (96px) au lieu de xxl (300px).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>