wjs-uploading-video
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinesewjs-uploading-video
wjs-uploading-video
Push finished videos to YouTube. Defaults are tuned for this user's workflow (王建硕 channel, China network with local proxy, 1080p horizontal recordings from Riverside / multicam edits).
将已完成的视频推送至YouTube。默认配置已针对该用户的工作流进行优化(王建硕频道、中国网络环境搭配本地代理、来自Riverside的1080p横向录制视频或多机位剪辑视频)。
When to use
使用场景
- User has one or more finished files and wants them on YouTube
.mp4 - User points to a directory with multiple segments and an
final/UPLOAD_META.md - User wants a specific privacy / playlist / scheduled publish
Don't use for:
- 微信视频号 upload (no public API; user uploads manually via web)
- 抖音 / 小红书 / B 站 (different APIs, not yet implemented here)
- YouTube Shorts variants from horizontal source (use first to produce the 9:16 cut, then upload that via this skill)
wjs-reframing-video
- 用户拥有一个或多个已完成的文件,希望上传至YouTube
.mp4 - 用户指定包含多段视频和文件的
UPLOAD_META.md目录final/ - 用户需要设置特定的隐私权限、播放列表或定时发布
请勿用于:
- 微信视频号上传(无公开API;用户需通过网页手动上传)
- 抖音/小红书/B站上传(不同API,暂未在此实现)
- 从横向视频源生成YouTube Shorts变体(需先使用生成9:16比例的剪辑版本,再通过本工具上传)
wjs-reframing-video
Prerequisites (one-time per machine)
前置条件(每台机器只需配置一次)
- Google Cloud OAuth client: must exist. See
~/.config/youtube/credentials.jsonfor the 5-minute setup if missing.references/credentials-setup.md - Python deps: (only
pip3 install google-auth-oauthlib google-api-python-client requests+google-authare strictly needed at upload time, but the OAuth-lib pulls them).requests - First-ever upload opens a browser for Google consent and writes . Subsequent runs reuse it silently.
~/.config/youtube/token.json
- Google Cloud OAuth客户端: 必须存在文件。若缺失,可查看
~/.config/youtube/credentials.json完成5分钟快速配置。references/credentials-setup.md - Python依赖: 执行(上传时仅严格需要
pip3 install google-auth-oauthlib google-api-python-client requests和google-auth,但OAuth库会自动拉取相关依赖)。requests - 首次上传: 会打开浏览器进行Google授权,并生成文件。后续运行将自动复用该令牌。
~/.config/youtube/token.json
How it works (and why it's not the stock youtube-uploader)
工作原理(为何不使用原生YouTube上传工具)
YouTube's resumable upload protocol issues a URL after the metadata POST, then accepts the bytes in chunked requests. The stock runs this over , which under this user's local SOCKS+HTTP proxy stack throws or on those follow-up PUTs and stalls indefinitely.
Location:PUTgoogle-api-python-clienthttplib2[Errno 65] No route to hostsocket.timeoutThis skill bypasses : it does OAuth via , then drives the resumable upload manually with , passing the proxy explicitly. 8 MB chunks (not the stock 256 KB) — fewer round-trips through the proxy. Exponential-backoff retry on / / 5xx.
httplib2google-authrequestssocket.timeoutConnectionErrorIf you're tempted to "just call the YouTube API client directly," don't — it'll fail in this environment.
YouTube的可恢复上传协议会在元数据POST请求后返回一个 URL,随后接受分块的请求来上传字节数据。原生基于实现该功能,但在该用户的本地SOCKS+HTTP代理环境下,后续的PUT请求会抛出或错误,并无限卡顿。
Location:PUTgoogle-api-python-clienthttplib2[Errno 65] No route to hostsocket.timeout本工具绕过了:通过完成OAuth授权,然后使用手动执行可恢复上传,并显式传递代理配置。采用8MB分块(而非原生的256KB),减少代理往返次数。针对//5xx错误采用指数退避重试机制。
httplib2google-authrequestssocket.timeoutConnectionError如果你想「直接调用YouTube API客户端」,请勿这样做——在该环境下会失败。
Usage
使用方法
Batch upload a final/
directory
final/批量上传final/
目录
final/bash
python3 ~/.claude/skills/wjs-uploading-video/scripts/upload_youtube.py \
--dir "/path/to/final" \
--meta "/path/to/final/UPLOAD_META.md"The script:
- Reads and pairs each
UPLOAD_META.mdblock to a video file in## NN · filename.mp4--dir - Skips videos already in (default
--results-file) — safe to re-run after failures<dir>/.youtube_upload_results.json - Uploads sequentially with progress every 8 MB
- Writes the final URL list to
--results-file
bash
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 - 按顺序上传,每上传8MB显示一次进度
- 将最终的URL列表写入
--results-file
Single file
单个文件上传
bash
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"bash
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"Common overrides
常用参数覆盖
| Flag | Default | Notes |
|---|---|---|
| | |
| | 28 = Science & Tech. 27 = Education. 24 = Entertainment. |
| | YouTube requires this declaration |
| none | Add each uploaded video to a playlist |
| none | Schedule publish (requires |
| | OAuth client JSON |
| | Cached OAuth token |
| | Smaller chunks if uploads keep failing mid-flight |
| off | Parse meta + list what would upload, don't touch network |
| 参数 | 默认值 | 说明 |
|---|---|---|
| | 可选值: |
| | 28=科技类,27=教育类,24=娱乐类 |
| | YouTube要求必须声明此项 |
| 无 | 将每个上传的视频添加至指定播放列表 |
| 无 | 定时发布(需配合 |
| | OAuth客户端JSON文件路径 |
| | 缓存的OAuth令牌文件路径 |
| | 若上传中途持续失败,可减小分块大小 |
| 关闭 | 仅解析元数据并列出待上传内容,不进行网络操作 |
UPLOAD_META.md format
UPLOAD_META.md格式
The parser expects the user's standard structure:
undefined解析器遵循用户的标准结构:
undefined01 · segment_01_no-bugs.mp4
01 · segment_01_no-bugs.mp4
短标题
代码没有错误,只有意图不一致
视频描述
AI 时代屎山的重新定义...
—— 王建硕 × 任鑫《...》第 1 集
#王建硕 #AI编程 #ClaudeCode
Mapping:
- `## NN · <filename>` → which video this block describes
- `**短标题**` (or `**Title**`) block → YouTube **title**, verbatim. Short titles work but consider that YouTube allows up to 100 chars — if you want a richer title with series name, write it that way in `**短标题**`
- `**视频描述**` (or `**Description**`) block → YouTube **description**, verbatim, with the `#tag` hashtags retained at the bottom
- All `#word` tokens in the 视频描述 → comma-separated YouTube **tags** (each `#` is stripped; the user's channel name `王建硕` is auto-prepended per global instructions)
Filename in the heading must match an actual file in `--dir`. If a file exists but has no meta block, the script errors loudly — pass `--allow-missing-meta` to upload it with `--title <basename>` and empty description.短标题
代码没有错误,只有意图不一致
视频描述
AI 时代屎山的重新定义...
—— 王建硕 × 任鑫《...》第 1 集
#王建硕 #AI编程 #ClaudeCode
映射关系:
- `## NN · <filename>` → 该块对应的视频文件
- `**短标题**`(或`**Title**`)块 → 直接作为YouTube的**标题**。短标题可用,但YouTube允许最多100字符——若想添加系列名称等更丰富的内容,可直接写在`**短标题**`中
- `**视频描述**`(或`**Description**`)块 → 直接作为YouTube的**描述**,底部的`#tag`标签将保留
- 视频描述中的所有`#word`标记 → 转换为逗号分隔的YouTube**标签**(会去除每个`#`符号;根据全局设置,会自动添加用户频道名`王建硕`作为前缀)
标题中的文件名必须与`--dir`目录下的实际文件匹配。若文件存在但无对应元数据块,脚本会报错——可添加`--allow-missing-meta`参数,以文件名作为标题、空描述上传该视频。Sensible defaults this skill bakes in
本工具内置的合理默认配置
- Privacy = public: videos go live immediately and appear in subscriber feeds + search. Override per call with (link-only) or
--privacy unlisted(owner-only) when you want to review first--privacy private - Category = 28 (Science & Tech): matches 王建硕 channel's main content; override with per upload
--category - selfDeclaredMadeForKids = false: YouTube requires this; the user's content is adult-targeted
- Chunk size = 8 MB: validated working size through this user's local proxy (256 KB stalled)
- Skip already-uploaded: results file is the source of truth; deleting it forces re-upload
- 隐私权限=公开:视频上传后立即上线,会出现在订阅者动态和搜索结果中。若需先审核,可在调用时通过(仅链接可访问)或
--privacy unlisted(仅所有者可访问)覆盖默认设置--privacy private - 分类=28(科技类):匹配王建硕频道的主要内容;可在每次上传时通过参数覆盖
--category - selfDeclaredMadeForKids = false:YouTube要求必须声明此项;用户的内容面向成人
- 分块大小=8MB:已在用户的本地代理环境下验证可用(256KB分块会卡顿)
- 跳过已上传视频:结果文件为判断依据;删除该文件将强制重新上传
Channel-name CTA rule
频道名CTA规则
If you write description footers, signatures, or "subscribe to me" lines into a video's metadata, use 王建硕 (the user's channel name). Don't put a guest's name there — guests like 任鑫 belong inside the description body when they're the conversation partner, never in the channel-CTA slot.
若在视频元数据中添加描述页脚、签名或「订阅我」等内容,请使用王建硕(用户的频道名)。请勿在此处添加嘉宾姓名——嘉宾如任鑫作为对话伙伴时,应出现在描述正文中,而非频道CTA区域。
Troubleshooting
故障排查
| Symptom | Fix |
|---|---|
| Add user's Google account to the OAuth client's Test users list in Google Cloud Console |
| Almost always a proxy issue — verify |
| Upload stalls with no progress lines | The proxy is silently buffering. Lower |
| YouTube Data API default quota is 10,000 units/day, each upload is 1,600 units — so ~6 uploads/day. Request a quota bump in Google Cloud Console, or split uploads across days |
| Token refresh fails | Delete |
| 症状 | 解决方法 |
|---|---|
授权页面显示 | 在Google Cloud控制台中,将用户的Google账号添加至OAuth客户端的测试用户列表 |
上传中途出现 | 几乎都是代理问题——验证 |
| 上传卡顿,无进度更新 | 代理在静默缓冲。可减小 |
出现 | YouTube Data API默认配额为每天10000单位,每次上传消耗1600单位——即每天约可上传6个视频。可在Google Cloud控制台申请配额提升,或分多天上传 |
| 令牌刷新失败 | 删除 |
After uploading
上传完成后
- Results saved to (JSON: file, title, video id, URL)
--results-file - Echo the URL list back to the user in a markdown table
- Videos are by default — they're live the moment the upload completes. If the user wanted a review buffer, mention they can re-upload with
public(or flip back to unlisted in YouTube Studio)--privacy unlisted - If they mentioned a 合集 / series, offer to create a YouTube playlist and batch-add the new uploads (the user must give a playlist ID or let you create one)
- 结果保存至(JSON格式:包含文件、标题、视频ID、URL)
--results-file - 以markdown表格形式向用户返回URL列表
- 视频默认设为状态——上传完成后立即上线。若用户需要审核缓冲,可告知其使用
public重新上传(或在YouTube Studio中切换为未列出状态)--privacy unlisted - 若用户提及合集/系列,可提议创建YouTube播放列表并批量添加新上传的视频(用户需提供播放列表ID或允许创建新列表)