AI 字幕 — AI Subtitles
你现在运行 ai-subtitles 技能。目标:把一段音视频(mp3 / wav / m4a / mp4 / mov / 一条 yt-dlp 可下载的链接)变成可下载、可二次编辑、可上传 YouTube / B 站 / Apple Podcasts 字幕区的字幕产物:
<basename>.srt— 标准 SubRip,时间轴对齐,一般编辑器都吃;<basename>.vtt— WebVTT,浏览器<video><track>直接挂载;transcript.html— 自包含单文件双语对照稿(原文 / 译文 + 时间戳锚点),双击就能打开;- (可选)
<basename>-burned.mp4— 用ffmpeg -vf subtitles=…把字幕烧录回原视频。
全程本地、零商业 API key、零远端推理、不联网(模型下载完成后可断网跑)。
这门课做什么(边界写在第一屏)
- ✅ 做:一段音视频 → 同名
.srt+.vtt+ 自包含transcript.html;可选烧录.mp4。99 语种转写(whisper 官方覆盖),中英双语 transcript 走 whispertask="translate"或 argostranslate 离线包。 - ❌ 不做:
- 把直播录像剪成 9:16 竖屏带字幕的社媒短片 → 请去
highlight-clips; - 把多条视频合成一条带 BGM 的混剪 → 请去
video-montage; - 把文字脚本读成 AI 配音 → 请去
ai-voiceover; - 把音乐分离成人声 / 伴奏 → 请去
stem-splitter。
- 把直播录像剪成 9:16 竖屏带字幕的社媒短片 → 请去
- 📎 如何分流:用户的真实任务里如果字幕文件本身是产物(要上传字幕区、要供搜索 / 无障碍 / SEO 用、要出一份对照稿),就是本课;如果字幕只是中间步骤、最终要的是一段能直接发布的成片,请改去
highlight-clips/video-montage。 - 🔒 铁律:默认零 LLM、零商业 API、零远端调用;翻译只走 whisper 自带
task="translate"或argostranslate离线模型。不要因为某个语种对找不到 argos 包就回退去任何远端 LLM;找不到就在终端明确告诉用户「不支持该语种对的双语 transcript,仅输出单语字幕」。
前置条件
- Python ≥ 3.9(
python3 --version); - 系统装 ffmpeg(macOS
brew install ffmpeg;Ubuntuapt install ffmpeg;Windowswinget install Gyan.FFmpeg);ffmpeg -version检查; - 处理在线链接需要 yt-dlp(
pip install yt-dlp或brew install yt-dlp); - 首次跑
faster-whisper会从 Hugging Face 下载模型权重,下载完成后完全离线可跑:tiny≈ 75 MB /small≈ 466 MB /medium≈ 1.5 GB /large-v3≈ 3.1 GB;
- 双语 transcript 走 argostranslate 时,每个语言对会一次性下载一份 ~200 MB 离线模型包;
- 零 API key、零 GPU 必需(GPU 有的话 faster-whisper 自动用 CUDA,速度 ×10–20;CPU 也能跑);
- 不需要 clone 任何私有仓库,全部上游公开 PyPI 包 + 公开二进制。
磁盘 / 资源真实占用(CPU-only 装法)
argostranslate 当前版本会把 PyTorch 作为传递依赖,默认装法会拉一整套 CUDA / NVIDIA wheels,单 venv 就有 5 GB+。课程本身是 CPU-only 的,因此强烈建议 pip install 时显式走 CPU-only 的 PyTorch index,避免无意义的 GPU 拷贝:
| 组件 | CPU-only 装法 venv 占用 |
|---|---|
| faster-whisper + 依赖 | ~ 0.4 GB |
| argostranslate + CPU torch + ctranslate2 | ~ 1.2 GB |
| yt-dlp | < 50 MB |
| venv 合计 | ~ 1.7 GB |
+ Hugging Face large-v3 缓存 |
+ 3.1 GB |
| + argostranslate en↔zh 离线包 | + 0.2 GB |
| 总盘占 (large-v3 + en↔zh) | ~ 5 GB(首次下载完即离线复用) |
如果走默认 pip install argostranslate,会额外拖进 torch==2.x + 一组 nvidia-* CUDA wheels,venv 直接膨胀到 5 GB+——纯 CPU 跑根本用不上,请按下方 CPU-only 安装方式走。
安装(CPU-only,推荐)
放进 venv,避免 Ubuntu 24+ 的 PEP 668:
python3 -m venv .ai-subtitles
source .ai-subtitles/bin/activate
pip install --upgrade pip
# 1. 先用 CPU-only torch index 装 PyTorch,避免 argostranslate 拖入 CUDA wheels
pip install --index-url https://download.pytorch.org/whl/cpu torch
# 2. 再装本课依赖(faster-whisper 自带的 ctranslate2 也是纯 CPU;argostranslate
# 会复用已经装好的 CPU torch,不会再去拉 nvidia-* wheels)
pip install faster-whisper argostranslate yt-dlp
ffmpeg -version | head -1 # 确认系统 ffmpeg 在 PATH 里
第一次安装 argostranslate 后,再装好语言对(按需):
import argostranslate.package, argostranslate.translate
argostranslate.package.update_package_index()
avail = argostranslate.package.get_available_packages()
pkg = next(p for p in avail if p.from_code == "en" and p.to_code == "zh")
argostranslate.package.install_from_path(pkg.download())
把上面四行存成 argos_install.py,第一次跑一次就行;之后离线。如果不打算用 argostranslate(只用 whisper task="translate" → 英文译文路径,或单语字幕),直接 pip install faster-whisper yt-dlp 即可,无需 torch、无需任何 GPU wheel。
工作流程(参考脚本 subtitles.py)
本课随课程一起 ship 了一份可直接跑的参考脚本 public/skills/ai-subtitles/subtitles.py:
python subtitles.py episode.mp3 --out ./subtitles-out --model large-v3 --lang zh
python subtitles.py lesson.mp4 --out ./subtitles-out --model large-v3 --lang en --burn
python subtitles.py https://... --out ./subtitles-out --lang en --bilingual zh
下面是它在内部各步骤做的事;想自己拼脚本可以直接抄。
1. 决定输入
-
本地文件(mp3 / wav / m4a / mp4 / mov / mkv):直接喂进
faster-whisper,它会用 ffmpeg 解码; -
在线链接(archive.org / Wikimedia / YouTube / B 站 / Vimeo / 任何 yt-dlp 支持的站):
yt-dlp -x --audio-format wav --no-playlist "<URL>" -o input.wav然后用
input.wav走下一步。不要把链接里的视频先上传到任何远端服务。CI / 自动化注意:YouTube 自 2024 起对无 cookies 的 yt-dlp 请求经常返回 "Sign in to confirm you're not a bot"。如果你的环境没法配 cookies,优先选 archive.org / Wikimedia / NASA 直链 —— 这些站不要求 cookies、yt-dlp 一把吃下,特别适合自动化 / CI 验收。Apollo 11 录音的稳定 PD 入口:
https://archive.org/details/nasa_tv-Apollo_11_Mission_Audio_-_Day_3(87 分钟,Apollo 11 任务音频)
桌面环境真的要走 YouTube:
yt-dlp --cookies-from-browser chrome -x --audio-format wav "<YouTube URL>" -o input.wav把
chrome替换成firefox/safari/edge/brave。仅限本地有 cookies 的桌面环境跑得通。
2. 选模型
| 用户情况 | 推荐模型 | 原因 |
|---|---|---|
| 高品质内容(podcast、课程录屏、采访) | large-v3 |
WER 最低,时间戳最稳 |
| 通用日常 | medium / medium.en(纯英文时) |
质量 90% 接近 large,CPU 跑得动 |
| 极致低配 / 2 GB RAM 笔记本 | small.en / tiny.en |
牺牲质量换可跑性 |
| 想用 GPU 加速 | large-v3 + device="cuda" + compute_type="float16" |
10–20× 实时速度 |
难点录音(口音重 / 专有名词 / 缩写)务必传
initial_prompt="<相关名词、人名、术语>"给模型当上下文,能直接救回 LM/AGI/RAG 这类容易被误听的缩写。
3. 转写(faster_whisper.WhisperModel)
from faster_whisper import WhisperModel
model = WhisperModel("large-v3", device="auto", compute_type="int8")
segments, info = model.transcribe(
"input.wav",
language="en", # 用户已知语种;否则传 None 让模型自动检测
task="transcribe", # "translate" 时直接输出英文译文(仅 → en 方向)
beam_size=5,
word_timestamps=True, # 让 .srt cue 时间轴更稳,能用 word 边界做断句
vad_filter=True, # 跳过静音段,省时
initial_prompt="<相关名词、人名、缩写>", # 可选;难点录音强烈推荐
)
segs = [(s.start, s.end, s.text.strip()) for s in segments]
print(f"language={info.language} prob={info.language_probability:.2f}")
4. 序列化为 .srt / .vtt
def fmt_srt(t):
h = int(t // 3600); m = int((t % 3600) // 60); s = int(t % 60); ms = int((t - int(t)) * 1000)
return f"{h:02d}:{m:02d}:{s:02d},{ms:03d}"
with open("out.srt", "w") as f:
for i, (a, b, text) in enumerate(segs, 1):
f.write(f"{i}\n{fmt_srt(a)} --> {fmt_srt(b)}\n{text}\n\n")
with open("out.vtt", "w") as f:
f.write("WEBVTT\n\n")
for a, b, text in segs:
f.write(f"{fmt_srt(a).replace(',', '.')} --> {fmt_srt(b).replace(',', '.')}\n{text}\n\n")
断句规则:每条 cue ≤ 42 字符、≤ 7 秒、句末或自然停顿处断;用
word_timestamps=True拿到的 word 边界做切分,比简单按字数硬切干净很多。
5. 双语 transcript.html(可选)
两条合法路径,禁止任何远端 LLM 兜底:
-
路径 A — whisper 自带
task="translate":任意语言 → English,无需额外模型;目标语是英文时首选。 -
路径 B — argostranslate 离线包:目标语不是英文(en→zh、ja→zh、fr→en…)时使用:
import argostranslate.translate as at zh = at.translate(text, "en", "zh")
写一份自包含 HTML:
- 左栏原文 + 时间戳锚点,右栏译文,逐 cue 对齐;
- 零外部 CDN、零 webfont、零远端字体,所有 CSS inline 写进
<style>; - 文件名
transcript.html,双击就能在浏览器里打开。
6. (可选) 烧录字幕到 mp4
ffmpeg -i original.mp4 \
-vf "subtitles=out.srt:force_style='FontName=DejaVu Sans,FontSize=20,PrimaryColour=&HFFFFFF&,OutlineColour=&H000000&,BorderStyle=1,Outline=2,Shadow=0,MarginV=48,Alignment=2'" \
-c:v libx264 -preset slow -crf 23 -pix_fmt yuv420p \
-c:a copy \
-movflags +faststart \
out-burned.mp4
样式细节可换成 .ass(ASS 支持精细排版);底部居中 + 白字 + 黑色描边 + 60 px 安全边距是社媒最稳的默认值。
7. 输出目录
./subtitles-out/
├── <basename>.srt
├── <basename>.vtt
├── transcript.html
└── <basename>-burned.mp4 # 仅在用户要求烧录时产出
跑完在终端告诉用户:四个文件路径、模型大小、总耗时、检测/确认到的语种。
极致低配 fallback:whisper.cpp
如果用户机器是 < 4 GB RAM 或者 Python 环境装不起来(公司 Win 受限、老 macOS),用 whisper.cpp 的预编译二进制兜底:
# macOS / Linux:克隆并编译一次
git clone https://github.com/ggerganov/whisper.cpp && cd whisper.cpp && make
./models/download-ggml-model.sh small
./main -m models/ggml-small.bin -f input.wav -osrt -ovtt -ml 42
输出 input.wav.srt + input.wav.vtt,纯 CPU、纯 C++、零 Python。本课不替 whisper.cpp 写 wrapper,按上游 README 走即可。注意:whisper.cpp 不带翻译模型,目标语是英文时可用其 --translate 旗标,其他语种对仍需要 argostranslate 离线包配合。
用户面规则
- 所有用户可见命令、链接、文案使用
clawvard.school域名; - 翻译只走 whisper
task="translate"或 argostranslate 离线模型; - 所有依赖均为公开 PyPI / 公开二进制。
学习完成后
告诉用户:
我已经学会了 ai-subtitles。给我一段音视频(本地文件或 yt-dlp 可下载的链接),我会在本地用 faster-whisper 转写、argostranslate 翻译、ffmpeg 烧录,产出同名
.srt+.vtt+ 自包含双语transcript.html,必要时附一份字幕烧录回去的.mp4。全程本地、零 API key、零远端推理。