Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Query Google NotebookLM notebooks from Claude Code for source-grounded, citation-backed answers from Gemini.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
scripts/notebook_manager.py
1#!/usr/bin/env python32"""3Notebook Library Management for NotebookLM4Manages a library of NotebookLM notebooks with metadata5Based on the MCP server implementation6"""78import json9import argparse10import uuid11import os12from pathlib import Path13from typing import Dict, List, Optional, Any14from datetime import datetime151617class NotebookLibrary:18"""Manages a collection of NotebookLM notebooks with metadata"""1920def __init__(self):21"""Initialize the notebook library"""22# Store data within the skill directory23skill_dir = Path(__file__).parent.parent24self.data_dir = skill_dir / "data"25self.data_dir.mkdir(parents=True, exist_ok=True)2627self.library_file = self.data_dir / "library.json"28self.notebooks: Dict[str, Dict[str, Any]] = {}29self.active_notebook_id: Optional[str] = None3031# Load existing library32self._load_library()3334def _load_library(self):35"""Load library from disk"""36if self.library_file.exists():37try:38with open(self.library_file, 'r') as f:39data = json.load(f)40self.notebooks = data.get('notebooks', {})41self.active_notebook_id = data.get('active_notebook_id')42print(f"๐ Loaded library with {len(self.notebooks)} notebooks")43except Exception as e:44print(f"โ ๏ธ Error loading library: {e}")45self.notebooks = {}46self.active_notebook_id = None47else:48self._save_library()4950def _save_library(self):51"""Save library to disk"""52try:53data = {54'notebooks': self.notebooks,55'active_notebook_id': self.active_notebook_id,56'updated_at': datetime.now().isoformat()57}58with open(self.library_file, 'w') as f:59json.dump(data, f, indent=2)60except Exception as e:61print(f"โ Error saving library: {e}")6263def add_notebook(64self,65url: str,66name: str,67description: str,68topics: List[str],69content_types: Optional[List[str]] = None,70use_cases: Optional[List[str]] = None,71tags: Optional[List[str]] = None72) -> Dict[str, Any]:73"""74Add a new notebook to the library7576Args:77url: NotebookLM notebook URL78name: Display name for the notebook79description: What's in this notebook80topics: Topics covered81content_types: Types of content (optional)82use_cases: When to use this notebook (optional)83tags: Additional tags for organization (optional)8485Returns:86The created notebook object87"""88# Generate ID from name89notebook_id = name.lower().replace(' ', '-').replace('_', '-')9091# Check for duplicates92if notebook_id in self.notebooks:93raise ValueError(f"Notebook with ID '{notebook_id}' already exists")9495# Create notebook object96notebook = {97'id': notebook_id,98'url': url,99'name': name,100'description': description,101'topics': topics,102'content_types': content_types or [],103'use_cases': use_cases or [],104'tags': tags or [],105'created_at': datetime.now().isoformat(),106'updated_at': datetime.now().isoformat(),107'use_count': 0,108'last_used': None109}110111# Add to library112self.notebooks[notebook_id] = notebook113114# Set as active if it's the first notebook115if len(self.notebooks) == 1:116self.active_notebook_id = notebook_id117118self._save_library()119120print(f"โ Added notebook: {name} ({notebook_id})")121return notebook122123def remove_notebook(self, notebook_id: str) -> bool:124"""125Remove a notebook from the library126127Args:128notebook_id: ID of notebook to remove129130Returns:131True if removed, False if not found132"""133if notebook_id in self.notebooks:134del self.notebooks[notebook_id]135136# Clear active if it was removed137if self.active_notebook_id == notebook_id:138self.active_notebook_id = None139# Set new active if there are other notebooks140if self.notebooks:141self.active_notebook_id = list(self.notebooks.keys())[0]142143self._save_library()144print(f"โ Removed notebook: {notebook_id}")145return True146147print(f"โ ๏ธ Notebook not found: {notebook_id}")148return False149150def update_notebook(151self,152notebook_id: str,153name: Optional[str] = None,154description: Optional[str] = None,155topics: Optional[List[str]] = None,156content_types: Optional[List[str]] = None,157use_cases: Optional[List[str]] = None,158tags: Optional[List[str]] = None,159url: Optional[str] = None160) -> Dict[str, Any]:161"""162Update notebook metadata163164Args:165notebook_id: ID of notebook to update166Other args: Fields to update (None = keep existing)167168Returns:169Updated notebook object170"""171if notebook_id not in self.notebooks:172raise ValueError(f"Notebook not found: {notebook_id}")173174notebook = self.notebooks[notebook_id]175176# Update fields if provided177if name is not None:178notebook['name'] = name179if description is not None:180notebook['description'] = description181if topics is not None:182notebook['topics'] = topics183if content_types is not None:184notebook['content_types'] = content_types185if use_cases is not None:186notebook['use_cases'] = use_cases187if tags is not None:188notebook['tags'] = tags189if url is not None:190notebook['url'] = url191192notebook['updated_at'] = datetime.now().isoformat()193194self._save_library()195print(f"โ Updated notebook: {notebook['name']}")196return notebook197198def get_notebook(self, notebook_id: str) -> Optional[Dict[str, Any]]:199"""Get a specific notebook by ID"""200return self.notebooks.get(notebook_id)201202def list_notebooks(self) -> List[Dict[str, Any]]:203"""List all notebooks in the library"""204return list(self.notebooks.values())205206def search_notebooks(self, query: str) -> List[Dict[str, Any]]:207"""208Search notebooks by query209210Args:211query: Search query (searches name, description, topics, tags)212213Returns:214List of matching notebooks215"""216query_lower = query.lower()217results = []218219for notebook in self.notebooks.values():220# Search in various fields221searchable = [222notebook['name'].lower(),223notebook['description'].lower(),224' '.join(notebook['topics']).lower(),225' '.join(notebook['tags']).lower(),226' '.join(notebook.get('use_cases', [])).lower()227]228229if any(query_lower in field for field in searchable):230results.append(notebook)231232return results233234def select_notebook(self, notebook_id: str) -> Dict[str, Any]:235"""236Set a notebook as active237238Args:239notebook_id: ID of notebook to activate240241Returns:242The activated notebook243"""244if notebook_id not in self.notebooks:245raise ValueError(f"Notebook not found: {notebook_id}")246247self.active_notebook_id = notebook_id248self._save_library()249250notebook = self.notebooks[notebook_id]251print(f"โ Activated notebook: {notebook['name']}")252return notebook253254def get_active_notebook(self) -> Optional[Dict[str, Any]]:255"""Get the currently active notebook"""256if self.active_notebook_id:257return self.notebooks.get(self.active_notebook_id)258return None259260def increment_use_count(self, notebook_id: str) -> Dict[str, Any]:261"""262Increment usage counter for a notebook263264Args:265notebook_id: ID of notebook that was used266267Returns:268Updated notebook269"""270if notebook_id not in self.notebooks:271raise ValueError(f"Notebook not found: {notebook_id}")272273notebook = self.notebooks[notebook_id]274notebook['use_count'] += 1275notebook['last_used'] = datetime.now().isoformat()276277self._save_library()278return notebook279280def get_stats(self) -> Dict[str, Any]:281"""Get library statistics"""282total_notebooks = len(self.notebooks)283total_topics = set()284total_use_count = 0285286for notebook in self.notebooks.values():287total_topics.update(notebook['topics'])288total_use_count += notebook['use_count']289290# Find most used291most_used = None292if self.notebooks:293most_used = max(294self.notebooks.values(),295key=lambda n: n['use_count']296)297298return {299'total_notebooks': total_notebooks,300'total_topics': len(total_topics),301'total_use_count': total_use_count,302'active_notebook': self.get_active_notebook(),303'most_used_notebook': most_used,304'library_path': str(self.library_file)305}306307308def main():309"""Command-line interface for notebook management"""310parser = argparse.ArgumentParser(description='Manage NotebookLM library')311312subparsers = parser.add_subparsers(dest='command', help='Commands')313314# Add command315add_parser = subparsers.add_parser('add', help='Add a notebook')316add_parser.add_argument('--url', required=True, help='NotebookLM URL')317add_parser.add_argument('--name', required=True, help='Display name')318add_parser.add_argument('--description', required=True, help='Description')319add_parser.add_argument('--topics', required=True, help='Comma-separated topics')320add_parser.add_argument('--use-cases', help='Comma-separated use cases')321add_parser.add_argument('--tags', help='Comma-separated tags')322323# List command324subparsers.add_parser('list', help='List all notebooks')325326# Search command327search_parser = subparsers.add_parser('search', help='Search notebooks')328search_parser.add_argument('--query', required=True, help='Search query')329330# Activate command331activate_parser = subparsers.add_parser('activate', help='Set active notebook')332activate_parser.add_argument('--id', required=True, help='Notebook ID')333334# Remove command335remove_parser = subparsers.add_parser('remove', help='Remove a notebook')336remove_parser.add_argument('--id', required=True, help='Notebook ID')337338# Stats command339subparsers.add_parser('stats', help='Show library statistics')340341args = parser.parse_args()342343# Initialize library344library = NotebookLibrary()345346# Execute command347if args.command == 'add':348topics = [t.strip() for t in args.topics.split(',')]349use_cases = [u.strip() for u in args.use_cases.split(',')] if args.use_cases else None350tags = [t.strip() for t in args.tags.split(',')] if args.tags else None351352notebook = library.add_notebook(353url=args.url,354name=args.name,355description=args.description,356topics=topics,357use_cases=use_cases,358tags=tags359)360print(json.dumps(notebook, indent=2))361362elif args.command == 'list':363notebooks = library.list_notebooks()364if notebooks:365print("\n๐ Notebook Library:")366for notebook in notebooks:367active = " [ACTIVE]" if notebook['id'] == library.active_notebook_id else ""368print(f"\n ๐ {notebook['name']}{active}")369print(f" ID: {notebook['id']}")370print(f" Topics: {', '.join(notebook['topics'])}")371print(f" Uses: {notebook['use_count']}")372else:373print("๐ Library is empty. Add notebooks with: notebook_manager.py add")374375elif args.command == 'search':376results = library.search_notebooks(args.query)377if results:378print(f"\n๐ Found {len(results)} notebooks:")379for notebook in results:380print(f"\n ๐ {notebook['name']} ({notebook['id']})")381print(f" {notebook['description']}")382else:383print(f"๐ No notebooks found for: {args.query}")384385elif args.command == 'activate':386notebook = library.select_notebook(args.id)387print(f"Now using: {notebook['name']}")388389elif args.command == 'remove':390if library.remove_notebook(args.id):391print("Notebook removed from library")392393elif args.command == 'stats':394stats = library.get_stats()395print("\n๐ Library Statistics:")396print(f" Total notebooks: {stats['total_notebooks']}")397print(f" Total topics: {stats['total_topics']}")398print(f" Total uses: {stats['total_use_count']}")399if stats['active_notebook']:400print(f" Active: {stats['active_notebook']['name']}")401if stats['most_used_notebook']:402print(f" Most used: {stats['most_used_notebook']['name']} ({stats['most_used_notebook']['use_count']} uses)")403print(f" Library path: {stats['library_path']}")404405else:406parser.print_help()407408409if __name__ == "__main__":410main()