"""오디오 전사(STT) 조회 API — /api/audio AudioPlayer 가 줄 단위로 렌더하고 클릭 시 audio.currentTime 으로 점프한다. """ from typing import Annotated from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from core.auth import get_current_user from core.database import get_session from models.audio_segment import AudioSegment from models.document import Document from models.user import User router = APIRouter() class AudioSegmentResponse(BaseModel): start: float end: float text: str model_config = {"from_attributes": True} class AudioSegmentsResponse(BaseModel): document_id: int language: str | None duration: float | None segments: list[AudioSegmentResponse] @router.get("/{doc_id}/segments", response_model=AudioSegmentsResponse) async def get_audio_segments( doc_id: int, user: Annotated[User, Depends(get_current_user)], session: Annotated[AsyncSession, Depends(get_session)], ): """audio 문서의 전사 세그먼트 조회. category='audio' 가 아닌 문서는 404. 세그먼트가 아직 없는 경우 빈 배열 반환. language / duration 은 현재 ORM 에 별도 컬럼이 없어 None (필요 시 후속 확장). """ doc = await session.get(Document, doc_id) if not doc or doc.deleted_at is not None: raise HTTPException(status_code=404, detail="문서를 찾을 수 없습니다") if getattr(doc, "category", None) != "audio": raise HTTPException(status_code=404, detail="오디오 문서가 아닙니다") result = await session.execute( select(AudioSegment) .where(AudioSegment.document_id == doc_id) .order_by(AudioSegment.start_s.asc()) ) rows = result.scalars().all() segments = [ AudioSegmentResponse(start=r.start_s, end=r.end_s, text=r.text) for r in rows ] return AudioSegmentsResponse( document_id=doc_id, language=None, duration=None, segments=segments, )