长视频 → 转写 + 分段摘要 + 手机可读报告,一条命令。不需要 ffmpeg。
给它一个视频链接(CCTV / B站 / YouTube / 1800+ 站点)、一个 CCTV pid,或者一个本地文件:
vidsum run https://tv.cctv.cn/2024/07/13/VIDETLqINVezlJzyxy6Sc8Nv240713.shtml
vidsum run https://www.bilibili.com/video/BV1xx411c7mD
vidsum run D:\videos\lecture.mp4它会给你一份带时间轴的完整转写稿、一份按章节组织的摘要,和一个在手机上读起来舒服的单文件网页。
一个两小时的访谈 / 讲座 / 纪录片,想看完得花两小时,想找里面讲了什么更麻烦。现有做法要么把整段音频丢给云端 API(贵、慢),要么下整段视频(几个 G)。
vidsum 走的是这条路:只取音频 → 本地 GPU 转写 → 按章节摘要 → LLM 只读文本。转写全程在本机跑,只有摘要那几千个 token 出网。
| 多站点 | CCTV 走专线(只有它给得出官方分段);B站 / YouTube / 其它 1800+ 站点走 yt-dlp;.m3u8 / .mp4 这类直链直接解,不绕道。 |
| 不需要 ffmpeg | 音频解码走 PyAV(faster-whisper 自带依赖)。少一个外部二进制,少一类"没装 / 不在 PATH"的故障。 |
| GPU 转写 | faster-whisper + CTranslate2。实测 RTX 5070 Ti Laptop 上 112 分钟中文音频转写 5 分钟。 |
| 按章节摘要 | 优先用视频自带的章节:CCTV 取接口的 segments(本例 17 段,毫秒级起止),yt-dlp 取 chapters。都没有才按固定时长切。 |
| 进度看板 | 跑长视频时开一个本地网页,四个阶段实时进度 + 日志,不用盯着终端猜。 |
| 手机可读报告 | 输出单文件 HTML:深色模式自适应、字号可调(14–34px)、双指缩放、零外部依赖,断网也能开。 |
| 不编造 | 素材不完整时显式禁止模型推测并标注覆盖率;完全没有语音内容时直接不调模型(见下面踩坑手册)。 |
以《国家宝藏》第四季 20240713(CCTV-3,1:51:59)为例,RTX 5070 Ti Laptop + small 模型:
| 阶段 | 耗时 | 备注 |
|---|---|---|
| 音频解码(PyAV 直读 HLS → 16k 单声道 WAV) | 19 分钟 | 约 11× 实时;WAV 215 MB |
语音转写(faster-whisper small, CUDA float16) |
5 分钟 | 2964 句,约 22× 实时 |
| 分段摘要 + 全片总览(deepseek-chat,17 段) | 40 秒 | |
| 合计 | 约 25 分钟 | 相对 112 分钟的原片 |
尚未发布到 PyPI,先从源码装:
git clone https://github.com/ExpertKT/vidsum
cd vidsum
pip install -e . # 不要 GPU 就这样
pip install -e ".[nvidia]" # Windows + NVIDIA,要 GPU 再加这个Python 版本:请用 3.10 – 3.12。CTranslate2 在 3.13+ 上常常还没有轮子。 没把握就用 uv:
uv venv --python 3.12 && uv pip install -e .
不需要 ffmpeg。yt-dlp 会随依赖一起装上;只处理 CCTV 和直链的话用不到它。
# 完整跑一遍,开进度看板
vidsum run <视频链接 / CCTV pid / 本地文件>
# 试跑:只处理前 4 分钟,几十秒就能看到结果长什么样
vidsum run <target> --limit-seconds 240
# 不开看板,只在终端看进度
vidsum run <target> --no-dashboard
# 指定输出目录 / 模型 / 设备
vidsum run <target> --out D:\summaries --asr-model large-v3 --device cuda
# 用本地已下载的模型(推荐,不联网)
vidsum run <target> --asr-model D:\models\faster-whisper-small
# 需要登录才能看的视频:借用浏览器的 cookie
vidsum run <target> --cookies-from-browser edge
# 环境里的代理挂了(报"连接被拒绝")时绕过它
vidsum run <target> --no-proxy
# 环境自检
vidsum doctor| 形式 | 例子 | 走哪条路 |
|---|---|---|
| CCTV 节目页 | https://tv.cctv.cn/2024/07/13/VIDETLq...shtml |
CCTV 专线,自动从页面里取 guid,能拿到官方分段 |
| CCTV pid | ea3b1f6c67b843c58594ca43323f6dd4 |
同上,32 位十六进制 |
| B站 | https://www.bilibili.com/video/BV1xx411c7mD |
yt-dlp |
| YouTube | https://www.youtube.com/watch?v=... |
yt-dlp(国内需自备网络) |
| 其它站点 | 微博 / 抖音 / Vimeo / 大量国内站点 | yt-dlp 官方支持列表里 1800+ 站点 |
| 直链媒体 | https://.../main.m3u8、.../a.m4a |
PyAV 直读,不绕 yt-dlp |
| 本地文件 | D:\videos\talk.mp4 |
PyAV |
有 chapters 的站点(YouTube 等)会自动拿它当摘要骨架。
取不到的:DRM 保护的付费正片(爱奇艺、腾讯视频、优酷会员片)。这类站点 yt-dlp 也无能为力, 只能自己录屏或下载后再把本地路径传进来。
vidsum-out/<pid 或标题>/
├── source.json # 解析结果:标题、时长、分段、可用播放地址
├── media.m4a # 仅通用站点:yt-dlp 下回来的音轨(可复用,重跑不再下)
├── audio_16k.wav # 16kHz 单声道(可复用,重跑跳过解码)
├── transcript.jsonl # 句级 + 词级时间戳
├── transcript.md # 带时间轴的完整转写稿
├── summary.md # 全片总览 + 分段摘要
└── report.html # 手机可读报告(单文件、可离线)
| 码 | 含义 |
|---|---|
| 0 | 全部完成 |
| 1 | 失败(网络、解析、解码……) |
| 2 | 没有 LLM API key,只出了转写稿,没出摘要 |
| 3 | 没有识别到语音内容(纯音乐 / 无对白),已跳过摘要 |
| 变量 | 必需 | 说明 |
|---|---|---|
LLM_API_KEY |
视情况 | LLM 密钥。默认端点按下面规则配对 |
LLM_API_URL |
否 | 自定义端点 |
LLM_MODEL |
否 | 自定义模型名 |
DEEPSEEK_API_KEY |
否 | 没设 LLM_API_KEY 时用这个,自动配 api.deepseek.com + deepseek-chat |
HF_ENDPOINT |
否 | 国内建议设 https://hf-mirror.com,加速模型下载 |
HTTP_PROXY / HTTPS_PROXY |
否 | 标准代理变量;设了就要保证它在运行,否则所有请求都会失败(见踩坑手册) |
密钥解析顺序:显式参数 → 环境变量 → ~/.dsh/.credentials.yaml。
key 与端点是成对配的,不会把 DeepSeek 的 key 发到别家地址上。
模型别名的下载走 HuggingFace;如果只想离线,把模型目录传给 --asr-model 即可,vidsum 会强制 HF_HUB_OFFLINE=1。
这些都是开发过程中真实撞到并修掉的问题,写下来省得你重踩。
先跑 vidsum doctor。如果它报出:
代理 X http://127.0.0.1:3128 端口连不上!
说明环境变量里挂着一个已经死掉的代理。这时候错不在目标站点——请求根本没发出去。
vidsum 遇到过一模一样的情况:HTTPS_PROXY=http://127.0.0.1:3128 被写进环境,
但那个端口没人监听;直连 B站是 200,走这个代理是 000。
vidsum run <target> --no-proxy # 救急或手动清掉(注意:Windows 上如果同名变量有大小写重复项,PowerShell 里 $env:X="" 可能改不动,
最可靠的是直接重启终端或用 --no-proxy):
$env:HTTPS_PROXY=""; $env:HTTP_PROXY=""pip 装的 nvidia-cublas-cu12 / nvidia-cudnn-cu12 把 DLL 放在 site-packages/nvidia/*/bin,这个路径不在 PATH 里,CTranslate2 找不到。
vidsum 启动时会自动把这些 bin 目录挂进 DLL 搜索路径(vidsum.asr.add_cuda_dll_dirs()),所以你只要装上 vidsum[nvidia] 就行。用 vidsum doctor 能看到挂了几个目录。
faster-whisper 1.2.x 的 decode_audio() 调用 av.open(path, metadata_errors="ignore"),而 PyAV 19 删掉了这个参数。
vidsum 不走那条路:自己用标准库 wave + numpy 把 WAV 读成 float32 数组再交给 model.transcribe()。因此不受 PyAV 版本影响。
内部 bug,已修。yt-dlp 的下载文件名原本用 source.%(ext)s,和 vidsum 自己写的元数据 source.json 撞名,
通配时把那个 JSON 当成了音频交给 PyAV。现在下载文件叫 media.<ext>,并且会过滤掉 .part / .info.json。
某些环境(尤其是被统一管控的桌面应用)会同时注入 NO_PROXY 和 no_proxy,值里带 [::1] 这类条目,httpx 解析时直接崩,请求都发不出去。
vidsum 全程只用标准库 urllib,不依赖 httpx。模型也建议用本地目录。
两道防线,都是被真实案例打出来的:
-
只覆盖了一部分素材(比如
--limit-seconds试跑)—— 实测喂 4 分钟素材却要它写「全片总览」, 模型会把整期节目脑补成另一套内容。现在覆盖率低于 95% 时会显式注入「严禁推测、材料支撑不了就别写」, 并在summary.md里标注实际覆盖率。 -
完全没有语音内容(纯音乐 MV、无人声画面)—— 实测转写 0 句时,模型照样写出了一千多字的 「全片总览」,镜头、服装、乐手全是它从标题和常识里编的。现在检测到 0 句就直接不调模型, 写一份说明并返回退出码 3。
git clone https://github.com/ExpertKT/vidsum
cd vidsum
python -m venv .venv && .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
python tests/test_units.py # 离线单元测试,不需要网络、不需要装 faster-whisper、不加载模型MIT