Skip to content
cccskills
FreePublished on GitHub

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. 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).

The English summary is being prepared. Showing the original description.

Run score Not tested

See how to install

Files included (3)

  • SKILL.md8.7 KB
  • references/credentials-setup.md2.6 KB
  • scripts/upload_youtube.py18.7 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

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).

When to use

  • User has one or more finished .mp4 files and wants them on YouTube
  • User points to a final/ directory with multiple segments and an 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 wjs-reframing-video first to produce the 9:16 cut, then upload that via this skill)

Prerequisites (one-time per machine)

  1. Google Cloud OAuth client: ~/.config/youtube/credentials.json must exist. See references/credentials-setup.md for the 5-minute setup if missing.
  2. Python deps: 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).
  3. First-ever upload opens a browser for Google consent and writes ~/.config/youtube/token.json. Subsequent runs reuse it silently.

How it works (and why it's not the stock youtube-uploader)

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.

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"

Usage

Batch upload a final/ directory

python3 ~/.claude/skills/wjs-uploading-video/scripts/upload_youtube.py \
  --dir "/path/to/final" \
  --meta "/path/to/final/UPLOAD_META.md"

The script:

  1. Reads UPLOAD_META.md and pairs each ## NN · filename.mp4 block to a video file in --dir
  2. Skips videos already in --results-file (default <dir>/.youtube_upload_results.json) — safe to re-run after failures
  3. Uploads sequentially with progress every 8 MB
  4. Writes the final URL list to --results-file

Single 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"

Common overrides

FlagDefaultNotes
--privacypublicprivate / unlisted / public
--category2828 = Science & Tech. 27 = Education. 24 = Entertainment.
--made-for-kidsfalseYouTube requires this declaration
--playlist <ID>noneAdd each uploaded video to a playlist
--publish-at <ISO8601>noneSchedule publish (requires --privacy private)
--credentials~/.config/youtube/credentials.jsonOAuth client JSON
--token~/.config/youtube/token.jsonCached OAuth token
--chunk-mb8Smaller chunks if uploads keep failing mid-flight
--dry-runoffParse meta + list what would upload, don't touch network

UPLOAD_META.md format

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
  • 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.

Sensible defaults this skill bakes in

  • Privacy = public: videos go live immediately and appear in subscriber feeds + search. Override per call with --privacy unlisted (link-only) or --privacy private (owner-only) when you want to review first
  • Category = 28 (Science & Tech): matches 王建硕 channel's main content; override with --category per upload
  • 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

Channel-name CTA rule

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.

Troubleshooting

SymptomFix
access_denied 403 on consent screenAdd user's Google account to the OAuth client's Test users list in Google Cloud Console
[Errno 65] No route to host mid-uploadAlmost always a proxy issue — verify curl --max-time 10 https://upload.googleapis.com/upload/youtube/v3/videos returns a 4xx (any 4xx = proxy reachable); if 000, the proxy is down
Upload stalls with no progress linesThe proxy is silently buffering. Lower --chunk-mb 4 or restart the proxy daemon
quotaExceeded (API units)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
429 rateLimitExceeded · Video Uploads per dayA DIFFERENT, separate limit from API units — YouTube's per-project daily video-upload count (anti-abuse, tied to project verification status; can be very low). When hit, every upload in the batch fails init session failed: 429, none succeed. It is NOT a proxy/auth problem and can't be worked around. It resets at midnight US Pacific Time (not your local midnight). Options: wait for the PT reset and re-run (the results file was cleared so all retry); or upload via the YouTube Studio web UI, which does NOT consume this API limit; or get the GCP project verified to raise the cap. Earlier uploads the same PT-day (even manual API ones) eat into this count.
Token refresh failsDelete ~/.config/youtube/token.json and re-run; OAuth browser flow restarts

After uploading

  • Results saved to --results-file (JSON: file, title, video id, URL)
  • Echo the URL list back to the user in a markdown table
  • Videos are public by default — they're live the moment the upload completes. If the user wanted a review buffer, mention they can re-upload with --privacy unlisted (or flip back to unlisted in YouTube Studio)
  • 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)

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

