Skip to content

Commit b77da7f

Browse files
authored
feat(youtube): add search command
Adds `gog youtube search list` / `gog yt search ls` for YouTube Data API search across videos, channels, and playlists, with table/JSON output and generated command docs. Maintainer fixups: rebased onto current main, reused the shared YouTube read-service selector so `--account`/`--account auto` use OAuth while public reads can still use API keys, added `videoCount` ordering support per YouTube `search.list`, added focused unit coverage, and updated the changelog. Proof: - `go test ./internal/cmd -run 'TestYouTube'` - `make ci` - Autoreview clean: no accepted/actionable findings after fixups - Live OAuth proof with `clawdbot@gmail.com`: `GOG_YOUTUBE_API_KEY=definitely-invalid ./bin/gog --account clawdbot@gmail.com --json youtube search list "openclaw" --type video --order relevance --max 2` returned 2 items, proving account OAuth won over the invalid API key - GitHub CI green: ci/test, ci/worker, ci/windows, ci/darwin-cgo-build, docker/image Co-authored-by: Siddhartha Varma <39856034+BRO3886@users.noreply.github.com>
1 parent 980db41 commit b77da7f

10 files changed

Lines changed: 363 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
- Docs: add `docs cell-update` for non-destructive table-cell content replacement by table, row, and column. (#646)
1515
- Gmail: pause watch push Gmail API fetches per account while a 429 Retry-After circuit is open. (#643)
1616
- YouTube: let `videos list` and `comments list` use OAuth when `--account` is supplied, preserving the API-key fallback for unauthenticated public reads. (#664)
17+
- YouTube: add `youtube search list` / `yt search ls` for YouTube Data API search across videos, channels, and playlists. (#650, #651) — thanks @BRO3886.
1718
- Docs: update the bundled `gog` agent skill to preserve broad user OAuth scopes during reauth and rely on command guards for scoped execution.
1819

1920
## 0.19.0 - 2026-05-22

docs/commands.generated.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,7 @@ Generated from `gog schema --json`.
561561
- [`gog upload (up,put) <localPath> [flags]`](commands/gog-upload.md) - Upload a file to Drive (alias for 'drive upload')
562562
- [`gog version [flags]`](commands/gog-version.md) - Print version
563563
- [`gog whoami (who-am-i) [flags]`](commands/gog-whoami.md) - Show your profile (alias for 'people me')
564-
- [`gog youtube (yt) <command> [flags]`](commands/gog-youtube.md) - YouTube Data API (activities, videos, playlists, comments, channels)
564+
- [`gog youtube (yt) <command> [flags]`](commands/gog-youtube.md) - YouTube Data API (search, activities, videos, playlists, comments, channels)
565565
- [`gog youtube (yt) activities (activity) <command>`](commands/gog-youtube-activities.md) - List channel activities
566566
- [`gog youtube (yt) activities (activity) list (ls) [flags]`](commands/gog-youtube-activities-list.md) - List activities for a channel (or authenticated user)
567567
- [`gog youtube (yt) channels (channel) <command>`](commands/gog-youtube-channels.md) - List channels
@@ -570,6 +570,8 @@ Generated from `gog schema --json`.
570570
- [`gog youtube (yt) comments (comment) list (ls) [flags]`](commands/gog-youtube-comments-list.md) - List comment threads for a video or channel
571571
- [`gog youtube (yt) playlists (playlist) <command>`](commands/gog-youtube-playlists.md) - List playlists
572572
- [`gog youtube (yt) playlists (playlist) list (ls) [flags]`](commands/gog-youtube-playlists-list.md) - List playlists by channel or authenticated user
573+
- [`gog youtube (yt) search (find) <command>`](commands/gog-youtube-search.md) - Search YouTube for videos, channels, or playlists
574+
- [`gog youtube (yt) search (find) list (ls) <query> [flags]`](commands/gog-youtube-search-list.md) - Search for videos, channels, or playlists
573575
- [`gog youtube (yt) videos (video) <command>`](commands/gog-youtube-videos.md) - List or get videos
574576
- [`gog youtube (yt) videos (video) list (ls) [flags]`](commands/gog-youtube-videos-list.md) - List videos by ID or chart
575577
- [`gog zoom <command> [flags]`](commands/gog-zoom.md) - Zoom

docs/commands/README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Every `gog` command has a generated docs page. The source of truth is the live CLI schema; run `make docs-commands` after changing command names, flags, help text, aliases, or arguments.
44

5-
Generated pages: 574.
5+
Generated pages: 576.
66

77
## Top-level Commands
88

@@ -48,7 +48,7 @@ Generated pages: 574.
4848
- [gog upload](gog-upload.md) - Upload a file to Drive (alias for 'drive upload')
4949
- [gog version](gog-version.md) - Print version
5050
- [gog whoami](gog-whoami.md) - Show your profile (alias for 'people me')
51-
- [gog youtube](gog-youtube.md) - YouTube Data API (activities, videos, playlists, comments, channels)
51+
- [gog youtube](gog-youtube.md) - YouTube Data API (search, activities, videos, playlists, comments, channels)
5252
- [gog zoom](gog-zoom.md) - Zoom
5353

5454
## All Commands
@@ -612,7 +612,7 @@ Generated pages: 574.
612612
- [gog upload](gog-upload.md) - Upload a file to Drive (alias for 'drive upload')
613613
- [gog version](gog-version.md) - Print version
614614
- [gog whoami](gog-whoami.md) - Show your profile (alias for 'people me')
615-
- [gog youtube](gog-youtube.md) - YouTube Data API (activities, videos, playlists, comments, channels)
615+
- [gog youtube](gog-youtube.md) - YouTube Data API (search, activities, videos, playlists, comments, channels)
616616
- [gog youtube activities](gog-youtube-activities.md) - List channel activities
617617
- [gog youtube activities list](gog-youtube-activities-list.md) - List activities for a channel (or authenticated user)
618618
- [gog youtube channels](gog-youtube-channels.md) - List channels
@@ -621,6 +621,8 @@ Generated pages: 574.
621621
- [gog youtube comments list](gog-youtube-comments-list.md) - List comment threads for a video or channel
622622
- [gog youtube playlists](gog-youtube-playlists.md) - List playlists
623623
- [gog youtube playlists list](gog-youtube-playlists-list.md) - List playlists by channel or authenticated user
624+
- [gog youtube search](gog-youtube-search.md) - Search YouTube for videos, channels, or playlists
625+
- [gog youtube search list](gog-youtube-search-list.md) - Search for videos, channels, or playlists
624626
- [gog youtube videos](gog-youtube-videos.md) - List or get videos
625627
- [gog youtube videos list](gog-youtube-videos-list.md) - List videos by ID or chart
626628
- [gog zoom](gog-zoom.md) - Zoom
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# `gog youtube search list`
2+
3+
> Generated from `gog schema --json`. Do not edit this page by hand; run `make docs-commands`.
4+
5+
Search for videos, channels, or playlists
6+
7+
## Usage
8+
9+
```bash
10+
gog youtube (yt) search (find) list (ls) <query> [flags]
11+
```
12+
13+
## Parent
14+
15+
- [gog youtube search](gog-youtube-search.md)
16+
17+
## Flags
18+
19+
| Flag | Type | Default | Help |
20+
| --- | --- | --- | --- |
21+
| `--access-token` | `string` | | Use provided access token directly (bypasses stored refresh tokens; token expires in ~1h) |
22+
| `-a`<br>`--account`<br>`--acct` | `string` | | Account email for API commands (gmail/calendar/chat/classroom/drive/drivelabels/docs/slides/contacts/tasks/people/sheets/forms/sites/appscript/analytics/searchconsole/ads/photos) |
23+
| `--channel-id` | `string` | | Restrict results to a specific channel |
24+
| `--client` | `string` | | OAuth client name (selects stored credentials + token bucket) |
25+
| `--color` | `string` | auto | Color output: auto\|always\|never |
26+
| `--disable-commands` | `string` | | Comma-separated list of disabled commands; dot paths allowed |
27+
| `-n`<br>`--dry-run`<br>`--dryrun`<br>`--noop`<br>`--preview` | `bool` | | Do not make changes; print intended actions and exit successfully |
28+
| `--enable-commands` | `string` | | Comma-separated list of enabled command prefixes; dot paths allowed (restricts CLI) |
29+
| `--enable-commands-exact` | `string` | | Comma-separated list of exact enabled commands; dot paths allowed and parent commands do not enable children |
30+
| `-y`<br>`--force`<br>`--assume-yes`<br>`--yes` | `bool` | | Skip confirmations for destructive commands |
31+
| `--gmail-no-send` | `bool` | false | Block Gmail send operations (agent safety) |
32+
| `-h`<br>`--help` | `kong.helpFlag` | | Show context-sensitive help. |
33+
| `--home` | `string` | | Override gogcli config/data/state/cache root (equivalent to GOG_HOME) |
34+
| `-j`<br>`--json`<br>`--machine` | `bool` | false | Output JSON to stdout (best for scripting) |
35+
| `--max`<br>`--limit` | `int64` | 25 | Max results |
36+
| `--no-input`<br>`--non-interactive`<br>`--noninteractive` | `bool` | | Never prompt; fail instead (useful for CI) |
37+
| `--order` | `string` | relevance | Sort order: relevance, date, rating, title, videoCount, viewCount |
38+
| `--page` | `string` | | Page token |
39+
| `-p`<br>`--plain`<br>`--tsv` | `bool` | false | Output stable, parseable text to stdout (TSV; no colors) |
40+
| `--results-only` | `bool` | | In JSON mode, emit only the primary result (drops envelope fields like nextPageToken) |
41+
| `--select`<br>`--pick`<br>`--project` | `string` | | In JSON mode, select comma-separated fields (best-effort; supports dot paths). Desire path: use --fields for most commands. |
42+
| `--type` | `string` | video | Resource type: video, channel, playlist (comma-separated) |
43+
| `-v`<br>`--verbose` | `bool` | | Enable verbose logging |
44+
| `--version` | `kong.VersionFlag` | | Print version and exit |
45+
| `--wrap-untrusted` | `bool` | false | In JSON/raw output, wrap fetched text fields in external untrusted-content markers |
46+
47+
## See Also
48+
49+
- [gog youtube search](gog-youtube-search.md)
50+
- [Command index](README.md)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# `gog youtube search`
2+
3+
> Generated from `gog schema --json`. Do not edit this page by hand; run `make docs-commands`.
4+
5+
Search YouTube for videos, channels, or playlists
6+
7+
## Usage
8+
9+
```bash
10+
gog youtube (yt) search (find) <command>
11+
```
12+
13+
## Parent
14+
15+
- [gog youtube](gog-youtube.md)
16+
17+
## Subcommands
18+
19+
- [gog youtube search list](gog-youtube-search-list.md) - Search for videos, channels, or playlists
20+
21+
## Flags
22+
23+
| Flag | Type | Default | Help |
24+
| --- | --- | --- | --- |
25+
| `--access-token` | `string` | | Use provided access token directly (bypasses stored refresh tokens; token expires in ~1h) |
26+
| `-a`<br>`--account`<br>`--acct` | `string` | | Account email for API commands (gmail/calendar/chat/classroom/drive/drivelabels/docs/slides/contacts/tasks/people/sheets/forms/sites/appscript/analytics/searchconsole/ads/photos) |
27+
| `--client` | `string` | | OAuth client name (selects stored credentials + token bucket) |
28+
| `--color` | `string` | auto | Color output: auto\|always\|never |
29+
| `--disable-commands` | `string` | | Comma-separated list of disabled commands; dot paths allowed |
30+
| `-n`<br>`--dry-run`<br>`--dryrun`<br>`--noop`<br>`--preview` | `bool` | | Do not make changes; print intended actions and exit successfully |
31+
| `--enable-commands` | `string` | | Comma-separated list of enabled command prefixes; dot paths allowed (restricts CLI) |
32+
| `--enable-commands-exact` | `string` | | Comma-separated list of exact enabled commands; dot paths allowed and parent commands do not enable children |
33+
| `-y`<br>`--force`<br>`--assume-yes`<br>`--yes` | `bool` | | Skip confirmations for destructive commands |
34+
| `--gmail-no-send` | `bool` | false | Block Gmail send operations (agent safety) |
35+
| `-h`<br>`--help` | `kong.helpFlag` | | Show context-sensitive help. |
36+
| `--home` | `string` | | Override gogcli config/data/state/cache root (equivalent to GOG_HOME) |
37+
| `-j`<br>`--json`<br>`--machine` | `bool` | false | Output JSON to stdout (best for scripting) |
38+
| `--no-input`<br>`--non-interactive`<br>`--noninteractive` | `bool` | | Never prompt; fail instead (useful for CI) |
39+
| `-p`<br>`--plain`<br>`--tsv` | `bool` | false | Output stable, parseable text to stdout (TSV; no colors) |
40+
| `--results-only` | `bool` | | In JSON mode, emit only the primary result (drops envelope fields like nextPageToken) |
41+
| `--select`<br>`--pick`<br>`--project` | `string` | | In JSON mode, select comma-separated fields (best-effort; supports dot paths). Desire path: use --fields for most commands. |
42+
| `-v`<br>`--verbose` | `bool` | | Enable verbose logging |
43+
| `--version` | `kong.VersionFlag` | | Print version and exit |
44+
| `--wrap-untrusted` | `bool` | false | In JSON/raw output, wrap fetched text fields in external untrusted-content markers |
45+
46+
## See Also
47+
48+
- [gog youtube](gog-youtube.md)
49+
- [Command index](README.md)

docs/commands/gog-youtube.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
> Generated from `gog schema --json`. Do not edit this page by hand; run `make docs-commands`.
44
5-
YouTube Data API (activities, videos, playlists, comments, channels)
5+
YouTube Data API (search, activities, videos, playlists, comments, channels)
66

77
## Usage
88

@@ -20,6 +20,7 @@ gog youtube (yt) <command> [flags]
2020
- [gog youtube channels](gog-youtube-channels.md) - List channels
2121
- [gog youtube comments](gog-youtube-comments.md) - List comment threads
2222
- [gog youtube playlists](gog-youtube-playlists.md) - List playlists
23+
- [gog youtube search](gog-youtube-search.md) - Search YouTube for videos, channels, or playlists
2324
- [gog youtube videos](gog-youtube-videos.md) - List or get videos
2425

2526
## Flags

docs/commands/gog.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ gog <command> [flags]
5858
- [gog upload](gog-upload.md) - Upload a file to Drive (alias for 'drive upload')
5959
- [gog version](gog-version.md) - Print version
6060
- [gog whoami](gog-whoami.md) - Show your profile (alias for 'people me')
61-
- [gog youtube](gog-youtube.md) - YouTube Data API (activities, videos, playlists, comments, channels)
61+
- [gog youtube](gog-youtube.md) - YouTube Data API (search, activities, videos, playlists, comments, channels)
6262
- [gog zoom](gog-zoom.md) - Zoom
6363

6464
## Flags

internal/cmd/root.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ type CLI struct {
9191
AppScript AppScriptCmd `cmd:"" name:"appscript" aliases:"script,apps-script" help:"Google Apps Script"`
9292
Analytics AnalyticsCmd `cmd:"" aliases:"ga" help:"Google Analytics"`
9393
SearchConsole SearchConsoleCmd `cmd:"" name:"searchconsole" aliases:"gsc,search-console,webmasters" help:"Google Search Console"`
94-
YouTube YouTubeCmd `cmd:"" name:"youtube" aliases:"yt" help:"YouTube Data API (activities, videos, playlists, comments, channels)"`
94+
YouTube YouTubeCmd `cmd:"" name:"youtube" aliases:"yt" help:"YouTube Data API (search, activities, videos, playlists, comments, channels)"`
9595
Photos PhotosCmd `cmd:"" name:"photos" aliases:"photo" help:"Google Photos Library API (app-created media)"`
9696
Config ConfigCmd `cmd:"" help:"Manage configuration"`
9797
ExitCodes AgentExitCodesCmd `cmd:"" name:"exit-codes" aliases:"exitcodes" help:"Print stable exit codes (alias for 'agent exit-codes')"`

internal/cmd/youtube.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ type YouTubeCmd struct {
2121
Playlists YouTubePlaylistsCmd `cmd:"" name:"playlists" aliases:"playlist" help:"List playlists"`
2222
Comments YouTubeCommentsCmd `cmd:"" name:"comments" aliases:"comment" help:"List comment threads"`
2323
Channels YouTubeChannelsCmd `cmd:"" name:"channels" aliases:"channel" help:"List channels"`
24+
Search YouTubeSearchCmd `cmd:"" name:"search" aliases:"find" help:"Search YouTube for videos, channels, or playlists"`
2425
}
2526

2627
type YouTubeActivitiesCmd struct {
@@ -431,6 +432,99 @@ func (c *YouTubeChannelsListCmd) Run(ctx context.Context, flags *RootFlags) erro
431432
return nil
432433
}
433434

435+
type YouTubeSearchCmd struct {
436+
List YouTubeSearchListCmd `cmd:"" name:"list" aliases:"ls" help:"Search for videos, channels, or playlists"`
437+
}
438+
439+
type YouTubeSearchListCmd struct {
440+
Query string `arg:"" help:"Search query"`
441+
Type string `name:"type" help:"Resource type: video, channel, playlist (comma-separated)" default:"video"`
442+
Order string `name:"order" help:"Sort order: relevance, date, rating, title, videoCount, viewCount" default:"relevance" enum:"relevance,date,rating,title,videoCount,viewCount"`
443+
ChannelID string `name:"channel-id" help:"Restrict results to a specific channel"`
444+
Max int64 `name:"max" aliases:"limit" help:"Max results" default:"25"`
445+
Page string `name:"page" help:"Page token"`
446+
}
447+
448+
func (c *YouTubeSearchListCmd) Run(ctx context.Context, flags *RootFlags) error {
449+
u := ui.FromContext(ctx)
450+
if err := validateYouTubeMax(c.Max); err != nil {
451+
return err
452+
}
453+
if c.Query == "" {
454+
return usage("search query is required")
455+
}
456+
457+
types := splitCSV(c.Type)
458+
for _, t := range types {
459+
switch t {
460+
case "video", "channel", "playlist":
461+
default:
462+
return usage("--type must be video, channel, or playlist (comma-separated)")
463+
}
464+
}
465+
466+
svc, err := getYouTubeReadService(ctx, flags)
467+
if err != nil {
468+
return err
469+
}
470+
471+
call := svc.Search.List([]string{"snippet"}).
472+
Q(c.Query).
473+
Type(types...).
474+
Order(c.Order).
475+
MaxResults(c.Max).
476+
PageToken(c.Page)
477+
if c.ChannelID != "" {
478+
call = call.ChannelId(c.ChannelID)
479+
}
480+
resp, err := call.Do()
481+
if err != nil {
482+
return err
483+
}
484+
485+
if outfmt.IsJSON(ctx) {
486+
return outfmt.WriteJSON(ctx, os.Stdout, map[string]any{
487+
"items": resp.Items,
488+
"nextPageToken": resp.NextPageToken,
489+
})
490+
}
491+
if len(resp.Items) == 0 {
492+
u.Err().Println("No results")
493+
return nil
494+
}
495+
w, flush := tableWriter(ctx)
496+
defer flush()
497+
fmt.Fprintln(w, "KIND\tID\tTITLE\tCHANNEL\tPUBLISHED_AT")
498+
for _, item := range resp.Items {
499+
id := ""
500+
kind := ""
501+
if item.Id != nil {
502+
switch {
503+
case item.Id.VideoId != "":
504+
id = item.Id.VideoId
505+
kind = "video"
506+
case item.Id.ChannelId != "":
507+
id = item.Id.ChannelId
508+
kind = "channel"
509+
case item.Id.PlaylistId != "":
510+
id = item.Id.PlaylistId
511+
kind = "playlist"
512+
}
513+
}
514+
title := ""
515+
ch := ""
516+
pubAt := ""
517+
if item.Snippet != nil {
518+
title = item.Snippet.Title
519+
ch = item.Snippet.ChannelTitle
520+
pubAt = item.Snippet.PublishedAt
521+
}
522+
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", kind, id, sanitizeTab(title), sanitizeTab(ch), sanitizeTab(pubAt))
523+
}
524+
printNextPageHint(u, resp.NextPageToken)
525+
return nil
526+
}
527+
434528
func validateYouTubeMax(limit int64) error {
435529
if limit < 1 || limit > 50 {
436530
return usage("--max must be between 1 and 50")

0 commit comments

Comments
 (0)