下载视频

GitHub

视频下载工具,支持下载视频、音频、播放列表及本地视频烧录中文字幕。具备字幕翻译、自动重试及路径安全校验功能,确保文件正确保存。

skills/xiaohu-video-download/SKILL.md xiaohuailabs/xiaohu-video-translate

Trigger Scenarios

下载视频 下载这个视频 下载音频 下载播放列表 yt-dlp 烧录 烧字幕 重新烧录 烧录字幕

Install

npx skills add xiaohuailabs/xiaohu-video-translate --skill 下载视频 -g -y
More Options

Use without installing

npx skills use xiaohuailabs/xiaohu-video-translate@下载视频

指定 Agent (Claude Code)

npx skills add xiaohuailabs/xiaohu-video-translate --skill 下载视频 -a claude-code -g -y

安装 repo 全部 skill

npx skills add xiaohuailabs/xiaohu-video-translate --all -g -y

预览 repo 内 skill

npx skills add xiaohuailabs/xiaohu-video-translate --list

SKILL.md

Frontmatter
{
    "name": "下载视频",
    "description": "视频下载工具:下载视频和外挂字幕文件、仅下载音频、批量下载播放列表、为本地视频烧录中文字幕。支持字幕翻译功能。当用户说'下载视频''下载这个视频''下载音频''下载播放列表''yt-dlp''烧录''烧字幕''重新烧录''烧录字幕'时使用。"
}

视频下载工具

适用/能力

  • 下载整段视频(获取素材):下载视频文件和外挂中文字幕,无字幕则仅下载视频。
  • 下载播放列表:批量下载多个视频,支持指定范围或全部。
  • 仅下载音频:提取音频并嵌入封面和元数据。
  • 为本地视频添加字幕:从本地视频提取音频 → Whisper 转写 → 翻译成中文 → 烧录字幕到视频画面

强制规则(先读配置再执行)

  • 执行任何命令/写入任何文件前,必须先读取本技能目录 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/...,不要在命令里手写/粘贴完整长路径。

通用配置

代理设置(可选):

  • 如需访问 YouTube 等受限网站,所有 yt-dlp 命令添加:--proxy http://127.0.0.1:7890

路径说明

  • 输出根目录:要求显式配置;临时目录 <输出根>/tmp/(下文用 <输出根> 指定的根路径)
  • 配置方式:在本技能目录下 config.json 设置 output_dir=<绝对路径>,或命令行 --outdir <绝对路径>(命令行优先)。output_dir 为空/缺失/相对路径时,脚本会强制提示用户输入绝对路径并写回 config.json,不允许默认回落技能目录。

转写引擎(默认走脚本 scripts/transcribe_srt.py,自动下模型、零配置):

  • 默认:MLX Whisper / faster-whisper——首次运行自动下载模型,是烧字幕场景的首选(whisper-cli 没有词级时间戳,字幕会不同步,不要用于烧字幕)。
  • 可选备份:whisper-cli(whisper-cpp)——仅纯转文字、或脚本不可用时才用,需自行 brew install whisper-cpp 并把模型下到 ~/.cache/whisper-cpp/
    • 精确模式:~/.cache/whisper-cpp/ggml-large-v3-turbo.bin(默认模型)
    • 快速模式:~/.cache/whisper-cpp/ggml-medium.bin(用户没有要求不允许使用此模型)

转写模式选择逻辑

  • 精确模式(默认):ggml-large-v3-turbo.bin,95% 精度
  • 快速模式ggml-medium.bin,90% 精度,触发条件为用户指令中包含"快速"、"快"或"快速模式"关键词

输出路径选择逻辑

默认:保存到 <输出根>/data/(临时 <输出根>/tmp/),不再询问。
用户/工作流指定路径:优先使用指定路径。
本地视频加字幕:默认询问保存位置;“随便/默认”→视频目录,“同名”→覆盖需确认。

  • 避免误写到当前目录:所有 -o 示例都包含 <输出根>/...,不要使用裸 data/… 相对路径,否则会写到当前工作目录的 data/
  • 首次未配置/配置无效时:脚本会引导用户手动输入绝对路径并写入 config.jsonoutput_dir(需用户确认,不自动填默认)。若已配置有效值则不再提示,如需重新指定请删除/清空 config.jsonoutput_dir 后运行。

使用指导

下载整段视频(获取素材)

使用场景:下载视频文件和外挂中文字幕,非中文字幕自动翻译成中文。不烧录字幕。

处理流程

  1. 下载视频:
