Skip to content

junjunjunbong/youtube-skill

Repository files navigation

YouTube Skill

Validate Python 3.11 License: MIT

YouTube Skill is an extensible plugin for safe, natural-language YouTube workflows in Codex and Claude Code. Platform-specific skill adapters stay separate while sharing one deterministic Python engine for YouTube Data API operations.

The first bundled skill is YouTube Upload (youtube-upload). It prepares and validates upload metadata, handles desktop OAuth, performs resumable uploads, and can optionally set a thumbnail or add the uploaded video to a playlist.

Important

Uploading is an external side effect. The plugin defaults to a dry run, private visibility, and subscriber notifications disabled. It never uploads unless execution is explicitly requested.

Features

  • Natural-language intake for upload metadata
  • YAML or JSON manifests with deterministic validation
  • OAuth 2.0 desktop authorization with refreshable local tokens
  • Resumable video uploads with bounded retry behavior
  • Private, unlisted, public, and scheduled publication
  • Explicit made-for-kids and realistic synthetic-media disclosure
  • Optional custom thumbnail and playlist insertion
  • Preserved video IDs and local result logs after partial failures
  • Separate Codex and Claude Code adapters over one shared engine

Safety model

The upload workflow intentionally requires progressive confirmation:

Action Required CLI flags Behavior
Validate or dry run none Normalizes metadata and reports warnings without contacting YouTube
Upload privately --execute Uploads with privacy_status: private
Upload unlisted or public --execute --confirm-public Requires a second explicit visibility confirmation
Schedule publication --execute --confirm-public Uploads privately with a future publish_at value

Additional safeguards:

  • made_for_kids and contains_synthetic_media must always be set explicitly.
  • Public, unlisted, and scheduled manifests emit warnings during validation.
  • Subscriber notifications default to false even though the YouTube API default is true.
  • OAuth secrets and tokens are kept outside the repository.
  • An uploaded video is never deleted automatically if thumbnail or playlist processing fails.

Architecture

Codex: $youtube-upload ───────┐
                              ├── shared manifest validator and YouTube API engine
Claude: /youtube-skill:youtube-upload ─┘

The adapters turn a natural-language request into the same validated manifest. The shared engine then performs dry runs, authorization, uploads, and optional post-upload operations.

Requirements

  • Python 3.11 (the version used by CI)
  • A Google account with a YouTube channel
  • A Google Cloud project with YouTube Data API v3 enabled
  • A Desktop app OAuth client
  • Codex or Claude Code for natural-language skill invocation

Install the plugin

Codex

Add this repository as a marketplace and install the plugin:

codex plugin marketplace add junjunjunbong/youtube-skill
codex plugin add youtube-skill@junwon-youtube-tools

Start a new Codex task after installation so the new skill is loaded. Invoke it explicitly:

$youtube-upload Prepare a private upload for /absolute/path/to/video.mp4.

Claude Code

In Claude Code, add the marketplace and install the plugin:

/plugin marketplace add junjunjunbong/youtube-skill
/plugin install youtube-skill@junwon-youtube-tools

Invoke the skill explicitly:

/youtube-skill:youtube-upload Prepare a private upload for /absolute/path/to/video.mp4.

For local Claude Code development, clone the repository and load it directly:

git clone https://github.com/junjunjunbong/youtube-skill.git
cd youtube-skill
claude --plugin-dir .

Local setup

Clone the repository and create an isolated Python environment:

git clone https://github.com/junjunjunbong/youtube-skill.git
cd youtube-skill
python3 -m venv common/youtube-upload/.venv
source common/youtube-upload/.venv/bin/activate
python3 -m pip install --upgrade pip
python3 -m pip install -r common/youtube-upload/requirements.txt

All commands below assume the repository root as the current directory and the virtual environment is active.

Configure OAuth

  1. Create or select a project in Google Cloud Console.

  2. Enable YouTube Data API v3.

  3. Configure the OAuth consent screen.

  4. Create an OAuth client ID with application type Desktop app.

  5. Download the JSON file and store it outside the repository, for example:

    ~/.config/youtube-upload/client_secret.json
    
  6. Authorize once:

    python3 common/youtube-upload/scripts/youtube_auth.py \
      --client-secrets ~/.config/youtube-upload/client_secret.json \
      --token-path ~/.config/youtube-upload/token.json

