feat: sync live-count, exemplar annotation modules, and update .gitignore

This commit is contained in:
ervanfahriaw committed 2026-08-24 14:57:42 +07:00
1 parent b6624eeff9
commit ac95674c07
39 files changed
+3979 -444

No files matched your search

+30 -6
View File
@@ -15,9 +15,9 @@ router = APIRouter(tags=["live-count"])
class StartRequest(BaseModel):
# Either a raw source (RTSP URL or absolute path) or an archive-relative
# path like "2026-08-13/batch001.mp4", which the backend resolves — the
# frontend never needs to know where the archive is mounted.
# Either a live stream — which must be a WebRTC (WHEP) URL, REQ-176 — or an
# archive-relative path like "2026-08-13/batch001.mp4", which the backend
# resolves so the frontend never needs to know where the archive is mounted.
source: str = ""
source_rel: Optional[str] = None
model_path: Optional[str] = None
@@ -60,14 +60,22 @@ def available_models(project_id: int) -> dict:
def start(project_id: int, request: StartRequest) -> dict:
project = project_or_404(project_id)
source = request.source
source, whep = request.source, ""
if request.source_rel:
try:
source = library.resolve(project["video_root"], request.source_rel)
except library.LibraryError as exc:
raise HTTPException(400, str(exc))
elif source:
# A live source is a WebRTC URL and nothing else. The browser watches it
# over WebRTC; the counter decodes the RTSP leg of the same MediaMTX
# path, derived here (REQ-176).
try:
whep, source = live_count.whep_url(source), live_count.whep_to_rtsp(source)
except live_count.LiveCountError as exc:
raise HTTPException(400, str(exc))
if not source:
raise HTTPException(400, "Pick a video or enter a stream URL")
raise HTTPException(400, "Pick a video or enter a WebRTC stream URL")
path = request.model_path
if not path and request.model_version_id is not None:
@@ -88,6 +96,7 @@ def start(project_id: int, request: StartRequest) -> dict:
unload_confirm_frames=request.unload_confirm_frames,
min_area_scale=request.min_area_scale,
spatial_dedup=request.spatial_dedup,
whep=whep,
)
except live_count.LiveCountError as exc:
raise HTTPException(400, str(exc))
@@ -118,9 +127,24 @@ def status() -> dict:
return live_count.status()
@router.get("/api/live-count/overlay")
def overlay() -> dict:
"""Geometry only — boxes, line and counts for the frame just processed.
What the WebRTC preview draws over the video, instead of the server
encoding a JPEG per frame for it (REQ-177).
"""
return live_count.overlay()
@router.get("/api/live-count/stream")
def stream():
"""MJPEG of the annotated frames. Ends when the session does."""
"""MJPEG of the annotated frames — the archive-file preview. Ends with the session."""
if live_count.status().get("preview") == "webrtc":
# Nothing is encoding JPEGs for this session; without this the generator
# would sit on a worker thread for ten seconds producing nothing.
raise HTTPException(409, "This session is watched over WebRTC, not MJPEG")
def frames():
blank_streak = 0
while True: