关于 wjs-uploading-video
wjs-uploading-video 是将完成的 MP4 上传到 YouTube 的技能,适用于单个视频或含多个视频及元数据的目录。它通过 requests 手动执行分块续传,并支持失败重试及跳过已上传文件。
主要功能
- 01上传单个视频或批量上传目录
- 02从 UPLOAD_META.md 读取视频元数据
- 03通过分块请求执行续传
- 04对网络错误和服务器错误重试
- 05记录结果并跳过已上传视频
适用场景
- 01将剪辑完成的视频发布到 YouTube
- 02批量上传带有标题和描述的系列视频
- 03为视频设置隐私、播放列表或定时发布
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jianshuo/ claude-skills --skill wjs-uploading-video安装后在 Claude Code 中输入/wjs-uploading-video确认已加载。
附带文件references/credentials-setup.mdscripts/upload_youtube.py
详细介绍
wjs-uploading-video
将已完成的视频上传到 YouTube。默认设置针对该用户的工作流进行了优化(王建硕频道、中国网络环境下使用本地代理、来自 Riverside / 多机位剪辑的 1080p 横屏录制)。
适用场景
- 用户有一个或多个已完成的
.mp4文件,并希望上传到 YouTube - 用户指定了包含多个片段和
UPLOAD_META.md的final/目录 - 用户需要指定隐私设置 / 播放列表 / 定时发布
不适用于:
- 微信视频号上传(没有公开 API;用户通过网页手动上传)
- 抖音 / 小红书 / B 站(使用不同的 API,此处尚未实现)
- 从横屏源制作 YouTube Shorts 版本(先使用
wjs-reframing-video生成 9:16 版本,再通过此技能上传)
前置条件(每台机器只需设置一次)
- Google Cloud OAuth 客户端:必须存在
~/.config/youtube/credentials.json。如果缺少,请参阅references/credentials-setup.md,按其中的 5 分钟设置指南操作。 - Python 依赖:
pip3 install google-auth-oauthlib google-api-python-client requests(上传时严格来说只需要google-auth+requests,但 OAuth 库会一并安装它们)。 - 首次上传会打开浏览器以获取 Google 授权,并写入
~/.config/youtube/token.json。后续运行会静默复用该令牌。
工作原理(以及为何不用常规的 youtube-uploader)
YouTube 的可续传上传协议会在元数据 POST 后返回一个 Location: URL,随后通过分块 PUT 请求接收数据。常规的 google-api-python-client 会通过 httplib2 执行此操作;在该用户的本地 SOCKS+HTTP 代理环境中,后续 PUT 请求会抛出 [Errno 65] No route to host 或 socket.timeout,并无限期卡住。
此技能绕过 httplib2:通过 google-auth 进行 OAuth,然后使用 requests 手动执行可续传上传,并显式传入代理。分块大小为 8 MB(而非常规的 256 KB),以减少通过代理的往返次数。遇到 socket.timeout / ConnectionError / 5xx 时会采用指数退避重试。
如果你想“直接调用 YouTube API 客户端”,不要这么做——它在此环境中会失败。
ModuleNotFoundError: No module named 'google.oauth2' — Homebrew 升级 Python 大版本后,site-packages 可能会丢失(2026-08 从 3.13→3.14 时实际发生过)。不要重新执行 pip install,直接使用 uvx 携带依赖运行(已验证):
uvx --with google-auth --with requests python \
~/.claude/skills/wjs-uploading-video/scripts/upload_youtube.py --dir "/path/to/final"
用法
批量上传 final/ 目录
python3 ~/.claude/skills/wjs-uploading-video/scripts/upload_youtube.py \
--dir "/path/to/final" \
--meta "/path/to/final/UPLOAD_META.md"
脚本会:
- 读取
UPLOAD_META.md,并将每个## NN · filename.mp4区块与--dir中的视频文件配对 - 跳过已记录在
--results-file中的视频(默认值为<dir>/.youtube_upload_results.json)——失败后可以安全地重新运行 - 按顺序上传,每 8 MB 显示一次进度
- 将最终 URL 列表写入
--results-file
单个文件
python3 ~/.claude/skills/wjs-uploading-video/scripts/upload_youtube.py \
--video /path/to/clip.mp4 \
--title "My Title" \
--description "Body text" \
--tags "tag1,tag2,tag3"
常用覆盖选项
| 标志 | 默认值 | 说明 |
|---|---|---|
--privacy | public | private / unlisted / public |
--category | 28 | 28 = 科技。27 = 教育。24 = 娱乐。 |
--made-for-kids | false | YouTube 要求对此进行声明 |
--playlist <ID> | 无 | 将每个已上传的视频添加到播放列表 |
--publish-at <ISO8601> | 无 | 定时发布(需要 --privacy private) |
--credentials | ~/.config/youtube/credentials.json | OAuth 客户端 JSON |
--token | ~/.config/youtube/token.json | 缓存的 OAuth 令牌 |
--chunk-mb | 8 | 如果上传过程中持续失败,可使用更小的分块 |
--dry-run | 关闭 | 解析元数据并列出待上传内容,不访问网络 |
UPLOAD_META.md 格式
解析器期望用户使用以下标准结构:
## 01 · segment_01_no-bugs.mp4
**短标题**
代码没有错误,只有意图不一致
**视频描述**
AI 时代屎山的重新定义...
—— 王建硕 × 任鑫《...》第 1 集
#王建硕 #AI编程 #ClaudeCode
---
映射关系:
## NN · <filename>→ 此区块描述的视频**短标题**(或**Title**)区块 → YouTube 标题,原文照录。短标题也可以,但请注意,YouTube 标题最多可包含 100 个字符——如果想加入系列名称,让标题更丰富,请直接写在**短标题**中**视频描述**(或**Description**)区块 → YouTube 描述,原文照录,并保留末尾的#tag主题标签- 视频描述中的所有
#word标记 → 以逗号分隔的 YouTube 标签(去掉每个标记开头的#;按照全局说明,用户的频道名称王建硕会自动添加在最前面)
标题中的文件名必须与 --dir 中的实际文件匹配。如果文件存在但没有对应的元数据区块,脚本会报错;传入 --allow-missing-meta,即可使用 --title <basename> 和空描述上传该文件。
此技能采用的合理默认值
- 隐私 = 公开:视频会立即发布,并显示在订阅者的信息流和搜索结果中。需要先审核时,可在每次调用时通过
--privacy unlisted(仅持链接者可观看)或--privacy private(仅所有者可观看)覆盖此设置 - 类别 = 28(科学与技术):符合王建硕频道的主要内容;可在每次上传时通过
--category覆盖此设置 - selfDeclaredMadeForKids = false:YouTube 要求设置此项;用户的内容面向成人
- 分块大小 = 8 MB:经验证,此大小在用户的本地代理下可正常工作(256 KB 会卡住)
- 跳过已上传文件:结果文件是唯一依据;删除该文件会强制重新上传
频道名称 CTA 规则
如果要在视频元数据中撰写描述页脚、署名或“订阅我的频道”之类的内容,请使用 王建硕(用户的频道名称)。不要在这些位置填写嘉宾姓名——像任鑫这样的嘉宾应出现在描述正文中,作为对话对象,而不是频道 CTA 栏位中的名称。
故障排除
| 症状 | 解决方法 |
|---|---|
同意页面出现 access_denied 403 | 在 Google Cloud Console 中,将用户的 Google 账号添加到 OAuth 客户端的测试用户列表 |
上传过程中出现 [Errno 65] No route to host | 几乎总是代理问题——验证 curl --max-time 10 https://upload.googleapis.com/upload/youtube/v3/videos 是否返回 4xx(任何 4xx 都表示代理可连接);如果返回 000,则代理已关闭 |
| 上传停滞,没有进度行 | 代理正在静默缓冲。降低 --chunk-mb 4,或重启代理守护进程 |
quotaExceeded(API 单位) | YouTube Data API 的默认配额为每天 10,000 个单位,每次上传消耗 1,600 个单位——因此每天约可上传 6 个视频。可在 Google Cloud Console 中申请提高配额,或分几天上传 |
429 rateLimitExceeded · Video Uploads per day | 这是与 API 单位配额不同的另一项独立限制——YouTube 每个项目的每日视频上传数量(反滥用限制,与项目验证状态有关;上限可能很低)。达到上限后,该批次中的每次上传都会因 init session failed: 429 而失败,全部无法成功。这不是代理或身份验证问题,也无法绕过。该限制会在美国太平洋时间午夜重置(不是你所在地的午夜)。可选方案:等待 PT 时间重置后重新运行(结果文件已清空,因此会重试全部上传);或通过 YouTube Studio 网页界面上传,此方式不会消耗此 API 限制;也可以验证 GCP 项目以提高上限。同一 PT 日内此前的上传(包括手动 API 上传)也会计入此数量。 |
| 令牌刷新失败 | 删除 ~/.config/youtube/token.json 并重新运行;OAuth 浏览器流程会重新启动 |
上传后
- 结果保存到
--results-file(JSON:文件、标题、视频 ID、URL) - 以 Markdown 表格形式将 URL 列表返回给用户
- 视频默认设为
public——上传完成后会立即发布。如果用户希望先预留审核时间,请告知他们可以使用--privacy unlisted重新上传(或在 YouTube Studio 中改回仅持链接者可观看) - 如果用户提到合集或系列,可提出创建 YouTube 播放列表并将新上传的视频批量添加进去(用户必须提供播放列表 ID,或允许你创建一个)