feat: sync live-count, exemplar annotation modules, and update .gitignore
This commit is contained in:
1 parent
b6624eeff9
commit
ac95674c07
39 files changed
+3979
-444
No files matched your search
@@ -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:
|
||||
|
||||
Reference in new issue
Block a user