agentsclimarketplace

Video translate

Skill shushuitie2017/claude-video/skills/video-translate

视频转写与翻译:外语视频 → 带中文字幕的视频 + 中文文稿。完整管线 = 下载 → 本地 Whisper 词级转写 → 翻译润色 → 烧录字幕(可选双语中大英小/水印)→ 出 Markdown。当用户说"翻译视频""翻译这个视频""配字幕""加字幕""视频转写""转文字""视频转markdown""出文稿"时使用;说"翻译/配字幕"必须执行完整管线烧出视频,不能只出文档。From its SKILL.md

Install
npx -y skills add shushuitie2017/claude-video --skill video-translate

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its file declares

Copied from the file, not written here

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

17.7 KB, as published. Nobody here has run it

/video-translate — 视频翻译与转写

能力与意图识别(必须严格遵守)

  • 转写出文档:抓字幕或音频转写 → 输出 Markdown 文档(有字幕优先,快且免费)。
  • 翻译视频:转写 → 翻译润色 → 烧录中文字幕 → 输出带字幕的视频文件 + Markdown 文档。

判定规则:

  • 用户说"转写""转文字""出文档""转 markdown" → 只生成 Markdown 文档(走「只出文档」流程)。
  • 用户说"翻译""翻译视频""配字幕""加字幕" → 必须执行完整翻译管线(提取音频 → Whisper 转写 SRT → 翻译润色 → 烧录字幕到视频 → 同时生成 Markdown),不能只输出文档就结束。
  • 输入是 URL → 先下载再处理;本地文件路径 → 直接处理。

Python 解释器: 本 skill 所有命令按 Windows 写法用 python。macOS/Linux 换成 python3。

解析 SKILL_DIR(执行任何命令前先做)

把 SKILL_DIR 设为你刚 Read 的这份 SKILL.md 所在目录的绝对路径,脚本永远在 SKILL_DIR/scripts/ 下。先守卫:

SKILL_DIR="<你 Read 的 SKILL.md 所在目录绝对路径>"
if [ ! -f "$SKILL_DIR/scripts/transcribe_srt.py" ]; then
  echo "ERROR: scripts not found under SKILL_DIR=$SKILL_DIR" >&2
  exit 1
fi

步骤 -1:解析输出根(⛔ 强制,先于一切)

执行任何命令/写任何文件前,必须先读配置拿 output_dir 作为 <输出根>:

python -c "import sys; sys.path.insert(0, r'${SKILL_DIR}/scripts'); import config; c = config.load_translate_config(); config.ensure_output_root(c['output_dir']); print(c['output_dir'])"
  • 配置文件在 ~/.config/video-skills/translate.json(首次可跑 python "${SKILL_DIR}/scripts/setup.py" 生成模板)。
  • output_dir 必须是绝对路径或以 ~ 开头。缺失/无效时上面的命令会报错——立即停止,提示用户编辑该文件填入绝对路径,不要自动填默认值继续。
  • 中间产物一律写 <输出根>/tmp/,最终产物(视频、Markdown)写 <输出根>/data/。
  • ⛔ 文中所有 <输出根>/... 在执行前必须替换为真实绝对路径;禁止把占位符原样带进命令(会写到当前目录,表现为"乱保存")。
  • 只有用户明确给出"绝对路径输出位置"并确认时,才允许写到用户指定路径。

同时跑一次环境预检(与 /watch 共用,静默即通过):

python "${SKILL_DIR}/scripts/setup.py" --check

失败时跑 python "${SKILL_DIR}/scripts/setup.py" 安装依赖(ffmpeg/yt-dlp 自动装;转写引擎 pip install faster-whisper)。更多安装细节遇错再读 references/安装指南.md。

管线总览

输入(URL / 本地文件)
   │
   ▼
