Ajoute vue détail par passage (clip + galerie frames)
- Sauvegarde le clip MP4 et les top 10 frames par événement dans /data/events/{id}/
- Nouvelle route GET /event/{id} avec page détail : info, vignette interactive, lecteur vidéo, galerie frames
- Les cartes de l'index sont maintenant cliquables
- Migration DB : ajout colonne clip_path sur les BDD existantes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
8507987ea9
commit
528a158c42
+32
-5
@@ -1,9 +1,10 @@
|
||||
import os
|
||||
import glob
|
||||
import logging
|
||||
import threading
|
||||
from contextlib import asynccontextmanager
|
||||
from fastapi import FastAPI, Request, Query
|
||||
from fastapi.responses import HTMLResponse, FileResponse
|
||||
from fastapi import FastAPI, Request, Query, HTTPException
|
||||
from fastapi.responses import HTMLResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from datetime import datetime
|
||||
@@ -13,8 +14,10 @@ import watcher
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(name)s %(levelname)s %(message)s")
|
||||
|
||||
DATA_DIR = os.environ.get("SNAPSHOTS_DIR", "/data/snapshots")
|
||||
os.makedirs(DATA_DIR, exist_ok=True)
|
||||
SNAPSHOTS_DIR = os.environ.get("SNAPSHOTS_DIR", "/data/snapshots")
|
||||
EVENTS_DIR = os.environ.get("EVENTS_DIR", "/data/events")
|
||||
os.makedirs(SNAPSHOTS_DIR, exist_ok=True)
|
||||
os.makedirs(EVENTS_DIR, exist_ok=True)
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
@@ -26,7 +29,8 @@ async def lifespan(app: FastAPI):
|
||||
|
||||
|
||||
app = FastAPI(lifespan=lifespan)
|
||||
app.mount("/snapshots", StaticFiles(directory="/data/snapshots"), name="snapshots")
|
||||
app.mount("/snapshots", StaticFiles(directory=SNAPSHOTS_DIR), name="snapshots")
|
||||
app.mount("/events", StaticFiles(directory=EVENTS_DIR), name="events")
|
||||
|
||||
templates = Jinja2Templates(directory="/app/templates")
|
||||
|
||||
@@ -74,6 +78,29 @@ async def index(
|
||||
})
|
||||
|
||||
|
||||
@app.get("/event/{event_id}", response_class=HTMLResponse)
|
||||
async def event_detail(request: Request, event_id: str):
|
||||
ev = database.get_event(event_id)
|
||||
if not ev:
|
||||
raise HTTPException(status_code=404, detail="Événement introuvable")
|
||||
|
||||
ev["time_str"] = ts_to_str(ev["start_time"])
|
||||
|
||||
# List frames for this event
|
||||
event_dir = os.path.join(EVENTS_DIR, event_id)
|
||||
frame_paths = sorted(glob.glob(os.path.join(event_dir, "frame_*.jpg")))
|
||||
frames = [f"events/{event_id}/frame_{i+1:04d}.jpg" for i in range(len(frame_paths))]
|
||||
|
||||
has_clip = ev.get("clip_path") and os.path.exists(os.path.join("/data", ev["clip_path"]))
|
||||
|
||||
return templates.TemplateResponse("event_detail.html", {
|
||||
"request": request,
|
||||
"ev": ev,
|
||||
"frames": frames,
|
||||
"has_clip": has_clip,
|
||||
})
|
||||
|
||||
|
||||
@app.get("/api/events")
|
||||
async def api_events(
|
||||
page: int = Query(1, ge=1),
|
||||
|
||||
Reference in New Issue
Block a user