yt-dlp -f "bestvideo[vcodec^=avc1][ext=mp4]+bestaudio[ext=m4a]/bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best" \
  --restrict-filenames \
  -o "<输出根>/data/%(title).40s.%(ext)s" \
  "${VIDEO_URL}"
  • 重要:优先选择 H.264 (avc1) 编码。YouTube 的 mp4 容器可能装 AV1 编码,而 X/Twitter 等社交平台不支持 AV1,只认 H.264。vcodec^=avc1 确保下载 H.264 格式,兼容所有主流平台上传。
  • 输出<输出根>/data/<截断标题>.mp4
  • YouTube 失败自动兜底(推荐):如果遇到 HTTP Error 403 / SABR / PO Token 等问题(近一年 YouTube 风控更严格),建议使用本技能脚本自动重试并从浏览器读取 cookies:
python3 scripts/youtube_download.py "${VIDEO_URL}"
  • 首次尝试会走无 cookies(更快);失败后会自动用 --cookies-from-browser chrome 重试。
  • 如你常用浏览器不是 Chrome:python3 scripts/youtube_download.py --browser firefox "${VIDEO_URL}"
  1. 下载字幕文件(VTT 格式):
yt-dlp --skip-download --write-subs --write-auto-subs \
  --sub-lang "zh-Hans,zh-CN,en,ja" \
  -o "<输出根>/tmp/%(id)s.%(ext)s" \
  "${VIDEO_URL}"
  • 临时输出<输出根>/tmp/<视频ID>.<语言代码>.vtt
  • 支持外挂字幕和内嵌字幕流
  1. 按“字幕处理通用规则”整理字幕(见下文),无字幕则仅保留视频。

注意事项

  • 登录/地区限制:提示用户提供 cookies 或切换代理
  • 字幕格式:VTT 格式保留时间戳,AI 翻译时保持结构不变
  • 硬字幕(烧录字幕):无法提取,按"无字幕"处理

仅下载音频

yt-dlp -x --audio-format mp3 --embed-thumbnail --add-metadata \
  --restrict-filenames \
  -o "<输出根>/data/%(title).40s.%(ext)s" \
  "${VIDEO_URL}"
  • 提取音频,嵌入封面和元数据
  • 支持多种音频格式(默认 mp3)
  • 最终输出:保存到 <输出根>/data/<截断标题>.mp3

字幕处理通用规则(单视频/批量共用)

  • 中文字幕:<输出根>/tmp/<视频ID>.zh-Hans.vtt.zh-CN.vtt → 重命名为最终文件名并放入 <输出根>/data/(单视频文件名前缀 <截断标题>,播放列表前缀 <视频标题>-<序号>)。
  • 非中文字幕:AI 翻译原 VTT(保时间戳)→ <输出根>/data/<目标文件名>.zh-Hans.vtt,删除原字幕。
  • 无字幕:保留视频即可。
  • 文件名:单视频用 <截断标题>,播放列表用 <视频标题>-<序号> 对应字幕同名。

批量下载播放列表

使用场景:YouTube/Bilibili 等平台的播放列表、合集、分P。

触发关键词:URL 包含播放列表特征(如 list=、BV 开头的合集等)

