notebooklm-py 0.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. notebooklm_py-0.1.2/.env.example +35 -0
  2. notebooklm_py-0.1.2/.github/workflows/nightly.yml +65 -0
  3. notebooklm_py-0.1.2/.github/workflows/publish.yml +34 -0
  4. notebooklm_py-0.1.2/.github/workflows/test.yml +83 -0
  5. notebooklm_py-0.1.2/.github/workflows/verify-artifacts.yml +123 -0
  6. notebooklm_py-0.1.2/.gitignore +20 -0
  7. notebooklm_py-0.1.2/.pre-commit-config.yaml +7 -0
  8. notebooklm_py-0.1.2/AGENTS.md +115 -0
  9. notebooklm_py-0.1.2/CHANGELOG.md +110 -0
  10. notebooklm_py-0.1.2/CLAUDE.md +168 -0
  11. notebooklm_py-0.1.2/CONTRIBUTING.md +156 -0
  12. notebooklm_py-0.1.2/LICENSE +21 -0
  13. notebooklm_py-0.1.2/PKG-INFO +200 -0
  14. notebooklm_py-0.1.2/README.md +153 -0
  15. notebooklm_py-0.1.2/RELEASING.md +50 -0
  16. notebooklm_py-0.1.2/SECURITY.md +106 -0
  17. notebooklm_py-0.1.2/dev/test-versions.sh +202 -0
  18. notebooklm_py-0.1.2/docs/README.md +35 -0
  19. notebooklm_py-0.1.2/docs/cli-reference.md +537 -0
  20. notebooklm_py-0.1.2/docs/configuration.md +389 -0
  21. notebooklm_py-0.1.2/docs/contributing/adding-rpc-methods.md +379 -0
  22. notebooklm_py-0.1.2/docs/contributing/architecture.md +249 -0
  23. notebooklm_py-0.1.2/docs/contributing/debugging.md +325 -0
  24. notebooklm_py-0.1.2/docs/contributing/testing.md +558 -0
  25. notebooklm_py-0.1.2/docs/designs/notebooklm-skill.md +137 -0
  26. notebooklm_py-0.1.2/docs/examples/bulk-import.py +110 -0
  27. notebooklm_py-0.1.2/docs/examples/quickstart.py +78 -0
  28. notebooklm_py-0.1.2/docs/examples/research-to-podcast.py +95 -0
  29. notebooklm_py-0.1.2/docs/getting-started.md +180 -0
  30. notebooklm_py-0.1.2/docs/python-api.md +783 -0
  31. notebooklm_py-0.1.2/docs/reference/internals/rpc-capture.md +380 -0
  32. notebooklm_py-0.1.2/docs/reference/internals/rpc-ui-reference.md +1290 -0
  33. notebooklm_py-0.1.2/docs/releasing.md +130 -0
  34. notebooklm_py-0.1.2/docs/stability.md +168 -0
  35. notebooklm_py-0.1.2/docs/troubleshooting.md +395 -0
  36. notebooklm_py-0.1.2/examples/chat_and_ask.py +185 -0
  37. notebooklm_py-0.1.2/examples/file_upload_example.py +53 -0
  38. notebooklm_py-0.1.2/examples/notes_and_mind_maps.py +256 -0
  39. notebooklm_py-0.1.2/examples/podcast_generation.py +102 -0
  40. notebooklm_py-0.1.2/examples/research_workflow.py +158 -0
  41. notebooklm_py-0.1.2/examples/video_generation.py +157 -0
  42. notebooklm_py-0.1.2/notebooklm-py.png +0 -0
  43. notebooklm_py-0.1.2/pyproject.toml +121 -0
  44. notebooklm_py-0.1.2/scripts/heal_golden_notebook.py +252 -0
  45. notebooklm_py-0.1.2/scripts/pre_release_check.sh +49 -0
  46. notebooklm_py-0.1.2/src/notebooklm/__init__.py +108 -0
  47. notebooklm_py-0.1.2/src/notebooklm/_artifacts.py +1489 -0
  48. notebooklm_py-0.1.2/src/notebooklm/_chat.py +379 -0
  49. notebooklm_py-0.1.2/src/notebooklm/_core.py +254 -0
  50. notebooklm_py-0.1.2/src/notebooklm/_notebooks.py +273 -0
  51. notebooklm_py-0.1.2/src/notebooklm/_notes.py +298 -0
  52. notebooklm_py-0.1.2/src/notebooklm/_research.py +246 -0
  53. notebooklm_py-0.1.2/src/notebooklm/_sources.py +764 -0
  54. notebooklm_py-0.1.2/src/notebooklm/auth.py +418 -0
  55. notebooklm_py-0.1.2/src/notebooklm/cli/__init__.py +134 -0
  56. notebooklm_py-0.1.2/src/notebooklm/cli/artifact.py +464 -0
  57. notebooklm_py-0.1.2/src/notebooklm/cli/chat.py +235 -0
  58. notebooklm_py-0.1.2/src/notebooklm/cli/download.py +717 -0
  59. notebooklm_py-0.1.2/src/notebooklm/cli/download_helpers.py +135 -0
  60. notebooklm_py-0.1.2/src/notebooklm/cli/generate.py +668 -0
  61. notebooklm_py-0.1.2/src/notebooklm/cli/grouped.py +83 -0
  62. notebooklm_py-0.1.2/src/notebooklm/cli/helpers.py +489 -0
  63. notebooklm_py-0.1.2/src/notebooklm/cli/note.py +222 -0
  64. notebooklm_py-0.1.2/src/notebooklm/cli/notebook.py +245 -0
  65. notebooklm_py-0.1.2/src/notebooklm/cli/options.py +93 -0
  66. notebooklm_py-0.1.2/src/notebooklm/cli/research.py +207 -0
  67. notebooklm_py-0.1.2/src/notebooklm/cli/session.py +302 -0
  68. notebooklm_py-0.1.2/src/notebooklm/cli/skill.py +153 -0
  69. notebooklm_py-0.1.2/src/notebooklm/cli/source.py +696 -0
  70. notebooklm_py-0.1.2/src/notebooklm/client.py +172 -0
  71. notebooklm_py-0.1.2/src/notebooklm/data/SKILL.md +402 -0
  72. notebooklm_py-0.1.2/src/notebooklm/notebooklm_cli.py +112 -0
  73. notebooklm_py-0.1.2/src/notebooklm/paths.py +105 -0
  74. notebooklm_py-0.1.2/src/notebooklm/rpc/__init__.py +66 -0
  75. notebooklm_py-0.1.2/src/notebooklm/rpc/decoder.py +250 -0
  76. notebooklm_py-0.1.2/src/notebooklm/rpc/encoder.py +98 -0
  77. notebooklm_py-0.1.2/src/notebooklm/rpc/types.py +274 -0
  78. notebooklm_py-0.1.2/src/notebooklm/types.py +629 -0
  79. notebooklm_py-0.1.2/tests/conftest.py +86 -0
  80. notebooklm_py-0.1.2/tests/e2e/__init__.py +0 -0
  81. notebooklm_py-0.1.2/tests/e2e/conftest.py +441 -0
  82. notebooklm_py-0.1.2/tests/e2e/test_artifacts.py +241 -0
  83. notebooklm_py-0.1.2/tests/e2e/test_downloads.py +137 -0
  84. notebooklm_py-0.1.2/tests/e2e/test_file_upload.py +102 -0
  85. notebooklm_py-0.1.2/tests/e2e/test_generation.py +360 -0
  86. notebooklm_py-0.1.2/tests/e2e/test_notebooks.py +140 -0
  87. notebooklm_py-0.1.2/tests/e2e/test_notes.py +107 -0
  88. notebooklm_py-0.1.2/tests/e2e/test_research.py +260 -0
  89. notebooklm_py-0.1.2/tests/e2e/test_sources.py +247 -0
  90. notebooklm_py-0.1.2/tests/integration/conftest.py +73 -0
  91. notebooklm_py-0.1.2/tests/integration/test_artifacts.py +807 -0
  92. notebooklm_py-0.1.2/tests/integration/test_chat.py +157 -0
  93. notebooklm_py-0.1.2/tests/integration/test_core.py +25 -0
  94. notebooklm_py-0.1.2/tests/integration/test_download_multi_artifact.py +295 -0
  95. notebooklm_py-0.1.2/tests/integration/test_notebooks.py +493 -0
  96. notebooklm_py-0.1.2/tests/integration/test_notes.py +241 -0
  97. notebooklm_py-0.1.2/tests/integration/test_research_api.py +240 -0
  98. notebooklm_py-0.1.2/tests/integration/test_sources.py +585 -0
  99. notebooklm_py-0.1.2/tests/unit/cli/__init__.py +1 -0
  100. notebooklm_py-0.1.2/tests/unit/cli/conftest.py +194 -0
  101. notebooklm_py-0.1.2/tests/unit/cli/test_artifact.py +571 -0
  102. notebooklm_py-0.1.2/tests/unit/cli/test_download.py +384 -0
  103. notebooklm_py-0.1.2/tests/unit/cli/test_generate.py +463 -0
  104. notebooklm_py-0.1.2/tests/unit/cli/test_grouped.py +128 -0
  105. notebooklm_py-0.1.2/tests/unit/cli/test_helpers.py +566 -0
  106. notebooklm_py-0.1.2/tests/unit/cli/test_note.py +294 -0
  107. notebooklm_py-0.1.2/tests/unit/cli/test_notebook.py +708 -0
  108. notebooklm_py-0.1.2/tests/unit/cli/test_resolve.py +361 -0
  109. notebooklm_py-0.1.2/tests/unit/cli/test_session.py +350 -0
  110. notebooklm_py-0.1.2/tests/unit/cli/test_skill.py +167 -0
  111. notebooklm_py-0.1.2/tests/unit/cli/test_source.py +625 -0
  112. notebooklm_py-0.1.2/tests/unit/test_api_coverage.py +306 -0
  113. notebooklm_py-0.1.2/tests/unit/test_artifact_downloads.py +391 -0
  114. notebooklm_py-0.1.2/tests/unit/test_auth.py +682 -0
  115. notebooklm_py-0.1.2/tests/unit/test_client.py +304 -0
  116. notebooklm_py-0.1.2/tests/unit/test_conversation.py +96 -0
  117. notebooklm_py-0.1.2/tests/unit/test_decoder.py +402 -0
  118. notebooklm_py-0.1.2/tests/unit/test_download_helpers.py +189 -0
  119. notebooklm_py-0.1.2/tests/unit/test_encoder.py +192 -0
  120. notebooklm_py-0.1.2/tests/unit/test_notes_unit.py +637 -0
  121. notebooklm_py-0.1.2/tests/unit/test_paths.py +127 -0
  122. notebooklm_py-0.1.2/tests/unit/test_research.py +88 -0
  123. notebooklm_py-0.1.2/tests/unit/test_rpc_types.py +88 -0
  124. notebooklm_py-0.1.2/tests/unit/test_source_status.py +257 -0
  125. notebooklm_py-0.1.2/tests/unit/test_sources_upload.py +514 -0
  126. notebooklm_py-0.1.2/tests/unit/test_types.py +489 -0
  127. notebooklm_py-0.1.2/tests/unit/test_youtube_extraction.py +69 -0