The browser authorization flow requests the youtube.force-ssl scope and writes a refreshable token with file mode 0600. See the detailed OAuth setup guide.

Default credential paths:

Purpose Default path
OAuth client secrets ~/.config/youtube-upload/client_secret.json
Refreshable token ~/.config/youtube-upload/token.json
Result logs ~/.local/state/youtube-upload/

Environment overrides:

export YOUTUBE_CLIENT_SECRETS=/absolute/path/to/client_secret.json
export YOUTUBE_TOKEN_FILE=/absolute/path/to/token.json
export YOUTUBE_RESULT_LOG_DIR=/absolute/path/to/result-logs

Command-line --client-secrets and --token-path values take precedence over their corresponding environment variables.

Quick start with the skill

The adapter extracts values already supplied in the request and asks only for required decisions that are still missing. Before an upload it must know:

  • Local video path
  • Video title
  • Whether the video is made for kids
  • Whether it contains realistic altered or synthetic media

Description, tags, category, thumbnail, and playlist are optional. Scheduling is requested only when you ask for it. The safe defaults are private visibility, no schedule, no thumbnail, no playlist, and no subscriber notification.

Example request:

$youtube-upload Upload /absolute/path/to/demo.mp4 privately with the title
"Project demo". It is not made for kids and contains no realistic synthetic media.
Do not notify subscribers. Add it to playlist PLxxxxxxxxxxxxxxxx.

Before execution, the skill presents one normalized summary covering the file, title, visibility or schedule, audience, synthetic-media disclosure, notifications, thumbnail, and playlist. Actual upload begins only after the required confirmation.

Quick start with the CLI

1. Create a manifest

Copy the example beside your own working files:

cp common/youtube-upload/examples/upload.example.yaml /path/to/upload.yaml

Edit /path/to/upload.yaml:

video_path: "./videos/example.mp4"

snippet:
  title: "Example private upload"
  description: |
    A description for the uploaded video.
  tags:
    - "example"
    - "demo"
  category_id: "22"

status:
  privacy_status: "private"
  publish_at: null
  made_for_kids: false
  contains_synthetic_media: false
  notify_subscribers: false

assets:
  thumbnail_path: null
  playlist_id: null

execution:
  result_log_dir: null

Relative file paths are resolved from the directory containing the manifest, not from the shell's current directory.

2. Validate and normalize

python3 common/youtube-upload/scripts/validate_manifest.py \
  --manifest /path/to/upload.yaml

The command prints normalized JSON and warnings. It does not authenticate or upload anything.

3. Run the uploader in dry-run mode

python3 common/youtube-upload/scripts/youtube_upload.py \
  --manifest /path/to/upload.yaml

Without --execute, the uploader always returns a dry-run result.

4. Upload privately

After reviewing the normalized metadata:

python3 common/youtube-upload/scripts/youtube_upload.py \
  --manifest /path/to/upload.yaml \
  --execute

5. Upload publicly, unlisted, or on a schedule

Set privacy_status to public or unlisted for immediate publication, then provide both execution flags:

python3 common/youtube-upload/scripts/youtube_upload.py \
  --manifest /path/to/upload.yaml \
  --execute \
  --confirm-public

For scheduled publication, keep privacy_status: private and set publish_at to a future ISO 8601 timestamp with an explicit UTC offset, such as 2030-01-15T09:00:00+09:00. Scheduled execution also requires --confirm-public.

Manifest reference

See the complete input schema and validation rules.

Field Required Default Notes
video_path yes none Must resolve to an existing file
snippet.title yes none Non-empty, at most 100 characters
snippet.description no "" At most 5,000 UTF-8 bytes
snippet.tags no [] Uses YouTube's 500-character accounting rule
snippet.category_id no null Numeric category ID represented as a string or number
status.privacy_status no private private, unlisted, or public
status.publish_at no null Future ISO 8601 timestamp with an offset; requires private
status.made_for_kids yes none Must be explicitly true or false
status.contains_synthetic_media yes none Disclosure for realistic altered or synthetic content
status.notify_subscribers no false Emits a validation warning when enabled
assets.thumbnail_path no null JPEG or PNG, no larger than 2 MB
assets.playlist_id no null Existing playlist ID, not a playlist name or URL
execution.result_log_dir no environment or local state directory Overrides the result-log destination

