dashboard preview thumbnail par job
dispatcher scp frame_*.jpg (premiere apres trim head) vers
/var/lib/cosma-qc/thumbnails/job_N.jpg a la fin de do_extract.
Endpoint GET /jobs/{id}/thumbnail serve via FileResponse. Template:
<img class=thumb src=jobs/N/thumbnail> si has_thumbnail. 48x27 px,
object-fit cover.
Backfill manuel des jobs deja done (9, 12, 13, 16, 19) via scp direct.
This commit is contained in:
11
app/main.py
11
app/main.py
@@ -167,6 +167,7 @@ def _build_acquisitions():
|
||||
d["gp_label"] = gp_label.get((j["acquisition_id"], j["auv"], j["gopro_serial"]), "?")
|
||||
d["video_duration_fmt"] = _fmt_dur(int(j["video_duration_s"] or 0)) if (j["video_duration_s"] or 0) > 0 else "—"
|
||||
d["trimmed_total"] = (j["trimmed_head"] or 0) + (j["trimmed_tail"] or 0)
|
||||
d["has_thumbnail"] = (DB_PATH.parent / "thumbnails" / f"job_{j['id']}.jpg").exists()
|
||||
# Only expose a native viser link when port is listening. Probed on render via TCP check.
|
||||
d["native_viser_url"] = None # filled below
|
||||
by_acq.setdefault(j["acquisition_id"], []).append(d)
|
||||
@@ -392,6 +393,16 @@ async def view_job(job_id: int):
|
||||
return {"url": f"http://{worker['host']}:{port}"}
|
||||
|
||||
|
||||
@app.get("/jobs/{job_id}/thumbnail")
|
||||
async def job_thumbnail(job_id: int):
|
||||
"""Serve the cached thumbnail the dispatcher scp'd after trimming out-of-water frames."""
|
||||
from fastapi.responses import FileResponse
|
||||
thumb = DB_PATH.parent / "thumbnails" / f"job_{job_id}.jpg"
|
||||
if not thumb.exists():
|
||||
raise HTTPException(404, "no thumbnail yet")
|
||||
return FileResponse(thumb, media_type="image/jpeg")
|
||||
|
||||
|
||||
@app.post("/jobs/{job_id}/live")
|
||||
async def live_job(job_id: int):
|
||||
"""Return the URL of demo.py's native viser (PointCloudViewer with camera frustums,
|
||||
|
||||
Reference in New Issue
Block a user