diff --git a/backend/camera.py b/backend/camera.py index efd8149..3d25761 100644 --- a/backend/camera.py +++ b/backend/camera.py @@ -64,6 +64,7 @@ class Camera: self.webcam = None self.webcam_index = -1 self._preview_webcam_fallback = None + self.preview_dslr_ok = True # True si le dernier preview DSLR a reussi def connecter(self, source=None) -> bool: """Connecte la camera. source: 'gphoto2', 'webcam:0', 'webcam:2', etc.""" @@ -202,7 +203,9 @@ class Camera: elif self.mode == "gphoto2": data = self._preview_gphoto2() if data: + self.preview_dslr_ok = True return data + self.preview_dslr_ok = False # Fallback webcam pour le preview si le DSLR ne supporte pas le live view if not self._preview_webcam_fallback: self._init_preview_fallback() diff --git a/backend/main.py b/backend/main.py index 430fd9f..d86eb78 100644 --- a/backend/main.py +++ b/backend/main.py @@ -37,6 +37,7 @@ clients_ws: list[WebSocket] = [] capture_en_cours = False _dslr_erreurs = 0 +_dernier_envoi_erreur_preview = 0.0 # timestamp du dernier camera_erreur envoye depuis le preview async def surveiller_dslr(): """Verifie periodiquement si un DSLR est branche et bascule dessus.""" @@ -529,6 +530,8 @@ async def traiter_message_ws(msg: dict, ws: WebSocket): if type_msg == "ping": await ws.send_json({"type": "pong"}) elif type_msg == "preview": + import time as _time + global _dernier_envoi_erreur_preview # Pendant une capture DSLR, utiliser directement la webcam fallback donnees = None if capture_en_cours and camera._preview_webcam_fallback: @@ -541,6 +544,12 @@ async def traiter_message_ws(msg: dict, ws: WebSocket): if donnees: b64 = base64.b64encode(donnees).decode("ascii") await ws.send_json({"type": "preview", "image": f"data:image/jpeg;base64,{b64}"}) + # Si le DSLR est cense etre connecte mais le preview echoue, signaler l'erreur + if camera.mode == "gphoto2" and not camera.preview_dslr_ok: + now = _time.monotonic() + if now - _dernier_envoi_erreur_preview > 5.0: + _dernier_envoi_erreur_preview = now + await ws.send_json({"type": "camera_erreur", "message": "Le DSLR ne repond pas au preview"}) async def diffuser_ws(message: dict): diff --git a/frontend/js/camera.js b/frontend/js/camera.js index 3102161..3ec8811 100644 --- a/frontend/js/camera.js +++ b/frontend/js/camera.js @@ -9,11 +9,28 @@ const EMOJI_CAR = { 3: '🤪', 2: '😱', 1: '🔥' }; // --- Preview live --- +function afficherErreurCapture(message) { + const el = document.getElementById('preview-erreur-camera'); + if (!el) { allerA('accueil'); return; } + el.querySelector('p').textContent = message; + el.querySelector('span').textContent = 'Retour dans 3 secondes...'; + el.classList.remove('cache'); + setTimeout(() => { + el.classList.add('cache'); + allerA('accueil'); + }, 3000); +} + function afficherErreurPreview(visible) { const el = document.getElementById('preview-erreur-camera'); if (!el) return; - if (visible) el.classList.remove('cache'); - else el.classList.add('cache'); + if (visible) { + el.querySelector('p').textContent = 'Appareil photo deconnecte'; + el.querySelector('span').textContent = 'Reconnexion en cours...'; + el.classList.remove('cache'); + } else { + el.classList.add('cache'); + } } function resetPreviewTimeout() { @@ -111,7 +128,10 @@ async function lancerCapture() { } if (resultat.erreur) { - allerA('accueil'); + arreterPreview(); + afficherErreurCapture(resultat.erreur === 'Limite de photos atteinte' + ? 'Limite de photos atteinte' + : 'Echec de la capture — verifier le DSLR'); return; }