YouTube CLI uploader
This is a small Python client for uploading videos to an owned YouTube channel through the YouTube Data API v3. It uses OAuth authorization, resumable uploads, and private uploads by default.
The uploader can send a title, description, tags, category, language, audience setting, synthetic media disclosure, thumbnail, and caption track. It then checks YouTube until processing succeeds.
Installation
Install the CLI from PyPI with uv:
uv tool install youtube-cli-uploader
This installs the youtube-upload, youtube-authorize, and youtube-delete commands.
Authorization
Create an OAuth desktop client in Google Cloud for a project with the YouTube Data API v3 enabled. Download the client JSON to:
~/.config/youtube-api-uploader/client_secret.json
Authorize the channel:
youtube-authorize
Authorization prints the channel title, ID, and URL. On a headless host, forward the selected port
over SSH and run authorization with --no-browser --port PORT.
Upload
youtube-upload video.mp4 \
--title "Example upload" \
--description-file description.txt \
--tag example \
--thumbnail thumbnail.jpg \
--captions captions.srt \
--contains-synthetic-media
Uploads are private unless --privacy unlisted or --privacy public is provided. API projects
that have not completed YouTube's compliance audit may be restricted to private uploads.
The uploader saves the video ID and completed steps beside the video in
VIDEO.youtube-upload.json. Run the same command again after a failure to resume the existing
upload instead of creating a duplicate. Use --state PATH to choose another state location.
Delete an uploaded video by ID:
youtube-delete VIDEO_ID
Example
example/ contains a ready-to-upload private test with a 5.2 KB MP4, thumbnail,
description, and captions.
API operations
videos.insertcreates the video, sends its metadata, and uploads the MP4 in resumable chunks.thumbnails.setattaches the optional JPEG thumbnail.videos.listchecks upload and processing status.captions.insertattaches the optional caption file after video processing succeeds.captions.listchecks that the caption track reachesservingstatus.
The client requests the youtube.upload and youtube.force-ssl OAuth scopes. OAuth client secrets
and user tokens remain in the local configuration directory and are excluded from Git.
Tests
uv --no-config run --locked python -m unittest discover -s tests
uv --no-config build --no-sources
The acceptance demo builds a wheel, installs it in a clean environment, records a deterministic upload and deletion against a local YouTube API fixture, checks the transcript, and stores the wheel, recording, transcript, and checksums as CI artifacts. It does not need GitHub secrets or use YouTube quota.
Live verification recording
Create a reviewer-facing recording against the authorized YouTube channel with:
demo/record-live.sh
Pass a different token or artifact directory as the first and second arguments. The script runs the locked tests, builds one wheel, installs that wheel in a clean environment, records a private upload using the committed example assets, prints the channel URL returned by YouTube, and deletes the test video. An exit trap also attempts deletion if a later upload step fails. The token is neither shown in the recording nor copied into the artifacts. Idle gaps longer than two seconds are shortened in the saved cast without changing its output, and the final screen remains visible for five seconds.
The recording, transcript, wheel, and checksums are written to artifacts/live-demo/. Review the
recording before sharing it and provide the printed channel URL in the verification email.
Data handling
The scripts run locally and communicate directly with Google's OAuth and YouTube API endpoints. They do not operate a server, collect analytics, or send video data or credentials to any other service. OAuth tokens and upload state are stored locally with owner-only permissions. Revoking the app in the Google Account permissions page disables future access.
Metadata
Release files for youtube-cli-uploader 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| youtube_cli_uploader-0.2.0.tar.gz | 54.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| youtube_cli_uploader-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 63.8 kB
Release files / youtube_cli_uploader-0.2.0.tar.gz
| Download URL | youtube_cli_uploader-0.2.0.tar.gz |
|---|---|
| Size | 54.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c2bc3f00307ba6c91fd5fc8bec4ca997f11a3e59c9fb98099b55057ded5cf124
|
|
BLAKE2b-256 checksum How to use checksums |
9577fa3642f5442a0249065b9f34a35c4507e1046f43dfba69cddf098ea6ed81
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / youtube_cli_uploader-0.2.0-py3-none-any.whl
| Download URL | youtube_cli_uploader-0.2.0-py3-none-any.whl |
|---|---|
| Size | 9.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ae14c03386df22af28b3d01e3536cfcc49b88e56eeb2821a963624df4f95ea6f
|
|
BLAKE2b-256 checksum How to use checksums |
5a9702f24af1e5c986f426184b1d598a26f481e1074d55f6b993afbfa8c1bc14
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|