Titles and descriptions cannot contain < or >. A public, unlisted, scheduled, or notification-enabled manifest produces a warning so the final intent is visible before execution.

Results, retries, and partial failures

An execution result includes the video ID, YouTube URL, final privacy status, scheduled time, warnings, thumbnail status, playlist status, errors, and result-log path.

  • Results are written as JSON under execution.result_log_dir, YOUTUBE_RESULT_LOG_DIR, or ~/.local/state/youtube-upload/, in that order.
  • Network failures and HTTP 500, 502, 503, and 504 responses are retried with bounded exponential backoff and jitter, up to eight retries.
  • Invalid metadata, quota, permission, and policy errors are not retried automatically.
  • If the video upload succeeds but thumbnail or playlist processing fails, the command returns a partial failure while preserving the uploaded video ID and result log.
  • The uploader never performs an automatic delete or rollback of a successfully uploaded video.

YouTube API constraints

  • videos.insert accepts uploads up to 256 GB with video/* or application/octet-stream media types.
  • Uploads made through unverified API projects created after July 28, 2020 may be restricted to private visibility until the project passes a compliance audit.
  • status.publishAt can be set only for a private video that has never been published. This project additionally requires a future timestamp with an explicit timezone offset.
  • thumbnails.set accepts files up to 2 MB. This project deliberately restricts thumbnail inputs to JPEG and PNG.
  • Playlist insertion uses playlistItems.insert and requires access to the target playlist.

YouTube quotas, channel eligibility, and policy enforcement remain outside this plugin's control.

Troubleshooting

Missing Google API packages

If the command reports missing Google API packages, activate the project virtual environment and install the pinned dependency ranges:

source common/youtube-upload/.venv/bin/activate
python3 -m pip install -r common/youtube-upload/requirements.txt

OAuth client secrets not found

Confirm that the Desktop app client JSON exists at the default location, pass --client-secrets, or set YOUTUBE_CLIENT_SECRETS. Do not paste the file contents into an issue or conversation.

Manifest validation failed

Run validate_manifest.py and fix the reported field before attempting execution. Common causes include a missing video, omitted audience or synthetic-media booleans, an expired schedule, invalid visibility, oversized text, or an invalid thumbnail.

Public or scheduled execution was rejected

Public, unlisted, and scheduled uploads require both --execute and --confirm-public. The second flag is an intentional confirmation boundary, not an authentication option.

A requested public upload remains private

Check whether the Google Cloud API project is unverified. YouTube restricts uploads from certain unverified projects to private visibility until the project completes an audit.

Quota, permission, or policy error

The uploader does not retry these errors because repeating the same request will not resolve the underlying condition. Review the returned API error, channel permissions, project quota, and policy status before trying again.

Thumbnail or playlist step failed after upload

Use the returned video ID and result-log path to continue manually. The original video remains uploaded; the plugin never deletes it as an automatic rollback.

Repository layout

.codex-plugin/plugin.json                 # Codex plugin manifest
.agents/plugins/marketplace.json          # Codex marketplace metadata
.claude-plugin/                           # Claude Code plugin and marketplace metadata
skills/youtube-upload/                    # Codex skill adapter
claude/skills/youtube-upload/             # Claude Code skill adapter
common/youtube-upload/
├── examples/                             # Example upload manifest
├── references/                           # Input schema and OAuth guide
├── requirements.txt                      # Shared Python dependencies
└── scripts/                              # Validator, OAuth, and uploader engine
tests/                                    # Manifest and safety tests

Development

Install dependencies, run the tests, and compile all Python entry points:

python3 -m pip install -r common/youtube-upload/requirements.txt
python3 -m unittest discover -s tests -v
python3 -m py_compile common/youtube-upload/scripts/*.py

GitHub Actions runs the same checks on every push and pull request using Python 3.11.

Security

  • Never commit client_secret.json, token.json, access tokens, refresh tokens, manifests containing private metadata, or result logs.
  • The included .gitignore excludes common OAuth credential names, virtual environments, *.log, and upload-result*.json files.
  • Keep OAuth files outside the repository and restrict access to the local user.
  • Review normalized metadata before every upload and use private visibility while testing.
  • If a credential is exposed, revoke it in Google Cloud immediately and create a replacement.

Official references

License

MIT

About

A collection of safe natural-language workflows for YouTube in Codex and Claude Code.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages