notebooklm-api/API.md

461 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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: <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/<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
```
```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/<id>/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/<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"}'
```
```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/<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"}'
```
```json
{
"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:**
```json
{ "message": "Câu hỏi của bạn" }
```
```bash
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
```
```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/<id>/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/<id>/chat/stream`
**Gửi:** `{ "message": "câu hỏi" }`
**Nhận:**
```json
{ "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:**
```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:**
```bash
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
```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://<ip-máy>:3456/api/notebooks/<id>/chat`
- Method: `POST`
- Body (JSON): `{"message": "{{$json.input}}"}`
### Python
```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
```javascript
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