# 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](#cài-đặt--khởi-động) 2. [Biến môi trường](#biến-môi-trường) 3. [Xác thực](#xác-thực) 4. [Notebooks](#notebooks) 5. [Sources](#sources) 6. [Chat](#chat) 7. [Trạng thái server](#trạng-thái-server) 8. [Mã lỗi](#mã-lỗi) 9. [Tích hợp](#tích-hợp) --- ## Cài đặt & Khởi động **Yêu cầu:** Node.js 18+, Google Chrome ```bash 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: ` | --- ## Xác thực ### GET /api/auth/status Kiểm tra trạng thái đăng nhập Google. ```bash curl http://localhost:3456/api/auth/status ``` ```json { "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. ```bash curl -X POST http://localhost:3456/api/auth/login ``` ```json { "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. ```bash curl http://localhost:3456/api/notebooks ``` ```json { "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:** ```json { "title": "Tên notebook" } ``` ```bash curl -X POST http://localhost:3456/api/notebooks \ -H "Content-Type: application/json" \ -d '{"title": "Dự án Q3 2026"}' ``` ```json { "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. ```bash curl -X DELETE http://localhost:3456/api/notebooks/2c4f0f26-0797-4cc0-a350-48c4b70d14cc ``` ```json { "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) | ```bash # Mặc định chờ 5 phút curl "http://localhost:3456/api/notebooks//sources" --max-time 310 # Notebook nặng — chờ tối đa 10 phút curl "http://localhost:3456/api/notebooks//sources?timeout=600" --max-time 610 ``` ```json { "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 ```json { "type": "url", "content": "https://...", "title": "Tuỳ chọn" } ``` ```bash curl -X POST http://localhost:3456/api/notebooks//sources \ -H "Content-Type: application/json" \ -d '{"type":"url","content":"https://vnexpress.net/bai-viet-abc","title":"VnExpress"}' ``` ```json { "ok": true, "added": true, "type": "url", "url": "https://..." } ``` --- #### Loại 2: Văn bản paste ```json { "type": "text", "content": "Nội dung văn bản...", "title": "Tên tài liệu" } ``` ```bash curl -X POST http://localhost:3456/api/notebooks//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"}' ``` ```json { "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). ```json { "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` ```bash # File bình thường curl -X POST http://localhost:3456/api/notebooks//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"}' ``` ```json { "ok": true, "added": true, "type": "file", "filename": "report.pdf", "path": "/Users/me/Documents/report.pdf" } ``` > **Lưu ý:** Upload có thể mất 30–60 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:** ```json { "message": "Câu hỏi của bạn" } ``` ```bash curl -X POST http://localhost:3456/api/notebooks//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 ``` ```json { "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. ```bash curl http://localhost:3456/api/notebooks//chat/history ``` ```json { "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//chat/stream` **Gửi:** `{ "message": "câu hỏi" }` **Nhận:** ```json { "type": "connected", "notebookId": "" } { "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:** ```javascript const ws = new WebSocket('ws://localhost:3456/api/notebooks//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:** ```bash npx wscat -c "ws://localhost:3456/api/notebooks//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 ```bash curl http://localhost:3456/health ``` ```json { "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://:3456/api/notebooks//chat` - Method: `POST` - Body (JSON): `{"message": "{{$json.input}}"}` ### Python ```python import requests BASE = "http://localhost:3456" NB_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 ```javascript const BASE = 'http://localhost:3456'; const NB_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