@michaelkory/notebooklm
v1.3.0
Published
CDP-native NotebookLM client: build & query notebooks from YouTube channels/playlists/search, a person's interviews, cross-creator topics, blogs & LinkedIn — via a logged-in Chrome bridge (bun, zero runtime deps)
Readme
@michaelkory/notebooklm
A bun CLI (zero runtime deps) that builds and queries Google NotebookLM notebooks programmatically. It drives NotebookLM over raw Chrome DevTools Protocol against a Chrome you're already signed into — no API key, no extension, no reimplemented auth. The in-page calls inherit your existing session.
Build notebooks from a YouTube channel, playlist, or search; from a person's teaching videos + interviews; from a cross-creator topic; from blogs; or from LinkedIn articles. Discover the directory channels and experts behind a niche, subscribe to channels, mine your watch history, and ask grounded, citation-backed questions.
⚠️ Unofficial. This automates NotebookLM's private web backend from your own logged-in session. Keep volume human-scale; everything depends on the one signed-in tab. Not affiliated with Google. (LinkedIn/YouTube scraping is likewise ToS-sensitive — go gently.)
Install
bunx @michaelkory/notebooklm <command> # no install
bun add -g @michaelkory/notebooklm # or: global `notebooklm` binaryPrerequisites
- bun installed (runs the TypeScript directly).
- A logged-in NotebookLM Chrome reachable over CDP — default
http://127.0.0.1:9224(override withNBLM_CDP). For LinkedIn/YouTube features that Chrome should also be signed into those. listis the health check — if it errors with "no CSRF token" or a connection failure, open/sign into NotebookLM in that Chrome.
Commands
Harvest / inspect (read-only — return data, create nothing)
| Command | Action |
|---|---|
| list | Notebooks [{id,title,emoji,source_count}] — also the health check. |
| harvest <channel> [maxScrolls] | A channel's videos (handle/URL/id) + duration/views/age. |
| harvest-playlist <playlistUrl> [maxScrolls] | A playlist's videos, in order. |
| harvest-search <query…> | YouTube search results (title/duration/views/channel). |
| harvest-person <name> [ownChannelHandle] | A person's teaching videos (own channel + interviews, filtered). |
| harvest-blog <rootUrl> [limit] | Article URLs from a site (sitemap → RSS fallback). |
| harvest-linkedin <profileUrl> | A LinkedIn profile's articles → text cached as markdown. |
| history <takeout.zip\|watch-history.json> [sinceISO] | Your watch history → channels ranked by views (music filtered). |
| caption-check <videoUrl…> | Whether each video has captions. |
Build notebooks (always create new; record a manifest)
| Command | Action |
|---|---|
| build <label> <channel> [maxScrolls] [filters] | From a channel. |
| build-urls <label> <url…> | From explicit URLs. |
| build-directory <label> <channelNameOrHandle> [maxScrolls] | Resolve real channel → harvest → drop Shorts → 300 most-recent. |
| build-person <label> <name> [ownChannelHandle] | A person's teaching corpus. |
| build-topic <label> '<q1\|q2\|…>' | Cross-creator topic (teaching-filtered, \|-separated queries). |
| build-linkedin <label> <profileUrl> | LinkedIn articles → pasted-text sources. |
build filters: --limit N --min-duration SEC --max-age-months M --min-views N --include kw,kw --exclude kw,kw
Discover / curate
| Command | Action |
|---|---|
| find-directories <seed…> | Rank channels by how many of your seed people they host (interview directories). |
| person-hosts <name> [ownChannelHandle] | The channels that host a given person. |
| find-experts <topic> <notebookId…> | Ask each notebook who the topic's experts are → per-notebook answers + citations. |
| subscribe <name\|handle…> | Subscribe to a channel (resolver-guarded). Unsubscribe must be done manually — YouTube needs a real click. |
| delete-notebook <id…> | PERMANENTLY delete notebooks (no undo). |
| ask <notebookId> <question…> | Grounded answer → {answer, citations}. |
bunx @michaelkory/notebooklm build-directory "My First Million" "My First Million"
bunx @michaelkory/notebooklm find-directories "Alex Hormozi" "Rob Walling" "Dan Koe"
bunx @michaelkory/notebooklm find-experts "first 100 SaaS customers" <nbId1> <nbId2> …
bunx @michaelkory/notebooklm build-linkedin "Justin Welsh" https://www.linkedin.com/in/justinwelsh/Configuration
| Env var | Default | Purpose |
|---|---|---|
| NBLM_CDP | http://127.0.0.1:9224 | CDP endpoint of your logged-in Chrome. |
| NBLM_NOTEBOOKS_DIR | ~/projects/ideacode/Atlas/NotebookLM | Manifests, per-notebook markdown notes, and the LinkedIn text cache (sources/linkedin/). |
How it picks sources (the curation model)
- Teaching density over views. Ranking favors instructional content (how-to / framework / playbook / teardown), not popularity; views are at most a tiebreak, never a gate.
- Own-channel-first for teachers; search for the channel-less. A teacher's lessons live on their channel; people like Seth Godin / Justin Welsh live in others' interviews + their writing — so person/topic notebooks pull from search and LinkedIn too.
- Directories = multi-guest interview shows, found by who repeatedly hosts your seeds.
- Channel resolution is squatter-guarded —
resolveChannelrequires ≥5k subs + a distinctive-name match and picks the highest-sub result, so a 3-video imposter can't slip in. - NotebookLM cap: 300 sources / notebook.
build-directorytakes the 300 most-recent long-form videos. NotebookLM does not reject captionless videos (verified).
Notes
- Always creates NEW notebooks — no get-or-create matching.
- Builds record a canonical
manifest.json(keyed by notebook id) + a per-notebook markdown note — handy for an Obsidian vault. - LinkedIn URLs can't be ingested by NotebookLM directly (auth wall), so
build-linkedinscrapes the text via your session, caches it as markdown, and adds it as pasted text. - Runtime: bun only (raw CDP over bun's native WebSocket; no Playwright, no Node).
- Maintainer release steps: see
PUBLISHING.md.
