Upload videos to YouTube with full metadata, thumbnails, playlists, Shorts detection, and scheduled publishing.
Resources
2Install
npx skillscat add cgk-platform/cgk/youtube-uploader Install via the SkillsCat registry.
YouTube Uploader
Upload videos to YouTube via the YouTube Data API v3 with OAuth 2.0.
ALWAYS use
youtube_upload_safe.sh— NEVER call_youtube_upload_internal.pydirectly. The internal script will reject execution without the env gate.
Quick Start
SLACK_CHANNEL_ID="C0ADZGUJS4A" \
SLACK_THREAD_TS="1234567890.123456" \
~/.openclaw/skills/youtube-uploader/scripts/youtube_upload_safe.sh \
--file "/Users/novarussell/.openclaw/media/my-video.mp4" \
--title "My Video Title" \
--description "Video description here" \
--privacy privateParameters
| Parameter | Short | Required | Default | Description |
|---|---|---|---|---|
--file |
-f |
YES | — | Video file path (absolute) |
--title |
-t |
YES | — | Video title (max 100 chars) |
--description |
-d |
no | "" | Description (max 5000 chars) |
--tags |
— | no | [] | Comma-separated tags |
--category |
-c |
no | "22" | YouTube category ID (22 = People & Blogs) |
--privacy |
-p |
no | "private" | private / unlisted / public |
--publish-at |
— | no | — | ISO 8601 scheduled publish time (requires private) |
--thumbnail |
— | no | — | Custom thumbnail image path |
--playlist-id |
— | no | — | Add to playlist after upload |
--notify / --no-notify |
— | no | true | Notify subscribers |
--no-synthetic-flag |
— | no | — | Skip AI content declaration |
--no-upload-msg |
— | no | — | Skip Slack success message |
Shorts Auto-Detection
The script automatically detects YouTube Shorts:
- Video is vertical (height > width)
- Duration is 60 seconds or less
When detected, #Shorts is appended to the title if not already present.
Examples
Upload as private (default, safest)
SLACK_CHANNEL_ID="$CH" SLACK_THREAD_TS="$TS" \
youtube_upload_safe.sh -f "/path/to/video.mp4" -t "My Video" -p privateUpload with thumbnail and playlist
SLACK_CHANNEL_ID="$CH" SLACK_THREAD_TS="$TS" \
youtube_upload_safe.sh \
-f "/path/to/video.mp4" \
-t "Product Showcase" \
-d "Our latest collection" \
--tags "brand,product,showcase" \
--thumbnail "/path/to/thumb.jpg" \
--playlist-id "PLxxxxxxxxxxxxxxxx" \
-p unlistedSchedule for future publish
SLACK_CHANNEL_ID="$CH" SLACK_THREAD_TS="$TS" \
youtube_upload_safe.sh \
-f "/path/to/video.mp4" \
-t "Scheduled Video" \
-p private \
--publish-at "2026-03-15T10:00:00-08:00"Upload without AI content flag
SLACK_CHANNEL_ID="$CH" SLACK_THREAD_TS="$TS" \
youtube_upload_safe.sh -f "/path/to/video.mp4" -t "Non-AI Video" --no-synthetic-flagAI Content Declaration
By default, containsSyntheticMedia: true is set on all uploads (most openCLAW videos are AI-generated). Use --no-synthetic-flag to skip this for non-AI content.
Credential Setup
- Create a Google Cloud Project with YouTube Data API v3 enabled
- Create OAuth 2.0 Desktop credentials -> get client ID + secret
- Fill
YOUTUBE_CLIENT_IDandYOUTUBE_CLIENT_SECRETin.env - Run the auth helper:
uv run ~/.openclaw/skills/youtube-uploader/scripts/youtube_auth_setup.py - Complete the browser consent flow with the YouTube channel owner
- The script will print the refresh token and channel ID
Quota
YouTube Data API default quota: 10,000 units/day. Each videos.insert costs 100 units, thumbnails.set ~50 units, playlistItems.insert ~50 units. A full upload with thumbnail + playlist = ~200 units (~50 uploads/day). Request quota increase via Google Cloud Console if needed.
Category IDs (Common)
| ID | Category |
|---|---|
| 1 | Film & Animation |
| 2 | Autos & Vehicles |
| 10 | Music |
| 15 | Pets & Animals |
| 17 | Sports |
| 20 | Gaming |
| 22 | People & Blogs (default) |
| 24 | Entertainment |
| 25 | News & Politics |
| 26 | Howto & Style |
| 28 | Science & Technology |
Critical Rules
- ALWAYS use
youtube_upload_safe.sh— never the internal script - ALWAYS set
SLACK_CHANNEL_IDandSLACK_THREAD_TS - ALWAYS upload as
privatefirst unless the user explicitly requests public/unlisted - NEVER hardcode credentials — they live in
.env - Video file path MUST be absolute
- On failure: STOP. No hacks, no workarounds.