# -*- coding: utf-8 -*-
|
import logging
|
import os
|
import subprocess
|
import tempfile
|
from typing import List, Optional, Tuple
|
|
from app.schemas import AsrHit, KeywordConfig
|
from app.video_io import get_ffmpeg_cmd
|
|
logger = logging.getLogger(__name__)
|
|
_whisper_model = None
|
|
|
def _get_whisper():
|
global _whisper_model
|
if _whisper_model is None:
|
from faster_whisper import WhisperModel
|
model_size = os.environ.get("WHISPER_MODEL", "tiny")
|
_whisper_model = WhisperModel(model_size, device="cpu", compute_type="int8")
|
logger.info("加载 Whisper 模型: %s", model_size)
|
return _whisper_model
|
|
|
def extract_audio_wav(video_path: str) -> str:
|
out = tempfile.NamedTemporaryFile(suffix=".wav", delete=False)
|
out.close()
|
cmd = [
|
get_ffmpeg_cmd("ffmpeg"), "-y", "-i", video_path,
|
"-vn", "-acodec", "pcm_s16le", "-ar", "16000", "-ac", "1",
|
out.name,
|
]
|
subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
return out.name
|
|
|
def transcribe(video_path: str) -> List[Tuple[str, float, float]]:
|
wav = extract_audio_wav(video_path)
|
try:
|
model = _get_whisper()
|
segments, _ = model.transcribe(wav, language="zh", vad_filter=True)
|
return [(seg.text.strip(), seg.start, seg.end) for seg in segments if seg.text.strip()]
|
except Exception as e:
|
logger.warning("ASR 失败: %s", e)
|
return []
|
finally:
|
if os.path.isfile(wav):
|
os.remove(wav)
|
|
|
def match_keywords(
|
segments: List[Tuple[str, float, float]],
|
keywords: KeywordConfig,
|
) -> List[AsrHit]:
|
hits: List[AsrHit] = []
|
for text, start, _end in segments:
|
for kw in keywords.storefront:
|
if kw in text:
|
hits.append(AsrHit(keyword=kw, time_sec=round(start, 2)))
|
break
|
for kw in keywords.handover:
|
if kw in text:
|
hits.append(AsrHit(keyword=kw, time_sec=round(start, 2)))
|
break
|
return hits
|
|
|
def best_asr_time(hits: List[AsrHit], keywords: List[str]) -> Optional[float]:
|
for hit in hits:
|
if hit.keyword in keywords:
|
return hit.time_sec
|
return None
|
|
|
def asr_available() -> bool:
|
try:
|
import faster_whisper # noqa: F401
|
return True
|
except ImportError:
|
return False
|