1
0
Fork 0
notebooklm-api/API.md

11 KiB
Raw Permalink Blame History

NotebookLM API — Tài liệu đầy đủ

Server Node.js điều khiển Google NotebookLM qua Puppeteer, giả lập thao tác người dùng thật.

Base URL: http://localhost:3456


Mục lục

  1. Cài đặt & Khởi động
  2. Biến môi trường
  3. Xác thực
  4. Notebooks
  5. Sources
  6. Chat
  7. Trạng thái server
  8. Mã lỗi
  9. Tích hợp

Cài đặt & Khởi động

Yêu cầu: Node.js 18+, Google Chrome

bash setup.sh   # cài đặt + khởi động

# Hoặc thủ công:
npm install
cp .env.example .env
npm start
  • Server khởi động tại http://localhost:3456
  • Swagger UI tại http://localhost:3456/docs
  • Chrome tự mở với profile riêng tại ./chrome-profile/

Dừng server: Ctrl+C — KHÔNG dùng kill -9 (sẽ mất cookies Google)


Biến môi trường

File .env (copy từ .env.example):

Biến Mặc định Mô tả
PORT 3456 Cổng HTTP/WebSocket
HEADLESS false true = ẩn cửa sổ Chrome
CHROME_PATH tự phát hiện Đường dẫn Chrome tuỳ chỉnh
API_KEY (trống) Nếu đặt, mọi request phải có header x-api-key: <key>

Xác thực

GET /api/auth/status

Kiểm tra trạng thái đăng nhập Google.

curl http://localhost:3456/api/auth/status
{ "ok": true, "authenticated": true }
{ "ok": true, "authenticated": false }

POST /api/auth/login

Mở browser để đăng nhập Google. Chờ tối đa 5 phút.

curl -X POST http://localhost:3456/api/auth/login
{ "ok": true, "authenticated": true, "message": "Đăng nhập thành công, session đã lưu" }

Session lưu vào ./chrome-profile/ — các lần khởi động lại server không cần đăng nhập lại.


Notebooks

GET /api/notebooks

Lấy danh sách tất cả notebooks.

curl http://localhost:3456/api/notebooks
{
  "ok": true,
  "total": 3,
  "notebooks": [
    {
      "id": "2c4f0f26-0797-4cc0-a350-48c4b70d14cc",
      "title": "Dự án Q3 2026",
      "url": "https://notebooklm.google.com/notebook/2c4f0f26-..."
    }
  ]
}

POST /api/notebooks

Tạo notebook mới.

Body:

{ "title": "Tên notebook" }
curl -X POST http://localhost:3456/api/notebooks \
  -H "Content-Type: application/json" \
  -d '{"title": "Dự án Q3 2026"}'
{
  "ok": true,
  "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "title": "Dự án Q3 2026",
  "url": "https://notebooklm.google.com/notebook/..."
}

DELETE /api/notebooks/:id

Xoá notebook.

curl -X DELETE http://localhost:3456/api/notebooks/2c4f0f26-0797-4cc0-a350-48c4b70d14cc
{ "ok": true, "deleted": true, "id": "2c4f0f26-..." }

Sources

GET /api/notebooks/:id/sources

Lấy danh sách sources trong notebook. Tự chờ đến khi tất cả sources convert xong.

Query params:

Param Mặc định Mô tả
timeout 300 Giây tối đa chờ converting xong (max 600)
# Mặc định chờ 5 phút
curl "http://localhost:3456/api/notebooks/<id>/sources" --max-time 310

# Notebook nặng — chờ tối đa 10 phút
curl "http://localhost:3456/api/notebooks/<id>/sources?timeout=600" --max-time 610
{
  "ok": true,
  "total": 3,
  "stillLoading": 0,
  "sources": [
    { "index": 0, "title": "Báo cáo Q2.pdf",             "type": "pdf",  "loading": false },
    { "index": 1, "title": "https://example.com/article", "type": "url",  "loading": false },
    { "index": 2, "title": "Ghi chú cuộc họp",           "type": "text", "loading": false }
  ]
}

