"""ASR 封装：faster-whisper 转写，带词级时间戳。

faster-whisper 内部用 PyAV 直接解视频抽音频，无需系统 ffmpeg。
"""
import av
from faster_whisper import WhisperModel


def _has_audio(path):
    """检查输入是否含音频流，给出友好报错而非 PyAV 底层异常。"""
    try:
        with av.open(path) as c:
            return any(s.type == "audio" for s in c.streams)
    except Exception:
        return True  # 无法判断时不阻断，交给 faster-whisper 报错


def transcribe(video_path, model_size="small", device="cpu",
               compute_type="int8", language="zh", beam_size=5,
               vad_min_silence_ms=500):
    """转写视频/音频，返回 (segments, info)。

    segments: list[dict]，每个含 start/end/text/words/avg_logprob/no_speech_prob
    words:    list[dict]，每个含 word/start/end（词级时间戳，字幕对齐的关键）
    info:     语言、置信度、音频总时长
    """
    if not _has_audio(video_path):
        raise ValueError(f"输入文件没有音频流：{video_path}")

    model = WhisperModel(model_size, device=device, compute_type=compute_type)

    segments_iter, info = model.transcribe(
        video_path,
        language=language,
        task="transcribe",
        beam_size=beam_size,
        word_timestamps=True,           # 词级时间戳
        vad_filter=True,                # Silero VAD 过滤静音
        vad_parameters=dict(min_silence_duration_ms=vad_min_silence_ms),
    )

    segments = []
    for seg in segments_iter:
        segments.append({
            "start": round(seg.start, 3),
            "end": round(seg.end, 3),
            "text": seg.text.strip(),
            "words": [
                {
                    "word": w.word,
                    "start": round(w.start, 3),
                    "end": round(w.end, 3),
                }
                for w in (seg.words or [])
            ],
            "avg_logprob": round(seg.avg_logprob, 3) if seg.avg_logprob is not None else None,
            "no_speech_prob": round(seg.no_speech_prob, 3) if seg.no_speech_prob is not None else None,
        })

    meta = {
        "language": info.language,
        "language_probability": round(info.language_probability, 4),
        "duration": round(info.duration, 3),
        "duration_after_vad": round(info.duration_after_vad, 3),
    }
    return segments, meta
