grdev.youtube-cli 0.3.6

There is a newer version of this package available.
See the version list below for details.
dotnet tool install --global grdev.youtube-cli --version 0.3.6
                    
This package contains a .NET tool you can call from the shell/command line.
dotnet new tool-manifest
                    
if you are setting up this repo
dotnet tool install --local grdev.youtube-cli --version 0.3.6
                    
This package contains a .NET tool you can call from the shell/command line.
#tool dotnet:?package=grdev.youtube-cli&version=0.3.6
                    
nuke :add-package grdev.youtube-cli --version 0.3.6
                    

youtube-cli

Upload a video to YouTube from the command line — set its title, description, tags and category, and either publish it at a chosen visibility or schedule it to go public later.

It is built to be driven by an AI agent as much as by a person: nothing prompts, results go to stdout (optionally as JSON), progress goes to stderr, and the exit code tells you what happened.

dotnet tool install --global grdev.youtube-cli

Project page: greekdeveloper.com/tools/youtube-cli

Before the first run

The tool uploads to your channel using your Google Cloud OAuth client. It does not ship one, because YouTube's daily quota belongs to the Cloud project — a shared client would mean every user on earth splitting roughly six uploads a day.

Setting one up is a one-off, and free:

  1. At console.cloud.google.com, create or pick a project.
  2. APIs & Services → Library → enable YouTube Data API v3. Without this every call returns a 403 that reads like a permissions problem.
  3. OAuth consent screenInternal if your channel is in a Google Workspace domain you control, otherwise External. Add the scopes .../auth/youtube.upload and .../auth/youtube.readonly. On an External app, add your own account under Test users — without it consent fails with access_denied.
  4. Credentials → Create credentials → OAuth client ID → Desktop app. The type matters: consent completes on a loopback listener, which only Desktop clients allow implicitly.
  5. Download JSON and save it as C:\Users\<username>\.grdev.youtube-cli\client-secret.json (~/.grdev.youtube-cli/client-secret.json elsewhere). That is the whole of configuration.

Then, once per machine:

youtube auth login

An unverified app shows "Google hasn't verified this app"AdvancedGo to …. That warning is about your own unpublished app.

Do not leave the Cloud app in "Testing". Google invalidates refresh tokens after 7 days for an External app in Testing, so an unattended tool starts failing with exit 3 every week for no visible reason. Use Internal user type, or publish the External app (Audience → Publish app) — publishing unverified is fine for personal use.

Use

Describe the video in a markdown file beside it — talk.mp4 is described by talk.md:

---
Title: "Shipping a CLI in an afternoon"
Category: Science & Technology
Visibility: Private
Tags: dotnet, cli, shipping
ForKids: false
---

Everything below the marker becomes the description, exactly as written.
youtube upload talk.mp4 --DryRun    # resolve everything, print it, send nothing
youtube upload talk.mp4             # upload
youtube list                        # what's on the channel, newest first
youtube list --Scheduled            # what's booked to publish, soonest first
youtube auth status                 # which channel am I authorized for?
youtube version

youtube list reports each video's id, title, privacy, date and lifetime view, like and comment counts. It reports facts only — working out where the gaps in a schedule are, or which date to use next, is left to whoever is reading, because that depends on a cadence and a plan the tool has no business guessing.

Any option overrides the file — youtube upload talk.mp4 --Visibility Public. With no sidecar, pass everything as options instead.

Sidecar fields

Field Value Default
Title Text, max 100 characters — required
Category A category name (Gaming, Education, …) or a numeric id People & Blogs
Visibility Public, Unlisted or Private Private
PublishAt ISO 8601 timestamp; implies private until then none
ForKids true or false false
Tags Search keywords, comma-separated or a YAML list, 500 characters total none

The body is the description — there is no Description field. An unrecognised field is an error rather than a silently ignored line, so a typo cannot upload an untitled video.

Quote any value containing #, or YAML will read it as a comment and truncate it silently: Title: "5 ways to say no #shorts".

Options

Option names are shown in PascalCase but matched loosely — --PublishAt, --publishat and --publish-at are the same option.

upload takes --File, --Title, --Description, --Category, --Visibility, --PublishAt, --ForKids, --Tags, --DryRun. list takes --Scheduled and --Limit. --Json works on everything.

Driving it from an agent

The tool carries its own agent guide. Installing it is two commands:

dotnet tool install --global grdev.youtube-cli
youtube skill > ~/.claude/skills/youtube-upload/SKILL.md

youtube skill prints a ready-to-use skill file — the sidecar format, the exit-code contract, and the failure modes an agent needs to handle. Add --Json to get the same content as a field alongside its suggested path, if the agent would rather place the file itself.

Every command is non-interactive and safe to run unattended, except auth login, which is a browser consent flow a human must complete once. After that an agent can upload indefinitely without anyone present.

Branch on the exit code:

Code Meaning What to do
0 Done stdout has the video id and its watch and Studio URLs
1 Failed Read stderr; YouTube's own reason is reported verbatim
2 Usage error Fix the sidecar or the options; nothing was sent, no quota spent
3 Not authorized A human must run youtube auth login
4 Quota exhausted Stop for the day — retrying cannot succeed

Add --Json for a single JSON object on stdout. Use --DryRun first: uploads are irreversible and cost roughly a sixth of the default daily quota each, so confirming the merge before spending it is nearly free.

Uploads default to private. Publishing is effectively irreversible, so Public is always something the caller asked for explicitly.

Licence

MIT.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

Version Downloads Last Updated
0.3.7 0 9/1/2026
0.3.6 0 9/1/2026
0.3.1 142 8/13/2026