1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| """whisper_asr.py — faster-whisper 命令行转写工具.
用法: whisper <媒体文件> [-m 模型] [-l 语言] [-o 输出] [--plain] """ from __future__ import annotations
import argparse import os import subprocess import sys import tempfile import wave from pathlib import Path
os.environ.setdefault("HF_ENDPOINT", "https://hf-mirror.com")
import numpy as np from faster_whisper import WhisperModel
def extract_audio(media: Path) -> np.ndarray: """用 ffmpeg 将任意媒体解码为 16kHz 单声道 float32 数组.""" with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmp: tmp_path = tmp.name try: cmd = [ "ffmpeg", "-hide_banner", "-loglevel", "error", "-y", "-i", str(media), "-vn", "-ac", "1", "-ar", "16000", tmp_path, ] result = subprocess.run(cmd, capture_output=True, text=True) if result.returncode != 0: sys.exit(f"ffmpeg 解码失败:\n{result.stderr.strip()}") with wave.open(tmp_path, "rb") as w: frames = np.frombuffer(w.readframes(w.getnframes()), dtype=np.int16) return frames.astype(np.float32) / 32768.0 finally: os.unlink(tmp_path)
def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser( prog="whisper", description="通过 faster-whisper 将音视频文件转写为文案", ) parser.add_argument("media", type=Path, help="待转写的音频/视频文件路径") parser.add_argument("-m", "--model", default="small", help="模型名称: tiny/base/small/medium/large-v3 (默认: small)") parser.add_argument("-l", "--language", default=None, help="语言代码, 如 zh/en (默认: 自动检测)") parser.add_argument("-o", "--out", type=Path, default=None, help="输出文件路径 (默认: 与输入同名的 .txt)") parser.add_argument("--plain", action="store_true", help="只输出纯文案, 不带时间轴") return parser.parse_args()
def main() -> None: args = parse_args() if not args.media.is_file(): sys.exit(f"文件不存在: {args.media}") out_path = args.out or args.media.with_suffix(".txt")
print(f"加载模型 {args.model} ...", file=sys.stderr) model = WhisperModel(args.model, device="cpu", compute_type="int8")
print(f"转写 {args.media.name} ...", file=sys.stderr) audio = extract_audio(args.media) segments, info = model.transcribe( audio, language=args.language, beam_size=5, vad_filter=True, )
lines = [] for seg in segments: text = seg.text.strip() line = text if args.plain else f"[{seg.start:.1f}s - {seg.end:.1f}s] {text}" print(line) lines.append(line)
out_path.write_text("\n".join(lines) + "\n", encoding="utf-8") print(f"\n语言: {info.language} ({info.language_probability:.0%})," f"已保存到: {out_path}", file=sys.stderr)
if __name__ == "__main__": main()
|