import sqlite3 from typing import Optional from fastapi import APIRouter, HTTPException from fastapi.responses import FileResponse from pydantic import BaseModel class TagRequest(BaseModel): speaker_id: Optional[int] = None name: Optional[str] = None create_new: bool = False def make_router(settings, db_factory): router = APIRouter(prefix="/api/utterances") @router.get("") def list_utterances( date: Optional[str] = None, room_id: Optional[int] = None, speaker_id: Optional[int] = None, match_status: Optional[str] = None, ): db: sqlite3.Connection = db_factory() query = """ SELECT u.*, r.name as room_name, s.name as speaker_name FROM utterances u JOIN rooms r ON u.room_id = r.id LEFT JOIN speakers s ON u.speaker_id = s.id WHERE 1=1 """ params: list = [] if date: query += " AND date(u.start_time) = ?" params.append(date) if room_id: query += " AND u.room_id = ?" params.append(room_id) if speaker_id: query += " AND u.speaker_id = ?" params.append(speaker_id) if match_status: query += " AND u.match_status = ?" params.append(match_status) query += " ORDER BY u.start_time DESC LIMIT 200" rows = db.execute(query, params).fetchall() return [dict(r) for r in rows] @router.get("/{utterance_id}/clip") def get_clip(utterance_id: int): db: sqlite3.Connection = db_factory() row = db.execute( "SELECT audio_clip_path FROM utterances WHERE id=?", (utterance_id,) ).fetchone() if not row or not row["audio_clip_path"]: raise HTTPException(404, "Clip not found or expired") return FileResponse(row["audio_clip_path"], media_type="audio/wav") @router.post("/{utterance_id}/tag") def tag_utterance(utterance_id: int, body: TagRequest): db: sqlite3.Connection = db_factory() row = db.execute("SELECT * FROM utterances WHERE id=?", (utterance_id,)).fetchone() if not row: raise HTTPException(404, "Utterance not found") speaker_id = body.speaker_id if body.create_new and body.name: cur = db.execute("INSERT INTO speakers (name) VALUES (?)", (body.name,)) db.commit() speaker_id = cur.lastrowid if speaker_id is None: raise HTTPException(400, "Provide speaker_id or name+create_new") db.execute( "UPDATE utterances SET speaker_id=?, match_status='known', match_confidence=1.0 WHERE id=?", (speaker_id, utterance_id), ) db.commit() # Store utterance embedding as a reference sample for this speaker if row["embedding"]: db.execute( "INSERT INTO voice_embeddings (speaker_id, embedding) VALUES (?,?)", (speaker_id, row["embedding"]), ) db.commit() return {"ok": True, "speaker_id": speaker_id} @router.post("/{utterance_id}/dismiss") def dismiss_utterance(utterance_id: int): db: sqlite3.Connection = db_factory() db.execute( "UPDATE utterances SET match_status='unknown' WHERE id=?", (utterance_id,) ) db.commit() return {"ok": True} return router