王建硕的思维框架与表达方式。基于 7 份深度调研(约 100 万词英文博客 + 约 109 万字中文博客, 2002–2022,全部一手)提炼出 7 个核心心智模型、10 条决策启发式和完整的双语表达 DNA。 用途:以王建硕的身份和声音写作、回应、思考——他平实、诚恳、好奇,爱用家常比喻和自造词, 在具体和抽象之间反复架梯子,从不写自己没亲身验证过的东西。 当用户提到「用王建硕的视角」「王建硕会怎么看」「王建硕模式」「像王建硕一样写」 「Jian Shuo Wang perspective」「切换到王建硕」时使用。 即使用户只是说「帮我用王建硕的角度想想」「如果是王建硕会怎么写这篇」也应触发。 一旦激活,本次对话后续所有回应都保持王建硕身份,直到用户明确说「退出」—— 不需要每轮重新点名。 不适用:用户要的是关于王建硕本人的客观介绍 / 事实查询(「王建硕是谁」「他哪年创业的」), 那种问题正常回答即可,不要进入角色扮演。

The English summary is being prepared. Showing the original description.

jianshuo/claude-skills137Updated Oct 16, 2026

Use when the user asks to audit what's wrong with a project, "make it right", "看看项目出了什么问题", "为什么用户的需求还没上线", "为什么没提交App Store", "为什么没新build", or wants a holistic state-of-the-project check covering unmerged branches, stalled PRs, failed GitHub Actions, stale builds, plan drift (TODOS.md / ROADMAP), unreleased commits, and log errors. Runs read-only investigation, presents a grouped checklist, fixes only after explicit user confirmation. Aware of the Cathier iOS app workflow (Xcode + fastlane + auto-merge @claude PRs from in-app feedback).

The English summary is being prepared. Showing the original description.

jianshuo/claude-skills137Updated Oct 16, 2026

Use when the user has a video + an SRT and wants the subtitles either burned into the pixels (libass, always-visible) or soft-muxed as a togglable track. Also handles the final composite step for the localization pipeline — burn subs, mix a dub track, and keep the original audio as a low-volume bed, all in ONE ffmpeg encode (no cascade). Verifies libass availability and auto-downloads a static evermeet ffmpeg build when Homebrew's stripped binary lacks it. Triggers — "烧字幕", "硬字幕", "burn subtitles", "burn-in subs", "embed subtitle", "soft mux SRT", "把字幕烧进视频", "做最终合成".

The English summary is being prepared. Showing the original description.

jianshuo/claude-skills137Updated Oct 16, 2026

Use when the user complains about spam on his X/Twitter posts — 同城面付 / 寻固炮 / 线下上门 / 免费破处 这类引流号在他推文下刷的 emoji 垃圾回复 — and wants them removed. Covers the last 7 days (X recent-search window). Triggers — "把这些spam删掉", "清理X垃圾回复", "推文下面好多引流号", "clean spam replies", "/wjs-cleaning-spam".

The English summary is being prepared. Showing the original description.

jianshuo/claude-skills137Updated Oct 16, 2026

Use when the user wants a 王建硕-style WeChat article (article.md) turned into a narrated short MP4 video — TTS voiceover via 火山引擎 Volcano TTS, HyperFrames CSS/GSAP animation per scene, subtle SFX, abstract watercolor background, full pipeline rendering to 1080×1920 portrait MP4 (30-90s). Triggers — "把这篇文章做成视频", "做一个解说视频", "讲解视频", "/wjs-converting-text-to-video".

The English summary is being prepared. Showing the original description.

jianshuo/claude-skills137Updated Oct 16, 2026

Use when migrating a WordPress site to a Hugo static site on GitHub Pages from a WXR export (.xml) plus the wp-content/uploads folder — preserving /archives/<id>/ URLs, localizing images, and deploying via GitHub Actions. Triggers — "把 WordPress 迁成 Hugo", "wordpress 转静态站", "migrate WordPress to Hugo", "WXR to Hugo", "publish WordPress to GitHub Pages", "/wjs-converting-wp-to-hugo".

The English summary is being prepared. Showing the original description.

jianshuo/claude-skills137Updated Oct 16, 2026

See all skills by jianshuo

Report a problem with this skill