feat: add source management (list, add, read, remove)#31
Open
chazmaniandinkle wants to merge 9 commits intoPleasePrompto:masterfrom
Open
feat: add source management (list, add, read, remove)#31chazmaniandinkle wants to merge 9 commits intoPleasePrompto:masterfrom
chazmaniandinkle wants to merge 9 commits intoPleasePrompto:masterfrom
Conversation
New source_manager.py script for managing NotebookLM sources via browser automation. Supports listing sources, adding text/file sources, reading source content, and removing sources — all headless by default. Operations: - list: enumerate all sources in a notebook - add-text: add copied text as a source (NLM auto-titles) - add-file: upload files via Playwright file chooser - add-website: semi-automated (opens browser for manual Submit) - read: extract source guide content - remove: delete with force-click through CDK overlay - test: integration round-trip (add → read → remove) Also adds source management selectors to config.py and documents all new commands in SKILL.md. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Angular MatInput textarea doesn't trigger reactive form validation with standard Playwright input methods (fill, type, pressSequentially, execCommand, clipboard paste). The fix uses [formcontrolname='urls'] selector which binds directly to Angular's reactive FormControl, and submits via the "Insert" button instead of the disabled "Submit" arrow. Credit: DataNath/notebooklm_source_automation for the formcontrolname approach. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… delete) Extends notebook_manager.py with browser automation for: - sync: discover all notebooks from web, upsert into local library - sync --library-only: only refresh existing entries - sync --force: ignore exclusion list - find: fuzzy search across name, description, topics - import: auto-discover title + source count from URL - create: create new notebook on web + add to library - rename: rename on web + update library - delete: delete from web + remove from library (requires --confirm) - exclude: remove from library + prevent sync re-add Uses JavaScript-based scraping for reliable title extraction from Angular Material cards. IDs include 8-char UUID suffix for uniqueness. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
source_manager.py: - rename: rename sources via more_vert menu - select --names: select specific sources (deselect others) - select --all: reset to all sources selected notebook_manager.py: - list --format json: machine-readable output for piping ask_question.py: - --sources: select specific sources before asking (e.g. --sources "joes-book,cybernetics" queries only those) The source selection feature lets agents scope queries to specific documents instead of searching all 300 sources. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All source operations now persist to library.json as single source of truth. list_sources writes scraped names back before printing. Mutating ops (add/remove/rename) refresh the library after each change. - Add _update_library_sources() to SourceManager for write-back - list_sources reads from library after scrape, not raw page - sync --deep scrapes source names per notebook into library - sync --deep --stale only re-scrapes when counts diverge - find searches source names and shows matched source - list shows staleness when homepage count != scraped count - update_notebook accepts sources and sources_scraped_at fields Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The rename/delete functions were using card.evaluate("e.closest('a')")
to find the link, but the <a> tag is a sibling overlay, not a parent.
Added _find_notebook_card_by_uuid that finds the link by UUID then
navigates to its sibling card element.
Note: card matching still fails in headless mode — the evaluate_handle
→ as_element() pattern needs further debugging. Tracked as known issue.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two bugs fixed: 1. create_notebook_web: Added page.wait_for_url() after clicking Create to capture the real notebook UUID URL instead of the transitional /notebook/creating URL. 2. _find_notebook_card_by_uuid: Replaced broken evaluate_handle → as_element() approach with JS-index strategy. JS finds the link matching the UUID, locates its sibling mat-card, and returns the card's integer index among all cards. Python fetches by index. Avoids crossing the JS/Python boundary with DOM handles. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The <a> link is a child of <mat-card>, not a sibling. Using
link.closest('mat-card.project-button-card') correctly finds the
parent card. All notebook operations (create, rename, delete) now
pass end-to-end testing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds full source and notebook management to the NotebookLM skill via browser automation. All operations run headless by default.
Source Management (
source_manager.py)listadd-textadd-fileadd-websiteformcontrolnameselectorreadrenameselect --namesselect --allremovetestNotebook Management (
notebook_manager.py)syncsync --deepsync --deep --stalesync --library-onlyfindimport --urlcreate --titlerename --id --titledelete --id --confirmexclude --idlist --format jsonTargeted Querying (
ask_question.py)--sources "name1,name2"Architecture
library.jsonfirst, then displays from library. The library is always the reader, the web is always the writer.listand all mutations write source names back to the notebook's library entry withsources_scraped_attimestamp.listshows warnings when homepage source count diverges from last deep scrape.[formcontrolname='urls']+Insertbutton (credit: DataNath/notebooklm_source_automation).link.closest('mat-card.project-button-card')for homepage operations.New selectors in
config.pySource management + notebook management selectors for Angular Material UI elements.
Test plan
All features verified against live NotebookLM:
🤖 Generated with Claude Code