From notebooklm
Use when the user explicitly mentions NotebookLM, says "/notebooklm", asks to create a podcast/audio overview from documents, generate a quiz or flashcards from sources, or add sources to NotebookLM. Do NOT trigger on generic summarize/mindmap requests without NotebookLM mentioned.
How this skill is triggered — by the user, by Claude, or both
Slash command
/notebooklm:notebooklmThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
<!-- notebooklm-py v0.3.4 -->
Programmatic access to Google NotebookLM via notebooklm-py.
Full command reference: see COMMANDS.md in this directory.
Always use absolute path — PATH is unreliable in systemd/bot contexts:
# Auto-detect OS (bin on Linux/Mac, Scripts on Windows)
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" || -n "$WINDIR" ]]; then
NLM=~/.notebooklm-venv/Scripts/notebooklm
export PYTHONIOENCODING=utf-8 PYTHONUTF8=1 # prevent cp1251 crash on Windows
else
NLM=~/.notebooklm-venv/bin/notebooklm
fi
# Detect Python (handles Windows py launcher and direct paths)
if command -v python3.12 &>/dev/null; then
PYTHON=python3.12
elif command -v python3.11 &>/dev/null; then
PYTHON=python3.11
elif command -v python3.10 &>/dev/null; then
PYTHON=python3.10
elif command -v py &>/dev/null && py -3 --version &>/dev/null; then
PYTHON="py -3" # Windows launcher
elif [[ -f ~/AppData/Local/Programs/Python/Python312/python.exe ]]; then
PYTHON=~/AppData/Local/Programs/Python/Python312/python.exe
else
PYTHON=$(command -v python3 || command -v python)
fi
$PYTHON -c "import sys; assert sys.version_info >= (3,10), f'Too old: {sys.version}'" || exit 1
$PYTHON -m venv ~/.notebooklm-venv
# Use correct pip/notebooklm path per OS
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" || -n "$WINDIR" ]]; then
PIP=~/.notebooklm-venv/Scripts/pip
NLM=~/.notebooklm-venv/Scripts/notebooklm
export PYTHONIOENCODING=utf-8 PYTHONUTF8=1
else
PIP=~/.notebooklm-venv/bin/pip
NLM=~/.notebooklm-venv/bin/notebooklm
fi
$PIP install "notebooklm-py[browser]==0.3.4"
~/.notebooklm-venv/bin/playwright install chromium 2>/dev/null \
|| ~/.notebooklm-venv/Scripts/playwright install chromium
$NLM --help && echo "OK"
This server is headless — no display for a browser window.
Run on your local machine (Mac/Linux with a desktop):
pip install "notebooklm-py[browser]==0.3.4"
playwright install chromium
notebooklm login # opens browser, sign in
scp ~/.notebooklm/storage_state.json deploy@YOUR_SERVER:~/.notebooklm/storage_state.json
Then verify on server:
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" || -n "$WINDIR" ]]; then
NLM=~/.notebooklm-venv/Scripts/notebooklm; export PYTHONIOENCODING=utf-8 PYTHONUTF8=1
else
NLM=~/.notebooklm-venv/bin/notebooklm
fi
$NLM auth check && $NLM list
Tell user: "Opening a browser — sign in to Google and go to notebooklm.google.com. Tell me when you're on the homepage."
PID=$$
SCRIPT=/tmp/nlm_login_${PID}.py
LOG=/tmp/nlm_login_${PID}.log
SIGNAL=~/.notebooklm/save_signal
mkdir -p ~/.notebooklm
rm -f "$SIGNAL"
cat > "$SCRIPT" << 'PYEOF'
import json, os, sys, time
from pathlib import Path
from playwright.sync_api import sync_playwright
STORAGE_PATH = Path.home() / ".notebooklm" / "storage_state.json"
PROFILE_PATH = Path.home() / ".notebooklm" / "browser_profile"
SIGNAL_FILE = Path.home() / ".notebooklm" / "save_signal"
TIMEOUT_SEC = 300 # 5 minutes
SIGNAL_FILE.unlink(missing_ok=True)
STORAGE_PATH.parent.mkdir(parents=True, exist_ok=True)
print("Opening browser...", flush=True)
with sync_playwright() as p:
browser = p.chromium.launch_persistent_context(
user_data_dir=str(PROFILE_PATH),
headless=False,
args=["--disable-blink-features=AutomationControlled"],
)
page = browser.pages[0] if browser.pages else browser.new_page()
page.goto("https://notebooklm.google.com/")
print("Waiting for signal (5 min timeout)...", flush=True)
deadline = time.time() + TIMEOUT_SEC
while not SIGNAL_FILE.exists():
if time.time() > deadline:
print("TIMEOUT — no signal received. Exiting.")
browser.close()
sys.exit(1)
time.sleep(1)
storage = browser.storage_state()
STORAGE_PATH.write_text(json.dumps(storage))
names = [c["name"] for c in storage.get("cookies", [])]
print(f"Saved {len(names)} cookies: {names}")
browser.close()
SIGNAL_FILE.unlink(missing_ok=True)
print(f"Auth saved to: {STORAGE_PATH}")
PYEOF
VENV_PYTHON=$(ls ~/.notebooklm-venv/bin/python3 2>/dev/null || ls ~/.notebooklm-venv/Scripts/python.exe)
"$VENV_PYTHON" "$SCRIPT" > "$LOG" 2>&1 &
echo "Browser starting (PID=$!)... watching $LOG"
Once user confirms they're on NotebookLM:
touch ~/.notebooklm/save_signal && sleep 8 && cat /tmp/nlm_login_${PID}.log
Verify, then clean up:
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" || -n "$WINDIR" ]]; then
NLM=~/.notebooklm-venv/Scripts/notebooklm; export PYTHONIOENCODING=utf-8 PYTHONUTF8=1
else
NLM=~/.notebooklm-venv/bin/notebooklm
fi
$NLM auth check && $NLM list
rm -f /tmp/nlm_login_${PID}.py /tmp/nlm_login_${PID}.log
If SID cookie missing: rm -rf ~/.notebooklm/browser_profile ~/.notebooklm/storage_state.json and retry.
Before any generate or download command:
# Detect OS and set NLM path
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" || -n "$WINDIR" ]]; then
NLM=~/.notebooklm-venv/Scripts/notebooklm
export PYTHONIOENCODING=utf-8 PYTHONUTF8=1
else
NLM=~/.notebooklm-venv/bin/notebooklm
fi
# Check installed
[[ -x "$NLM" ]] || { echo "NOT INSTALLED — run Step 0 first"; exit 1; }
$NLM auth check || { echo "AUTH EXPIRED — re-login required"; exit 1; }
Signs of expiry: Auth/cookie error, missing SID, 401 responses.
In Telegram context: bot cannot guide interactive login. Instead:
scp ~/.notebooklm/storage_state.json ... from your local machine and tell me when done."auth check.# Detect OS and set NLM path
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" || -n "$WINDIR" ]]; then
NLM=~/.notebooklm-venv/Scripts/notebooklm
export PYTHONIOENCODING=utf-8 PYTHONUTF8=1
else
NLM=~/.notebooklm-venv/bin/notebooklm
fi
[[ -x "$NLM" ]] || { echo "NOT INSTALLED — run Step 0 first"; exit 1; }
$NLM create "Research: [topic]"
$NLM source add "url1" && $NLM source add "url2"
# Wait for all sources — abort if any errored
$NLM source list --json | python3 -c "
import sys, json
sources = json.load(sys.stdin)['sources']
errors = [s['title'] for s in sources if s['status'] == 'error']
if errors: sys.exit('ERROR sources: ' + str(errors))
print(f'All {len(sources)} sources ready')
" || exit 1
$NLM auth check || exit 1
$NLM generate audio "Focus on [angle]" # confirm with user first
# Get artifact ID automatically and poll (45 min max)
ARTIFACT_ID=$($NLM artifact list --json | python3 -c "
import sys, json
arts = json.load(sys.stdin)['artifacts']
pending = [a for a in arts if a['status'] in ('in_progress', 'pending')]
print(pending[0]['id']) if pending else sys.exit('No pending artifact found')
")
$NLM artifact wait "$ARTIFACT_ID" --timeout 2700 # 45 min max
$NLM download audio ./podcast.mp3
VAULT=~/obsidian-vault
DEST="$VAULT/NotebookLM"
mkdir -p "$DEST"
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" || -n "$WINDIR" ]]; then
NLM=~/.notebooklm-venv/Scripts/notebooklm; export PYTHONIOENCODING=utf-8 PYTHONUTF8=1
else
NLM=~/.notebooklm-venv/bin/notebooklm
fi
SLUG=$(date +%Y-%m-%d_%H%M%S)
$NLM download report ./report.md
cp ./report.md "$DEST/${SLUG}-report.md"
# For quiz/flashcards — download then hand off to obsidian agent for conversion
Or delegate to the obsidian agent for rich linking and tagging.
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" || -n "$WINDIR" ]]; then
NLM=~/.notebooklm-venv/Scripts/notebooklm; export PYTHONIOENCODING=utf-8 PYTHONUTF8=1
else
NLM=~/.notebooklm-venv/bin/notebooklm
fi
$NLM create "Analysis: [project]"
$NLM source add ./doc.pdf
$NLM ask "Summarize key points"
test -f ~/.notebooklm/storage_state.json && echo OK || echo MISSING
$(ls ~/.notebooklm-venv/bin/python3 2>/dev/null || echo ~/.notebooklm-venv/Scripts/python.exe) << 'PYEOF'
import json, os
path = os.path.expanduser("~/.notebooklm/storage_state.json")
with open(path) as f:
data = json.load(f)
essential = {".google.com", "notebooklm.google.com", "accounts.google.com"}
skip = {"_gcl_au", "_ga", "_ga_W0LDH41ZCB", "OTZ", "ACCOUNT_CHOOSER"}
stripped = {
"cookies": [c for c in data["cookies"]
if c["domain"] in essential and c["name"] not in skip],
"origins": [o for o in data.get("origins", [])
if "notebooklm" in o.get("origin", "")]
}
print(json.dumps(stripped, separators=(',',':')))
PYEOF
Read this skill file. Replace "Step 0: Install", "Step 0b: Authenticate", and "Adding NotebookLM to Co-work" sections with:
## Auto-Authentication (Co-work)
Before ANY notebooklm command:
\`\`\`bash
pip install "notebooklm-py[browser]==0.3.4" 2>/dev/null
export NOTEBOOKLM_AUTH_JSON='<STRIPPED AUTH JSON FROM STEP 2>'
\`\`\`
Verify: `notebooklm auth check && notebooklm list`
If auth fails — cookies expired. Ask user to re-login locally and regenerate this skill.
Save to ~/Desktop/NotebookLMSkill-Cowork.md.
SECURITY WARNING: This file contains your live Google session cookies in plaintext. Do NOT commit it to git, share it, or upload it anywhere except Claude Co-work.
Tell user: "Saved to Desktop as NotebookLMSkill-Cowork.md. Upload it in Claude Co-work. When cookies expire, say 'regenerate my Co-work NotebookLM skill'."
Run automatically: auth check, list, status, use, create, source add/list/wait/fulltext, artifact list/wait, ask (without --save-as-note), history, research status/wait, language *
Ask before running: delete, generate *, download *, ask --save-as-note, history --save
| Error | Cause | Fix |
|---|---|---|
| Auth/cookie error | Session expired | Re-login (scp from local) |
| "No notebook context" | Context not set | $NLM use <id> |
| Rate limiting | Google throttle | Wait 5-10 min, retry |
| Download fails | Generation incomplete | $NLM artifact list |
| source status=ERROR | Bad URL / unsupported format | Remove source, fix URL |
| artifact wait timeout | >45 min stuck | Check rate limits, retry |
Generation times: audio 10-20 min, video 15-45 min, quiz/flashcards 5-15 min. Unofficial API — Google can change it without warning.
Guides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.
npx claudepluginhub assche2/notebooklm-plugin --plugin notebooklm