Admin backoffice : rubriques General/Evenement + videos situation + surprise + rembobinage ruban
- Reorganisation menu admin en 2 rubriques (General / Evenement) - Destinations deplace dans Evenement - Nouveau panneau Videos : upload par situation (montage, depannage, bourrage, rechargement) - Nouveau panneau Surprise : photo/video affichee ~1s avant capture pour provoquer sourire - Toggle rembobinage ruban Mitsubishi (StpiDecklist) pour economiser le ruban sur petits formats - Historique email cloisonne par evenement - Memoire projet consolidee dans memoire.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1072,19 +1072,28 @@ async def api_obtenir_evenement(event_id: str):
|
||||
return event
|
||||
|
||||
|
||||
def _event_id_actif() -> str | None:
|
||||
return charger_config().get("evenement", {}).get("event_id")
|
||||
|
||||
|
||||
@app.put("/api/evenements/{event_id}")
|
||||
async def api_modifier_evenement(event_id: str, donnees: dict):
|
||||
event = modifier_evenement(event_id, donnees)
|
||||
if not event:
|
||||
return JSONResponse({"erreur": "Evenement introuvable"}, status_code=404)
|
||||
if event_id == _event_id_actif():
|
||||
await diffuser_ws({"type": "config_maj", "config": charger_config()})
|
||||
return event
|
||||
|
||||
|
||||
@app.delete("/api/evenements/{event_id}")
|
||||
async def api_supprimer_evenement(event_id: str):
|
||||
etait_actif = event_id == _event_id_actif()
|
||||
ok = supprimer_evenement(event_id)
|
||||
if not ok:
|
||||
return JSONResponse({"erreur": "Evenement introuvable"}, status_code=404)
|
||||
if etait_actif:
|
||||
await diffuser_ws({"type": "config_maj", "config": charger_config()})
|
||||
return {"succes": True}
|
||||
|
||||
|
||||
@@ -1093,6 +1102,7 @@ async def api_activer_evenement(event_id: str):
|
||||
event = activer_evenement(event_id)
|
||||
if not event:
|
||||
return JSONResponse({"erreur": "Evenement introuvable"}, status_code=404)
|
||||
await diffuser_ws({"type": "config_maj", "config": charger_config()})
|
||||
return event
|
||||
|
||||
|
||||
@@ -1113,6 +1123,8 @@ async def api_set_cadre_event(event_id: str, format_papier: str, donnees: dict):
|
||||
mode = donnees.get("mode", "aucun")
|
||||
cadre = donnees.get("cadre")
|
||||
set_cadre_event(event_id, format_papier, mode, cadre)
|
||||
if event_id == _event_id_actif():
|
||||
await diffuser_ws({"type": "config_maj", "config": charger_config()})
|
||||
return {"succes": True}
|
||||
|
||||
|
||||
@@ -1384,6 +1396,87 @@ async def _lancer_photostation():
|
||||
FLAG_PHOTOSTATION.unlink(missing_ok=True)
|
||||
|
||||
|
||||
# --- API Videos de situation ---
|
||||
|
||||
DOSSIER_VIDEOS = RACINE / "data" / "videos"
|
||||
DOSSIER_VIDEOS.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
@app.get("/api/videos")
|
||||
async def api_videos():
|
||||
videos = {}
|
||||
for f in DOSSIER_VIDEOS.iterdir():
|
||||
if f.is_file() and f.suffix.lower() in ('.mp4', '.webm', '.mov'):
|
||||
videos[f.stem] = f.name
|
||||
return {"videos": videos}
|
||||
|
||||
|
||||
@app.post("/api/videos/upload")
|
||||
async def api_upload_video(video: UploadFile = File(...), situation: str = ""):
|
||||
if not situation:
|
||||
return JSONResponse({"erreur": "situation requise"}, 400)
|
||||
ext = Path(video.filename).suffix.lower() or '.mp4'
|
||||
dest = DOSSIER_VIDEOS / f"{situation}{ext}"
|
||||
for old in DOSSIER_VIDEOS.glob(f"{situation}.*"):
|
||||
old.unlink()
|
||||
with open(dest, "wb") as f:
|
||||
while chunk := await video.read(1024 * 1024):
|
||||
f.write(chunk)
|
||||
return {"ok": True, "fichier": dest.name}
|
||||
|
||||
|
||||
@app.delete("/api/videos/{situation}")
|
||||
async def api_delete_video(situation: str):
|
||||
for f in DOSSIER_VIDEOS.glob(f"{situation}.*"):
|
||||
f.unlink()
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
@app.get("/api/videos/{situation}/stream")
|
||||
async def api_stream_video(situation: str):
|
||||
for f in DOSSIER_VIDEOS.glob(f"{situation}.*"):
|
||||
media = "video/mp4" if f.suffix == ".mp4" else "video/webm"
|
||||
return FileResponse(f, media_type=media)
|
||||
return JSONResponse({"erreur": "video introuvable"}, 404)
|
||||
|
||||
|
||||
# --- API Surprise ---
|
||||
|
||||
DOSSIER_SURPRISE = RACINE / "data" / "surprise"
|
||||
DOSSIER_SURPRISE.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
@app.post("/api/surprise/upload")
|
||||
async def api_upload_surprise(media: UploadFile = File(...)):
|
||||
for old in DOSSIER_SURPRISE.iterdir():
|
||||
old.unlink()
|
||||
ext = Path(media.filename).suffix.lower() or '.jpg'
|
||||
dest = DOSSIER_SURPRISE / f"surprise{ext}"
|
||||
with open(dest, "wb") as f:
|
||||
while chunk := await media.read(1024 * 1024):
|
||||
f.write(chunk)
|
||||
fichier_type = "video" if ext in ('.mp4', '.webm', '.mov') else "photo"
|
||||
mettre_a_jour_config({"surprise": {"fichier": dest.name, "type": fichier_type}})
|
||||
return {"ok": True, "fichier": dest.name, "type": fichier_type}
|
||||
|
||||
|
||||
@app.delete("/api/surprise/media")
|
||||
async def api_delete_surprise():
|
||||
for f in DOSSIER_SURPRISE.iterdir():
|
||||
f.unlink()
|
||||
mettre_a_jour_config({"surprise": {"fichier": None}})
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
@app.get("/api/surprise/media")
|
||||
async def api_get_surprise():
|
||||
for f in DOSSIER_SURPRISE.iterdir():
|
||||
if f.is_file():
|
||||
ext = f.suffix.lower()
|
||||
if ext in ('.mp4', '.webm', '.mov'):
|
||||
return FileResponse(f, media_type="video/mp4")
|
||||
return FileResponse(f, media_type="image/jpeg")
|
||||
return JSONResponse({"erreur": "aucun media"}, 404)
|
||||
|
||||
|
||||
# --- API Systeme ---
|
||||
|
||||
@app.post("/api/systeme/redemarrer")
|
||||
|
||||
Reference in New Issue
Block a user