wjs-uploading-video
Upload one or many videos to YouTube. Use when the user wants to "上传到 YouTube", "发 YouTube", "批量上传", "upload to YouTube", "post videos to YouTube", or to publish a finished `final/` directory of MP4s.
Upload one or many videos to YouTube. Use when the user wants to "上传到 YouTube", "发 YouTube", "批量上传", "upload to YouTube", "post videos to YouTube", or to publish a finished `final/` directory of MP4s.
Real data. Real impact.
Emerging
Developers
Per week
Open source
Skills give you superpowers. Install in 30 seconds.
final/ directory of MP4s. Reads per-video metadata (title / description / tags) from a sibling UPLOAD_META.md file when present (the user's standard markdown format), or from command-line flags. Survives behind a SOCKS/HTTP proxy by using requests directly for the resumable upload (the stock google-api-python-client MediaFileUpload stalls under this user's proxy setup).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).
.mp4 files and wants them on YouTubefinal/ directory with multiple segments and an UPLOAD_META.mdDon't use for:
wjs-reframing-video first to produce the 9:16 cut, then upload that via this skill)~/.config/youtube/credentials.json must exist. See references/credentials-setup.md for the 5-minute setup if missing.pip3 install google-auth-oauthlib google-api-python-client requests (only google-auth + requests are strictly needed at upload time, but the OAuth-lib pulls them).~/.config/youtube/token.json. Subsequent runs reuse it silently.YouTube's resumable upload protocol issues a
Location: URL after the metadata POST, then accepts the bytes in chunked PUT requests. The stock google-api-python-client runs this over httplib2, which under this user's local SOCKS+HTTP proxy stack throws [Errno 65] No route to host or socket.timeout on those follow-up PUTs and stalls indefinitely.
This skill bypasses
httplib2: it does OAuth via google-auth, then drives the resumable upload manually with requests, passing the proxy explicitly. 8 MB chunks (not the stock 256 KB) — fewer round-trips through the proxy. Exponential-backoff retry on socket.timeout / ConnectionError / 5xx.
If you're tempted to "just call the YouTube API client directly," don't — it'll fail in this environment.
final/ directorypython3 ~/.claude/skills/wjs-uploading-video/scripts/upload_youtube.py \ --dir "/path/to/final" \ --meta "/path/to/final/UPLOAD_META.md"
The script:
UPLOAD_META.md and pairs each ## NN · filename.mp4 block to a video file in --dir--results-file (default <dir>/.youtube_upload_results.json) — safe to re-run after failures--results-filepython3 ~/.claude/skills/wjs-uploading-video/scripts/upload_youtube.py \ --video /path/to/clip.mp4 \ --title "My Title" \ --description "Body text" \ --tags "tag1,tag2,tag3"
| 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 |
The parser expects the user's standard structure:
## 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#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.
--privacy public)--category per uploadIf 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.
| Symptom | Fix |
|---|---|
on consent screen | Add user's Google account to the OAuth client's Test users list in Google Cloud Console |
mid-upload | Almost always a proxy issue — verify returns a 4xx (any 4xx = proxy reachable); if , the proxy is down |
| Upload stalls with no progress lines | The proxy is silently buffering. Lower or restart the proxy daemon |
| 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 and re-run; OAuth browser flow restarts |
--results-file (JSON: file, title, video id, URL)unlisted by default and remind them to flip to public in YouTube Studio when readyMIT
No automatic installation available. Please visit the source repository for installation instructions.
View Installation Instructions1,500+ AI skills, agents & workflows. Install in 30 seconds. Part of the Torly.ai family.
© 2026 Torly.ai. All rights reserved.