import os from pathlib import Path from typing import Any, Dict from fastapi import Depends, FastAPI, HTTPException, Path as ApiPath from .models import SentenceBoundaryDocument from .store import BoundaryStore DEFAULT_INDEX_PATH = Path(__file__).resolve().parent / "data" / "sentence_boundaries.json" def create_app(store: BoundaryStore = None) -> FastAPI: application = FastAPI( title="Oral Trainer Sentence Boundary API", version="1.0.0", description="Looks up pre-generated sentence boundaries by video SHA-256.", ) index_path = Path(os.getenv("SENTENCE_BOUNDARIES_FILE", str(DEFAULT_INDEX_PATH))) application.state.boundary_store = store or BoundaryStore(index_path) def get_store() -> BoundaryStore: return application.state.boundary_store @application.get("/healthz") def healthz(boundary_store: BoundaryStore = Depends(get_store)) -> Dict[str, Any]: return {"status": "ok", "video_count": boundary_store.count()} @application.get( "/api/v1/videos/{video_hash}/sentence-boundaries", response_model=SentenceBoundaryDocument, ) @application.get( "/api/v1/sentence-boundaries/{video_hash}", response_model=SentenceBoundaryDocument, include_in_schema=False, ) def get_sentence_boundaries( video_hash: str = ApiPath( min_length=64, max_length=64, pattern=r"^[A-Fa-f0-9]{64}$", description="SHA-256 hex digest of the exact video bytes", ), boundary_store: BoundaryStore = Depends(get_store), ) -> SentenceBoundaryDocument: document = boundary_store.get(video_hash) if document is None: raise HTTPException( status_code=404, detail={ "code": "SENTENCE_BOUNDARIES_NOT_FOUND", "message": "No sentence boundaries are registered for this video hash.", }, ) return document return application app = create_app()