Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions docs/FILE_UPLOAD_IMPLEMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,14 @@ source_add(notebook_id="...", source_type="file", file_path="/path/to/file.pdf")

### Supported File Types

- Documents (`.pdf`, `.txt`, `.md`, `.docx`, `.csv`)
- Ebooks (`.epub`)
- Audio (`.mp3`, `.m4a`, `.wav`, `.aac`, `.ogg`, `.opus`)
- Video (`.mp4`)
- Images (`.jpg`, `.jpeg`, `.png`, `.gif`, `.webp`)
The local gate admits the official 43-extension contract case-insensitively:

OFFICIAL_FILE_EXTENSIONS: .pdf, .txt, .md, .docx, .csv, .pptx, .epub, .avif, .bmp, .gif, .heic, .heif, .ico, .jp2, .jpe, .jpeg, .jpg, .png, .tif, .tiff, .webp, .3g2, .3gp, .aac, .aif, .aifc, .aiff, .amr, .au, .avi, .cda, .m4a, .mid, .mp3, .mp4, .mpeg, .ogg, .opus, .ra, .ram, .snd, .wav, .wma

This establishes local admission eligibility only. NotebookLM may still reject or
fail to process corrupt files, misleading extensions, inaccessible media, or
reference-only formats such as some `.cda` and `.ram` files. Those outcomes are
provider ingestion errors, not local unsupported-extension errors.

### Advantages

Expand Down
3 changes: 2 additions & 1 deletion src/notebooklm_tools/cli/ai_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,8 @@
nlm source add <notebook-id> --drive <doc-id> # Add Drive doc
nlm source add <notebook-id> --drive <doc-id> --type slides # Add Drive slides
# Types: doc, slides, sheets, pdf
# Supported file types: PDF, TXT, MD, DOCX, CSV, EPUB, MP3, M4A, WAV, AAC, OGG, OPUS, MP4, JPG, JPEG, PNG, GIF, WEBP
# OFFICIAL_FILE_EXTENSIONS: .pdf, .txt, .md, .docx, .csv, .pptx, .epub, .avif, .bmp, .gif, .heic, .heif, .ico, .jp2, .jpe, .jpeg, .jpg, .png, .tif, .tiff, .webp, .3g2, .3gp, .aac, .aif, .aifc, .aiff, .amr, .au, .avi, .cda, .m4a, .mid, .mp3, .mp4, .mpeg, .ogg, .opus, .ra, .ram, .snd, .wav, .wma
# Local admission does not guarantee provider processing success for every file.

nlm source get <source-id> # Get source metadata
nlm source get <source-id> --json # JSON output
Expand Down
86 changes: 86 additions & 0 deletions src/notebooklm_tools/core/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,92 @@ def names(self) -> list[str]:
}
)

# =============================================================================
# Official local file-upload admission contract
# =============================================================================
# This registry controls local eligibility only. NotebookLM may still reject or
# fail to process corrupt, misleading, inaccessible, or reference-only files.
SUPPORTED_FILE_EXTENSIONS: frozenset[str] = frozenset(
{
".pdf",
".txt",
".md",
".docx",
".csv",
".pptx",
".epub",
".avif",
".bmp",
".gif",
".heic",
".heif",
".ico",
".jp2",
".jpe",
".jpeg",
".jpg",
".png",
".tif",
".tiff",
".webp",
".3g2",
".3gp",
".aac",
".aif",
".aifc",
".aiff",
".amr",
".au",
".avi",
".cda",
".m4a",
".mid",
".mp3",
".mp4",
".mpeg",
".ogg",
".opus",
".ra",
".ram",
".snd",
".wav",
".wma",
}
)

# Media sources can transiently report an upstream processing error immediately
# after upload. This is a processing classification, not a second admission list.
TRANSIENT_MEDIA_FILE_EXTENSIONS: frozenset[str] = frozenset(
{
".3g2",
".3gp",
".aac",
".aif",
".aifc",
".aiff",
".amr",
".au",
".avi",
".cda",
".m4a",
".mid",
".mp3",
".mp4",
".mpeg",
".ogg",
".opus",
".ra",
".ram",
".snd",
".wav",
".wma",
}
)

if len(SUPPORTED_FILE_EXTENSIONS) != 43: # pragma: no cover - import-time invariant
raise RuntimeError("NotebookLM official file extension contract must contain 43 entries.")