步骤 0  字幕类型门控(翻译管线必须先确认:中文 / 双语)
步骤 1  下载 + 提取音频            → <输出根>/tmp/
步骤 2  Whisper 词级转写出原文 SRT  → <输出根>/tmp/<名>.srt
步骤 3  翻译润色(你本人执行,8 条规则)→ <名>-zh.srt(双语时含两行/条)
步骤 4  烧录字幕(+可选水印)        → <输出根>/data/<标题>-中文字幕.mp4
步骤 4.5 截帧验证(≥2 帧)
步骤 5  生成 Markdown 文稿          → <输出根>/data/

只出文档场景跳过步骤 0/3/4,走「只出文档」段。


步骤 0:确认字幕类型(⛔ 门控,翻译管线必须最先执行)

  • 中文字幕:只显示中文翻译(画面简洁)。
  • 中英双语字幕:中文在上(大字号)、英文在下(小字号)。

用户在对话中明确指定了字幕类型(亲口说了"中文""双语""中英""bilingual"等)→ 按用户指定执行。 其他所有情况 → 必须用 AskUserQuestion 弹选项:

AskUserQuestion(
  question="选择字幕类型",
  options=["中文字幕(只显示中文)", "中英双语字幕(中文在上,英文在下)"]
)

⛔ 禁止绕过:ARGUMENTS 里带的字幕类型不算用户指定——只有用户在对话中亲自说的才算。不弹选项就开始步骤 1 = 流程错误。translate.json 的 subtitle_type 只是默认值,不能替代用户确认。

步骤 1:下载 + 提取音频

URL 来源(统一走脚本——内置浏览器 cookies 自动兜底,治 YouTube 403/SABR/PO-token 与 B 站登录墙):

