| from fastapi import FastAPI, HTTPException |
| from fastapi.responses import FileResponse |
| from fastapi.staticfiles import StaticFiles |
| from pydantic import BaseModel |
| import json |
| import time |
| from pathlib import Path |
| from engine import BrowserFrameAnalyzer |
|
|
| app = FastAPI() |
| BASE_DIR = Path(__file__).resolve().parent |
| app.mount("/static", StaticFiles(directory=str(BASE_DIR)), name="static") |
|
|
| browser_analyzers: dict = {} |
| SESSION_TTL_SECONDS = 30 * 60 |
|
|
|
|
| class BrowserFrameInput(BaseModel): |
| session_id: str |
| image: str |
| label: str = "Browser Camera" |
|
|
|
|
| @app.get("/") |
| def home(): |
| return FileResponse(BASE_DIR / "templates" / "browser_camera.html") |
|
|
|
|
| @app.get("/browser-camera") |
| def browser_camera_page(): |
| return FileResponse(BASE_DIR / "templates" / "browser_camera.html") |
|
|
|
|
| @app.get("/alerts") |
| def alerts(): |
| data = [] |
| try: |
| with open("alerts.jsonl") as f: |
| for line in f: |
| data.append(json.loads(line)) |
| except Exception: |
| pass |
| return data |
|
|
|
|
| @app.post("/predict-frame") |
| def predict_frame(payload: BrowserFrameInput): |
| session_id = payload.session_id.strip() |
| if not session_id: |
| raise HTTPException(status_code=400, detail="session_id is required") |
|
|
| now = time.time() |
| expired = [ |
| key for key, item in browser_analyzers.items() |
| if now - item["last_seen"] > SESSION_TTL_SECONDS |
| ] |
| for key in expired: |
| browser_analyzers.pop(key, None) |
|
|
| item = browser_analyzers.get(session_id) |
| if item is None: |
| item = { |
| "analyzer": BrowserFrameAnalyzer(payload.label.strip() or "Browser Camera"), |
| "last_seen": now, |
| } |
| browser_analyzers[session_id] = item |
| item["last_seen"] = now |
|
|
| try: |
| result = item["analyzer"].analyze_data_url(payload.image) |
| except Exception as exc: |
| raise HTTPException(status_code=400, detail=str(exc)) from exc |
|
|
| return result |
|
|