Skills / Skills / Youtube Grab
All skills

/ skill

youtube-grab

Skills/youtube-grab.md

Extract YouTube URLs from a single video / channel / playlist via yt-dlp and bulk-add them to a NotebookLM library.

Open as raw file

Run this skill

Ignored for single-video URLs.

Runs with bypassPermissions — local dev only.

youtube-grab

Take any YouTube URL — single video, channel, or playlist — and bulk-add the constituent videos to a NotebookLM library. Wraps yt-dlp for URL extraction (metadata-only, no downloads) and notebooklm source add for ingest.

Pre-flight checks

  1. NotebookLM session valid (Integrations/NotebookLM.md status: active). Abort with retry message if not.
  2. .venv/bin/yt-dlp exists. If missing, surface install command: uv pip install --python .venv/bin/python yt-dlp.
  3. url is parseable by yt-dlp (test with --simulate --no-warnings). Reject if not.

Procedure

1. Detect URL type

Use a fast yt-dlp probe:

.venv/bin/yt-dlp --flat-playlist --no-warnings -O "%(_type)s" --playlist-items 1 "{url}"
  • Output playlist → channel or playlist (multi-video)
  • Output url (or empty) → single video
  • Output blank line / error → invalid URL; abort

2. Extract URLs

Single video:

.venv/bin/yt-dlp --flat-playlist --print "%(id)s|%(title)s" "{url}"

Channel or playlist (with limit):

# Channel handle → use the /videos tab if not already specified
URL="{url}"
if <a class="wikilink wikilink-broken" href="#">"$URL" == *"/@"* && "$URL" != *"/videos"* && "$URL" != *"/playlists"*</a>; then
  URL="${URL%/}/videos"
fi
.venv/bin/yt-dlp --flat-playlist --print "%(id)s|%(title)s" --playlist-items "1-{limit}" "$URL"

Channel or playlist (no limit / "all"): Same as above without --playlist-items. Warn before proceeding if count > 50 — surface to Adam.

Each output line: videoId|videoTitle. Reconstruct full URLs as https://youtube.com/watch?v={videoId}.

3. Resolve target library

If library_slug provided AND Wiki/Libraries/{library_slug}.md exists → use it.

If new_library_name provided → invoke notebooklm-create-library with:

  • display_name: {new_library_name}
  • slug: kebab-cased version of new_library_name
  • subject_kind: topic (default — caller can override)

If neither: surface to caller (slash command) to prompt Adam.

4. Confirm before bulk-adding

If extracted count > 5: report the URLs with titles and ask Adam to confirm (via the slash command's UI). Adam can pick a subset.

For each URL after confirmation:

  1. Call .venv/bin/notebooklm source add {notebooklm_id} youtube "https://youtube.com/watch?v={video_id}".
  2. Capture returned source_id (or error).
  3. Sleep delay_seconds (default 2.0) before next call to avoid rate-limit.

Track added: [{source_id, video_id, title}] and failed: [{url, reason}].

5. Sync the library

After all adds, invoke notebooklm-sync for library_slug. This:

  • Reconciles the Sources table in Wiki/Libraries/{slug}.md with NotebookLM's source list.
  • Refreshes the Cached Summary (now includes the new content).

6. Append Log entry

  • agent: ea-orchestrator
  • skill: youtube-grab
  • input: url={url}, type={detected_type}, limit={limit}, library={library_slug}
  • output: added={count}, failed={count}, library={library_path}
  • artifacts: <a class="wikilink wikilink-broken" href="#">Wiki/Libraries/{library_slug}</a>
  • notes: list any failed URLs with reasons

CLI command map

OperationCommand
Detect type.venv/bin/yt-dlp --flat-playlist --no-warnings -O "%(_type)s" --playlist-items 1 "{url}"
List videos (limit N).venv/bin/yt-dlp --flat-playlist --print "%(id)s|%(title)s" --playlist-items 1-{N} "{url}"
List allsame without --playlist-items
Add to NLM.venv/bin/notebooklm source add {nb_id} youtube "{video_url}"

Channel URL handling

yt-dlp treats youtube.com/@Handle as the channel home (mixed playlists tab). For "last N videos by upload date", append /videos:

  • https://youtube.com/@AlexHormozi/videos
  • ⚠️ https://youtube.com/@AlexHormozi — works but may include shorts / live streams in unexpected order

The skill auto-appends /videos for @handle URLs that don't already specify a tab.

Failure modes

  • yt-dlp can't parse URL — abort early with the URL pattern that worked best as a hint.
  • NotebookLM rate-limit — back off (double delay_seconds), retry once, then surface partial completion.
  • Source already in library — NotebookLM dedupes by URL; skill should treat this as success (not failure) and note already_present in added.
  • Channel returns 0 videos — surface; likely a region-block or wrong URL form. Suggest appending /videos if not already there.
  • JS runtime warnings from yt-dlp — informational; don't fail on them. Future: brew install deno to silence.

Hard rules

  • Never download the actual video--flat-playlist mode only extracts URLs + lightweight metadata. No bandwidth, no disk.
  • Always confirm before bulk-adding > 5 videos — rate-limit cost + library quality matters.
  • Default delay_seconds: 2.0 — keeps NotebookLM happy.
  • Never push to a library that's subject_kind: brand without confirmation — brand libraries should be tightly curated.

Future extensions (out of scope for v1)

  • Filter by duration (--match-filter "duration > 600" to skip shorts).
  • Filter by date range (--dateafter 20260101).
  • Ingest playlists into multiple libraries based on title regex.
  • Ingest non-YouTube media (Vimeo, Twitter, podcasts) — yt-dlp supports these; skill name would generalize to media-grab.