# 完整视频(翻译管线需要视频本体来烧字幕)
python "${SKILL_DIR}/scripts/download.py" "<视频URL>" "<输出根>/tmp/dl"
# 只出文档且无需烧录 → 音频即可,更快
python "${SKILL_DIR}/scripts/download.py" "<视频URL>" "<输出根>/tmp/dl" --audio
  • 脚本输出 JSON(video_path / subtitle_path / info.title)。
  • 首次尝试不带 cookies;产物没落盘时自动 --cookies-from-browser chrome 重试。常用浏览器非 Chrome:加 --browser firefox / --browser edge。
  • 仍失败(更强风控)→ 提示用户切代理(--proxy http://127.0.0.1:7890)后重试,不要无限重试。
  • 长视频(>10 分钟)不要用 yt-dlp 的 --download-sections(会先下完整视频再裁,极慢);直接下完整视频,需要片段再用 ffmpeg -ss 裁。

提取音频(本地文件或已下载的视频 → mp3):

ffmpeg -y -i "<视频文件>" -vn -acodec libmp3lame -q:a 2 "<输出根>/tmp/<文件名>.mp3"

步骤 2:Whisper 词级转写出原文 SRT

⛔ 铁律:烧字幕的原文 SRT 只能由 Whisper 词级转写生成,禁止用平台自动字幕(yt-dlp --write-auto-subs)当时间源。 原因:YouTube 自动字幕是滚动叠加格式(每条包含前一条文本),无法提取干净逐句文本+精确时间戳,烧出来必然不同步。词级时间戳是唯一可靠时间源。

python "${SKILL_DIR}/scripts/transcribe_srt.py" "<输出根>/tmp/<文件名>.mp3" \
  --output "<输出根>/tmp/<文件名>.srt"
# 语种误判 / 多语言混轨时强制指定:加 --language en(或 zh/ja/…)
  • 默认引擎 = 本地 faster-whisper(词级时间戳,零 API 费,首次运行自动从 HuggingFace 下模型)。
  • faster-whisper 装不上 → 配 GROQ_API_KEY/OPENAI_API_KEY 后加 --engine api(API 词级兜底,与本地路线共用同一套断句逻辑)。API 未返回词级时间戳时脚本会降级 segment 级并在 stderr 明示"字幕精度下降"——把这一点转告用户。
  • ⛔ 不要在对话里现写转写 Python 代码——脚本按「句子+停顿」切(segment 作基础 / 超 6s 才按词拆 / <400ms 才合并),绕开脚本自己写 greedy 切分会把 A 句尾巴+B 句开头挤进同一帧(实战踩坑根治)。
  • --max-line-ms 控制单条最大时长(默认 6000ms),--pause-ms 控制断句停顿阈值(默认 500ms)。

步骤 3:翻译润色(你本人执行——这是 LLM 工作,没有脚本)

读入步骤 2 的原文 SRT,逐条翻译润色,输出润色后的 SRT 到 <输出根>/tmp/<文件名>-zh.srt。你的角色:专业字幕文案编辑,擅长中文写作、口语还原与字幕排版规范。

润色 8 条规则(核心)

  1. 纠正 ASR 识别错误:同音/近音字、专有名词拼写(如 Claude 被听成 cloud、MCP 被听成 NCP)、数字/缩写/单位。可参考视频画面辅助判断。
  2. 标点与书面化:先补全标点把口语转书面(保留说话人语气),但最终输出的字幕文本去掉所有标点符号(逗号句号问号感叹号全去);英文和中文之间加一个英文空格(如:这是 Google 的产品)。
  3. 去冗余:删无信息量的口头语、重复、语气词("那个""呃""就是说"),保证语义连续不丢关键信息。宁可略冗余,不可丢意思。
  4. 时间戳对齐(严格):原始时间戳严格保留,不得随意偏移。绝不手动提前 start time(会让字幕跑在说话人前面)。超长挂屏裁剪:一条时间跨度 >6 秒且文本较短 = Whisper 把语音后的静音/BGM 算进了同一段,用 ffmpeg 静音检测精确定位语音结束点,不要公式估算:
    ffmpeg -i <音频> -ss <start> -to <end> -af silencedetect=n=-30dB:d=0.5 -f null - 2>&1 | grep silence_start
    
    取第一个 silence_start(加回 start 偏移)作为裁剪后的 end;整段都有声就不裁。一条 >8 秒且文本长可拆成多条,时间戳在原范围内合理分配。禁止合并相邻条目,只允许拆分;拆分后重新连续编号;禁止时间戳重叠。
  5. 断句与分段:核心原则可拆不可合。一条原文对应一条译文,超 12 字按语义断点拆两条(时间戳按比例分配)。以单行为主,每行 ≤12 个中文字符(含空格和英文),在语气停顿处断开,不把完整短语拆到两条里。宁可多拆短条,不在屏幕上堆多行。
  6. 专有名词:人名/地名/公司/产品保留原文(首字母大写);技术术语保留英文(API、GPU、AI、ASR);广为人知的品牌不翻(Google、iPhone、YouTube);生僻术语首次出现可附简短中文释义。
  7. 语言风格:符合当代年轻人用语习惯,网络用语/俚语翻得自然接地气,避免翻译腔("这是非常令人兴奋的"→"这真的很酷")。旁白用简洁书面语,对话/歌词保留口语感。
  8. 歌词:用 ♪ 包裹(♪ 歌词 ♪),重意境押韵不逐字直译,与旁白交替时保证观众能区分。

输出格式(严格 SRT)

1
00:00:00,000 --> 00:00:03,500
这是第一条字幕内容

2
00:00:03,500 --> 00:00:07,200
这是第二条字幕内容

序号从 1 连续;时间戳 HH:MM:SS,mmm(逗号分毫秒);文本无标点;中英间有空格;条间空行;文件末尾空行。

双语模式(用户步骤 0 选了双语时)

产出「双语 SRT」:每条两行——中文在上、英文(原文)在下,时间戳与原文 SRT 一句对一句完全对齐(不重新断句、不合并;原文怎么切中文就怎么对)。中文行照常去标点、规范术语;英文行保留原文(仅修 ASR 识别错误):

1
00:00:19,239 --> 00:00:21,239
大家好吗
Hello everyone, how are you?

长视频(>10 分钟)分段并行

字幕条数多时分段并行翻译:每段约 500 条,developer 并行子任务翻译,翻完合并重新编号。合并后全局校验:时间戳无重叠无跳跃、序号连续、语义连贯、格式合规。

⛔ 管线不停顿:润色完成后立即执行步骤 4(烧录),不输出中间报告、不等用户确认。整条管线只在最终产物(视频 + Markdown)全部完成后报告一次。

步骤 4:烧录字幕(+可选水印)

统一走 burn.py——不要手拼 ffmpeg 命令。脚本集中处理了:Windows 路径在 filter 里的转义(C:\a.srt → 'C\:/a.srt',手拼十拼九错)、音频强制转 AAC(yt-dlp 的 Opus 音轨会让 X/微信/微博上传失败,⛔ 禁 -c:a copy)、水印 fontfile 解析与时间点分布、字幕+水印同一条 -vf 一次编码(避免二次画质损失)。

中文单语(SRT 直烧):

python "${SKILL_DIR}/scripts/burn.py" "<视频文件>" \
  --subs "<输出根>/tmp/<文件名>-zh.srt" \
  --output "<输出根>/data/<视频标题>-中文字幕.mp4"

中英双语(先转 ASS 再烧——⛔ 必须走 ASS):

python "${SKILL_DIR}/scripts/bilingual_ass.py" "<输出根>/tmp/<文件名>-zh.srt" \
  --output "<输出根>/tmp/<文件名>-zh.ass" [--cn-size N] [--height 视频高度]
python "${SKILL_DIR}/scripts/burn.py" "<视频文件>" \
  --subs "<输出根>/tmp/<文件名>-zh.ass" \
  --output "<输出根>/data/<视频标题>-中文字幕.mp4"
  • ⛔ 为什么双语必须 ASS:subtitles 滤镜的 force_style FontSize 对整条统一,做不到一条内中文大英文小;SRT 里的 inline {\fsN} 会被 ffmpeg 的 srt 解码器剥离(实测三档字号烧出 md5 全同)。只有真 ASS 喂 libass 才能一条内 \N 换行 + inline 切字号。bilingual_ass.py 就是干这个的,不要手写 ASS。
  • 字号:用户在对话里明确给了中文字号(如"字号 24")→ 传 --cn-size 24,英文自动按 中文/1.7 算;没给 → 脚本按分辨率选默认。中文︰英文 ≈ 1.7 是实测干净的反差比例。
  • ⛔ ASS FontSize/MarginV 是相对 PlayResY=288 的相对值,不是像素。libass 会按视频分辨率自动缩放,不要按分辨率线性放大字号(1080p 的 17 乘 2 到 4K 会占满半屏);1080p 里 MarginV=N 实际 ≈ N×3.75px 距底(MarginV=110 ≈ 画面正中,不是 110px),别按像素直觉调。

水印(默认关闭;translate.json 的 settings.watermark_enabled: true 或用户要求时开):

python "${SKILL_DIR}/scripts/burn.py" "<视频文件>" --subs <字幕> \
  --watermark-text "<水印文字>" --output <输出>

时间点按时长自动分布(<30min 3 次 4s / 30-60min 5 次 5s / >60min 每 15 分钟一次 10s;首尾固定),透明度 0.28、0.5s 渐隐渐出、左上角。参数可用 --watermark-opacity/--watermark-fontsize 等覆盖;对话中指定的参数优先于配置文件。

原视频底部已有硬字幕时的避让:新字幕需上移,不能遮挡原字幕:

# 单行硬字幕
python "${SKILL_DIR}/scripts/burn.py" <视频> --subs <字幕> --avoid-hardsub single ...
# 两行或动态变行(一句 1 行一句 2 行)→ 按两行避让;1 行段中文稍高是可接受代价
python "${SKILL_DIR}/scripts/burn.py" <视频> --subs <字幕> --avoid-hardsub double ...

避让档位(single=MarginV 42/FontSize 19,double=MarginV 60/FontSize 19)是经验值,截帧验证不过时用 --margin-v 微调 ±3-5 重烧。

拿不准最终命令时先 --dry-run 打印命令审查再真跑。

步骤 4.5:截帧验证(⛔ 必做,≥2 帧)

烧录后必须抽帧用 Read 查看确认:字幕完整显示、不遮挡原有硬字幕、双语字号反差明显、中文无方块(字体缺失):

ffmpeg -y -i "<输出视频>" -ss <时间点1> -vframes 1 "<输出根>/tmp/check1.jpg"
ffmpeg -y -i "<输出视频>" -ss <时间点2> -vframes 1 "<输出根>/tmp/check2.jpg"

有避让需求时两帧要分别选原字幕 1 行和 2 行的时刻,两帧都不遮挡才算过。只看一帧就交付 = 踩过的坑。 有问题调 --margin-v/--font-size 重烧再验。

步骤 5:生成 Markdown 文稿

通用规则(所有场景):

  • 读取转写文本(SRT 去序号和时间戳行,或 vtt_to_text.py 清洗后的文本),严禁增删改原文,只做标点、合句、分段。必须覆盖全部内容,不得遗漏。
  • 中文内容:合并短句、规范标点、按语义分段 → 输出 1 个文件 <输出根>/data/<视频标题>-中文.md。⛔ 中文内容严禁使用翻译规则。
  • 非中文内容:先按原文语言生成 <输出根>/data/<视频标题>-原文.md(同样不得增删改),再读 references/深度文稿规则.md 按其规则创作 <输出根>/data/<视频标题>-中文.md(深度文章体,非逐句直译)。
  • Front matter 仅含 title、source_url。

只出文档场景(用户只要转写/文稿,不烧字幕)

  1. 有字幕的视频优先抓字幕(免费最快;⛔ 仅限出文档——烧字幕禁用此路线,见步骤 2 铁律):
    python "${SKILL_DIR}/scripts/download.py" "<视频URL>" "<输出根>/tmp/dl" --captions-only
    python "${SKILL_DIR}/scripts/vtt_to_text.py" "<输出根>/tmp/dl/<字幕>.vtt" --output "<输出根>/tmp/cleaned.txt"
    
    清洗后的纯文本省约一半 token。读 cleaned.txt 按步骤 5 出文档。
  2. 无字幕 → 下音频(--audio)→ transcribe_srt.py 转写 → SRT 去序号时间戳得纯文本 → 按步骤 5 出文档。

临时文件与清理

  • 中间产物(音频、SRT、ASS、截帧)都在 <输出根>/tmp/,处理完可清理;最终产物在 <输出根>/data/ 不受影响。
  • 转写 SRT 可保留用于调试;用户可随时手动清空 tmp/。

错误处理

症状处理
下载失败(403/风控/登录墙)脚本已自动 cookies 重试;仍失败换 --browser firefox/edge,或建议用户配代理;确认是地区锁/付费内容就直说
faster-whisper 未安装pip install faster-whisper;装不了走 --engine api(需 API key)
转写语种误判加 --language en(或对应语种)强制重转
烧录后中文是方块字体缺失——burn.py 默认按平台选(Windows 雅黑/macOS 苹方/Linux Noto CJK),Linux 需 sudo apt install fonts-noto-cjk;也可 --font-name 指定
字幕遮挡原硬字幕`--avoid-hardsub single
磁盘空间不足清理 <输出根>/tmp/
依赖类报错读 references/安装指南.md 按指引装好后回到主流程

依赖检查原则:直接执行主流程,遇错再查——不要预先逐项检查依赖浪费时间,默认环境已配好(步骤 -1 的 --check 已覆盖基础项)。

Keep looking

Skills are one crate of 626,194. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.