from __future__ import annotations import asyncio from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.staticfiles import StaticFiles from app.routers import biomass, ingest from app.services.action_watch import run_action_watch_loop from app.settings import get_settings @asynccontextmanager async def lifespan(app: FastAPI): s = get_settings() s.media_root.mkdir(parents=True, exist_ok=True) s.stream_tmp_dir.mkdir(parents=True, exist_ok=True) watch_task: asyncio.Task[None] | None = None if s.action_watch_dir is not None: watch_task = asyncio.create_task(run_action_watch_loop(s)) yield if watch_task is not None: watch_task.cancel() try: await watch_task except asyncio.CancelledError: pass app = FastAPI(title="Fish API", lifespan=lifespan) app.include_router(ingest.router) app.include_router(biomass.router) _settings = get_settings() _settings.media_root.mkdir(parents=True, exist_ok=True) app.mount( "/media", StaticFiles(directory=str(_settings.media_root)), name="media", ) @app.get("/") async def root(): return { "service": "fish-api", "docs": "/docs", "ingest": "/api/v1/ingest/", "biomass_camera": "/api/v1/biomass/real/camera/", "biomass_health": "/api/v1/biomass/health/result/", }