@@ -0,0 +1,35 @@
1
+ # NotebookLM Configuration
2
+ # Copy this file to .env and fill in your values
3
+
4
+ # =============================================================================
5
+ # Configuration Environment Variables
6
+ # =============================================================================
7
+
8
+ # Custom home directory for all config files (optional)
9
+ # Default: ~/.notebooklm
10
+ # NOTEBOOKLM_HOME=/custom/path
11
+
12
+ # Inline authentication JSON for CI/CD (optional)
13
+ # When set, authentication is read from this variable instead of a file
14
+ # Get the value from: cat $NOTEBOOKLM_HOME/storage_state.json (default: ~/.notebooklm)
15
+ # NOTEBOOKLM_AUTH_JSON='{"cookies":[...]}'
16
+
17
+ # Enable RPC debug logging (optional)
18
+ # NOTEBOOKLM_DEBUG_RPC=1
19
+
20
+ # =============================================================================
21
+ # E2E Testing Configuration
22
+ # =============================================================================
23
+
24
+ # Required for E2E tests: Your READ-ONLY test notebook ID
25
+ # Create a notebook at https://notebooklm.google.com with:
26
+ # - Multiple sources (text, URL, PDF, etc.)
27
+ # - Some pre-generated artifacts (audio, quiz, etc.)
28
+ # Copy the notebook ID from the URL: notebooklm.google.com/notebook/YOUR_ID
29
+ # This notebook is used for READ-ONLY tests (list, get, download operations)
30
+ NOTEBOOKLM_READ_ONLY_NOTEBOOK_ID=your-notebook-id-here
31
+
32
+ # Optional: Generation test notebook ID
33
+ # If not set, a notebook will be auto-created and its ID stored in
34
+ # NOTEBOOKLM_HOME/generation_notebook_id for reuse across test runs.
35
+ # NOTEBOOKLM_GENERATION_NOTEBOOK_ID=your-generation-notebook-id
@@ -0,0 +1,65 @@
1
+ name: Nightly E2E Tests
2
+
3
+ on:
4
+ schedule:
5
+ # Run at 6 AM UTC daily (10 PM PST / 1 AM EST)
6
+ - cron: '0 6 * * *'
7
+ workflow_dispatch: # Allow manual trigger
8
+
9
+ concurrency:
10
+ group: ${{ github.workflow }}
11
+ cancel-in-progress: true
12
+
13
+ jobs:
14
+ e2e:
15
+ name: E2E Tests
16
+ runs-on: ubuntu-latest
17
+ # Only run on main repo (not forks) due to secrets requirement
18
+ if: github.repository == 'teng-lin/notebooklm-py'
19
+
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+
23
+ - name: Set up Python
24
+ uses: actions/setup-python@v5
25
+ with:
26
+ python-version: "3.12"
27
+ cache: 'pip'
28
+
29
+ - name: Install dependencies
30
+ run: |
31
+ python -m pip install --upgrade pip
32
+ pip install -e ".[all]"
33
+
34
+ - name: Get Playwright version
35
+ id: playwright-version
36
+ run: |
37
+ echo "version=$(pip show playwright | grep '^Version:' | cut -d' ' -f2)" >> $GITHUB_OUTPUT
38
+
39
+ - name: Cache Playwright browsers
40
+ uses: actions/cache@v4
41
+ with:
42
+ path: ~/.cache/ms-playwright
43
+ key: playwright-ubuntu-latest-${{ steps.playwright-version.outputs.version }}
44
+
45
+ - name: Install Playwright browsers and dependencies
46
+ run: |
47
+ playwright install chromium
48
+ playwright install-deps
49
+
50
+ - name: Debug - Print notebook IDs
51
+ env:
52
+ NOTEBOOKLM_READ_ONLY_NOTEBOOK_ID: ${{ secrets.NOTEBOOKLM_READ_ONLY_NOTEBOOK_ID }}
53
+ NOTEBOOKLM_GENERATION_NOTEBOOK_ID: ${{ secrets.NOTEBOOKLM_GENERATION_NOTEBOOK_ID }}
54
+ run: |
55
+ echo "READ_ONLY_NOTEBOOK_ID: $NOTEBOOKLM_READ_ONLY_NOTEBOOK_ID"
56
+ echo "GENERATION_NOTEBOOK_ID: $NOTEBOOKLM_GENERATION_NOTEBOOK_ID"
57
+ echo "READ_ONLY length: ${#NOTEBOOKLM_READ_ONLY_NOTEBOOK_ID}"
58
+ echo "GENERATION length: ${#NOTEBOOKLM_GENERATION_NOTEBOOK_ID}"
59
+
60
+ - name: Run E2E tests
61
+ env:
62
+ NOTEBOOKLM_AUTH_JSON: ${{ secrets.NOTEBOOKLM_AUTH_JSON }}
63
+ NOTEBOOKLM_READ_ONLY_NOTEBOOK_ID: ${{ secrets.NOTEBOOKLM_READ_ONLY_NOTEBOOK_ID }}
64
+ NOTEBOOKLM_GENERATION_NOTEBOOK_ID: ${{ secrets.NOTEBOOKLM_GENERATION_NOTEBOOK_ID }}
65
+ run: pytest tests/e2e -m "not variants" -s -v --tb=short
@@ -0,0 +1,34 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ build-and-publish:
10
+ name: Build and publish to PyPI
11
+ runs-on: ubuntu-latest
12
+ permissions:
13
+ id-token: write
14
+ contents: read
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Set up Python 3.12
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: "3.12"
23
+
24
+ - name: Install build tools
25
+ run: |
26
+ python -m pip install --upgrade pip
27
+ pip install build
28
+
29
+ - name: Build package
30
+ run: |
31
+ python -m build
32
+
33
+ - name: Publish to PyPI
34
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,83 @@
1
+ name: Test
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ concurrency:
10
+ group: ${{ github.workflow }}-${{ github.ref }}
11
+ cancel-in-progress: true
12
+
13
+ jobs:
14
+ quality:
15
+ name: Code Quality
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Set up Python
21
+ uses: actions/setup-python@v5
22
+ with:
23
+ python-version: "3.12"
24
+ cache: 'pip'
25
+
26
+ - name: Install dependencies
27
+ run: |
28
+ python -m pip install --upgrade pip
29
+ pip install -e ".[all]"
30
+
31
+ - name: Run linting
32
+ run: ruff check src/ tests/
33
+
34
+ - name: Check formatting
35
+ run: ruff format --check src/ tests/
36
+
37
+ - name: Run type checking
38
+ run: mypy src/notebooklm --ignore-missing-imports
39
+
40
+ test:
41
+ name: Test (${{ matrix.os }}, Python ${{ matrix.python-version }})
42
+ runs-on: ${{ matrix.os }}
43
+ needs: quality
44
+ strategy:
45
+ fail-fast: false
46
+ matrix:
47
+ os: [ubuntu-latest, macos-latest]
48
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
49
+
50
+ steps:
51
+ - uses: actions/checkout@v4
52
+
53
+ - name: Set up Python ${{ matrix.python-version }}
54
+ uses: actions/setup-python@v5
55
+ with:
56
+ python-version: ${{ matrix.python-version }}
57
+ cache: 'pip'
58
+
59
+ - name: Install dependencies
60
+ run: |
61
+ python -m pip install --upgrade pip
62
+ pip install -e ".[all]"
63
+
64
+ - name: Get Playwright version
65
+ id: playwright-version
66
+ run: |
67
+ echo "version=$(pip show playwright | grep '^Version:' | cut -d' ' -f2)" >> $GITHUB_OUTPUT
68
+
69
+ - name: Cache Playwright browsers
70
+ uses: actions/cache@v4
71
+ with:
72
+ path: ~/.cache/ms-playwright
73
+ key: playwright-${{ matrix.os }}-${{ steps.playwright-version.outputs.version }}
74
+
75
+ - name: Install Playwright browsers
76
+ run: playwright install chromium
77
+
78
+ - name: Install Playwright system dependencies (Linux)
79
+ if: runner.os == 'Linux'
80
+ run: playwright install-deps
81
+
82
+ - name: Run tests with coverage
83
+ run: pytest --cov=src/notebooklm --cov-report=term-missing --cov-fail-under=70
@@ -0,0 +1,123 @@
1
+ name: Verify Generated Artifacts
2
+
3
+ on:
4
+ schedule:
5
+ # Run at 8 AM UTC daily (2 hours after nightly e2e tests)
6
+ - cron: '0 8 * * *'
7
+ workflow_dispatch: # Allow manual trigger
8
+
9
+ jobs:
10
+ verify:
11
+ name: Verify Artifacts
12
+ runs-on: ubuntu-latest
13
+ if: github.repository == 'teng-lin/notebooklm-py'
14
+
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - name: Set up Python
19
+ uses: actions/setup-python@v5
20
+ with:
21
+ python-version: "3.12"
22
+ cache: 'pip'
23
+
24
+ - name: Install dependencies
25
+ run: |
26
+ python -m pip install --upgrade pip
27
+ pip install -e "."
28
+
29
+ - name: Verify artifacts exist
30
+ env:
31
+ NOTEBOOKLM_AUTH_JSON: ${{ secrets.NOTEBOOKLM_AUTH_JSON }}
32
+ NOTEBOOKLM_GENERATION_NOTEBOOK_ID: ${{ secrets.NOTEBOOKLM_GENERATION_NOTEBOOK_ID }}
33
+ run: |
34
+ python -c "
35
+ import asyncio
36
+ import os
37
+ import sys
38
+ from notebooklm import NotebookLMClient
39
+
40
+ # Type ID to display name mapping
41
+ TYPE_NAMES = {
42
+ 1: 'Audio',
43
+ 2: 'Report', # Study Guide, Briefing Doc, Blog Post
44
+ 3: 'Video',
45
+ 4: 'Quiz/Flashcards',
46
+ 5: 'Mind Map',
47
+ 7: 'Infographic',
48
+ 8: 'Slide Deck',
49
+ 9: 'Data Table',
50
+ }
51
+
52
+ # Expected type IDs from generation tests
53
+ # Note: Type 2 is Reports (study guide), Type 4 is Quiz+Flashcards
54
+ EXPECTED_TYPES = {1, 2, 3, 4, 5, 7, 8, 9}
55
+
56
+ async def verify():
57
+ async with await NotebookLMClient.from_storage() as client:
58
+ nb_id = os.environ['NOTEBOOKLM_GENERATION_NOTEBOOK_ID']
59
+ print(f'Checking notebook: {nb_id}')
60
+
61
+ # List artifacts
62
+ artifacts = await client.artifacts.list(nb_id)
63
+ print(f'\nTotal artifacts: {len(artifacts)}')
64
+
65
+ # Group by type and status
66
+ by_type = {}
67
+ for a in artifacts:
68
+ key = a.artifact_type
69
+ if key not in by_type:
70
+ by_type[key] = []
71
+ by_type[key].append(a)
72
+
73
+ print('\nArtifacts by type:')
74
+ for t in sorted(by_type.keys()):
75
+ items = by_type[t]
76
+ type_name = TYPE_NAMES.get(t, f'Unknown({t})')
77
+ print(f' {type_name} (type {t}): {len(items)}')
78
+ for a in items:
79
+ status = a.status or 'unknown'
80
+ variant_info = f', variant={a.variant}' if a.variant else ''
81
+ print(f' - {a.title} ({status}{variant_info})')
82
+
83
+ # Check expected types
84
+ found = set(by_type.keys())
85
+ missing = EXPECTED_TYPES - found
86
+
87
+ print(f'\nExpected types: {len(EXPECTED_TYPES)}')
88
+ print(f'Found types: {len(found)}')
89
+
90
+ if missing:
91
+ missing_names = [TYPE_NAMES.get(t, str(t)) for t in missing]
92
+ print(f'\nWARNING: Missing artifact types: {missing_names}')
93
+
94
+ # Check for completed artifacts
95
+ completed = sum(1 for a in artifacts if a.status == 'completed')
96
+ processing = sum(1 for a in artifacts if a.status == 'processing')
97
+ failed = sum(1 for a in artifacts if a.status == 'failed')
98
+
99
+ print(f'\nStatus summary:')
100
+ print(f' Completed: {completed}')
101
+ print(f' Processing: {processing}')
102
+ print(f' Failed: {failed}')
103
+
104
+ # List notes
105
+ notes = await client.notes.list(nb_id)
106
+ print(f'\nTotal notes: {len(notes)}')
107
+ for n in notes[:10]:
108
+ print(f' - {n.title or \"(untitled)\"}')
109
+ if len(notes) > 10:
110
+ print(f' ... and {len(notes) - 10} more')
111
+
112
+ # Fail if too many failures or no artifacts at all
113
+ if len(artifacts) == 0:
114
+ print('\nERROR: No artifacts found!')
115
+ sys.exit(1)
116
+ if failed > len(artifacts) // 2:
117
+ print(f'\nERROR: Too many failed artifacts ({failed}/{len(artifacts)})')
118
+ sys.exit(1)
119
+
120
+ print('\nVerification complete!')
121
+
122
+ asyncio.run(verify())
123
+ "
@@ -0,0 +1,20 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.class
4
+ .venv/
5
+ env/
6
+ venv/
7
+ .pytest_cache/
8
+ .coverage
9
+ htmlcov/
10
+ dist/
11
+ build/
12
+ *.egg-info/
13
+ .DS_Store
14
+ .env
15
+ .notebooklm/
16
+ captured_rpcs/
17
+ .worktrees/
18
+ .worktree/
19
+ .sisyphus/
20
+ .claude/
@@ -0,0 +1,7 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.8.6
4
+ hooks:
5
+ - id: ruff
6
+ args: [--fix]
7
+ - id: ruff-format
@@ -0,0 +1,115 @@
1
+ # AGENTS.md
2
+
3
+ Guidelines for AI agents working on `notebooklm-py`.
4
+
5
+ **IMPORTANT:** Follow documentation rules in [CONTRIBUTING.md](CONTRIBUTING.md) - especially the file creation and naming conventions.
6
+
7
+ ## Quick Reference
8
+
9
+ See [CLAUDE.md](CLAUDE.md) for full project context. Essential commands:
10
+
11
+ ```bash
12
+ source .venv/bin/activate # Always activate venv first
13
+ pytest # Run tests
14
+ pip install -e ".[all]" # Install in dev mode
15
+ ```
16
+
17
+ ## Code Style Guidelines
18
+
19
+ ### Type Annotations (Python 3.10+)
20
+ ```python
21
+ def process(items: list[str]) -> dict[str, Any]: ...
22
+ async def query(notebook_id: str, source_ids: Optional[list[str]] = None): ...
23
+
24
+ # Use TYPE_CHECKING for circular imports
25
+ from typing import TYPE_CHECKING
26
+ if TYPE_CHECKING:
27
+ from ..api_client import NotebookLMClient
28
+ ```
29
+
30
+ ### Async Patterns
31
+ ```python
32
+ # All client methods are async - use namespaced APIs
33
+ async with await NotebookLMClient.from_storage() as client:
34
+ notebooks = await client.notebooks.list()
35
+ await client.sources.add_url(nb_id, url)
36
+ result = await client.chat.ask(nb_id, question)
37
+ ```
38
+
39
+ ### Data Structures
40
+ ```python
41
+ @dataclass
42
+ class Notebook:
43
+ id: str
44
+ title: str
45
+ created_at: Optional[datetime] = None
46
+
47
+ @classmethod
48
+ def from_api_response(cls, data: list[Any]) -> "Notebook": ...
49
+ ```
50
+
51
+ ### Enums for Constants
52
+ ```python
53
+ class RPCMethod(str, Enum):
54
+ LIST_NOTEBOOKS = "wXbhsf"
55
+
56
+ class AudioFormat(int, Enum):
57
+ DEEP_DIVE = 1
58
+ ```
59
+
60
+ ### Error Handling
61
+ ```python
62
+ class RPCError(Exception):
63
+ def __init__(self, message: str, rpc_id: Optional[str] = None, code: Optional[Any] = None):
64
+ self.rpc_id, self.code = rpc_id, code
65
+ super().__init__(message)
66
+
67
+ raise RPCError(f"No result found for RPC ID: {rpc_id}", rpc_id=rpc_id)
68
+ raise ValueError(f"Invalid YouTube URL: {url}") # For validation
69
+ ```
70
+
71
+ ### Docstrings
72
+ ```python
73
+ def decode_response(raw_response: str, rpc_id: str, allow_null: bool = False) -> Any:
74
+ """Complete decode pipeline: strip prefix -> parse chunks -> extract result.
75
+
76
+ Args:
77
+ raw_response: Raw response text from batchexecute
78
+ rpc_id: RPC method ID to extract result for
79
+ allow_null: If True, return None instead of raising when null
80
+
81
+ Returns:
82
+ Decoded result data
83
+
84
+ Raises:
85
+ RPCError: If RPC returned an error or result not found
86
+ """
87
+ ```
88
+
89
+ ## Testing Patterns
90
+
91
+ ```python
92
+ # Class-based for related tests
93
+ class TestDecodeResponse:
94
+ def test_full_decode_pipeline(self): ...
95
+
96
+ # Markers
97
+ @pytest.mark.e2e # End-to-end (requires auth)
98
+ @pytest.mark.slow # Long-running (audio/video)
99
+ @pytest.mark.asyncio # Async tests
100
+
101
+ # Async tests
102
+ @pytest.mark.asyncio
103
+ async def test_list_notebooks(self, client):
104
+ notebooks = await client.notebooks.list()
105
+ assert isinstance(notebooks, list)
106
+ ```
107
+
108
+ ## Do NOT
109
+
110
+ - Suppress type errors with `# type: ignore`
111
+ - Commit `.env` files or credentials
112
+ - Add dependencies without updating `pyproject.toml`
113
+ - Change RPC method IDs without verifying via network capture
114
+ - Delete or modify e2e tests without running them
115
+ - Create documentation files without following CONTRIBUTING.md rules
@@ -0,0 +1,110 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.2] - 2026-01-10
11
+
12
+ ### Added
13
+ - **Ruff linter/formatter** - Added to development workflow with pre-commit hooks and CI integration
14
+ - **Multi-version testing** - Docker-based test runner script for Python 3.10-3.14 (`/matrix` skill)
15
+ - **Artifact verification workflow** - New CI workflow runs 2 hours after nightly tests to verify generated artifacts
16
+
17
+ ### Changed
18
+ - **Python version support** - Now supports Python 3.10-3.14 (dropped 3.9)
19
+ - **CI authentication** - Use `NOTEBOOKLM_AUTH_JSON` environment variable (inline JSON, no file writes)
20
+
21
+ ### Fixed
22
+ - **E2E test cleanup** - Generation notebook fixture now only cleans artifacts once per session (was deleting artifacts between tests)
23
+ - **Nightly CI** - Fixed pytest marker from `-m e2e` to `-m "not variants"` (e2e marker didn't exist)
24
+ - macOS CI fix for Playwright version extraction (grep pattern anchoring)
25
+ - Python 3.10 test compatibility with mock.patch resolution
26
+
27
+ ### Documentation
28
+ - Claude Code skill: parallel agent safety guidance
29
+ - Claude Code skill: timeout recommendations for all artifact types
30
+ - Claude Code skill: clarified `-n` vs `--notebook` flag availability
31
+
32
+ ## [0.1.1] - 2026-01-08
33
+
34
+ ### Added
35
+ - `NOTEBOOKLM_HOME` environment variable for custom storage location
36
+ - `NOTEBOOKLM_AUTH_JSON` environment variable for inline authentication (CI/CD friendly)
37
+ - Claude Code skill installation via `notebooklm skill install`
38
+
39
+ ### Fixed
40
+ - Infographic generation parameter structure
41
+ - Mind map artifacts now persist as notes after generation
42
+ - Artifact export with proper ExportType enum handling
43
+ - Skill install path resolution for package data
44
+
45
+ ### Documentation
46
+ - PyPI release checklist
47
+ - Streamlined README
48
+ - E2E test fixture documentation
49
+
50
+ ## [0.1.0] - 2026-01-06
51
+
52
+ ### Added
53
+ - Initial release of `notebooklm-py` - unofficial Python client for Google NotebookLM
54
+ - Full notebook CRUD operations (create, list, rename, delete)
55
+ - **Research polling CLI commands** for LLM agent workflows:
56
+ - `notebooklm research status` - Check research progress (non-blocking)
57
+ - `notebooklm research wait --import-all` - Wait for completion and import sources
58
+ - `notebooklm source add-research --no-wait` - Start deep research without blocking
59
+ - **Multi-artifact downloads** with intelligent selection:
60
+ - `download audio`, `download video`, `download infographic`, `download slide-deck`
61
+ - Multiple artifact selection (--all flag)
62
+ - Smart defaults and intelligent filtering (--latest, --earliest, --name, --artifact-id)
63
+ - File/directory conflict handling (--force, --no-clobber, auto-rename)
64
+ - Preview mode (--dry-run) and structured output (--json)
65
+ - Source management:
66
+ - Add URL sources (with YouTube transcript support)
67
+ - Add text sources
68
+ - Add file sources (PDF, TXT, MD, DOCX) via native upload
69
+ - Delete sources
70
+ - Rename sources
71
+ - Studio artifact generation:
72
+ - Audio overviews (podcasts) with 4 formats and 3 lengths
73
+ - Video overviews with 9 visual styles
74
+ - Quizzes and flashcards
75
+ - Infographics, slide decks, and data tables
76
+ - Study guides, briefing docs, and reports
77
+ - Query/chat interface with conversation history support
78
+ - Research agents (Fast and Deep modes)
79
+ - Artifact downloads (audio, video, infographics, slides)
80
+ - CLI with 27 commands
81
+ - Comprehensive documentation (API, RPC, examples)
82
+ - 96 unit tests (100% passing)
83
+ - E2E tests for all major features
84
+
85
+ ### Fixed
86
+ - Audio overview instructions parameter now properly supported at RPC position [6][1][0]
87
+ - Quiz and flashcard distinction via title-based filtering
88
+ - Package renamed from `notebooklm-automation` to `notebooklm`
89
+ - CLI module renamed from `cli.py` to `notebooklm_cli.py`
90
+ - Removed orphaned `cli_query.py` file
91
+
92
+ ### ⚠️ Beta Release Notice
93
+
94
+ This is the initial public release of `notebooklm-py`. While core functionality is tested and working, please note:
95
+
96
+ - **RPC Protocol Fragility**: This library uses undocumented Google APIs. Method IDs can change without notice, potentially breaking functionality. See [Troubleshooting](docs/troubleshooting.md) for debugging guidance.
97
+ - **Unofficial Status**: This is not affiliated with or endorsed by Google.
98
+ - **API Stability**: The Python API may change in future releases as we refine the interface.
99
+
100
+ ### Known Issues
101
+
102
+ - **RPC method IDs may change**: Google can update their internal APIs at any time, breaking this library. Check the [RPC Capture](docs/reference/internals/rpc-capture.md) for how to identify and update method IDs.
103
+ - **Rate limiting**: Heavy usage may trigger Google's rate limits. Add delays between bulk operations.
104
+ - **Authentication expiry**: CSRF tokens expire after some time. Re-run `notebooklm login` if you encounter auth errors.
105
+ - **Large file uploads**: Files over 50MB may fail or timeout. Split large documents if needed.
106
+
107
+ [Unreleased]: https://github.com/teng-lin/notebooklm-py/compare/v0.1.2...HEAD
108
+ [0.1.2]: https://github.com/teng-lin/notebooklm-py/compare/v0.1.1...v0.1.2
109
+ [0.1.1]: https://github.com/teng-lin/notebooklm-py/compare/v0.1.0...v0.1.1
110
+ [0.1.0]: https://github.com/teng-lin/notebooklm-py/releases/tag/v0.1.0