# SPDX-License-Identifier: GPL-3.0-only """Tiny persistence for the web app: recordings in SQLite, audio on disk. Deliberately dependency-free (sqlite3 is stdlib) so the demo runs anywhere. Not meant for high concurrency; the hosted service uses Postgres + object storage (see docs/). """ from __future__ import annotations import json import os import sqlite3 import time import uuid from typing import Any from .config import settings _DB = os.path.join(settings.local_media_dir, "nightjar.db") def _conn() -> sqlite3.Connection: os.makedirs(settings.local_media_dir, exist_ok=True) c = sqlite3.connect(_DB) c.row_factory = sqlite3.Row return c def init() -> None: with _conn() as c: c.execute( """CREATE TABLE IF NOT EXISTS recordings ( id TEXT PRIMARY KEY, title TEXT, filename TEXT, mime TEXT, created_at REAL, status TEXT, -- queued|transcribing|summarising|done|error error TEXT, language TEXT, transcript TEXT, -- JSON: {text, segments} summary TEXT -- JSON: {overview, key_points, action_items} )""" ) def audio_path(rec_id: str) -> str: return os.path.join(settings.local_media_dir, f"{rec_id}.audio") def create(title: str, filename: str, mime: str) -> str: rec_id = "rec_" + uuid.uuid4().hex[:12] with _conn() as c: c.execute( "INSERT INTO recordings (id,title,filename,mime,created_at,status) " "VALUES (?,?,?,?,?,?)", (rec_id, title, filename, mime, time.time(), "queued"), ) return rec_id def set_status(rec_id: str, status: str, error: str | None = None) -> None: with _conn() as c: c.execute("UPDATE recordings SET status=?, error=? WHERE id=?", (status, error, rec_id)) def set_result(rec_id: str, language: str, transcript: dict, summary: dict) -> None: with _conn() as c: c.execute( "UPDATE recordings SET status='done', language=?, transcript=?, summary=? WHERE id=?", (language, json.dumps(transcript), json.dumps(summary), rec_id), ) def _row(r: sqlite3.Row) -> dict[str, Any]: return { "id": r["id"], "title": r["title"], "created_at": r["created_at"], "status": r["status"], "error": r["error"], "language": r["language"], "mime": r["mime"], "transcript": json.loads(r["transcript"]) if r["transcript"] else None, "summary": json.loads(r["summary"]) if r["summary"] else None, } def get(rec_id: str) -> dict | None: with _conn() as c: r = c.execute("SELECT * FROM recordings WHERE id=?", (rec_id,)).fetchone() return _row(r) if r else None def list_all() -> list[dict]: with _conn() as c: rows = c.execute("SELECT * FROM recordings ORDER BY created_at DESC").fetchall() return [_row(r) for r in rows] def delete(rec_id: str) -> bool: with _conn() as c: cur = c.execute("DELETE FROM recordings WHERE id=?", (rec_id,)) try: os.remove(audio_path(rec_id)) except OSError: pass return cur.rowcount > 0