Fix erreur DSLR: detection immediate + message clair a l'ecran
- Backend: detecte quand le preview DSLR echoue (fallback silencieux) et envoie camera_erreur au frontend (debounce 5s) - Frontend: overlay d'erreur avec message si preview DSLR indisponible - Frontend: si capture echoue, affiche le motif 3s avant retour accueil au lieu de retourner silencieusement Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -64,6 +64,7 @@ class Camera:
|
|||||||
self.webcam = None
|
self.webcam = None
|
||||||
self.webcam_index = -1
|
self.webcam_index = -1
|
||||||
self._preview_webcam_fallback = None
|
self._preview_webcam_fallback = None
|
||||||
|
self.preview_dslr_ok = True # True si le dernier preview DSLR a reussi
|
||||||
|
|
||||||
def connecter(self, source=None) -> bool:
|
def connecter(self, source=None) -> bool:
|
||||||
"""Connecte la camera. source: 'gphoto2', 'webcam:0', 'webcam:2', etc."""
|
"""Connecte la camera. source: 'gphoto2', 'webcam:0', 'webcam:2', etc."""
|
||||||
@@ -202,7 +203,9 @@ class Camera:
|
|||||||
elif self.mode == "gphoto2":
|
elif self.mode == "gphoto2":
|
||||||
data = self._preview_gphoto2()
|
data = self._preview_gphoto2()
|
||||||
if data:
|
if data:
|
||||||
|
self.preview_dslr_ok = True
|
||||||
return data
|
return data
|
||||||
|
self.preview_dslr_ok = False
|
||||||
# Fallback webcam pour le preview si le DSLR ne supporte pas le live view
|
# Fallback webcam pour le preview si le DSLR ne supporte pas le live view
|
||||||
if not self._preview_webcam_fallback:
|
if not self._preview_webcam_fallback:
|
||||||
self._init_preview_fallback()
|
self._init_preview_fallback()
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ clients_ws: list[WebSocket] = []
|
|||||||
|
|
||||||
capture_en_cours = False
|
capture_en_cours = False
|
||||||
_dslr_erreurs = 0
|
_dslr_erreurs = 0
|
||||||
|
_dernier_envoi_erreur_preview = 0.0 # timestamp du dernier camera_erreur envoye depuis le preview
|
||||||
|
|
||||||
async def surveiller_dslr():
|
async def surveiller_dslr():
|
||||||
"""Verifie periodiquement si un DSLR est branche et bascule dessus."""
|
"""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":
|
if type_msg == "ping":
|
||||||
await ws.send_json({"type": "pong"})
|
await ws.send_json({"type": "pong"})
|
||||||
elif type_msg == "preview":
|
elif type_msg == "preview":
|
||||||
|
import time as _time
|
||||||
|
global _dernier_envoi_erreur_preview
|
||||||
# Pendant une capture DSLR, utiliser directement la webcam fallback
|
# Pendant une capture DSLR, utiliser directement la webcam fallback
|
||||||
donnees = None
|
donnees = None
|
||||||
if capture_en_cours and camera._preview_webcam_fallback:
|
if capture_en_cours and camera._preview_webcam_fallback:
|
||||||
@@ -541,6 +544,12 @@ async def traiter_message_ws(msg: dict, ws: WebSocket):
|
|||||||
if donnees:
|
if donnees:
|
||||||
b64 = base64.b64encode(donnees).decode("ascii")
|
b64 = base64.b64encode(donnees).decode("ascii")
|
||||||
await ws.send_json({"type": "preview", "image": f"data:image/jpeg;base64,{b64}"})
|
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):
|
async def diffuser_ws(message: dict):
|
||||||
|
|||||||
@@ -9,11 +9,28 @@ const EMOJI_CAR = { 3: '🤪', 2: '😱', 1: '🔥' };
|
|||||||
|
|
||||||
// --- Preview live ---
|
// --- 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) {
|
function afficherErreurPreview(visible) {
|
||||||
const el = document.getElementById('preview-erreur-camera');
|
const el = document.getElementById('preview-erreur-camera');
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
if (visible) el.classList.remove('cache');
|
if (visible) {
|
||||||
else el.classList.add('cache');
|
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() {
|
function resetPreviewTimeout() {
|
||||||
@@ -111,7 +128,10 @@ async function lancerCapture() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (resultat.erreur) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user