Flash Canon configurable, choix de cadre a l'impression, fixes capture/reconnexion DSLR
- 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
This commit is contained in:
@@ -212,6 +212,33 @@ class Camera:
|
||||
else:
|
||||
return None # Pas de simulation ni webcam en dehors du mode dedie
|
||||
|
||||
def configurer_flash(self, actif: bool):
|
||||
"""Active ou désactive le flash intégré Canon.
|
||||
Essaie plusieurs noms de widgets selon le modèle."""
|
||||
if not self.camera or self.mode != "gphoto2":
|
||||
return
|
||||
# strobofiring : "0"=inhibé(off), "2"=auto — Canon EOS 60D (widget radio, valeurs string)
|
||||
# flashmode : "On"/"Off" — autres Canon
|
||||
candidats = [
|
||||
("strobofiring", "2", "0"), # (widget, valeur_on, valeur_off)
|
||||
("flashmode", "On", "Off"),
|
||||
]
|
||||
try:
|
||||
with self._gp_lock:
|
||||
cfg = self.camera.get_config()
|
||||
for nom, val_on, val_off in candidats:
|
||||
try:
|
||||
widget = cfg.get_child_by_name(nom)
|
||||
widget.set_value(val_on if actif else val_off)
|
||||
self.camera.set_config(cfg)
|
||||
log.info(f"Flash intégré ({nom}) : {'On' if actif else 'Off'}")
|
||||
return
|
||||
except Exception:
|
||||
pass
|
||||
log.debug("Aucun widget flash trouvé sur ce modèle Canon")
|
||||
except Exception as e:
|
||||
log.debug(f"configurer_flash : {e}")
|
||||
|
||||
def _activer_viewfinder_init(self):
|
||||
"""Active le LiveView pendant l'init (pas de lock, appelé avant que le thread démarre)."""
|
||||
try:
|
||||
|
||||
@@ -49,7 +49,7 @@ _loop_principal: asyncio.AbstractEventLoop | None = None # loop asyncio princip
|
||||
|
||||
|
||||
_preview_none_count = 0
|
||||
_PREVIEW_GRACE_FRAMES = 15 # ~500ms avant de declarer une erreur
|
||||
_PREVIEW_GRACE_FRAMES = 40 # 40 x 50ms = 2s avant de declarer une erreur (Canon LiveView peut être lent à démarrer)
|
||||
|
||||
|
||||
def _thread_preview():
|
||||
@@ -88,6 +88,7 @@ async def _pusher_preview():
|
||||
# Grace period : attendre plusieurs frames avant de signaler l'erreur
|
||||
# Ne pas signaler pendant une capture (race condition HTTP/WS)
|
||||
if camera.mode == "gphoto2" and _preview_none_count > _PREVIEW_GRACE_FRAMES and not capture_en_cours:
|
||||
log.warning(f"camera_erreur envoyé : preview None depuis {_preview_none_count * 50}ms")
|
||||
await diffuser_ws({"type": "camera_erreur", "message": "DSLR ne repond pas au preview"})
|
||||
_preview_actif = False
|
||||
_preview_none_count = 0
|
||||
@@ -134,8 +135,8 @@ async def surveiller_dslr():
|
||||
_echecs_connexion = 0
|
||||
else:
|
||||
_dslr_erreurs += 1
|
||||
if _dslr_erreurs >= 3: # 3 x 5s = 15s sans preview valide
|
||||
log.warning("DSLR ne repond plus (preview KO depuis 15s), reconnexion forcee...")
|
||||
if _dslr_erreurs >= 3 and not _preview_actif: # Ne pas couper pendant le countdown
|
||||
log.warning(f"camera_erreur : DSLR preview KO depuis {_dslr_erreurs * 5}s, reconnexion forcee...")
|
||||
_dslr_erreurs = 0
|
||||
camera.deconnecter()
|
||||
await diffuser_ws({"type": "camera_erreur", "message": "Appareil photo deconnecte"})
|
||||
@@ -144,6 +145,7 @@ async def surveiller_dslr():
|
||||
if camera.connectee:
|
||||
log.info("DSLR reconnecte avec succes")
|
||||
_echecs_connexion = 0
|
||||
_appliquer_config_camera()
|
||||
await diffuser_ws({"type": "camera_ok"})
|
||||
else:
|
||||
log.warning("Echec reconnexion DSLR — nouvelle tentative dans 5s")
|
||||
@@ -156,6 +158,7 @@ async def surveiller_dslr():
|
||||
if camera.connectee:
|
||||
log.info("DSLR connecte avec succes")
|
||||
_echecs_connexion = 0
|
||||
_appliquer_config_camera()
|
||||
await diffuser_ws({"type": "camera_ok"})
|
||||
else:
|
||||
_echecs_connexion += 1
|
||||
@@ -164,6 +167,13 @@ async def surveiller_dslr():
|
||||
log.debug(f"surveiller_dslr: {e}")
|
||||
|
||||
|
||||
def _appliquer_config_camera():
|
||||
"""Applique les paramètres caméra persistants (flash, etc.) après connexion."""
|
||||
cfg = charger_config()
|
||||
flash_integre = cfg.get("camera", {}).get("flash_integre", True)
|
||||
camera.configurer_flash(flash_integre)
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
"""Demarrage et arret de l'application."""
|
||||
@@ -171,6 +181,7 @@ async def lifespan(app: FastAPI):
|
||||
log.info("Demarrage du photobooth")
|
||||
_loop_principal = asyncio.get_event_loop()
|
||||
camera.connecter()
|
||||
_appliquer_config_camera()
|
||||
# Thread de capture preview (bloquant, tourne en parallele)
|
||||
t = threading.Thread(target=_thread_preview, daemon=True)
|
||||
t.start()
|
||||
@@ -276,6 +287,9 @@ async def api_config():
|
||||
async def api_config_update(modifications: dict):
|
||||
config = mettre_a_jour_config(modifications)
|
||||
await diffuser_ws({"type": "config_maj", "config": config})
|
||||
# Appliquer immédiatement les paramètres caméra si modifiés
|
||||
if "camera" in modifications and camera.connectee:
|
||||
_appliquer_config_camera()
|
||||
return config
|
||||
|
||||
|
||||
@@ -430,6 +444,13 @@ async def api_systeme_info():
|
||||
}
|
||||
|
||||
|
||||
@app.post("/api/log-js")
|
||||
async def api_log_js(request: Request):
|
||||
data = await request.json()
|
||||
log.warning(f"[JS] {data.get('msg', '?')}")
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
@app.get("/api/camera/statut")
|
||||
async def api_camera_statut():
|
||||
return {
|
||||
@@ -476,6 +497,8 @@ async def api_camera_reconnecter(body: dict = {}):
|
||||
camera.deconnecter()
|
||||
source = body.get("source")
|
||||
ok = camera.connecter(source=source)
|
||||
if ok:
|
||||
_appliquer_config_camera()
|
||||
return {"connectee": ok, "mode": camera.mode}
|
||||
|
||||
|
||||
@@ -1073,8 +1096,10 @@ async def traiter_message_ws(msg: dict, ws: WebSocket):
|
||||
elif type_msg == "preview_start":
|
||||
global _preview_actif
|
||||
_preview_actif = True
|
||||
log.info("WS: preview_start recu")
|
||||
elif type_msg == "preview_stop":
|
||||
_preview_actif = False
|
||||
log.info("WS: preview_stop recu")
|
||||
|
||||
|
||||
async def diffuser_ws(message: dict):
|
||||
|
||||
Reference in New Issue
Block a user