stillLoading > 0 — gọi lại sau vài phút để lấy kết quả đầy đủ.


POST /api/notebooks/:id/sources

Thêm source vào notebook. Hỗ trợ 3 loại:

Loại 1: URL website

{ "type": "url", "content": "https://...", "title": "Tuỳ chọn" }
curl -X POST http://localhost:3456/api/notebooks/<id>/sources \
  -H "Content-Type: application/json" \
  -d '{"type":"url","content":"https://vnexpress.net/bai-viet-abc","title":"VnExpress"}'
{ "ok": true, "added": true, "type": "url", "url": "https://..." }

Loại 2: Văn bản paste

{ "type": "text", "content": "Nội dung văn bản...", "title": "Tên tài liệu" }
curl -X POST http://localhost:3456/api/notebooks/<id>/sources \
  -H "Content-Type: application/json" \
  -d '{"type":"text","content":"Báo cáo doanh thu tháng 6: ...","title":"Báo cáo T6/2026"}'
{ "ok": true, "added": true, "type": "text", "length": 1234 }

Loại 3: File local

Upload file từ máy đang chạy server. Đường dẫn phải là đường dẫn tuyệt đối (absolute path). Hỗ trợ cả URL-encoded path (có %20 thay dấu cách).

{ "type": "file", "content": "/đường/dẫn/tuyệt/đối/file.pdf" }

Định dạng hỗ trợ: .pdf .txt .md .docx .doc .pptx .ppt .xlsx .xls .mp3 .mp4 .jpg .jpeg .png

# File bình thường
curl -X POST http://localhost:3456/api/notebooks/<id>/sources \
  -H "Content-Type: application/json" \
  -d '{"type":"file","content":"/Users/me/Documents/report.pdf"}' \
  --max-time 90

# Đường dẫn có dấu cách (cả 2 cách đều dùng được)
-d '{"type":"file","content":"/Users/me/Downloads/Telegram Desktop/file.md"}'
-d '{"type":"file","content":"/Users/me/Downloads/Telegram%20Desktop/file.md"}'
{
  "ok": true,
  "added": true,
  "type": "file",
  "filename": "report.pdf",
  "path": "/Users/me/Documents/report.pdf"
}

Lưu ý: Upload có thể mất 3060 giây. Đặt --max-time 90 khi dùng curl. Notebook phải do bạn sở hữu (không phải notebook public/shared).


Chat

POST /api/notebooks/:id/chat

Gửi câu hỏi và chờ câu trả lời đầy đủ (đồng bộ).

Body:

{ "message": "Câu hỏi của bạn" }
curl -X POST http://localhost:3456/api/notebooks/<id>/chat \
  -H "Content-Type: application/json" \
  -d '{"message":"Tóm tắt các điểm chính trong tài liệu này"}' \
  --max-time 120
{
  "ok": true,
  "question": "Tóm tắt các điểm chính trong tài liệu này",
  "answer": "Dựa trên các tài liệu, các điểm chính bao gồm: ..."
}

Timeout mặc định 90 giây. Câu hỏi phức tạp với nhiều sources có thể cần lâu hơn.


GET /api/notebooks/:id/chat/history

Lấy toàn bộ lịch sử hội thoại trong notebook.

curl http://localhost:3456/api/notebooks/<id>/chat/history
{
  "ok": true,
  "total": 4,
  "history": [
    { "role": "user",      "content": "Tóm tắt tài liệu" },
    { "role": "assistant", "content": "Tài liệu đề cập đến..." }
  ]
}

WS /api/notebooks/:id/chat/stream

Chat streaming qua WebSocket. Nhận câu trả lời theo từng chunk realtime.

Kết nối: ws://localhost:3456/api/notebooks/<id>/chat/stream

Gửi: { "message": "câu hỏi" }

Nhận:

