23 KiB
SynthMind - 视频音频提取与转录工具
将视频文件转换为文字稿的自动化工具链,为后续 AI 内容分析与总结提供数据基础。
流水线:视频文件 → MP3 音频 → TXT 文字稿 → (后续 AI 分析)
目录
功能特性
通用特性
- ✅ 支持单文件或递归扫描目录
- ✅ 完善的断点续传:中断后重新运行自动跳过已完成项
- ✅ JSON manifest 进度追踪,两个脚本共享同一文件
- ✅ 失败自动记录 + 重试机制(
--retry-failed) - ✅ 状态查询(
--status)实时查看进度 - ✅ 完全支持含空格、中文的文件名
- ✅ 详细的时间戳日志
extract_audio.py 特性
- 使用 FFmpeg 提取音频:MP3 / 64kbps / 22050Hz / 单声道(语音场景优化,体积小)
- 支持视频切分模式:超大视频按时长切分为多段分别处理
- 支持多种输入格式:
.mp4/.mkv/.avi/.mov/.flv/.wmv/.webm/.m4v
transcribe_audio.py 特性
- 基于 openai-whisper 命令行工具
- 支持 4 种模型:
tiny / base / small / medium(详见 Whisper 模型选择) - 多模型批量转录(
--all-models):一次运行对每个音频用 4 个模型分别转录,输出<stem>.<model>.txt便于精度对比 - 模型预下载(
--preload-models):一次性下载全部支持的模型到本地缓存 - 自定义输出后缀(
--output-suffix):便于同一模型多次转录(不同参数)不覆盖 - 支持指定语言(
--language zh/en/...)或自动检测 - 支持音频切分转录:超长音频切分后逐段转录再合并
- 支持多种音频格式:
.mp3/.m4a/.wav/.flac/.ogg/.aac - Manifest 嵌套 transcriptions:同一视频/音频记录里,多模型多参数结果按
<model>[:<suffix>]为 subkey 共存,互不覆盖
环境要求
| 依赖 | 版本 | 用途 |
|---|---|---|
| Python | 3.8+ | 脚本运行 |
| FFmpeg | 4.0+ | 音视频处理(含 ffprobe) |
| openai-whisper | 最新 | 音频转录 |
当前实测环境:Ubuntu 22.04 (WSL2) + Python 3.13 + FFmpeg 6.0 + Whisper 20250625
安装
1. 安装 FFmpeg
sudo apt update
sudo apt install -y ffmpeg
ffmpeg -version # 验证
2. 安装 Whisper
pip install openai-whisper
whisper --help # 验证
3. 预下载所有 Whisper 模型(推荐)
首次运行前,建议一次性下载所需的 4 个模型(共约 2.1GB):
python3 transcribe_audio.py --preload-models
下载耗时参考(本机测试):
tiny (~75MB) → 数秒
base (~140MB) → 数秒
small (~460MB) → 数十秒
medium (~1.5GB) → 75s
模型会缓存到 ~/.cache/whisper/。
4. 下载脚本
将 extract_audio.py 和 transcribe_audio.py 放到同一目录即可,无需额外配置。
快速开始
场景 1:处理某目录下所有视频(简单流水线)
# Step 1: 提取音频(生成 .mp3)
python3 extract_audio.py /path/to/videos/
# Step 2: 转录文字(生成 .txt,默认 base 模型)
python3 transcribe_audio.py /path/to/videos/ --language zh
场景 2:对比 4 个模型的精度
# 一次运行,每个音频用 4 个模型都跑一遍
python3 transcribe_audio.py /path/to/audio/ --all-models --language zh
输出(每个 mp3 生成 4 个 txt):
audio1.mp3
audio1.tiny.txt
audio1.base.txt
audio1.small.txt
audio1.medium.txt
场景 3:高精度转录(推荐配置)
python3 transcribe_audio.py /path/to/audio/ --model medium --language zh
脚本一:extract_audio.py
用法
python3 extract_audio.py <source> [选项]
参数说明
| 参数 | 简写 | 默认 | 说明 |
|---|---|---|---|
source |
- | 必填 | 视频文件路径 或 包含视频的目录 |
--output |
-o |
与源同目录 | MP3 输出目录 |
--manifest |
-m |
source 目录下 | manifest.json 路径 |
--split-size |
- | 不切分 | 视频时长超过此秒数则切分(单位:秒) |
--split-segment |
- | 600 | 每个切分片段时长(单位:秒) |
--retry-failed |
- | false | 重新处理上次失败的文件 |
--status |
- | false | 仅查看进度状态,不执行提取 |
使用示例
1. 处理目录下所有视频
python3 extract_audio.py /home/user/videos/
2. 处理单个视频
python3 extract_audio.py /home/user/videos/lesson1.mp4
3. 指定 MP3 输出目录
python3 extract_audio.py /home/user/videos/ -o /home/user/audio/
4. 超过 1 小时的视频自动切分为 10 分钟片段
python3 extract_audio.py /home/user/videos/ --split-size 3600 --split-segment 600
5. 查看进度
python3 extract_audio.py /home/user/videos/ --status
6. 重试失败的文件
python3 extract_audio.py /home/user/videos/ --retry-failed
输出规格(FFmpeg 参数)
-vn 去除视频轨道
-acodec libmp3lame MP3 编码
-ab 64k 比特率 64kbps
-ar 22050 采样率 22050Hz
-ac 1 单声道
场景优化:这套参数针对语音课程/讲座优化,体积约为原视频的 20%,同时保留清晰的人声。
脚本二:transcribe_audio.py
用法
python3 transcribe_audio.py <source> [选项]
python3 transcribe_audio.py --preload-models # 仅下载模型(source 可省略)
参数说明
| 参数 | 简写 | 默认 | 说明 |
|---|---|---|---|
source |
- | 见备注 | MP3 文件路径 或 包含音频的目录 |
--output |
-o |
与源同目录 | TXT 输出目录 |
--manifest |
-m |
source 目录下 | manifest.json 路径 |
--model |
- | base |
Whisper 模型:tiny/base/small/medium(4 选一) |
--language |
- | 自动检测 | 语言代码。推荐 zh(中文)或 en(英文) |
--all-models |
- | false | 每个音频用全部 4 个模型转录,输出 <stem>.<model>.txt |
--output-suffix |
- | 空 | 输出 txt 附加后缀:<stem><suffix>.txt |
--split-size |
- | 不切分 | 音频超过此秒数则切分(需要 ffmpeg) |
--split-segment |
- | 1800 | 每个切分片段时长(单位:秒) |
--retry-failed |
- | false | 重新处理上次失败的文件 |
--status |
- | false | 仅查看进度状态 |
--preload-models |
- | false | 预下载全部支持模型,无需 source |
备注:
--preload-models或--status模式下可省略source。
Whisper 模型选择
| 模型 | 参数量 | 磁盘 | 显存 | CPU 转录 890s 音频 | 中文精度 | 语言输出 |
|---|---|---|---|---|---|---|
tiny |
39M | ~75MB | ~1GB | 47s | ⭐⭐ | 繁体 |
base |
74M | ~140MB | ~1GB | 58s | ⭐⭐⭐ | 简体 |
small |
244M | ~460MB | ~2GB | 93s | ⭐⭐⭐⭐ | 简体 |
medium |
769M | ~1.5GB | ~5GB | 170s | ⭐⭐⭐⭐⭐ | 简体 + 标点 |
实测数据来源:本项目 测试报告(14 分 50 秒中文课程视频) 推荐:日常速稿用
base,重要内容用medium。
使用示例
1. 转录目录下所有 MP3(默认 base 模型)
python3 transcribe_audio.py /home/user/audio/
2. 中文课程使用 medium 模型(高精度)
python3 transcribe_audio.py /home/user/audio/ --model medium --language zh
3. 一次跑 4 个模型对比精度
python3 transcribe_audio.py /home/user/audio/ --all-models --language zh
4. 预下载模型(首次安装后一次性下载全部)
python3 transcribe_audio.py --preload-models
5. 自定义输出后缀(避免覆盖)
# 用 base 模型转录,输出为 <stem>_v1.txt
python3 transcribe_audio.py /home/user/audio/ --model base --output-suffix _v1
# 稍后再用 base + 不同参数转录,输出为 <stem>_v2.txt(不覆盖 v1)
python3 transcribe_audio.py /home/user/audio/ --model base --output-suffix _v2
6. 超长音频(>30 分钟)切分转录
python3 transcribe_audio.py /home/user/audio/ \
--model medium --language zh \
--split-size 1800 --split-segment 600
7. 查看进度(含每个模型的记录)
python3 transcribe_audio.py /home/user/audio/ --status
8. 重试失败的文件
python3 transcribe_audio.py /home/user/audio/ --retry-failed
多模型精度对比
实测样本
- 音频:SketchUp 软件优化设置课程(14 分 50 秒,中文普通话)
- 命令:
python3 transcribe_audio.py <mp3> --all-models --language zh
精度对比(关键词识别)
| 关键词 | 原意 | tiny | base | small | medium |
|---|---|---|---|---|---|
| SketchUp 中文名 | 草图大师 | 草毒大師 ❌ | 吵毒大师 ❌ | 草读大师 ❌ | 草毒大师 ❌ |
| 出厂设置 | 常规的设置 | 殘酷 ❌ | 残酷 ❌ | 常规 ✅ | 常规 ✅ |
| 行业 | 行业 | 含業 ❌ | 行业 ✅ | 行业 ✅ | 行业 ✅ |
| 轮廓线 | 轮廓线 | 能夠像 ❌ | 能扩限 ❌ | 轮廓线 ✅ | 轮廓线 ✅ |
| 场景 | 场景 | 殘景 ❌ | 场景 ✅ | 场景 ✅ | 场景 ✅ |
| 输出语言 | 简体中文 | 繁体 ⚠️ | 简体 ✅ | 简体 ✅ | 简体 ✅ |
| 标点符号 | 有 | 无 ❌ | 无 ❌ | 少量 ⚠️ | 有 ✅ |
各模型样例输出(首行)
原音频:"大家好,这一节我们对草图大师来进行一个软件的一些优化设置方面的一些操作"
| 模型 | 输出 | 备注 |
|---|---|---|
| tiny | 大家好,這一節我們對草毒大師來進行一個軟件的一些優化設置方面的一些操作 | 繁体,专有名词错 |
| base | 大家好,这一节我们对吵毒大师来进行一个软件的一些优化设置方面的一些操作 | 简体,专有名词错 |
| small | 大家好,这一节我们对草读大师来进行一个软件的一些优化设置方面的一些操作 | 简体,专有名词接近 |
| medium | 大家好,这一节我们对草毒大师来进行一个软件的一些优化设置方面的一些操作 | 简体,专有名词接近,有逗号 |
结论
| 场景 | 推荐模型 |
|---|---|
| 快速内容概览、草稿 | tiny 或 base(快,可接受) |
| 日常转录、笔记 | base 或 small(平衡) |
| 正式转录、发布 | medium(最好精度) |
| 专有名词很多的领域 | 结合 --initial_prompt 或人工校对 |
提示:所有 whisper 模型对专有名词(品牌名、人名、技术术语)识别都容易出错。生产环境建议:
- 使用 medium 模型
- 通过
whisper --initial_prompt "本视频讨论 SketchUp / 草图大师"提示 whisper(本脚本暂未透传此参数,可直接调 whisper CLI)- 人工二次校对
Manifest 文件说明
两个脚本共享同一个 manifest.json(schema v2)。每个视频文件在 manifest 中只有一条记录,extract_audio.py 与 transcribe_audio.py 分别写入其中的 extraction 与 transcriptions 两个子对象,互不覆盖。
key 选择规则
| Key | 来源 | 场景 |
|---|---|---|
视频绝对路径(如 /path/to/x.mp4) |
extract_audio.py 产生的记录 |
完整流水线(视频 → 音频 → 转录) |
音频绝对路径(如 /path/to/x.mp3) |
直接对 mp3 跑 transcribe_audio.py |
独立音频转录(无对应视频) |
反向查找:当 transcribe_audio.py 处理某个 mp3 时,会先扫描 manifest,如果发现某条视频记录的 extraction.audio_path 等于该 mp3 路径,就把转录结果并入该视频记录,而不是新建 key。
完整示例(v2)
{
"version": 2,
"files": {
"/path/to/lesson1.mp4": {
"extraction": {
"extracted": true,
"audio_path": "/path/to/lesson1.mp3",
"split_segments": [],
"error": null,
"updated_at": "2026-08-23T10:30:00"
},
"transcriptions": {
"tiny": {
"transcribed": true,
"txt_path": "/path/to/lesson1.tiny.txt",
"split_segments": [],
"model": "tiny",
"language": "zh",
"output_suffix": null,
"error": null,
"updated_at": "2026-08-23T10:35:00"
},
"medium": {
"transcribed": true,
"txt_path": "/path/to/lesson1.medium.txt",
"split_segments": [],
"model": "medium",
"language": "zh",
"output_suffix": null,
"error": null,
"updated_at": "2026-08-23T10:50:00"
},
"base:_v1": {
"transcribed": true,
"txt_path": "/path/to/lesson1_v1.txt",
"split_segments": [],
"model": "base",
"language": null,
"output_suffix": "_v1",
"error": null,
"updated_at": "2026-08-23T11:00:00"
}
}
},
"/path/to/standalone.mp3": {
"extraction": null,
"transcriptions": {
"medium": {
"transcribed": true,
"txt_path": "/path/to/standalone.medium.txt",
"split_segments": [],
"model": "medium",
"language": "en",
"output_suffix": null,
"error": null,
"updated_at": "2026-08-23T11:15:00"
}
}
}
}
}
字段说明
顶层每条记录
extraction-extract_audio.py的产物;null表示没有走过提取(例如直接对 mp3 跑转录)transcriptions-transcribe_audio.py的产物;以<model>或<model>:<suffix>为 subkey,允许同一音频用多种模型/参数并存
extraction 字段
extracted- 是否已提取音频(布尔);false+ 有error表示上次失败audio_path- 生成的 MP3 路径(切分模式下为第一个片段)split_segments- 切分片段列表(非切分模式为空[])error- 失败时的错误信息,成功为nullupdated_at- 最后更新时间(ISO 8601)
transcriptions.<subkey> 字段
transcribed- 是否已转录(布尔)txt_path- 生成的 TXT 路径split_segments- 切分片段列表model- 使用的 whisper 模型(tiny/base/small/medium)language- 使用的语言代码(zh/en/...)或null(自动检测)output_suffix- 输出后缀(默认null)error- 失败错误信息updated_at- 最后更新时间
断点续传逻辑
脚本判断是否需要处理某文件时,同时校验:
- manifest 中标记为已完成(
extraction.extracted=true或transcriptions.<sk>.transcribed=true) - 目标文件实际存在且非空
任一条件不满足则重新处理(例如手动删除了 mp3 后重跑,会重新提取)。
多模型模式:每个 (音频 × 模型 × 后缀) 组合独立跟踪,对应 transcriptions 里独立的 subkey。已完成的组合被跳过,未完成的继续。
从旧版 (v1) 升级
老版本使用扁平 schema:视频记录 key = 视频路径;转录记录 key = transcribe:<model>[:<suffix>]:<音频路径>。这些散落的记录在 v2 里会被合并到同一条视频记录里。
升级方式:脚本首次加载旧 manifest 时会打印警告并以空 v2 schema 重新开始(旧数据将被下次保存覆盖)。如果你想保留旧记录,运行升级前先手动备份 manifest.json。所有已提取的 mp3 和已转录的 txt 会在下次运行时被识别为"存在但 manifest 无记录"→ 重新处理并写入新 schema。
高级用法
1. 完整流水线脚本
#!/bin/bash
VIDEO_DIR="/path/to/videos"
echo "=== Step 0: 预下载模型(仅首次)==="
python3 transcribe_audio.py --preload-models
echo "=== Step 1: 提取音频 ==="
python3 extract_audio.py "$VIDEO_DIR" --split-size 3600 --split-segment 600
echo "=== Step 2: 转录文字(medium 模型高精度)==="
python3 transcribe_audio.py "$VIDEO_DIR" \
--model medium --language zh \
--split-size 1800 --split-segment 600
echo "=== Step 3: 查看最终状态 ==="
python3 extract_audio.py "$VIDEO_DIR" --status
python3 transcribe_audio.py "$VIDEO_DIR" --status
2. 多模型对比工作流
# 1) 提取音频
python3 extract_audio.py /videos/
# 2) 一次跑 4 个模型(此步骤会跑 4x 时长,请预留时间)
python3 transcribe_audio.py /videos/ --all-models --language zh
# 3) 查看所有结果
python3 transcribe_audio.py /videos/ --status
# 4) 对比不同模型输出(例如用 diff)
diff /videos/lesson1.base.txt /videos/lesson1.medium.txt
3. 独立分离 MP3 与 TXT 输出
python3 extract_audio.py /videos/ -o /audios/
python3 transcribe_audio.py /audios/ -o /transcripts/ --model medium
4. 自定义 manifest 位置(多套并行)
python3 extract_audio.py /videos/ -m /var/run/task1_manifest.json
python3 extract_audio.py /videos2/ -m /var/run/task2_manifest.json
5. 同一模型不同参数的多轮转录
# 第一版:自动检测语言
python3 transcribe_audio.py audio.mp3 --model base --output-suffix _auto
# 第二版:强制中文
python3 transcribe_audio.py audio.mp3 --model base --language zh --output-suffix _zh
# 两个 txt 独立保存,manifest 各自记录
ls audio_*.txt
# audio_auto.txt audio_zh.txt
故障排查
FFmpeg 相关
| 症状 | 原因 | 解决 |
|---|---|---|
| 未找到 ffmpeg 命令 | 未安装 | sudo apt install ffmpeg |
| 视频时长为 0 | ffprobe 无法解析 | 检查视频是否损坏 ffprobe <file> |
| 切分片段为空 | 编码不兼容 codec copy | 手动尝试 ffmpeg -i in.mp4 -c copy out.mp4 |
Whisper 相关
| 症状 | 原因 | 解决 |
|---|---|---|
| 未找到 whisper 命令 | 未安装 | pip install openai-whisper |
| 首次转录卡住 | 正在下载模型 | 提前用 --preload-models 一次下完 |
| CUDA out of memory | 显存不足 | 使用更小模型;whisper 自动 fallback 到 CPU |
| 转录内容为繁体 | tiny 模型对中文倾向输出繁体 | 使用 base 及以上模型 |
| 专有名词识别错 | 模型未训练过 | 使用 medium;未来可支持 --initial-prompt |
Manifest 相关
| 症状 | 解决 |
|---|---|
| 想强制重跑所有文件 | 删除 manifest.json |
| 想强制重跑某个文件 | 编辑 manifest 或删除对应 .mp3/.txt 文件 |
| 想重跑失败的文件 | 加 --retry-failed 参数 |
| 想同一音频用同模型换参数再跑一次 | 用 --output-suffix 区分 |
文件名相关
- 中文文件名:完全支持
- 空格文件名:完全支持(内部用 subprocess 参数列表,非 shell 拼接)
- 特殊字符(如
&、|):应可正常处理,遇到问题请提 issue
测试报告
测试环境
- OS: Ubuntu 22.04 (WSL2 on Windows)
- Python: 3.13
- FFmpeg: 6.0
- Whisper: 20250625 (全部 4 个模型)
测试用例
测试视频:03-SU软件的优化设置.mp4
- 时长:890 秒(14 分 50 秒)
- 大小:37 MB
- 视频:H.264 1280x720
- 音频:AAC 44.1kHz 立体声
- 语言:中文(普通话)
- 内容:SketchUp 软件优化设置教学
提取音频 & 通用功能测试
| # | 测试项 | 结果 | 耗时 | 备注 |
|---|---|---|---|---|
| 1 | 基本音频提取 | ✅ | 4s | 输出 6.9MB MP3 (22050Hz mono 64kbps) |
| 2 | 断点续传(重跑跳过) | ✅ | <1s | manifest 校验通过 |
| 3 | --status 状态查看 |
✅ | <1s | 正确显示 1/1 完成 |
| 4 | 视频切分模式(500s 阈值) | ✅ | 4s | 3 片段完美衔接(修复 pipe:0 bug) |
| 5 | 失败重试 + 含空格文件名 | ✅ | - | 失败记录、跳过、--retry-failed 均正常 |
| 6 | 端到端流水线(视频→MP3→TXT) | ✅ | 54s | 从零开始生成完整产物 |
转录 & 多模型功能测试
| # | 测试项 | 结果 | 耗时 | 备注 |
|---|---|---|---|---|
| 7 | --preload-models 预下载 4 模型 |
✅ | ~120s | 共 2.1GB,缓存到 ~/.cache/whisper/ |
| 8 | 单模型转录(tiny + zh) | ✅ | 47s | 输出 9KB TXT |
| 9 | 单模型转录(base + zh) | ✅ | 58s | 输出 9KB TXT |
| 10 | 单模型转录(small + zh) | ✅ | 93s | 输出 9KB TXT |
| 11 | 单模型转录(medium + zh) | ✅ | 170s | 输出 9KB TXT,有标点符号 |
| 12 | --all-models 4 模型一次跑 |
✅ | 368s | 生成 4 个独立 txt |
| 13 | 多模型 manifest 记录 | ✅ | - | 4 条独立 key,互不覆盖 |
| 14 | 多模型 --status 展示 |
✅ | <1s | 每个模型独立行显示 |
| 15 | 断点续传(重跑跳过) | ✅ | <1s | 4/4 全部跳过 |
| 16 | --output-suffix 自定义后缀 |
✅ | 46s | _custom 生成独立 key + 独立 txt |
| 17 | 音频切分转录 | ✅ | 59s | 3 片段并转录后合并成完整 TXT |
| 18 | manifest 共享验证 | ✅ | - | 提取+转录记录并存互不干扰 |
已修复 Bug
BUG-01:切分模式下 MP3 为空文件(148 字节)
- 原因:使用
cat <file> | ffmpeg -i pipe:0时,MP4 的moovatom 位于文件末尾需要 seek,pipe 不支持 seek 导致 ffmpeg 静默失败 - 修复:改用
ffmpeg -i <file>直接读取本地文件,同时增加输出文件非空校验(> 1KB) - 验证:切分后 3 个片段合计 890.4s ≈ 原视频 890.2s
BUG-02:--output-suffix 被 manifest 误跳过
- 原因:初版 manifest key 只含
model + path,未含output_suffix,导致同一模型换 suffix 后被判定为已完成 - 修复:key 结构改为
transcribe:<model>[:<suffix>]:<path>,suffix 参与 key 判定 - 验证:同一 tiny 模型,无 suffix 和
_customsuffix 生成独立 manifest 条目和独立 txt
性能参考(本次测试环境,CPU 转录)
| 阶段 | 输入 | 输出 | 耗时 | 吞吐 |
|---|---|---|---|---|
| 音频提取 | 37MB / 890s 视频 | 6.9MB MP3 | 4s | 223x realtime |
| 转录(tiny) | 6.9MB / 890s 音频 | 9KB TXT | 47s | 19x realtime |
| 转录(base) | 6.9MB / 890s 音频 | 9KB TXT | 58s | 15x realtime |
| 转录(small) | 6.9MB / 890s 音频 | 9KB TXT | 93s | 9.6x realtime |
| 转录(medium) | 6.9MB / 890s 音频 | 9KB TXT | 170s | 5.2x realtime |
| 多模型一次跑(4 model) | 6.9MB / 890s 音频 | 4 个 9KB TXT | 368s | - |
上述数据为 CPU 转录性能。GPU 转录速度可提升 5-10x。
项目路线图
- Phase 1: 音频提取(extract_audio.py)
- Phase 2: 音频转录(transcribe_audio.py)
- 单模型转录
- 多模型批量对比(
--all-models) - 模型预下载(
--preload-models) - 输出后缀自定义(
--output-suffix) - 语言指定(
--language zh/en)
- Phase 3: 内容归纳整理(summarize.py)- 待定
- AI 分析文字内容 + 视频内容
- 生成脑图、结构化文档
相关脚本
extract_audio.py- 本地通用视频音频提取(本项目 Phase 1)transcribe_audio.py- 本地音频转录(本项目 Phase 2)