deepinfra.py
Language Python2.9 KB68 lines
"""DeepInfra's hosted Whisper large-v3-turbo.The audio leaves the machine for this step only; alignment and speakerseparation still run locally."""from __future__ import annotationsimport jsonimport urllib.errorimport urllib.requestimport uuidfrom pathlib import PathAPI_URL = "https://api.deepinfra.com/v1/inference/openai/whisper-large-v3-turbo"MODEL_LABEL = "openai/whisper-large-v3-turbo (DeepInfra)"class EngineError(RuntimeError):passdef _multipart(fields: dict[str, str], file_field: str, path: Path) -> tuple[bytes, str]:boundary = uuid.uuid4().hexparts = []for k, v in fields.items():parts.append((f'--{boundary}\r\nContent-Disposition: form-data; name="{k}"\r\n\r\n'f'{v}\r\n').encode())parts.append((f'--{boundary}\r\nContent-Disposition: form-data; name="{file_field}"; 'f'filename="{path.name}"\r\n''Content-Type: application/octet-stream\r\n\r\n').encode())parts.append(path.read_bytes())parts.append(f"\r\n--{boundary}--\r\n".encode())return b"".join(parts), f"multipart/form-data; boundary={boundary}"def transcribe(audio_path: Path, token: str | None, language: str | None = None,api_url: str = API_URL, timeout: float = 900) -> dict:if not token:raise EngineError("No DeepInfra token: set DEEPINFRA_TOKEN or add it to the Keychain ""(see docs/SETUP.md).")fields = {"language": language} if language else {}body, ctype = _multipart(fields, "audio", audio_path)headers = {"Authorization": f"bearer {token}", "Content-Type": ctype}req = urllib.request.Request(api_url, data=body, method="POST", headers=headers)try:with urllib.request.urlopen(req, timeout=timeout) as res:data = json.loads(res.read().decode("utf-8"))except urllib.error.HTTPError as e:if e.code in (401, 403):raise EngineError("DeepInfra rejected the token: it has been revoked or rotated.") from eif e.code == 429:raise EngineError("DeepInfra is rate limiting, or the account is out of credit.") from eif e.code == 413:raise EngineError("The file is too large for DeepInfra.") from edetail = e.read().decode("utf-8", "replace")[:300]raise EngineError(f"DeepInfra error {e.code}: {detail}") from eexcept urllib.error.URLError as e:raise EngineError(f"Could not reach DeepInfra: {e.reason}") from esegments = [{"start": float(s.get("start") or 0), "end": float(s.get("end") or 0),"text": (s.get("text") or "").strip()}for s in (data.get("segments") or []) if (s.get("text") or "").strip()]if not segments and (data.get("text") or "").strip():segments = [{"start": 0.0, "end": 0.0, "text": data["text"].strip()}]return {"segments": segments, "language": data.get("language") or language,"model": MODEL_LABEL}