{ "type": "connected", "notebookId": "<id>" }
{ "type": "chunk", "data": "Dựa trên" }
{ "type": "chunk", "data": " các tài liệu..." }
{ "type": "done",  "data": { "answer": "Dựa trên các tài liệu..." } }
{ "type": "error", "data": "Thông báo lỗi" }

Ví dụ JavaScript:

const ws = new WebSocket('ws://localhost:3456/api/notebooks/<id>/chat/stream');

ws.onopen  = () => ws.send(JSON.stringify({ message: 'Tóm tắt nội dung chính' }));

ws.onmessage = ({ data }) => {
  const msg = JSON.parse(data);
  if (msg.type === 'chunk') process.stdout.write(msg.data);
  if (msg.type === 'done')  console.log('\n[Xong]');
  if (msg.type === 'error') console.error('[Lỗi]', msg.data);
};

Test nhanh bằng wscat:

npx wscat -c "ws://localhost:3456/api/notebooks/<id>/chat/stream"
# Sau khi kết nối:
{"message":"Tóm tắt tài liệu này"}

Trạng thái server

GET /health

curl http://localhost:3456/health
{
  "ok": true,
  "status": "running",
  "queue": { "busy": false, "pending": 0 }
}

queue.busy = true — đang có thao tác browser chạy; các request khác xếp hàng chờ tự động.


Mã lỗi

HTTP error Ý nghĩa
400 Thiếu type hoặc content Body thiếu field bắt buộc
401 NOT_AUTHENTICATED Chưa đăng nhập → gọi POST /api/auth/login
401 Thiếu hoặc sai API key Header x-api-key sai hoặc thiếu
404 NOTEBOOK_NOT_FOUND Notebook ID không tồn tại hoặc không có quyền
500 (message) Lỗi nội bộ — xem log server

Tích hợp

n8n / Make / Zapier

  • Node: HTTP Request
  • URL: http://<ip-máy>:3456/api/notebooks/<id>/chat
  • Method: POST
  • Body (JSON): {"message": "{{$json.input}}"}

Python

import requests

BASE  = "http://localhost:3456"
NB_ID = "<notebook-id>"

# Thêm file
requests.post(f"{BASE}/api/notebooks/{NB_ID}/sources",
              json={"type": "file", "content": "/path/to/file.pdf"},
              timeout=90)

# Thêm URL
requests.post(f"{BASE}/api/notebooks/{NB_ID}/sources",
              json={"type": "url", "content": "https://example.com"},
              timeout=30)

# Hỏi
resp  = requests.post(f"{BASE}/api/notebooks/{NB_ID}/chat",
                      json={"message": "Tóm tắt tài liệu"}, timeout=120)
print(resp.json()["answer"])

# Danh sách notebooks
notebooks = requests.get(f"{BASE}/api/notebooks").json()["notebooks"]

Node.js / fetch

const BASE  = 'http://localhost:3456';
const NB_ID = '<notebook-id>';

// Thêm file
await fetch(`${BASE}/api/notebooks/${NB_ID}/sources`, {
  method:  'POST',
  headers: { 'Content-Type': 'application/json' },
  body:    JSON.stringify({ type: 'file', content: '/path/to/file.pdf' }),
  signal:  AbortSignal.timeout(90_000),
});

// Hỏi
const res  = await fetch(`${BASE}/api/notebooks/${NB_ID}/chat`, {
  method:  'POST',
  headers: { 'Content-Type': 'application/json' },
  body:    JSON.stringify({ message: 'Tóm tắt nội dung' }),
  signal:  AbortSignal.timeout(120_000),
});
const { answer } = await res.json();

Lưu ý quan trọng

  • Chỉ 1 thao tác browser tại một thời điểm — các request song song xếp hàng tự động
  • Upload file chỉ hoạt động với notebook do bạn sở hữu (không phải public/shared)
  • Dừng server đúng cách: Ctrl+C — KHÔNG dùng kill -9
  • Chrome crash: xoá chrome-profile/SingletonLock rồi restart server
  • Google giới hạn automation: nếu bị chặn, đợi vài phút