from typing import Annotated import asyncpg from dishka.integrations.fastapi import DishkaRoute, FromDishka from fastapi import APIRouter, HTTPException, Query from fastapi.responses import FileResponse from utils.read import peers from utils.read.models import DEFAULT_LIMIT, Page, StoryView from utils.storage import ContentAddressedStorage router = APIRouter(prefix="/api", tags=["stories"], route_class=DishkaRoute) AccountId = Annotated[int, Query()] _STORY_MIME = {"photo": "image/jpeg", "video": "video/mp4"} @router.get("/stories") async def list_stories( pool: FromDishka[asyncpg.Pool], account_id: AccountId, peer_id: Annotated[int | None, Query()] = None, limit: Annotated[int, Query()] = DEFAULT_LIMIT, offset: Annotated[int, Query()] = 0, ) -> list[StoryView]: return await peers.get_stories( pool, account_id, Page(limit=limit, offset=offset), peer_id=peer_id ) @router.get("/stories/{peer_id}/{story_id}/media") async def serve_story_media( pool: FromDishka[asyncpg.Pool], storage: FromDishka[ContentAddressedStorage], peer_id: int, story_id: int, account_id: AccountId, ) -> FileResponse: story = await peers.get_story(pool, account_id, peer_id, story_id) if story is None: raise HTTPException(status_code=404, detail="story not found") if not story.downloaded or story.storage_key is None: raise HTTPException(status_code=409, detail="story media not downloaded") return FileResponse( storage.url(story.storage_key), media_type=_STORY_MIME.get(story.media_kind or "", "application/octet-stream"), )