DSLR auto-recovery USB reset + cadre overlay non-mirroré + lien galerie événement
- surveiller_dslr: reset USB ioctl après 2 échecs, double reset après 3 - deconnecter(): timeout threading 3s pour éviter blocage PTP - CSS: .preview-live .cadre-overlay sans scaleX(-1) - Admin événement: lien direct vers galerie en ligne Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -114,13 +114,19 @@ class Camera:
|
||||
log.error(f"Impossible d'ouvrir /dev/video{index}")
|
||||
return False
|
||||
|
||||
def deconnecter(self):
|
||||
"""Deconnecte l'appareil photo."""
|
||||
def deconnecter(self, timeout: float = 3.0):
|
||||
"""Deconnecte l'appareil photo avec timeout pour eviter le blocage PTP."""
|
||||
if self.camera and GPHOTO2_DISPONIBLE:
|
||||
def _exit():
|
||||
try:
|
||||
self.camera.exit()
|
||||
except gp.GPhoto2Error:
|
||||
except Exception:
|
||||
pass
|
||||
t = threading.Thread(target=_exit, daemon=True)
|
||||
t.start()
|
||||
t.join(timeout=timeout)
|
||||
if t.is_alive():
|
||||
log.warning("camera.exit() bloque — abandon (le FD sera libere au GC)")
|
||||
if self.webcam:
|
||||
self.webcam.release()
|
||||
self.camera = None
|
||||
|
||||
@@ -124,7 +124,8 @@ def _usb_reset_canon():
|
||||
|
||||
|
||||
async def surveiller_dslr():
|
||||
"""Surveille le DSLR en continu : detecte les deconnexions et reconnecte automatiquement."""
|
||||
"""Surveille le DSLR en continu : detecte les deconnexions et reconnecte automatiquement.
|
||||
Apres plusieurs echecs, fait un reset USB ioctl pour debloquer le port PTP."""
|
||||
global _dslr_erreurs
|
||||
_echecs_connexion = 0
|
||||
while True:
|
||||
@@ -135,42 +136,55 @@ async def surveiller_dslr():
|
||||
if not GPHOTO2_DISPONIBLE:
|
||||
continue
|
||||
if camera.mode == "gphoto2" and camera.connectee:
|
||||
# Le thread preview capture en permanence — preview_dslr_ok reflète la santé réelle
|
||||
if camera.preview_dslr_ok:
|
||||
_dslr_erreurs = 0
|
||||
_echecs_connexion = 0
|
||||
else:
|
||||
_dslr_erreurs += 1
|
||||
if _dslr_erreurs >= 3 and not _preview_actif: # Ne pas couper pendant le countdown
|
||||
if _dslr_erreurs >= 3 and not _preview_actif:
|
||||
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"})
|
||||
await asyncio.sleep(2) # Laisser le bus USB se reinitialiser
|
||||
camera.connecter(source="gphoto2")
|
||||
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")
|
||||
_echecs_connexion += 1
|
||||
await _reconnecter_dslr_avec_reset(_echecs_connexion)
|
||||
else:
|
||||
# Non connecte : chercher et connecter un DSLR disponible
|
||||
dslrs = gp.Camera.autodetect()
|
||||
if len(dslrs) > 0:
|
||||
log.info(f"DSLR detecte : {dslrs[0][0]}, connexion automatique...")
|
||||
_echecs_connexion += 1
|
||||
log.info(f"DSLR detecte : {dslrs[0][0]}, connexion automatique... (tentative {_echecs_connexion})")
|
||||
await _reconnecter_dslr_avec_reset(_echecs_connexion)
|
||||
except Exception as e:
|
||||
log.debug(f"surveiller_dslr: {e}")
|
||||
|
||||
|
||||
async def _reconnecter_dslr_avec_reset(echecs: int):
|
||||
"""Reconnexion DSLR avec reset USB progressif."""
|
||||
camera.deconnecter()
|
||||
await diffuser_ws({"type": "camera_erreur", "message": "Appareil photo deconnecte"})
|
||||
|
||||
if echecs >= 2:
|
||||
log.info(f"Reset USB Canon (echec #{echecs})...")
|
||||
_usb_reset_canon()
|
||||
await asyncio.sleep(4)
|
||||
else:
|
||||
await asyncio.sleep(2)
|
||||
|
||||
camera.connecter(source="gphoto2")
|
||||
if camera.connectee:
|
||||
log.info("DSLR connecte avec succes")
|
||||
_echecs_connexion = 0
|
||||
log.info("DSLR reconnecte avec succes")
|
||||
_appliquer_config_camera()
|
||||
await diffuser_ws({"type": "camera_ok"})
|
||||
else:
|
||||
_echecs_connexion += 1
|
||||
log.warning(f"Echec connexion DSLR ({_echecs_connexion})")
|
||||
except Exception as e:
|
||||
log.debug(f"surveiller_dslr: {e}")
|
||||
log.warning(f"Echec reconnexion DSLR (tentative {echecs})")
|
||||
if echecs >= 3:
|
||||
log.info("Reset USB force supplementaire...")
|
||||
_usb_reset_canon()
|
||||
await asyncio.sleep(5)
|
||||
camera.connecter(source="gphoto2")
|
||||
if camera.connectee:
|
||||
log.info("DSLR reconnecte apres double reset")
|
||||
_appliquer_config_camera()
|
||||
await diffuser_ws({"type": "camera_ok"})
|
||||
|
||||
|
||||
def _appliquer_config_camera():
|
||||
|
||||
@@ -321,6 +321,10 @@ html, body {
|
||||
transform: scaleX(-1); /* miroir selfie : ce que voit l'utilisateur en se posant */
|
||||
}
|
||||
|
||||
.preview-live .cadre-overlay {
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.preview-live img.preview-chargement {
|
||||
filter: blur(8px) brightness(0.6);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
||||
<meta name="google" content="notranslate">
|
||||
<meta http-equiv="Content-Language" content="fr">
|
||||
<link rel="stylesheet" href="/css/style.css?v=7">
|
||||
<link rel="stylesheet" href="/css/style.css?v=8">
|
||||
<link rel="stylesheet" href="/css/themes.css?v=2">
|
||||
</head>
|
||||
<body>
|
||||
@@ -626,6 +626,11 @@
|
||||
</div>
|
||||
<button class="btn-action" onclick="sauvegarderEvenement()">Sauvegarder</button>
|
||||
|
||||
<div class="champ" id="lien-galerie-event" style="display:none">
|
||||
<label>Galerie en ligne</label>
|
||||
<a id="url-galerie-event" href="#" target="_blank" class="btn-secondaire btn-petit" style="text-decoration:none;display:inline-block">Ouvrir la galerie</a>
|
||||
</div>
|
||||
|
||||
<h3>Formats d'impression</h3>
|
||||
<p class="aide">Formats papier disponibles pour cet evenement.</p>
|
||||
<div class="toggle-groupe" id="event-formats-toggles">
|
||||
@@ -773,6 +778,6 @@
|
||||
<script src="/js/effects.js?v=4"></script>
|
||||
<script src="/js/gallery.js?v=3"></script>
|
||||
<script src="/js/share.js?v=7"></script>
|
||||
<script src="/js/admin.js?v=7"></script>
|
||||
<script src="/js/admin.js?v=8"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -768,6 +768,20 @@ function afficherDetailEvent(event) {
|
||||
document.getElementById('tog-event-fmt-15x20').checked = fmts.includes('15x20');
|
||||
document.getElementById('tog-event-fmt-strip').checked = fmts.includes('strip');
|
||||
|
||||
// Lien galerie en ligne
|
||||
const booth = config.booth || {};
|
||||
const lienBloc = document.getElementById('lien-galerie-event');
|
||||
const lienA = document.getElementById('url-galerie-event');
|
||||
if (booth.actif && booth.url) {
|
||||
const evtId = booth.event_id || eventEditId || 'default';
|
||||
const url = booth.url.replace(/\/$/, '') + '/' + evtId;
|
||||
lienA.href = url;
|
||||
lienA.textContent = url;
|
||||
lienBloc.style.display = '';
|
||||
} else {
|
||||
lienBloc.style.display = 'none';
|
||||
}
|
||||
|
||||
chargerCadresEvent();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user