视频转写
GitHub视频转写与翻译工具,支持音频转文字生成Markdown或执行完整管线(提取、转写、翻译、烧录字幕)输出带字幕视频。支持精确/快速模式及中英双语定制。
Trigger Scenarios
Install
npx skills add xiaohuailabs/xiaohu-video-translate --skill 视频转写 -g -y
SKILL.md
Frontmatter
{
"name": "视频转写",
"description": "视频转写与翻译工具:抓取字幕\/音频转写 → 输出 Markdown 文档;翻译视频 → 转写+翻译+烧录中文字幕。支持精确\/快速两种转写模式。当用户说'视频转写''转写视频''翻译视频''视频转文字''配字幕''加字幕''视频转markdown'时必须执行完整翻译管线(提取音频→Whisper转写SRT→调用subtitle-polish翻译→烧录字幕到视频),不能只输出文档。"
}
视频转写工具
适用/能力
- 转写出文档:抓取字幕或音频转写 → 输出 Markdown 文档(有字幕优先)。
- 翻译视频:转写 → 翻译 → 烧录中文字幕 → 输出带字幕的视频文件。
- 支持精确模式(默认,large-v3-turbo,95% 精度)和快速模式(medium,90% 精度,快 81%)。
- 双版本输出:中文内容生成中文版,非中文内容生成原文版 + 中文翻译版。
意图识别(必须严格遵守)
- 用户说"转写""转文字""出文档" → 只生成 Markdown 文档
- 用户说"翻译""翻译视频""配字幕""加字幕" → 必须执行完整的翻译视频管线(提取音频 → Whisper 生成 SRT → 调用 subtitle-polish 翻译 → 烧录字幕到视频 → 同时生成 Markdown 文档),不能只输出文档就结束
- 输入是 URL → 先下载再处理
- 输入是本地文件路径 → 直接处理
字幕类型(翻译管线必须先确认)
- 中文字幕:只显示中文翻译
- 中英双语字幕:中文在上,英文在下
翻译视频时,除非用户在对话中明确指定了字幕类型(说了"中文""双语""中英"等),否则必须用 AskUserQuestion 弹选项让用户选择。 ARGUMENTS 里的字幕类型不算用户指定。示例:
AskUserQuestion(
question="选择字幕类型",
options=["中文字幕(只显示中文)", "中英双语字幕(中文在上,英文在下)"]
)
双语字幕 SRT 格式(每条两行):
1
00:00:03,660 --> 00:00:06,360
中文翻译在上面
English original below
双语烧录走 ASS,不走 subtitles + force_style(中文大 / 英文小,形成反差):
- subtitle-polish 翻译产出「双语 SRT」(中文在上、英文在下,时间戳一句对一句)后,调脚本转双语 ASS:
python3 ~/.claude/skills/xiaohu-subtitle-polish/scripts/bilingual_ass.py \ <双语SRT> --output <双语ASS> [--cn-size N] [--height 视频高度] - 再用
ass=<双语ASS>滤镜烧录(不是subtitles=)。完整规则见xiaohu-subtitle-polishSKILL.md「双语字幕模式」段。 - 字号:用户给了中文字号就传
--cn-size,英文自动按 中文 / 1.7 算;没给则脚本按分辨率选默认。中文︰英文 ≈ 1.7 是实测干净反差。
⛔ 为什么不用 subtitles force_style 做双语:force_style 的 FontSize 对整条统一,做不到一条内中文大英文小;SRT 里的 inline {\fsN} 会被 ffmpeg 剥离(2026-05-31 实测三档字号 md5 全同)。只有 ASS 喂 libass 才能一条内分字号。
⛔ ASS FontSize 是相对值不是像素:libass 已按视频分辨率自动缩放,不要按分辨率线性放大(1080p 的 17 乘 2 变 34 会让 4K 字幕占满下半屏)。2026-05-26 Jony Ive 法拉利视频踩坑根治。
强制规则(先读配置再执行)
- 执行任何命令/写入任何文件前,必须先读取本技能目录
config.json,取output_dir作为<输出根>(支持~展开)。 - 文档中的所有
<输出根>/...在执行前必须替换为真实绝对路径;禁止把<输出根>原样带到命令里执行(否则会写到当前目录,表现为“乱保存”)。 - 若
config.json缺失、output_dir为空/相对路径/不可写:立即停止并提示用户在网页面板填写,或用--outdir <绝对路径>覆盖。 - 只有当用户明确给出“绝对路径输出位置”并确认时,才允许写入用户指定路径;否则默认只写
<输出根>/tmp/与<输出根>/data/。
建议的“输出根解析”固定前置步骤(避免手打长路径导致误写目录):
# 在本技能目录执行
OUTPUT_ROOT="$(python3 - <<'PY'
import json, os
from pathlib import Path
cfg = json.load(open("config.json", "r", encoding="utf-8"))
out = Path(os.path.expanduser(str(cfg.get("output_dir", ""))).strip())
if not out.is_absolute():
raise SystemExit("config.json: output_dir 必须是绝对路径或以 ~ 开头")
print(str(out))
PY
)"
mkdir -p "$OUTPUT_ROOT/tmp" "$OUTPUT_ROOT/data"
echo "OUTPUT_ROOT=$OUTPUT_ROOT"
后续所有命令都必须使用 $OUTPUT_ROOT/...,不要在命令里手写/粘贴完整长路径。
通用配置
路径说明:
- 脚本目录:
scripts/ - 输出根需显式配置:在本技能目录下
config.json设置output_dir=<绝对路径>,或调用时用--outdir <绝对路径>(命令行优先)。必须是绝对路径;output_dir为空/缺失/相对路径时,脚本会强制提示用户输入绝对路径并写回config.json,不会默认回落技能目录。根目录下自动创建/使用tmp/(中间产物)和data/(最终产物)。
转写引擎(默认走脚本,零配置开箱即用):
- 默认:MLX Whisper / faster-whisper(
scripts/transcribe_srt.py)——首次运行自动从 HuggingFace 下载模型,无需手动准备。所有场景一律首选脚本。 - 可选备份:whisper-cli(whisper-cpp)——仅当用户明确指定、或上面两个都装不了时才用。需自行
brew install whisper-cpp并把 ggml 模型下到~/.cache/whisper-cpp/(见初始化.md):- 精确模式:
~/.cache/whisper-cpp/ggml-large-v3-turbo.bin(默认模型) - 快速模式:
~/.cache/whisper-cpp/ggml-medium.bin(用户没有要求不允许使用此模型)
- 精确模式:
输出路径选择逻辑
- 步骤0:先解析输出根(命令行
--outdir→config.json),output_dir缺失/无效时立即提示用户输入绝对路径并写回config.json,未配置不可继续。 - 默认将结果写入
<输出根>/data/,不再询问;用户指令/工作流若明确指定路径,优先使用指定路径(必须是绝对路径)。
转写方案选择逻辑(按场景和时长)
烧字幕场景(需要精确时间戳)
根据音频时长选择方案:
| 音频时长 | 方案 | 原因 |
|---|---|---|
| <2 分钟 | faster-whisper | word-level 精度最高,CPU 几秒完成 |
| ≥2 分钟 | MLX Whisper | word-level + Metal GPU,速度和精度兼得 |
faster-whisper 命令(<2分钟短视频):
python3 scripts/transcribe_srt.py "<输入文件>" --output "<输出文件>.srt"
MLX Whisper 命令(≥2分钟,翻译管线默认)— 一律走脚本,不在对话里现写 Python:
python3 scripts/transcribe_srt.py "<输入文件>" --output "<输出文件>.srt"
# 语种误判时加 --language en
脚本默认按 Whisper segment 边界切(不是按字符 greedy 切),下游字幕不会出现夹生帧。
禁止在烧字幕场景使用 whisper-cli:没有 word-level timestamps,字幕会明显不同步。
只出文档场景(不需要精确时间戳)
默认同样走脚本(自动下模型、零配置),转写后从 SRT 取纯文本(去掉序号和时间戳行)即可:
python3 scripts/transcribe_srt.py "<输入文件>" --output "<输出文件>.srt"
可选备份:若已装 whisper-cpp 且想要最快的纯文本输出,可用 whisper-cli(需自备 ggml 模型,见上方「转写引擎」):
whisper-cli -m ~/.cache/whisper-cpp/ggml-large-v3-turbo.bin -t 8 <输入文件> -otxt # 快速模式(用户说"快速"/"快"):把模型换成 ggml-medium.bin
长视频特殊处理(>10分钟)
- 字幕翻译必须分段并行:每段约 500 条,用并行 Agent 翻译,翻译完合并重新编号
- YouTube 视频下载不要用
--download-sections(会先下完整视频再裁剪,极慢),直接下完整视频后用 ffmpeg-ss裁剪 - 下载分辨率一步到位选 1080p,优先 AV1 格式(体积最小)
通用 Markdown 生成规则(适用于所有场景)
- 读取指定文本(清洗后的字幕
cleaned.txt或 Whisper 输出 txt),严禁增删改原文,只做标点、合句、分段。 - 判定语言:
- 中文:合并短句、规范标点、按语义分段,输出 1 个文件:
<输出根>/data/<视频标题>-中文.md。 - 非中文:先按原文语言生成
<输出根>/data/<视频标题>-原文.md(同样不得增删改),再按非中文翻译规则.md翻译该原文生成<输出根>/data/<视频标题>-中文.md。
- 中文:合并短句、规范标点、按语义分段,输出 1 个文件:
- 元信息 front matter:
title、source_url。 - 必须覆盖全部文本内容,不得遗漏。
使用指导
抓取字幕(仅限出文档场景)
使用场景:只出文档时,视频有字幕则优先抓取字幕(比 Whisper 快)。 ⛔ 烧字幕场景禁止使用本流程:YouTube 自动字幕是滚动叠加格式,无法提取干净的逐句文本+精确时间戳。烧字幕必须走"翻译视频管线"用 Whisper 转写。
处理流程:
-
先确保输出根有效(步骤0 已完成)。
-
仅抓字幕(不下载视频):
# 下载 .vtt 字幕(最快)
yt-dlp --skip-download --write-subs --write-auto-subs \
--sub-lang "zh-Hans,zh-CN,en" \
-o "<输出根>/tmp/%(id)s.%(ext)s" \
"<视频URL>"
- 优先下载 .vtt 格式(最快,无需转换)。
- 兜底机制:如果字幕下载失败,自动切换到"无字幕兜底(音频转写)"流程
- YouTube 若缺字幕/需 PO token:推荐直接用脚本自动重试并从浏览器读取 cookies:
python3 scripts/youtube_subs_download.py "<视频URL>"
- 首次尝试无 cookies(更快);失败后自动
--cookies-from-browser chrome重试。 - 常用浏览器非 Chrome:
python3 scripts/youtube_subs_download.py --browser firefox "<视频URL>" - 仍失败(PO token / 更强风控):再提示用户提供
--extractor-args "youtube:po_token=..."或切换代理后重试。
- 使用脚本清洗字幕(减少 50% tokens 消耗):
# 清洗 vtt/srt → 纯文本(每行一句)
# <id>.zh-Hans.vtt 为 yt-dlp 实际输出的字幕文件名(语言代码可能是 zh-Hans、zh-CN 等)
scripts/vtt_to_text.sh "<输出根>/tmp/<id>.<语言代码>.vtt" > "<输出根>/tmp/cleaned.txt"
- AI 读取清洗后的文本并生成 Markdown:
- 读取
<输出根>/tmp/cleaned.txt全部内容,按“通用 Markdown 生成规则”产出。
无字幕兜底(音频转写)
使用场景:当视频没有字幕时,自动提取音频并用 Whisper 转写。
处理流程:
- 提取音频(截断标题并去符号,文件名更短):
yt-dlp -x --audio-format mp3 --embed-thumbnail --add-metadata \
--restrict-filenames \
-o "<输出根>/tmp/%(title).40s.%(ext)s" \
"<视频URL>"
- YouTube 若遇 403/SABR:推荐用脚本失败自动读取浏览器 cookies 重试:
python3 scripts/youtube_audio_download.py "<视频URL>"
- Whisper 转写(默认走脚本,自动下模型;不指定语言让其自动检测中/英/日等):
python3 scripts/transcribe_srt.py "<输出根>/tmp/<audio>.mp3" --output "<输出根>/tmp/<audio>.srt"
可选备份:已装 whisper-cpp 时可用 whisper-cli 直接出 txt(需自备 ggml 模型,见上方「转写引擎」):
whisper-cli -m ~/.cache/whisper-cpp/ggml-large-v3-turbo.bin -t 8 "<输出根>/tmp/<audio>.mp3" -otxt
- AI 读取转写文本并生成 Markdown:
- 读取
<输出根>/tmp/<audio>.srt(默认脚本路线)或<audio>.mp3.txt(whisper-cli 路线),去掉序号和时间戳行得到纯文本,按“通用 Markdown 生成规则”产出(<audio>即视频ID)。
抖音视频处理
重要原则:
- 不要预先检查依赖和登录状态,直接执行主流程
- 遇到错误时再检查,这样可以节省时间并提高效率
- 信任环境配置,假设依赖和登录状态正常
- 输出根目录为必填:需在本技能目录
config.json设置output_dir(绝对路径),或调用时使用--outdir <绝对路径>(命令行优先)。缺失/无效时脚本会提示用户输入绝对路径并写回config.json。根目录下使用tmp/、data/。 - 抓取使用可见浏览器(非 headless)以获取 aweme detail,脚本会将链接标准化为
https://www.douyin.com/video/<ID>。
链接格式转换(脚本自动处理):短链需先跳转取完整 URL;modal_id 取 ID 后拼 /video/ID;标准 /video/<ID> 可直接使用。
处理流程:
- 下载音频:
python3 scripts/douyin_download.py <抖音视频URL> --audio
输出:<输出根>/tmp/<视频标题>-音频.m4a
- 转换音频格式:
ffmpeg -i "<输出根>/tmp/<视频标题>-音频.m4a" -acodec libmp3lame -q:a 2 "<输出根>/tmp/<视频标题>-音频.mp3"
- Whisper 转写(默认走脚本,自动下模型):
python3 scripts/transcribe_srt.py "<输出根>/tmp/<视频标题>-音频.mp3" --output "<输出根>/tmp/<视频标题>-音频.srt" --language zh
(其他语言可改 --language en/ja/...;可选备份引擎 whisper-cli 见上方「转写引擎」)
- 生成 Markdown:
- 读取
<输出根>/tmp/<视频标题>-音频.srt,去掉序号和时间戳行得到纯文本,按“通用 Markdown 生成规则”产出(默认中文)。
- 读取
错误处理:
- 步骤1失败(下载音频):
- 如果报错
ModuleNotFoundError: No module named 'patchright',读取抖音初始化.md安装依赖 - 如果报错
未检测到浏览器登录状态,运行python3 scripts/douyin_login.py进行登录 - 其他错误 → 读取
抖音初始化.md查看技术细节和解决方案
- 如果报错
- 其他步骤失败 → 读取
抖音初始化.md查看技术细节和解决方案
翻译视频管线(完整流程)
触发条件:用户说"翻译视频""翻译该视频""配字幕""加字幕"等——最终目标 = 输出一个带中文字幕的视频文件(同时生成 Markdown 文档)。
⛔ 铁律:翻译管线的英文 SRT 只能由 Whisper 转写生成,禁止使用 YouTube 自动字幕(yt-dlp --write-auto-subs)。 原因:YouTube 自动字幕是滚动叠加格式(每条包含前一条文本),无法提取干净逐句文本+精确时间戳,导致烧录后字幕不同步。Whisper word-level timestamps 是唯一可靠的时间源。
输入判断:
- URL → 先用 yt-dlp / douyin_download 下载视频文件到
<输出根>/tmp/(只下视频,不抓字幕) - 本地文件路径 → 直接使用
管线步骤:
步骤 0:确认字幕类型(⛔ 门控,必须在任何其他步骤之前执行)
用户在对话中明确指定了字幕类型(说了"中文""双语""中英""bilingual"等)→ 按用户指定的类型执行。 其他所有情况 → 必须用 AskUserQuestion 弹选项:
AskUserQuestion(
question="选择字幕类型",
options=["中文字幕(只显示中文)", "中英双语字幕(中文在上,英文在下)"]
)
⛔ 禁止绕过:ARGUMENTS 里的字幕类型不算用户指定——只有用户在对话中亲自说的才算。不弹选项就开始步骤1 = 流程错误。config.json 的 subtitle_type 只是默认值,不能替代用户确认。
步骤 1:提取音频
ffmpeg -i "<视频文件>" -vn -acodec libmp3lame -q:a 2 "<输出根>/tmp/<文件名>.mp3"
步骤 2:精确转写生成原文 SRT(按时长选方案)
短视频(<2分钟)→ faster-whisper:
python3 scripts/transcribe_srt.py "<输出根>/tmp/<文件名>.mp3" \
--output "<输出根>/tmp/<文件名>.srt"
长视频(≥2分钟)→ MLX Whisper(默认,仍走脚本):
python3 scripts/transcribe_srt.py "<输出根>/tmp/<文件名>.mp3" \
--output "<输出根>/tmp/<文件名>.srt"
多语言混轨/语种误判 → 加 --language en(强制重转):
python3 scripts/transcribe_srt.py "<输出根>/tmp/<文件名>.mp3" \
--output "<输出根>/tmp/<文件名>.srt" \
--language en
⛔ 任何情况下不要在对话里现写 mlx_whisper Python 代码——脚本已经按 Whisper segment 切(merge_words_to_segments 函数:segment 作基础 / 超 6s 才按词拆 / <500ms 才合并),绕开脚本自己写 MAX_CHARS=50 greedy 会把 A 句尾巴+B 句开头挤进同一帧(2026-05-28 ElevenLabs 踩坑根治)。
脚本特性:
- MLX Whisper Metal GPU 加速,word-level timestamps 精度最高(140 分钟音频约 5-8 分钟完成)
--language支持强制语种(en/zh/ja 等),不传则自动检测--max-line-ms控制单条最大时长(默认 6000ms)- MLX 不可用自动降级 faster-whisper
兜底:若 MLX Whisper 未安装(pip3 install --break-system-packages mlx-whisper),用 faster-whisper。若 faster-whisper 也不可用,最后用 whisper-cli -ml 50(精度最低,仅应急)。
输出:<输出根>/tmp/<文件名>.srt(原文 SRT,word-level 精确时间戳)
步骤 3:调用 xiaohu-subtitle-polish 技能翻译润色
使用 Skill 工具调用,传入原文 SRT 路径:
# 中文单语
/xiaohu-subtitle-polish <输出根>/tmp/<文件名>.srt --output <输出根>/tmp/<文件名>-zh.srt
# 中英双语(步骤 0 用户选了双语时)
/xiaohu-subtitle-polish <输出根>/tmp/<文件名>.srt --output <输出根>/tmp/<文件名>-zh.srt --bilingual
- subtitle-polish 负责全部字幕工作:翻译、去标点、断句(≤18字/行)、时间戳对齐、去冗余、专有名词处理
- 双语模式:subtitle-polish 产出「双语 SRT」(中文在上、英文在下,时间戳一句对一句),具体见其 SKILL.md「双语字幕模式」段
- video-md 不做任何字幕翻译和润色,全部交给 subtitle-polish
- 输出:润色后的中文 SRT(单语)或双语 SRT(双语)
⛔ subtitle-polish Skill 返回后,立即执行步骤 4(烧录)。不输出任何中间报告,不等用户确认。subtitle-polish 有自己的输出报告步骤,在管线中忽略它,直接继续。
步骤 4:烧录字幕 + 水印(默认带水印)
默认行为:从 config.json 的 settings 读取水印配置(watermark_enabled、watermark_text 等)。默认开启水印,用户说"不要水印""去掉水印"时关闭。
字幕 + 水印必须在同一条 ffmpeg 命令中完成(一次编码,避免画质损失)。
双语先转 ASS 再烧:双语模式下,先把双语 SRT 转成双语 ASS(中文大 / 英文小),烧录时用 ass=<双语ASS> 替代下面命令里的 subtitles=<SRT>:force_style=...,其余(水印 drawtext、-c:a aac)不变:
python3 ~/.claude/skills/xiaohu-subtitle-polish/scripts/bilingual_ass.py \
<输出根>/tmp/<文件名>-zh.srt --output <输出根>/tmp/<文件名>-zh.ass [--cn-size N] [--height 高度]
# 烧录:-vf "ass=<输出根>/tmp/<文件名>-zh.ass,drawtext=...水印..."
中文单语仍按下面的 subtitles= + force_style 烧。
音频编码强制 AAC:yt-dlp 下载的音频经常是 Opus 编码,X/微信等平台不支持。烧录时必须用 -c:a aac -b:a 128k,禁止用 -c:a copy(2026-03-17 踩坑:Opus 音频导致 X 上传失败)。
原视频有硬字幕时的避让:如果原视频底部已有烧录过的字幕(如英文原字幕),新字幕需上移避让,不能遮挡原字幕。按分辨率选参数(详见 xiaohu-subtitle-polish SKILL.md 烧录段):
- 720p 单行硬字幕:
MarginV=42, FontSize=18 - 1080p 单行硬字幕:
MarginV=42, FontSize=19 - 1080p 两行硬字幕(动态变行也按此值):
MarginV=60, FontSize=19 - 4K 单行硬字幕:
MarginV=42, FontSize=19
⚠️ MarginV 单位不是像素——它相对 ASS 默认 PlayResY=288。在 1080p 视频里 MarginV=N 实际渲染高度 ≈ N × 3.75 px from bottom。所以 MarginV=110 → 412px ≈ 画面正中(不是 110px 距底),别按像素直觉调(2026-05-29 陶哲轩视频踩坑根治:我之前按像素直觉设 110 把中文推到画面正中)。
原英文动态变行(一句 1 行一句 2 行):用避两行的 MarginV=60,1 行段中文显得稍高一点点是可接受代价(远好过中文挤进 2 行英文中间)。
烧录后必须抽 ≥2 帧验证——选原英文是 1 行的一帧 + 是 2 行的一帧,两帧都不遮挡才算过。只看一帧就交付 = 文档已警告过的踩坑。
无水印烧录(仅用户明确说"不要水印"时):
ffmpeg -y -i "<视频文件>" \
-vf "subtitles=<输出根>/tmp/<文件名>-zh.srt:force_style='FontName=PingFang SC,Bold=1,FontSize=20,PrimaryColour=&H00FFFFFF,OutlineColour=&H40000000,Outline=1,Shadow=0,MarginV=30'" \
-c:a aac -b:a 128k ~/Downloads/<视频标题>-中文字幕.mp4
带水印烧录(默认):
macOS drawtext 中文字体问题(重要): ffmpeg 的
drawtext滤镜使用 fontconfig 查找字体,但 macOS 的 fontconfig 无法索引苹方,会回退到 Verdana(不支持中文)→ 中文显示为方块。 必须使用fontfile=指定字体文件绝对路径,禁止使用font=名称查找。 注意:subtitles滤镜用 CoreText 不受此影响,只有drawtext需要fontfile。 水印字体用圆体(Yuanti SC),比苹方更好看,且 index 0 就是 SC 简体,不会出现方块问题。PingFang.ttc 的 index 0 是 HK 变体,简体字符显示为方块。
# 1. 解析水印中文字体路径(圆体 Yuanti SC,index 0 = SC 简体)
FONT=$(find /System/Library/AssetsV2 -name "Yuanti.ttc" 2>/dev/null | head -1)
[ -z "$FONT" ] && FONT=$(find /System/Library/AssetsV2 -name "PingFang.ttc" 2>/dev/null | head -1)
[ -z "$FONT" ] && FONT="/System/Library/Fonts/STHeiti Medium.ttc"
# 2. 字幕 + 间歇水印一次烧录
# 水印默认参数:透明度 28%、每次 4 秒、0.5 秒渐隐渐出、左上角、出现 3 次
# 时间点均匀分布(示例:8s/38s/72s,根据视频时长调整)
ffmpeg -y -i "<视频文件>" \
-vf "subtitles=<SRT路径>:force_style='FontName=PingFang SC,Bold=1,FontSize=20,PrimaryColour=&H00FFFFFF,OutlineColour=&H40000000,Outline=1,Shadow=0,MarginV=30',\
drawtext=text='<水印文字>':fontfile=${FONT}:fontsize=44:fontcolor=white:alpha='0.28*min(min((t-T1)/0.5,1),(T1+4-t)/0.5)':x=40:y=35:enable='between(t,T1,T1+4)',\
drawtext=text='<水印文字>':fontfile=${FONT}:fontsize=44:fontcolor=white:alpha='0.28*min(min((t-T2)/0.5,1),(T2+4-t)/0.5)':x=40:y=35:enable='between(t,T2,T2+4)',\
drawtext=text='<水印文字>':fontfile=${FONT}:fontsize=44:fontcolor=white:alpha='0.28*min(min((t-T3)/0.5,1),(T3+4-t)/0.5)':x=40:y=35:enable='between(t,T3,T3+4)'" \
-c:a aac -b:a 128k ~/Downloads/<视频标题>-中文字幕.mp4
水印参数说明:
| 参数 | 默认值 | 说明 |
|---|---|---|
| 透明度 | 0.28(28%) | 淡而可见,不干扰观看 |
| 渐隐渐出 | 0.5 秒 | 自然过渡,不突兀 |
| 每次持续 | 见下方规则 | 按视频时长调整 |
| 最少次数 | 3 次 | 开头、中间、结尾必须各有一次 |
| 位置 | 左上角 (x=40, y=35) | 不遮挡字幕 |
| 字号 | 44 | 4K 视频下小巧;1080p 可降至 24-28 |
水印参数从 config.json 的 settings 读取,用户可在配置面板修改。命令行/对话中指定的参数优先于配置文件。
水印次数和持续时间计算规则(按视频时长动态调整):
| 视频时长 | 水印次数 | 每次持续 |
|---|---|---|
| <5 分钟 | 3 次 | 4 秒 |
| 5-30 分钟 | 3 次 | 4 秒 |
| 30-60 分钟 | 5 次 | 5 秒 |
| >60 分钟 | 每 15 分钟 1 次(最少 5 次) | 10 秒 |
时间点分布规则:
- 第 1 次固定在开头(约 8-15 秒处)
- 最后 1 次固定在结尾前(结尾前 15-30 秒)
- 中间的次数在首尾之间均匀分布
- 例:140 分钟视频 → 9 次(每 15 分钟),T=15s/900s/1800s/2700s/3600s/4500s/5400s/6300s/8400s
输出保存到 ~/Downloads/。
步骤 5:生成 Markdown 文档(与字幕管线并行)
- 同时用
-otxt模式获取纯文本,或直接读取 SRT 内容 - 按"通用 Markdown 生成规则"生成文档(非中文 → 原文版 + 中文版)
核心原则:
- 字幕翻译润色的所有规则由
xiaohu-subtitle-polish技能定义和执行,video-md 不重复定义、不自行实现 - video-md 的职责 = 音频提取 + Whisper 转写 + 编排管线 + Markdown 生成
- 这是管线编排(orchestration),不是规则复制(duplication)
- ⛔ 管线不停顿:subtitle-polish 返回后必须直接继续烧录步骤(步骤 4),不得停下来报告中间结果等用户确认。整条管线只在最终产物(视频文件 + Markdown)全部完成后报告一次
Markdown 清洗要点
遵循“通用 Markdown 生成规则”:严禁增删改原文,仅做标点/分段/合并短句,front matter 仅含 title、source_url。
依赖
所需依赖:
- 工具:
yt-dlp、ffmpeg;whisper-cpp(可选备份转写引擎,不装也能用) - Python 包:
mlx-whisper(Apple 芯片首选)或faster-whisper(通用首选)、patchright(抖音视频处理必需) - 脚本:
scripts/transcribe_srt.py、scripts/vtt_to_text.sh、scripts/douyin_login.py、scripts/douyin_download.py - 模型:mlx-whisper / faster-whisper 首次运行自动从 HuggingFace 下载,无需手动准备;whisper-cpp 备份引擎需自行把
ggml-large-v3-turbo.bin、ggml-medium.bin下到~/.cache/whisper-cpp/
依赖检查原则:
- 直接执行主流程,遇错再查
初始化.md/抖音初始化.md补依赖,默认假设环境已装好
当遇到依赖相关错误时:
- 读取相应的初始化文档并按指引自动安装
- 常规初始化:
初始化.md - 抖音初始化:
抖音初始化.md - 安装完成后返回主流程继续执行用户请求
临时文件处理
临时文件位置:<输出根>/tmp/
临时文件类型:
- 字幕文件(.vtt, .srt)
- Whisper 转写输出的文本文件(.mp3.txt)
- 清洗后的字幕文本文件(cleaned.txt,仅字幕抓取时使用)
清理策略:
- AI 处理完成后,可清理临时字幕文件
- Whisper 转写结果文件可保留用于调试
- 用户可随时手动清理
<输出根>/tmp/目录 - 重要:最终输出文件(.md)保存在
<输出根>/data/,不受影响
错误处理
常见错误及处理方法:
-
字幕下载失败:
- 原因:视频无字幕、地区限制、需要 PO token
- 处理:提示用户提供 cookies(
--cookies-from-browser chrome)或切换代理 - 兜底:自动切换到音频转写流程
-
Whisper 转写失败:
- 原因:模型文件缺失、音频格式不支持
- 处理:检查模型是否已下载,尝试转换音频格式
-
磁盘空间不足:
- 原因:临时文件或输出文件占用空间过大
- 处理:清理
<输出根>/tmp/临时文件目录
翻译规则文档
- 非中文内容翻译时,按需读取
非中文翻译规则.md并遵守其中规则。 - 中文内容严禁使用翻译规则;仅做标点、分段、合并短句,不得增删改原文。
Version History
- c7836c6 Current 2026-07-05 15:31


