Skip to content
Content
Command

/publish

Publish a finished video to YouTube

From plugin
claude-code-video-toolkit
2.4k14 skills14 commands
$ npx -y skills add digitalsamba/claude-code-video-toolkit --agent claude-code

Command definition

publish.md
description: Publish a finished video to YouTube

Publish to YouTube

Upload a rendered project to YouTube, auto-filling the metadata from `project.json`. Wraps `tools/youtube_upload.py` (OAuth 2.0 + Data API v3, resumable upload).

project.json + rendered MP4 → metadata draft → dry-run → upload → write back videoId/URL

> **One-time setup required.** YouTube uploads need OAuth (not an API key). If the user > hasn't set this up, point them at `docs/youtube-upload.md` and stop until > `YOUTUBE_CLIENT_SECRETS_FILE` is in `.env` and `uv run tools/youtube_upload.py --auth` > has been run once. Don't attempt an upload without a cached token.

Entry Point

Step 1: Locate the project and its rendered video

If the user named a project, use it. Otherwise scan for candidates:

cd /path/to/claude-code-video-toolkit && ls projects/*/project.json

Read the chosen `project.json` **defensively** — real files carry `render`, `format`, and `publish` blocks beyond the `lib/project/types.ts` schema. Resolve the video file: 1. `render.file` if present (e.g. `out/ai-agent-short.mp4`), relative to the project dir. 2. Else scan the project's `out/*.mp4` and pick the most recent. 3. Confirm the resolved path exists and is non-empty before continuing.

If the project's `phase` isn't `complete`, warn the user and confirm they still want to publish.

Step 2: Assemble metadata (into a `publish` block)

Build a `publish` object and write it back into `project.json` so it's reviewable, editable, and re-runnable. If a `publish` block already exists, use it as defaults.

| Field | How to derive | |-------|---------------| | `title` | Existing `publish.title`, else the hook/title scene's `title`, else the project `name` (humanized). Keep ≤100 chars. | | `description` | Existing `publish.description`, else auto-draft: a 1–2 line summary from the scene narration/titles + a channel footer (links, hashtags). Keep ≤5000 chars. | | `tags` | Existing `publish.tags`, else derive 5–12 topical tags from scene titles + the brand. Comma-joined when passed to the tool. | | `category` | Default `"22"` (People & Blogs). Override per channel/topic — e.g. `"28"` (Science & Tech), `"27"` (Education), `"24"` (Entertainment). | | `thumbnail` | Existing `publish.thumbnail`, else `out/thumbnail.*` or `public/thumbnail.*`. If none and the user wants one, run `/thumbnail` first. | | `privacy` | Default **`private`** (safe — the video uploads but stays hidden until you flip it). Offer `unlisted`, `public`, or a scheduled go-live (`--publish-at`, e.g. next morning ~09:00 local in UTC) if the user asks. | | `playlist` | Optional; only if the user has one. | | `synthetic` | **On** (`--synthetic-media`) when the video shows realistic people who were generated or animated by AI (an LTX-animated cast, FLUX portraits, a SoulX presenter) or a cloned voice saying new lines. YouTube requires the disclosure; it shows a label, it doesn't limit reach. Off for UI rebuilds, charts and stylised cartoons. |

**Show the assembled metadata to the user and let them edit before uploading.**

Step 3: Dry-run first (no upload)

cd /path/to/claude-code-video-toolkit && uv run tools/youtube_upload.py \
  --video "projects/NAME/out/video.mp4" \
  --title "TITLE" \
  --description-file "projects/NAME/.publish-description.txt" \
  --tags "tag1,tag2,tag3" \
  --category "28" \
  --publish-at "2026-06-10T09:00:00Z" \
  --thumbnail "projects/NAME/out/thumbnail.png" \
  --dry-run --json-out

Write the description to a temp file (`--description-file`) rather than passing a long `--description` on the command line. Parse the JSON: confirm `requestBody` looks right and `authOk` is `true`. If `authOk` is `false`, surface the auth error and have the user run `uv run tools/youtube_upload.py --auth` first — do not proceed.

Step 4: Upload

Re-run the same command **without** `--dry-run`, keeping `--json-out`. Parse the result.

Step 5: Write back and report

On `success`, merge into the project's `publish` block:

"publish": {
  "platform": "youtube",
  "videoId": "<id>",
  "url": "https://www.youtube.com/watch?v=<id>",
  "privacyStatus": "<actual returned status>",
  "publishAt": "<scheduled time or null>",
  "uploadedAt": "<today ISO date>"
}

Append a `sessions[]` entry summarizing the upload, then report to the user:

Published to YouTube

Title:    <title>
URL:      https://www.youtube.com/watch?v=<id>
Privacy:  <actual>  (requested: <requested>)
Schedule: <publishAt or "—">

**If `privacyStatus` came back `private` but you requested public/scheduled**, tell the user plainly: this is the unverified-app lock — the video uploaded but won't go public until their Google Cloud OAuth app is verified. They can publish manually in YouTube Studio.

---

Quick Mode

Direct invocation for experienced users:

/publish ai-agent-short
/publish ai-agent-short --privacy unlisted

Parse the project name and any privacy/schedule overrides, still show the metadata and run a dry-run before the real upload.

---

Tool Reference (`tools/youtube_upload.py`)

| Option | Description | |--------|-------------| | `--video, --input` | Path to the video file | | `--title` | Title (≤100 chars) | | `--description` / `--description-file` | Description text, or a file (`-` = stdin) | | `--tags` | Comma-separated tags (combined ≤500 chars) | | `--category` | Numeric category ID string (default `22`; `28` = Science & Tech) | | `--privacy` | `private` (default) / `unlisted` / `public` | | `--synthetic-media` | Disclose realistic AI-generated or altered people, places or voices | | `--publish-at` | ISO8601 UTC schedule, e.g. `2026-06-10T09:00:00Z` (forces private at insert) | | `--thumbnail` | Custom thumbnail (≤2MB, 1280×720) | | `--captions` + `--captions-language` | Caption file + language code (needs the force-ssl scope; an older token asks for `--auth`) | | `--playlist` | Playlist ID | | `--video-id` | Add captions

Read more
@digitalsamba
Ships withclaude-code-video-toolkit

Tell Claude Code what video you want — it writes the script, generates the voiceover, music, and visuals, and renders the MP4.

Stars on GitHub

Momentum

stars in total
2.4k
Star history of digitalsamba/claude-code-video-toolkit on GitHub, a chart by star-history.com

Other commands on claude-code-video-toolkit.

More commands from other plugins.