Files
JuYou/API/workers/transcribe.py
T
2026-08-25 17:59:42 +08:00

87 lines
3.3 KiB
Python

"""CPU-friendly faster-whisper entry point used by the Go media worker.
The process writes JSON only to stdout. Model/runtime diagnostics go to stderr so
the Go caller never mistakes logs for transcript data.
"""
from __future__ import annotations
import json
import os
import sys
import tempfile
def configure_model_cache() -> None:
cache_root = os.getenv("JCF_ASR_CACHE_DIR") or os.path.join(tempfile.gettempdir(), "jcf-asr-cache")
os.makedirs(cache_root, mode=0o700, exist_ok=True)
hf_home = os.getenv("HF_HOME") or os.path.join(cache_root, "huggingface")
os.makedirs(hf_home, mode=0o700, exist_ok=True)
os.environ.setdefault("XDG_CACHE_HOME", cache_root)
os.environ.setdefault("HF_HOME", hf_home)
def main() -> int:
if len(sys.argv) < 2:
print(json.dumps({"error": "missing_audio_path", "segments": []}, ensure_ascii=False))
return 2
configure_model_cache()
from faster_whisper import WhisperModel
audio_path = sys.argv[1]
language = (sys.argv[2] if len(sys.argv) > 2 else "").strip() or None
model_name = os.getenv("FASTER_WHISPER_MODEL_PATH") or os.getenv("FASTER_WHISPER_MODEL_SIZE", "small")
device = os.getenv("FASTER_WHISPER_DEVICE", "cpu")
compute_type = os.getenv("FASTER_WHISPER_COMPUTE_TYPE", "int8")
model = WhisperModel(model_name, device=device, compute_type=compute_type)
segments, info = model.transcribe(
audio_path,
language=language,
beam_size=5,
best_of=5,
temperature=0.0,
condition_on_previous_text=True,
vad_filter=True,
vad_parameters={"min_silence_duration_ms": 700},
word_timestamps=True,
)
result = []
for segment in segments:
text = (segment.text or "").strip()
if not text:
continue
words = []
probabilities = []
for word in segment.words or []:
if word.start is None or word.end is None or not word.word:
continue
probability = float(word.probability) if word.probability is not None else None
if probability is not None:
probabilities.append(probability)
words.append({
"start": round(float(word.start), 3),
"end": round(float(word.end), 3),
"text": str(word.word),
"probability": round(probability, 4) if probability is not None else None,
})
mean_probability = sum(probabilities) / len(probabilities) if probabilities else None
avg_logprob = float(getattr(segment, "avg_logprob", -1.0) or -1.0)
no_speech = float(getattr(segment, "no_speech_prob", 0.0) or 0.0)
confidence = max(0.0, min(1.0, (1.0 + avg_logprob) * 0.45 + (1.0 - no_speech) * 0.2 + (mean_probability if mean_probability is not None else max(0.0, 1.0 + avg_logprob)) * 0.35))
result.append({
"start": round(float(segment.start), 3),
"end": round(float(segment.end), 3),
"text": text,
"words": words,
"confidence": round(confidence, 3),
"needs_review": confidence < 0.62 or (mean_probability is not None and mean_probability < 0.55),
})
print(json.dumps({"language": getattr(info, "language", language), "segments": result}, ensure_ascii=False))
return 0
if __name__ == "__main__":
raise SystemExit(main())