chrysopedia/backend/routers/videos.py
jlightner dbc4afcf42 feat: Normalized /topics and /videos endpoints from bare lists to pagin…
- "backend/schemas.py"
- "backend/routers/topics.py"
- "backend/routers/videos.py"
- "frontend/src/api/topics.ts"
- "frontend/src/pages/TopicsBrowse.tsx"
- "frontend/src/pages/Home.tsx"

GSD-Task: S05/T03
2026-04-03 23:09:33 +00:00

46 lines
1.5 KiB
Python

"""Source video endpoints for Chrysopedia API."""
import logging
from typing import Annotated
from fastapi import APIRouter, Depends, Query
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession
from database import get_session
from models import SourceVideo
from schemas import SourceVideoRead, VideoListResponse
logger = logging.getLogger("chrysopedia.videos")
router = APIRouter(prefix="/videos", tags=["videos"])
@router.get("", response_model=VideoListResponse)
async def list_videos(
offset: Annotated[int, Query(ge=0)] = 0,
limit: Annotated[int, Query(ge=1, le=100)] = 50,
creator_id: str | None = None,
db: AsyncSession = Depends(get_session),
) -> VideoListResponse:
"""List source videos with optional filtering by creator."""
base_stmt = select(SourceVideo).order_by(SourceVideo.created_at.desc())
if creator_id:
base_stmt = base_stmt.where(SourceVideo.creator_id == creator_id)
# Total count (before offset/limit)
count_stmt = select(func.count()).select_from(base_stmt.subquery())
count_result = await db.execute(count_stmt)
total = count_result.scalar() or 0
stmt = base_stmt.offset(offset).limit(limit)
result = await db.execute(stmt)
videos = result.scalars().all()
logger.debug("Listed %d videos (offset=%d, limit=%d)", len(videos), offset, limit)
return VideoListResponse(
items=[SourceVideoRead.model_validate(v) for v in videos],
total=total,
offset=offset,
limit=limit,
)