Claude 5e2094f2ad
feat(media): local inference sidecar for Media Studio
FastAPI service in infra/media-worker implementing every
MediaInferenceProvider operation behind HTTP API v1, plus capabilities.
Bearer auth, MEDIA_ROOT realpath enforcement, strict request schemas,
cancellation on disconnect, GPU/CPU gates with 503 overflow, input
size/duration limits, lazy SHA256-pinned model downloads, fail-closed
redaction with atomic output, offline pytest suite with fake backends,
CPU/CUDA/ROCm Dockerfile and a license review of every model.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0138YqDj3ttfayRwuMKKrqVj
2026-10-03 18:19:47 +00:00

58 lines
2.1 KiB
Python

"""transcribe: faster-whisper with word timestamps."""
from __future__ import annotations
import os
import threading
from typing import Any
from ..errors import BadRequest, Cancelled
from ..schemas import TranscribeRequest
from .common import Ctx, clamp01, open_input
from .devices import ctranslate2_device
class WhisperTranscriber:
def __init__(self, model_dir: str, settings: Any) -> None:
from faster_whisper import WhisperModel
device = ctranslate2_device(settings)
self.model = WhisperModel(
model_dir,
device=device,
compute_type="float16" if device == "cuda" else "int8",
)
def transcribe(
self, path: str, language: str | None, cancel: threading.Event
) -> tuple[str | None, list[dict[str, Any]]]:
segments, info = self.model.transcribe(path, language=language, word_timestamps=True, vad_filter=True)
words: list[dict[str, Any]] = []
for segment in segments:
if cancel.is_set():
raise Cancelled()
for word in segment.words or []:
start = int(round(word.start * 1000))
words.append(
{
"startMs": start,
"endMs": max(int(round(word.end * 1000)), start),
"text": word.word.strip(),
"confidence": clamp01(word.probability),
}
)
return info.language, words
def load_transcriber(files: dict[str, str], settings: Any) -> WhisperTranscriber:
return WhisperTranscriber(os.path.dirname(files["model.bin"]), settings)
def run(ctx: Ctx, body: TranscribeRequest, cancel: threading.Event) -> dict[str, Any]:
real, info = open_input(ctx, body.path, cancel, need_video=False)
if not info.has_audio:
raise BadRequest("input has no audio stream")
transcriber = ctx.registry.runtime("transcribe")
language, words = transcriber.transcribe(real, body.language, cancel)
return {"language": language, "words": [w for w in words if w["text"]]}