Refactor code structure for improved readability and maintainability
This commit is contained in:
+85
@@ -1,9 +1,11 @@
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import sqlite3
|
||||
import subprocess
|
||||
import tempfile
|
||||
import time
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
@@ -18,6 +20,10 @@ AUDIO_DIR = DATA_DIR / "audio"
|
||||
DB_PATH = DATA_DIR / "faerro-kb.sqlite3"
|
||||
WHISPER_SERVER_URL = os.environ.get("WHISPER_SERVER_URL", "").strip()
|
||||
FRONTEND_DIR = Path(__file__).resolve().parent.parent / "frontend"
|
||||
WHISPER_TEST_AUDIO_PATH = Path(__file__).resolve().parent.parent / "audio test.wav"
|
||||
TRANSCRIPTION_TASKS: set[asyncio.Task[None]] = set()
|
||||
logger = logging.getLogger(__name__)
|
||||
logging.getLogger("httpx").setLevel(logging.WARNING)
|
||||
|
||||
DATA_DIR.mkdir(parents=True, exist_ok=True)
|
||||
AUDIO_DIR.mkdir(parents=True, exist_ok=True)
|
||||
@@ -120,6 +126,85 @@ async def transcribe_note(note_id: str, audio_path: Path) -> None:
|
||||
remove_transcribed_audio(note_id)
|
||||
|
||||
|
||||
async def test_whisper_connection() -> bool:
|
||||
if not WHISPER_SERVER_URL:
|
||||
logger.warning("Whisper startup test skipped: WHISPER_SERVER_URL is not configured")
|
||||
return False
|
||||
if not WHISPER_TEST_AUDIO_PATH.is_file():
|
||||
logger.error("Whisper startup test failed: bundled test audio is missing")
|
||||
return False
|
||||
|
||||
started_at = time.monotonic()
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=httpx.Timeout(120, connect=5)) as client:
|
||||
with WHISPER_TEST_AUDIO_PATH.open("rb") as audio_file:
|
||||
response = await client.post(
|
||||
WHISPER_SERVER_URL,
|
||||
params={"encode": "true", "task": "transcribe", "output": "json"},
|
||||
files={"audio_file": (WHISPER_TEST_AUDIO_PATH.name, audio_file, "audio/wav")},
|
||||
)
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
transcript = result.get("text") if isinstance(result, dict) else None
|
||||
if not isinstance(transcript, str) or not transcript.strip():
|
||||
logger.error(
|
||||
"Whisper startup test failed: HTTP %s returned no transcript (%.1fs)",
|
||||
response.status_code,
|
||||
time.monotonic() - started_at,
|
||||
)
|
||||
return False
|
||||
language = result.get("language", "unknown")
|
||||
if not isinstance(language, str) or not re.fullmatch(r"[A-Za-z-]{1,12}", language):
|
||||
language = "unknown"
|
||||
segments = result.get("segments", [])
|
||||
segment_count = len(segments) if isinstance(segments, list) else 0
|
||||
logger.info(
|
||||
"Whisper startup test passed: HTTP %s, language=%s, segments=%d, elapsed=%.1fs",
|
||||
response.status_code,
|
||||
language,
|
||||
segment_count,
|
||||
time.monotonic() - started_at,
|
||||
)
|
||||
return True
|
||||
except httpx.HTTPStatusError as error:
|
||||
logger.error("Whisper startup test failed: HTTP %s", error.response.status_code)
|
||||
except Exception as error:
|
||||
logger.error("Whisper startup test failed: %s", type(error).__name__)
|
||||
return False
|
||||
|
||||
|
||||
@app.on_event("startup")
|
||||
async def resume_pending_transcriptions() -> None:
|
||||
with connect_db() as connection:
|
||||
connection.execute(
|
||||
"UPDATE notes SET status = 'awaiting_transcription' WHERE status = 'transcribing'"
|
||||
)
|
||||
if not await test_whisper_connection():
|
||||
return
|
||||
with connect_db() as connection:
|
||||
notes = connection.execute(
|
||||
"""SELECT id, audio_path FROM notes
|
||||
WHERE status IN ('awaiting_transcription', 'transcribing', 'transcription_failed')"""
|
||||
).fetchall()
|
||||
for note in notes:
|
||||
audio_path = Path(note["audio_path"])
|
||||
if audio_path.is_file():
|
||||
with connect_db() as connection:
|
||||
connection.execute(
|
||||
"UPDATE notes SET status = 'transcribing' WHERE id = ?",
|
||||
(note["id"],),
|
||||
)
|
||||
task = asyncio.create_task(transcribe_note(note["id"], audio_path))
|
||||
TRANSCRIPTION_TASKS.add(task)
|
||||
task.add_done_callback(TRANSCRIPTION_TASKS.discard)
|
||||
else:
|
||||
with connect_db() as connection:
|
||||
connection.execute(
|
||||
"UPDATE notes SET status = 'transcription_failed' WHERE id = ?",
|
||||
(note["id"],),
|
||||
)
|
||||
|
||||
|
||||
@app.get("/api/health")
|
||||
def health() -> dict[str, str]:
|
||||
return {"status": "ok"}
|
||||
|
||||
Reference in New Issue
Block a user