import uuid from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException from sqlalchemy.ext.asyncio import AsyncSession from sqlmodel import select from ..db import get_session from ..models import Meeting, MeetingStatus, TranscriptSegment, Summary from ..schemas import ( MeetingRead, UploadCompleteRequest, UploadUrlRequest, UploadUrlResponse, ) from ..services.storage import presign_put from ..services.transcribe import process_meeting router = APIRouter(prefix="/api", tags=["meetings"]) @router.post("/upload-url", response_model=UploadUrlResponse) async def request_upload_url( body: UploadUrlRequest, session: AsyncSession = Depends(get_session) ): object_key = f"audio/{uuid.uuid4().hex}-{body.filename}" meeting = Meeting( title=body.title or body.filename, participants=body.participants, object_key=object_key, status=MeetingStatus.uploading, ) session.add(meeting) await session.commit() await session.refresh(meeting) upload_url = presign_put(object_key, body.content_type) return UploadUrlResponse( meeting_id=meeting.id, upload_url=upload_url, object_key=object_key, ) @router.post("/upload-complete") async def upload_complete( body: UploadCompleteRequest, background: BackgroundTasks, session: AsyncSession = Depends(get_session), ): meeting = await session.get(Meeting, body.meeting_id) if not meeting: raise HTTPException(404, "meeting not found") meeting.file_size = body.file_size meeting.status = MeetingStatus.uploaded await session.commit() background.add_task(process_meeting, meeting.id) return {"ok": True, "meeting_id": meeting.id} @router.get("/meetings", response_model=list[MeetingRead]) async def list_meetings(session: AsyncSession = Depends(get_session)): result = await session.execute(select(Meeting).order_by(Meeting.created_at.desc())) return result.scalars().all() @router.get("/meetings/{meeting_id}", response_model=MeetingRead) async def get_meeting(meeting_id: int, session: AsyncSession = Depends(get_session)): meeting = await session.get(Meeting, meeting_id) if not meeting: raise HTTPException(404, "meeting not found") return meeting @router.get("/meetings/{meeting_id}/transcript") async def get_transcript(meeting_id: int, session: AsyncSession = Depends(get_session)): result = await session.execute( select(TranscriptSegment) .where(TranscriptSegment.meeting_id == meeting_id) .order_by(TranscriptSegment.start) ) segments = result.scalars().all() return [ {"start": s.start, "end": s.end, "speaker": s.speaker, "text": s.text} for s in segments ] @router.get("/meetings/{meeting_id}/summary") async def get_summary(meeting_id: int, session: AsyncSession = Depends(get_session)): result = await session.execute( select(Summary).where(Summary.meeting_id == meeting_id) ) summary = result.scalar_one_or_none() if not summary: return None return { "key_points": summary.key_points, "todos": summary.todos, "decisions": summary.decisions, "keywords": summary.keywords, "preview": summary.preview, } @router.delete("/meetings/{meeting_id}") async def delete_meeting(meeting_id: int, session: AsyncSession = Depends(get_session)): meeting = await session.get(Meeting, meeting_id) if not meeting: raise HTTPException(404, "meeting not found") await session.delete(meeting) await session.commit() return {"ok": True}