# =============================================================================
# Source Types (Notebook Content)
# =============================================================================
Expand Down
36 changes: 9 additions & 27 deletions src/notebooklm_tools/core/sources.py
Original file line number Diff line number Diff line change
Expand Up @@ -944,7 +944,9 @@ def add_file(
2. Start upload session with SOURCE_ID → get upload URL
3. Stream upload file content (memory-efficient for large files)

Supported file types: PDF, TXT, MD, DOCX, CSV, EPUB, MP3, M4A, WAV, AAC, OGG, OPUS, MP4, JPG, PNG, GIF, WEBP
Supported local-admission formats are defined by the official 43-extension
registry in core.constants. Provider processing can still fail after upload.
OFFICIAL_FILE_EXTENSIONS: .pdf, .txt, .md, .docx, .csv, .pptx, .epub, .avif, .bmp, .gif, .heic, .heif, .ico, .jp2, .jpe, .jpeg, .jpg, .png, .tif, .tiff, .webp, .3g2, .3gp, .aac, .aif, .aifc, .aiff, .amr, .au, .avi, .cda, .m4a, .mid, .mp3, .mp4, .mpeg, .ogg, .opus, .ra, .ram, .snd, .wav, .wma

Args:
notebook_id: The notebook ID to add the source to
Expand Down Expand Up @@ -975,32 +977,13 @@ def add_file(
if file_size == 0:
raise FileValidationError(f"File is empty: {file_path}")

# Validate file type
supported_extensions = {
".pdf",
".txt",
".md",
".docx",
".csv", # Documents
".epub", # Ebooks
".mp3",
".m4a",
".wav",
".aac",
".ogg",
".opus", # Audio
".mp4", # Video
".jpg",
".jpeg",
".png",
".gif",
".webp", # Images
}
# Validate local admission eligibility against the provider contract.
file_extension = file_path.suffix.lower()
if file_extension not in supported_extensions:
if file_extension not in constants.SUPPORTED_FILE_EXTENSIONS:
raise FileValidationError(
f"Unsupported file type: {file_extension}\n"
f"Supported types: {', '.join(sorted(supported_extensions))}"
f"Unsupported file type: {file_extension or '[none]'}\n"
"Supported types: "
f"{', '.join(sorted(constants.SUPPORTED_FILE_EXTENSIONS))}"
)

# Step 1: Register source intent → get SOURCE_ID
Expand All @@ -1015,12 +998,11 @@ def add_file(
result = {"id": source_id, "title": filename}

if wait:
media_extensions = {".mp3", ".m4a", ".wav", ".aac", ".ogg", ".opus", ".mp4"}
return self.wait_for_source_ready(
notebook_id,
source_id,
wait_timeout,
allow_transient_error=file_extension in media_extensions,
allow_transient_error=(file_extension in constants.TRANSIENT_MEDIA_FILE_EXTENSIONS),
)

return result
Expand Down
9 changes: 5 additions & 4 deletions src/notebooklm_tools/data/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,10 +197,11 @@ Use `source_add` with these `source_type` values:
- `url` - Web page or YouTube URL (`url` param)
- `text` - Pasted content (`text` + `title` params)
- `file` - Server-local file upload (`file_path` param). The path must exist on
the machine running the MCP server, not merely on the client host. Failures
preserve the concrete reason and include a host-path hint. Supported:
`PDF, TXT, MD, DOCX, CSV, EPUB, MP3, M4A, WAV, AAC, OGG, OPUS, MP4, JPG,
JPEG, PNG, GIF, WEBP`.
the machine running the MCP server, not merely on the client host. Local
admission is case-insensitive and follows the official 43-extension contract:
OFFICIAL_FILE_EXTENSIONS: .pdf, .txt, .md, .docx, .csv, .pptx, .epub, .avif, .bmp, .gif, .heic, .heif, .ico, .jp2, .jpe, .jpeg, .jpg, .png, .tif, .tiff, .webp, .3g2, .3gp, .aac, .aif, .aifc, .aiff, .amr, .au, .avi, .cda, .m4a, .mid, .mp3, .mp4, .mpeg, .ogg, .opus, .ra, .ram, .snd, .wav, .wma
Admission does not guarantee provider processing success. Corrupt, misleading,
inaccessible, or reference-only files can still fail during NotebookLM ingestion.
- `drive` - Google Drive doc (`document_id` + `doc_type` params)

Other tools: `source_list_drive` (`skip_freshness=True` reports
Expand Down
13 changes: 6 additions & 7 deletions src/notebooklm_tools/mcp/tools/sources.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,12 @@ def source_add(
- url: Web page or YouTube URL
- text: Pasted text content
- drive: Google Drive document
- file: Local file upload. Supported extensions:
PDF, TXT, MD, DOCX, CSV, EPUB, MP3, M4A, WAV, AAC, OGG,
OPUS, MP4, JPG, JPEG, PNG, GIF, WEBP. Image-bearing
sources (PDF / JPG / PNG / etc.) feed Studio video
generation's visual-crop pipeline — charts, photos, and
diagrams may be extracted as on-screen aids in Video
Overviews.
- file: Local file upload. The canonical local-admission contract has
43 case-insensitive extensions. Admission does not guarantee provider
processing success for an individual file.
OFFICIAL_FILE_EXTENSIONS: .pdf, .txt, .md, .docx, .csv, .pptx, .epub, .avif, .bmp, .gif, .heic, .heif, .ico, .jp2, .jpe, .jpeg, .jpg, .png, .tif, .tiff, .webp, .3g2, .3gp, .aac, .aif, .aifc, .aiff, .amr, .au, .avi, .cda, .m4a, .mid, .mp3, .mp4, .mpeg, .ogg, .opus, .ra, .ram, .snd, .wav, .wma
Image-bearing sources may feed Studio video generation's visual-crop
pipeline; charts, photos, and diagrams may be extracted as on-screen aids.
url: URL to add (for source_type=url)
urls: List of URLs to add in bulk (for source_type=url, alternative to url)
text: Text content to add (for source_type=text)
Expand Down
Loading
Loading