Canon: fiabilite veille/reveil + detection PTP freeze + UX countdown + fix copies impression
- Canon PTP freeze: 5 keepalive FAIL consecutifs → deconnexion forcee - Canon veille: reset timestamps reconnexion (pas de boucle de mort) - Canon reveil: guard _reveil_en_cours dans thread preview + surveiller_dslr - Canon capture: attente auto reveil (60s max) au lieu de 503 immediat - Canon reconnexion quand _echecs_connexion == 0 - UX: sequence "Attention!" → "Preparez-vous!" → 3 → 2 → 1 - UX: pre-messages uniquement photo simple + 1ere photo pellicule - UX: pas de compteur "Photo 1/1" en mode simple - UX: pas de cadre propose pour pellicule - UX: taille texte countdown reduite pour Surface (scale-factor 3) - Impression: N jobs lp separes au lieu de lp -n (Mitsubishi ignore -n) - Bouton Terminer grise pendant impression Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -312,6 +312,7 @@ class Camera:
|
|||||||
("autopoweroff", 0),
|
("autopoweroff", 0),
|
||||||
("viewfinder", 1),
|
("viewfinder", 1),
|
||||||
("iso", "800"),
|
("iso", "800"),
|
||||||
|
("aperture", "4"),
|
||||||
("drivemode", "Single"),
|
("drivemode", "Single"),
|
||||||
]
|
]
|
||||||
for nom, valeur in reglages:
|
for nom, valeur in reglages:
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ def lister_evenements() -> list[dict]:
|
|||||||
|
|
||||||
|
|
||||||
def creer_evenement(nom: str, **kwargs) -> dict:
|
def creer_evenement(nom: str, **kwargs) -> dict:
|
||||||
event_id = uuid.uuid4().hex[:8]
|
event_id = kwargs.get("event_id") or uuid.uuid4().hex[:8]
|
||||||
event = {
|
event = {
|
||||||
"id": event_id,
|
"id": event_id,
|
||||||
"nom": nom,
|
"nom": nom,
|
||||||
|
|||||||
114
backend/main.py
114
backend/main.py
@@ -83,6 +83,7 @@ _canon_stats = {
|
|||||||
}
|
}
|
||||||
_CANON_VEILLE_MINUTES = 30 # couper le Canon après X min sans activité utilisateur
|
_CANON_VEILLE_MINUTES = 30 # couper le Canon après X min sans activité utilisateur
|
||||||
_canon_en_veille = False
|
_canon_en_veille = False
|
||||||
|
_reveil_en_cours = False
|
||||||
|
|
||||||
def _thread_preview():
|
def _thread_preview():
|
||||||
"""Thread de fond : capture les frames DSLR.
|
"""Thread de fond : capture les frames DSLR.
|
||||||
@@ -103,7 +104,7 @@ def _thread_preview():
|
|||||||
_canon_stats["derniere_capture"],
|
_canon_stats["derniere_capture"],
|
||||||
_canon_stats["derniere_preview"],
|
_canon_stats["derniere_preview"],
|
||||||
)
|
)
|
||||||
if derniere_activite > 0 and not _preview_actif and not _canon_en_veille:
|
if derniere_activite > 0 and not _preview_actif and not _canon_en_veille and not _reveil_en_cours:
|
||||||
inactif_min = (now - derniere_activite) / 60
|
inactif_min = (now - derniere_activite) / 60
|
||||||
if inactif_min >= _CANON_VEILLE_MINUTES and relais.est_connecte():
|
if inactif_min >= _CANON_VEILLE_MINUTES and relais.est_connecte():
|
||||||
log.info(f"Canon inactif depuis {inactif_min:.0f} min — mise en veille relais")
|
log.info(f"Canon inactif depuis {inactif_min:.0f} min — mise en veille relais")
|
||||||
@@ -124,14 +125,21 @@ def _thread_preview():
|
|||||||
_derniere_keepalive = now
|
_derniere_keepalive = now
|
||||||
if ok:
|
if ok:
|
||||||
_canon_stats["keepalive_ok"] += 1
|
_canon_stats["keepalive_ok"] += 1
|
||||||
|
_canon_stats["keepalive_fail_consecutifs"] = 0
|
||||||
_canon_stats["dernier_keepalive_ok"] = now
|
_canon_stats["dernier_keepalive_ok"] = now
|
||||||
if now - _dernier_log_keepalive > 300:
|
if now - _dernier_log_keepalive > 300:
|
||||||
log.info(f"Canon keepalive OK (total: {_canon_stats['keepalive_ok']}, fail: {_canon_stats['keepalive_fail']})")
|
log.info(f"Canon keepalive OK (total: {_canon_stats['keepalive_ok']}, fail: {_canon_stats['keepalive_fail']})")
|
||||||
_dernier_log_keepalive = now
|
_dernier_log_keepalive = now
|
||||||
else:
|
else:
|
||||||
_canon_stats["keepalive_fail"] += 1
|
_canon_stats["keepalive_fail"] += 1
|
||||||
|
_canon_stats["keepalive_fail_consecutifs"] = _canon_stats.get("keepalive_fail_consecutifs", 0) + 1
|
||||||
_canon_stats["dernier_keepalive_fail"] = now
|
_canon_stats["dernier_keepalive_fail"] = now
|
||||||
log.warning(f"Canon keepalive FAIL #{_canon_stats['keepalive_fail']}")
|
consec = _canon_stats["keepalive_fail_consecutifs"]
|
||||||
|
log.warning(f"Canon keepalive FAIL #{_canon_stats['keepalive_fail']} (consec: {consec})")
|
||||||
|
if consec >= 5:
|
||||||
|
log.error(f"Canon PTP freeze detecte — {consec} keepalive FAIL consecutifs, deconnexion forcee")
|
||||||
|
_canon_stats["keepalive_fail_consecutifs"] = 0
|
||||||
|
camera.deconnecter()
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
@@ -362,7 +370,7 @@ async def surveiller_dslr():
|
|||||||
await asyncio.sleep(60)
|
await asyncio.sleep(60)
|
||||||
else:
|
else:
|
||||||
await asyncio.sleep(5)
|
await asyncio.sleep(5)
|
||||||
if capture_en_cours:
|
if capture_en_cours or _reveil_en_cours:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
if not GPHOTO2_DISPONIBLE:
|
if not GPHOTO2_DISPONIBLE:
|
||||||
@@ -373,7 +381,13 @@ async def surveiller_dslr():
|
|||||||
with camera._gp_lock:
|
with camera._gp_lock:
|
||||||
camera.camera.get_config()
|
camera.camera.get_config()
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
_dslr_erreurs += 1
|
||||||
|
if _dslr_erreurs >= 3:
|
||||||
|
log.warning(f"Canon get_config echoue {_dslr_erreurs} fois — reconnexion forcee")
|
||||||
|
_dslr_erreurs = 0
|
||||||
|
_echecs_connexion += 1
|
||||||
|
await _reconnecter_dslr_avec_reset(_echecs_connexion)
|
||||||
|
continue
|
||||||
if camera.preview_dslr_ok:
|
if camera.preview_dslr_ok:
|
||||||
_dslr_erreurs = 0
|
_dslr_erreurs = 0
|
||||||
_echecs_connexion = 0
|
_echecs_connexion = 0
|
||||||
@@ -398,14 +412,28 @@ async def surveiller_dslr():
|
|||||||
_usb_reset_canon()
|
_usb_reset_canon()
|
||||||
await asyncio.sleep(3)
|
await asyncio.sleep(3)
|
||||||
await _reconnecter_dslr_avec_reset(_echecs_connexion)
|
await _reconnecter_dslr_avec_reset(_echecs_connexion)
|
||||||
elif _echecs_connexion > 0 and _echecs_connexion % 10 == 0:
|
else:
|
||||||
if relais.est_connecte() and _echecs_connexion % 20 == 0:
|
if _echecs_connexion > 0:
|
||||||
log.info("Canon absent USB — power cycle relais...")
|
_echecs_connexion += 1
|
||||||
await asyncio.get_event_loop().run_in_executor(None, relais.power_cycle_canon, 10.0)
|
if relais.est_connecte() and _echecs_connexion % 10 == 0:
|
||||||
await asyncio.sleep(20)
|
log.warning(f"Canon absent USB (tentative {_echecs_connexion}) — power cycle relais...")
|
||||||
else:
|
await asyncio.get_event_loop().run_in_executor(None, relais.power_cycle_canon, 10.0)
|
||||||
log.info("DSLR non detecte en USB, tentative USB rebind...")
|
await asyncio.sleep(25)
|
||||||
await asyncio.get_event_loop().run_in_executor(None, _usb_rebind_canon)
|
else:
|
||||||
|
log.info(f"DSLR non detecte en USB (tentative {_echecs_connexion}), rebind...")
|
||||||
|
await asyncio.get_event_loop().run_in_executor(None, _usb_rebind_canon)
|
||||||
|
elif _canon_sur_usb():
|
||||||
|
log.info("Canon detecte sur USB sans echec precedent — tentative connexion...")
|
||||||
|
_echecs_connexion = 1
|
||||||
|
camera.connecter(source="gphoto2")
|
||||||
|
if camera.connectee:
|
||||||
|
log.info("Canon reconnecte automatiquement")
|
||||||
|
_canon_stats["connexions"] += 1
|
||||||
|
_canon_stats["derniere_preview"] = time.time()
|
||||||
|
_canon_stats["derniere_capture"] = time.time()
|
||||||
|
_appliquer_config_camera()
|
||||||
|
_echecs_connexion = 0
|
||||||
|
await diffuser_ws({"type": "camera_ok"})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.debug(f"surveiller_dslr: {e}")
|
log.debug(f"surveiller_dslr: {e}")
|
||||||
|
|
||||||
@@ -457,6 +485,8 @@ async def _reconnecter_dslr_avec_reset(echecs: int):
|
|||||||
if camera.connectee:
|
if camera.connectee:
|
||||||
log.info("DSLR reconnecte avec succes")
|
log.info("DSLR reconnecte avec succes")
|
||||||
_canon_stats["connexions"] += 1
|
_canon_stats["connexions"] += 1
|
||||||
|
_canon_stats["derniere_preview"] = time.time()
|
||||||
|
_canon_stats["derniere_capture"] = time.time()
|
||||||
_appliquer_config_camera()
|
_appliquer_config_camera()
|
||||||
await diffuser_ws({"type": "camera_ok"})
|
await diffuser_ws({"type": "camera_ok"})
|
||||||
else:
|
else:
|
||||||
@@ -618,22 +648,29 @@ async def _surveiller_imprimante():
|
|||||||
|
|
||||||
def _reveiller_canon():
|
def _reveiller_canon():
|
||||||
"""Réveil Canon depuis veille relais : restaure alimentation, attend boot, reconnecte."""
|
"""Réveil Canon depuis veille relais : restaure alimentation, attend boot, reconnecte."""
|
||||||
log.info("Réveil Canon — restauration alimentation relais")
|
global _reveil_en_cours
|
||||||
relais.desactiver("canon")
|
_reveil_en_cours = True
|
||||||
time.sleep(15)
|
try:
|
||||||
relais.desactiver("canon_usb")
|
log.info("Réveil Canon — restauration alimentation relais")
|
||||||
time.sleep(10)
|
relais.desactiver("canon")
|
||||||
if _canon_sur_usb():
|
time.sleep(15)
|
||||||
log.info("Canon visible USB après réveil — connexion...")
|
relais.desactiver("canon_usb")
|
||||||
camera.connecter(source="gphoto2")
|
time.sleep(25)
|
||||||
if camera.connectee:
|
if _canon_sur_usb():
|
||||||
_appliquer_config_camera()
|
log.info("Canon visible USB après réveil — connexion...")
|
||||||
log.info("Canon reconnecté après réveil")
|
camera.connecter(source="gphoto2")
|
||||||
_canon_stats["connexions"] += 1
|
if camera.connectee:
|
||||||
|
_appliquer_config_camera()
|
||||||
|
_canon_stats["derniere_preview"] = time.time()
|
||||||
|
_canon_stats["derniere_capture"] = time.time()
|
||||||
|
log.info("Canon reconnecté après réveil")
|
||||||
|
_canon_stats["connexions"] += 1
|
||||||
|
else:
|
||||||
|
log.warning("Canon visible USB mais connexion gphoto2 échouée")
|
||||||
else:
|
else:
|
||||||
log.warning("Canon visible USB mais connexion gphoto2 échouée")
|
log.warning("Canon absent USB après réveil relais — vérifier branchement physique")
|
||||||
else:
|
finally:
|
||||||
log.warning("Canon absent USB après réveil relais — vérifier branchement physique")
|
_reveil_en_cours = False
|
||||||
|
|
||||||
|
|
||||||
def _canon_sur_usb() -> bool:
|
def _canon_sur_usb() -> bool:
|
||||||
@@ -846,9 +883,21 @@ async def api_config_update(modifications: dict):
|
|||||||
|
|
||||||
@app.post("/api/capturer")
|
@app.post("/api/capturer")
|
||||||
async def api_capturer():
|
async def api_capturer():
|
||||||
global capture_en_cours, _preview_actif
|
global capture_en_cours, _preview_actif, _canon_en_veille
|
||||||
|
if _canon_en_veille and not _reveil_en_cours:
|
||||||
|
log.info("Canon en veille lors de la capture — réveil automatique")
|
||||||
|
_canon_en_veille = False
|
||||||
|
await asyncio.get_event_loop().run_in_executor(None, _reveiller_canon)
|
||||||
|
if _reveil_en_cours:
|
||||||
|
log.info("Canon en cours de réveil — attente max 60s")
|
||||||
|
for _ in range(60):
|
||||||
|
if not _reveil_en_cours:
|
||||||
|
break
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
if not camera.connectee:
|
||||||
|
return JSONResponse({"erreur": "Appareil photo en cours de démarrage — réessayez"}, status_code=503)
|
||||||
capture_en_cours = True
|
capture_en_cours = True
|
||||||
_preview_actif = False # Eviter faux camera_erreur (race condition HTTP avant preview_stop)
|
_preview_actif = False
|
||||||
_canon_stats["captures"] += 1
|
_canon_stats["captures"] += 1
|
||||||
_canon_stats["derniere_capture"] = time.time()
|
_canon_stats["derniere_capture"] = time.time()
|
||||||
config = charger_config()
|
config = charger_config()
|
||||||
@@ -1107,7 +1156,7 @@ async def api_relais_veille():
|
|||||||
|
|
||||||
@app.post("/api/relais/reveil")
|
@app.post("/api/relais/reveil")
|
||||||
async def api_relais_reveil():
|
async def api_relais_reveil():
|
||||||
global _veille_eclairage
|
global _veille_eclairage, _canon_en_veille
|
||||||
_veille_eclairage = False
|
_veille_eclairage = False
|
||||||
if relais.est_connecte():
|
if relais.est_connecte():
|
||||||
config = charger_config()
|
config = charger_config()
|
||||||
@@ -1116,6 +1165,10 @@ async def api_relais_reveil():
|
|||||||
relais.activer("projecteur_gauche")
|
relais.activer("projecteur_gauche")
|
||||||
_proj_etat["proj1"] = True
|
_proj_etat["proj1"] = True
|
||||||
_proj_etat["proj2"] = False
|
_proj_etat["proj2"] = False
|
||||||
|
if _canon_en_veille:
|
||||||
|
log.info("Réveil Canon déclenché par reveil éclairage (HTTP)")
|
||||||
|
_canon_en_veille = False
|
||||||
|
asyncio.get_event_loop().run_in_executor(None, _reveiller_canon)
|
||||||
log.info("Eclairage reveille (1 projecteur)")
|
log.info("Eclairage reveille (1 projecteur)")
|
||||||
return {"ok": True}
|
return {"ok": True}
|
||||||
|
|
||||||
@@ -1634,6 +1687,7 @@ async def api_creer_evenement(donnees: dict):
|
|||||||
return JSONResponse({"erreur": "Nom requis"}, status_code=400)
|
return JSONResponse({"erreur": "Nom requis"}, status_code=400)
|
||||||
event = creer_evenement(
|
event = creer_evenement(
|
||||||
nom,
|
nom,
|
||||||
|
event_id=donnees.get("event_id"),
|
||||||
theme=donnees.get("theme", "base"),
|
theme=donnees.get("theme", "base"),
|
||||||
couleur_primaire=donnees.get("couleur_primaire", "#e91e63"),
|
couleur_primaire=donnees.get("couleur_primaire", "#e91e63"),
|
||||||
couleur_secondaire=donnees.get("couleur_secondaire", "#ffffff"),
|
couleur_secondaire=donnees.get("couleur_secondaire", "#ffffff"),
|
||||||
|
|||||||
@@ -302,50 +302,61 @@ def imprimer(
|
|||||||
rembobinage = conf_imp.get("rembobinage_ruban", False)
|
rembobinage = conf_imp.get("rembobinage_ruban", False)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
for tentative in range(1, 4):
|
jobs = []
|
||||||
cmd = [
|
for copie in range(1, copies + 1):
|
||||||
"lp",
|
job_ok = False
|
||||||
"-d", imprimante,
|
for tentative in range(1, 4):
|
||||||
"-n", str(copies),
|
cmd = [
|
||||||
"-o", f"PageSize={page_size}",
|
"lp",
|
||||||
"-o", "StpiShrinkOutput=Crop",
|
"-d", imprimante,
|
||||||
]
|
"-o", f"PageSize={page_size}",
|
||||||
if rembobinage:
|
"-o", "StpiShrinkOutput=Crop",
|
||||||
cmd.extend(["-o", "StpiDecklist=true"])
|
]
|
||||||
cmd.append(str(chemin_print))
|
if rembobinage:
|
||||||
|
cmd.extend(["-o", "StpiDecklist=true"])
|
||||||
|
cmd.append(str(chemin_print))
|
||||||
|
|
||||||
code, out, err = _run(cmd, timeout=30)
|
code, out, err = _run(cmd, timeout=30)
|
||||||
|
|
||||||
if code == 0:
|
if code == 0:
|
||||||
job = out.strip()
|
job = out.strip()
|
||||||
log.info(f"Impression lancée : {job} ({format_papier} {largeur}x{hauteur}px, x{copies})")
|
log.info(f"Impression copie {copie}/{copies} lancée : {job} ({format_papier} {largeur}x{hauteur}px)")
|
||||||
return {"succes": True, "job": job}
|
jobs.append(job)
|
||||||
|
job_ok = True
|
||||||
|
break
|
||||||
|
|
||||||
log.warning(f"Impression échouée (tentative {tentative}/3) : {err.strip()}")
|
log.warning(f"Impression copie {copie}/{copies} échouée (tentative {tentative}/3) : {err.strip()}")
|
||||||
|
|
||||||
if tentative < 3:
|
if tentative < 3:
|
||||||
if _statut(imprimante) == "stopped":
|
if _statut(imprimante) == "stopped":
|
||||||
erreur = _detecter_erreur_physique(imprimante)
|
erreur = _detecter_erreur_physique(imprimante)
|
||||||
if erreur == ERREUR_BOURRAGE:
|
if erreur == ERREUR_BOURRAGE:
|
||||||
return {
|
return {
|
||||||
"succes": False,
|
"succes": False,
|
||||||
"erreur": ERREUR_BOURRAGE,
|
"erreur": ERREUR_BOURRAGE,
|
||||||
"message": "Bourrage papier — retirez le papier coincé puis réessayez",
|
"message": "Bourrage papier — retirez le papier coincé puis réessayez",
|
||||||
}
|
}
|
||||||
if erreur == ERREUR_PAPIER:
|
if erreur == ERREUR_PAPIER:
|
||||||
return {
|
return {
|
||||||
"succes": False,
|
"succes": False,
|
||||||
"erreur": ERREUR_PAPIER,
|
"erreur": ERREUR_PAPIER,
|
||||||
"message": "Plus de papier — rechargez le rouleau",
|
"message": "Plus de papier — rechargez le rouleau",
|
||||||
}
|
}
|
||||||
_reactiver(imprimante)
|
_reactiver(imprimante)
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
|
|
||||||
return {
|
if not job_ok:
|
||||||
"succes": False,
|
return {
|
||||||
"erreur": ERREUR_IMPRESSION,
|
"succes": False,
|
||||||
"message": "Impression échouée après 3 tentatives",
|
"erreur": ERREUR_IMPRESSION,
|
||||||
}
|
"message": f"Impression copie {copie}/{copies} échouée après 3 tentatives",
|
||||||
|
}
|
||||||
|
|
||||||
|
if copie < copies:
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
log.info(f"Impression terminée : {copies} copie(s), jobs={jobs}")
|
||||||
|
return {"succes": True, "job": ", ".join(jobs)}
|
||||||
finally:
|
finally:
|
||||||
for tmp in tmp_a_supprimer:
|
for tmp in tmp_a_supprimer:
|
||||||
tmp.unlink(missing_ok=True)
|
tmp.unlink(missing_ok=True)
|
||||||
|
|||||||
@@ -1766,6 +1766,13 @@ h3 {
|
|||||||
color: var(--primaire);
|
color: var(--primaire);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.anim-texte {
|
||||||
|
font-size: clamp(0.9rem, 2.5vw, 1.6rem);
|
||||||
|
max-width: 80vw;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
/* === Animations compte a rebours === */
|
/* === Animations compte a rebours === */
|
||||||
/* Classique */
|
/* Classique */
|
||||||
.anim-classique { animation: anim-pop 0.5s ease; }
|
.anim-classique { animation: anim-pop 0.5s ease; }
|
||||||
|
|||||||
@@ -246,7 +246,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="statut-partage" class="statut-partage cache"></div>
|
<div id="statut-partage" class="statut-partage cache"></div>
|
||||||
<div class="partage-bas">
|
<div class="partage-bas">
|
||||||
<button class="btn-action" onclick="allerA('accueil')">Terminer</button>
|
<button class="btn-action" id="btn-terminer" onclick="allerA('accueil')">Terminer</button>
|
||||||
<button class="btn-secondaire" onclick="recommencer()">Nouvelle photo</button>
|
<button class="btn-secondaire" onclick="recommencer()">Nouvelle photo</button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -269,11 +269,14 @@ async function lancerCapture() {
|
|||||||
for (let i = 0; i < nbPhotos; i++) {
|
for (let i = 0; i < nbPhotos; i++) {
|
||||||
if (nbPhotos > 1) {
|
if (nbPhotos > 1) {
|
||||||
compteurEl.textContent = `Photo ${i + 1} / ${nbPhotos}`;
|
compteurEl.textContent = `Photo ${i + 1} / ${nbPhotos}`;
|
||||||
|
} else {
|
||||||
|
compteurEl.textContent = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
lancerPreview(); // Miroir live pendant le compte à rebours
|
lancerPreview(); // Miroir live pendant le compte à rebours
|
||||||
wsEnvoyer({type: 'prepare_capture'});
|
wsEnvoyer({type: 'prepare_capture'});
|
||||||
await compteARebours();
|
const avecPreMessages = (i === 0);
|
||||||
|
await compteARebours(avecPreMessages);
|
||||||
|
|
||||||
// Surprise : afficher media juste avant capture
|
// Surprise : afficher media juste avant capture
|
||||||
await afficherSurprise();
|
await afficherSurprise();
|
||||||
@@ -285,30 +288,29 @@ async function lancerCapture() {
|
|||||||
flash.offsetHeight;
|
flash.offsetHeight;
|
||||||
flash.style.animation = '';
|
flash.style.animation = '';
|
||||||
|
|
||||||
const promesseCapture = apiPost('/api/capturer').catch(() => null);
|
|
||||||
arreterPreview();
|
arreterPreview();
|
||||||
let resultat = await promesseCapture;
|
let resultat = await apiPost('/api/capturer').catch(() => null);
|
||||||
flash.classList.add('cache');
|
|
||||||
document.getElementById('capture-en-cours').classList.add('cache');
|
|
||||||
let erreurReseau = false;
|
|
||||||
|
|
||||||
if (!resultat) {
|
if (!resultat && !resultat?.erreur) {
|
||||||
erreurReseau = true;
|
flash.classList.add('cache');
|
||||||
}
|
document.getElementById('capture-en-cours').classList.add('cache');
|
||||||
|
|
||||||
if (erreurReseau) {
|
|
||||||
captureEnCours = false;
|
captureEnCours = false;
|
||||||
afficherErreurCapture('Erreur reseau', 'Le serveur ne repond pas — redemarrez le photobooth');
|
afficherErreurCapture('Erreur reseau', 'Le serveur ne repond pas — redemarrez le photobooth');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!resultat || resultat.erreur) {
|
if (resultat?.erreur) {
|
||||||
|
flash.classList.add('cache');
|
||||||
|
document.getElementById('capture-en-cours').classList.add('cache');
|
||||||
captureEnCours = false;
|
captureEnCours = false;
|
||||||
const msg = resultat?.erreur || 'Erreur inconnue';
|
const msg = resultat.erreur;
|
||||||
afficherErreurCapture('Echec de la capture', msg.includes('Echec') ? 'Verifiez le DSLR et reessayez' : msg);
|
afficherErreurCapture('Echec de la capture', msg.includes('Echec') ? 'Verifiez le DSLR et reessayez' : msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flash.classList.add('cache');
|
||||||
|
document.getElementById('capture-en-cours').classList.add('cache');
|
||||||
|
|
||||||
if (resultat.nom) {
|
if (resultat.nom) {
|
||||||
photosSession.push(resultat.nom);
|
photosSession.push(resultat.nom);
|
||||||
}
|
}
|
||||||
@@ -338,7 +340,7 @@ function choisirAnimationAleatoire() {
|
|||||||
return pool[Math.floor(Math.random() * pool.length)];
|
return pool[Math.floor(Math.random() * pool.length)];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function compteARebours() {
|
async function compteARebours(avecPreMessages = true) {
|
||||||
const conteneur = document.getElementById('compte-a-rebours');
|
const conteneur = document.getElementById('compte-a-rebours');
|
||||||
const chiffre = document.getElementById('chiffre-car');
|
const chiffre = document.getElementById('chiffre-car');
|
||||||
const duree = config.camera?.compte_a_rebours || 3;
|
const duree = config.camera?.compte_a_rebours || 3;
|
||||||
@@ -354,6 +356,17 @@ async function compteARebours() {
|
|||||||
// Animation CSS classique
|
// Animation CSS classique
|
||||||
conteneur.classList.remove('cache');
|
conteneur.classList.remove('cache');
|
||||||
|
|
||||||
|
if (avecPreMessages) {
|
||||||
|
const preMessages = ['Attention !', 'Préparez-vous !'];
|
||||||
|
for (const msg of preMessages) {
|
||||||
|
chiffre.textContent = msg;
|
||||||
|
chiffre.className = 'anim-chiffre anim-texte';
|
||||||
|
void chiffre.offsetWidth;
|
||||||
|
chiffre.classList.add('anim-' + anim.id);
|
||||||
|
await pause(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = duree; i > 0; i--) {
|
for (let i = duree; i > 0; i--) {
|
||||||
if (anim.id === 'emoji') {
|
if (anim.id === 'emoji') {
|
||||||
chiffre.textContent = EMOJI_CAR[i] || i;
|
chiffre.textContent = EMOJI_CAR[i] || i;
|
||||||
|
|||||||
@@ -9,6 +9,11 @@ async function ouvrirImpression() {
|
|||||||
document.getElementById('nb-exemplaires').textContent = nbExemplaires;
|
document.getElementById('nb-exemplaires').textContent = nbExemplaires;
|
||||||
cadreChoisi = null;
|
cadreChoisi = null;
|
||||||
|
|
||||||
|
if (modeActuel === 'multi') {
|
||||||
|
document.getElementById('form-impression').classList.remove('cache');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const hasCadres = await chargerCadresChoixImpression();
|
const hasCadres = await chargerCadresChoixImpression();
|
||||||
if (hasCadres) {
|
if (hasCadres) {
|
||||||
document.getElementById('popup-cadre-impression').classList.remove('cache');
|
document.getElementById('popup-cadre-impression').classList.remove('cache');
|
||||||
@@ -101,6 +106,8 @@ function changerExemplaires(delta) {
|
|||||||
async function lancerImpression() {
|
async function lancerImpression() {
|
||||||
if (!photoFinale) return;
|
if (!photoFinale) return;
|
||||||
fermerImpression();
|
fermerImpression();
|
||||||
|
const btnTerminer = document.getElementById('btn-terminer');
|
||||||
|
if (btnTerminer) { btnTerminer.disabled = true; btnTerminer.style.opacity = '0.4'; }
|
||||||
// Pour les strips, imprimer la version 2 bandes sur 10x15
|
// Pour les strips, imprimer la version 2 bandes sur 10x15
|
||||||
const fichierImpression = photoImpression || photoFinale;
|
const fichierImpression = photoImpression || photoFinale;
|
||||||
afficherStatut(`Impression de ${nbExemplaires} exemplaire(s)...`, 'succes');
|
afficherStatut(`Impression de ${nbExemplaires} exemplaire(s)...`, 'succes');
|
||||||
@@ -110,6 +117,7 @@ async function lancerImpression() {
|
|||||||
cadre: cadreChoisi || undefined,
|
cadre: cadreChoisi || undefined,
|
||||||
format_papier: formatImpression || undefined,
|
format_papier: formatImpression || undefined,
|
||||||
});
|
});
|
||||||
|
if (btnTerminer) { btnTerminer.disabled = false; btnTerminer.style.opacity = ''; }
|
||||||
if (resultat.attente) {
|
if (resultat.attente) {
|
||||||
afficherStatutEco('⏳', resultat.message || 'Votre photo sortira avec la suivante !', 'attente-eco');
|
afficherStatutEco('⏳', resultat.message || 'Votre photo sortira avec la suivante !', 'attente-eco');
|
||||||
} else if (resultat.jumeau && resultat.succes) {
|
} else if (resultat.jumeau && resultat.succes) {
|
||||||
|
|||||||
Reference in New Issue
Block a user