交互逻辑

  1. 检测到播放链接 → 查询播放列表信息(视频数量)
  2. 询问用户:"这个播放列表有 X 个视频,要下载哪些?"
  3. 用户回答:
    • "全部"、"所有" → 下载所有视频
    • "前5个"、"1-5"、"1:5" → 下载指定范围
    • "第1、3、5个" → 下载指定视频
    • "只下载第一个" → 下载单个视频(--no-playlist

处理流程

  1. 批量下载视频到 <输出根>/data/(文件名含序号):
yt-dlp -f "bestvideo[vcodec^=avc1][ext=mp4]+bestaudio[ext=m4a]/bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best" \
  --restrict-filenames \
  --playlist-items 1:5 \
  -o "<输出根>/data/%(title).40s-%(playlist_index)s.%(ext)s" \
  "${PLAYLIST_URL}"
  • 输出示例:火星移民计划-1.mp4
  • 关键:使用 %(playlist_index)s 直接写入序号,避免重命名。
  1. 对每个视频:下载字幕(同单视频命令)→ 按“字幕处理通用规则”处理,字幕文件名用 <视频标题>-<序号>
  • 无字幕:保留对应视频即可。
  • 不使用 --embed-subs,不烧录字幕。

抖音下载

  • 命令:python3 scripts/douyin_download.py <URL> --audio|--video|--list [--resolution 1080p] [--outdir <绝对路径>]
  • 输出根来自 config.jsonoutput_dir(或 --outdir 覆盖);临时文件写入 <输出根>/tmp/,最终文件写入 <输出根>/data/
  • 脚本会标准化链接为 /video/<ID>,使用可见浏览器抓取 aweme detail,成功后自动关闭。未抓到/提示未登录时,运行 python3 scripts/douyin_login.py 再重试(状态保存在 data/browser_state/douyin_profile/)。
  • 若下载失败:先阅读 抖音初始化.md,按文档检查登录状态(用登录脚本刷新)、代理/网络、依赖是否缺失,然后重试。

为本地视频添加字幕

使用场景:用户已下载视频,需要添加中文字幕并烧录到视频画面。

触发关键词"添加字幕""补救""加上字幕" + 本地视频路径

处理流程

步骤1:检查是否已有字幕文件

检查视频所在目录是否存在字幕文件(按优先级):

  1. <视频标题>.vtt
  2. <视频标题>.zh-Hans.vtt
  3. <视频标题>.srt

步骤2:根据检查结果选择分支

├─ 步骤2A:如果已有字幕文件 │ (跳转到步骤3烧录) │ └─ 步骤2B:如果没有字幕文件 (使用 Whisper 转写后跳转到步骤3烧录)

步骤2A(已有字幕):中文字幕直接用;外语字幕翻译为 <输出根>/tmp/subtitle_zh.srt|.vtt 后使用。

步骤2B(无字幕)

  1. 提取音频(WAV 格式,16kHz 单声道,Whisper 最佳输入):ffmpeg -y -i "/path/to/video.mp4" -vn -acodec pcm_s16le -ar 16000 -ac 1 <输出根>/tmp/audio.wav
  2. Whisper 转写(默认走脚本,自动下模型;烧字幕必须用脚本拿词级时间戳,不能用 whisper-cli):python3 scripts/transcribe_srt.py "<输出根>/tmp/audio.wav" --output "<输出根>/tmp/audio.srt" --language <语言>
    • 可选备份引擎 whisper-cli 仅用于纯转文字,命令与模型路径见上方「转写引擎」
  3. 若转写结果非中文,按 xiaohu-subtitle-polish 技能的润色规则翻译为 <输出根>/tmp/audio_zh.srt,中文则直接使用。
    • 润色要求:去标点、英文中文间加空格、专有名词保留原文、歌词用 ♪ 包裹、去末尾重复/幻觉

步骤2C:检测视频是否已有硬字幕(烧录前必做)

烧录新字幕前,必须检查视频画面中是否已有硬编码(烧录过的)字幕:

  1. 截帧检查:在视频有人说话的时间点截一帧(通常 10s 左右):
ffmpeg -y -i video.mp4 -ss 00:00:10 -vframes 1 -update 1 /tmp/check_frame.jpg
  1. 目视确认:用 Read 工具查看截帧图片,看底部是否有已烧录的文字
  2. 如果有已有硬字幕 → 走"避让烧录"流程(见下方步骤 3B)
  3. 如果没有已有硬字幕 → 走默认烧录流程(步骤 3A,MarginV=30)

步骤3A:烧录字幕到视频(无已有硬字幕)

使用实际验证成功的烧录命令:

cd "$(dirname "/path/to/video.mp4")"

cp "<视频标题>.mp4" temp_video.mp4
cp "<选定字幕文件>" temp_subs.srt

ffmpeg -y -i temp_video.mp4 \
  -vf "subtitles=temp_subs.srt:force_style='FontName=PingFang SC,Bold=1,FontSize=24,PrimaryColour=&H00FFFFFF,OutlineColour=&H40000000,Outline=1,Shadow=0,MarginV=30'" \
  -c:a copy \
  "<输出文件名>.mp4"

步骤3B:避让烧录(视频已有硬字幕时)

当视频画面底部已有烧录过的字幕(如英文原字幕),新的中文字幕需要放在已有字幕上方,互不遮挡。

关键:ASS 的 MarginV 不等于像素值,不能用像素检测值直接填。必须截帧校准。

流程:

  1. 确定初始 MarginV(根据视频分辨率和已有字幕行数):
视频分辨率 已有字幕单行 已有字幕可能双行
1080p MarginV=38 MarginV=50
720p MarginV=28 MarginV=36
4K MarginV=55 MarginV=70

经验值来源:2026-03-10 Copilot Cowork 视频(1080p,英文字幕单行+双行混合),MarginV=50 验证通过。

  1. 用初始值烧录 → 截帧验证(选一帧已有字幕为双行的时间点):
ffmpeg -y -i output.mp4 -ss <双行字幕时间点> -vframes 1 -update 1 /tmp/verify.jpg
  1. 用 Read 查看截帧

    • 中文和英文之间有间距、互不遮挡 → 完成
    • 中文还是盖住了英文 → MarginV 加 5 重试
    • 中文离英文太远 → MarginV 减 3 重试
  2. 字号建议:已有硬字幕时,新字幕用 FontSize=22(比默认的 24 小一号),视觉上更协调

避让烧录命令模板

cd "$(dirname "/path/to/video.mp4")"

cp "<视频标题>.mp4" temp_video.mp4
cp "<选定字幕文件>" temp_subs.srt

ffmpeg -y -i temp_video.mp4 \
  -vf "subtitles=temp_subs.srt:force_style='FontName=PingFang SC,Bold=1,FontSize=22,PrimaryColour=&H00FFFFFF,OutlineColour=&H40000000,Outline=1,Shadow=0,MarginV=50'" \
  -c:a copy \
  "<输出文件名>.mp4"

说明

  • 先复制视频/字幕为简单文件名(temp_video/temp_subs),避免特殊字符;-c:a copy 不重编码。
  • 烧录字幕后无法关闭。
  • 默认样式:苹方加粗、白字、FontSize=24、轻微半透明黑边(Outline=1)、无阴影、底部留白 30px。此样式经用户确认(2026-02-19,2026-04-24 字号从 20 调到 24)。
  • 避让样式:同上但 FontSize=22、MarginV=50(1080p 双行经验值,2026-03-10 验证,2026-04-24 字号从 18 调到 22)。

步骤4:清理临时文件

# 清理 <输出根>/tmp/ 和当前目录临时文件
rm -f "<输出根>/tmp/audio.mp3" "<输出根>/tmp/audio"*.srt "<输出根>/tmp/subtitle_zh".* temp_video.mp4 temp_subs.srt

输出说明

  • 原视频:保持不变
  • 烧录字幕视频:默认文件名 video_with_subs.mp4字幕已烧录到画面
  • 输出路径
    • 默认询问用户:"烧录字幕的视频需要保存到哪里?"
    • 用户回答"随便"、"默认" → 保存到原视频所在目录
    • 用户回答"同名" → 覆盖原视频(需确认)
    • 用户指定路径 → 保存到指定位置

依赖

所需依赖

  • 工具:yt-dlpffmpegwhisper-cpp(可选备份转写引擎,不装也能用)
  • Python 包:mlx-whisper(Apple 芯片首选)或 faster-whisper(通用首选)
  • 模型:脚本引擎首次运行自动从 HuggingFace 下载;whisper-cpp 备份引擎需自行把 ggml-large-v3-turbo.binggml-medium.bin 下到 ~/.cache/whisper-cpp/

首次使用或依赖缺失时: AI 应自动检测依赖,如发现缺失,读取 初始化.md 文档并按指引自动安装

安装完成后返回主流程继续执行用户请求,后续运行无需再次读取初始化文档。

临时文件处理

临时文件位置<输出根>/tmp/

临时文件类型

  • 字幕文件(.vtt, .srt)- YouTube 下载的原始字幕
  • 音频文件(.mp3)- Whisper 转写时提取的音频
  • 临时复制文件(temp_video.mp4, temp_subs.srt)- 烧录字幕时的简单文件名

清理策略

  • 下载/翻译/转写完成后,可清理 tmp/ 中的原始外文字幕或音频;烧录后清理 temp_video/temp_subs。
  • 用户可随时手动清理 <输出根>/tmp/;最终输出文件(.mp4/.mp3/.vtt)在 <输出根>/data/,不受影响。

错误处理

  • 字幕下载失败:视频无字幕/地区限制/需 PO token → 提供 cookies 或切换代理。
  • 转写失败:模型缺失或音频不支持 → 检查模型、转换音频。
  • 视频下载失败:网络/URL/地区限制 → 检查网络、代理或 cookies。
  • 磁盘不足:清理 <输出根>/tmp/
  • ffmpeg 缺失:按 初始化.md 安装依赖后重试。

Version History

  • c7836c6 Current 2026-07-05 15:30

Same Skill Collection

skills/xiaohu-subtitle-polish/SKILL.md
skills/xiaohu-video-md/SKILL.md

Metadata

Files
0
Version
c7836c6
Hash
40bd5635
Indexed
2026-07-05 15:30

Home - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-07-13 22:07
浙ICP备14020137号-1 $Map of visitor$