docs(api): rewrite KOReader bookmark sync for current protocol
sync_bookmarks.md documented a request shape the handler never accepted. - Document the real body: book_uuid/book_sha256 (either required, SHA-256 is format-aware), plus separate bookmarks/notes/highlights arrays using the shared KOReader annotation shape (pos0/pos1, page, text, type, per-annotation book_sha256, dedup_key, percentage) - Document color semantics from 178fb2e/dafcadd: KOReader palette names map to web hex swatches at the boundary, echoes carry no color so stored web colors survive round-trips, explicit colors are device edits - koreader-protocol.md: cross-link the bookmark shape/color/dedup rules from the progress-sync field table
This commit is contained in:
@@ -1,49 +1,81 @@
|
||||
# Sync Bookmarks
|
||||
|
||||
Sync bookmarks from KOReader device.
|
||||
Sync bookmarks, notes, and highlights from a KOReader device (bidirectional — the response also returns the server's current state for the book so the device can reconcile).
|
||||
|
||||
**Endpoint**: `POST /api/sync/koreader/bookmarks`
|
||||
**Auth**: Required (Device authentication)
|
||||
|
||||
## Device Authentication
|
||||
|
||||
This endpoint requires device authentication (not user JWT). Devices authenticate using their device credentials.
|
||||
**Auth**: Device token (Bearer)
|
||||
|
||||
## Request Body
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
| --------- | ------------- | -------- | ------------------------- |
|
||||
| device_id | string (UUID) | Yes | Device UUID |
|
||||
| bookmarks | array | Yes | Array of bookmark objects |
|
||||
| Field | Type | Required | Description |
|
||||
| ------------ | ------ | --------------------- | ----------------------------------------------------------------- |
|
||||
| book_uuid | string | one of uuid/sha | Book UUID (highest-confidence match) |
|
||||
| book_sha256 | string | one of uuid/sha | Full-file SHA-256 (64 hex chars); format-aware (also matches `media_item_formats`, so a KEPUB/PDF download matches) |
|
||||
| bookmarks | array | No | Bookmark objects |
|
||||
| notes | array | No | Note objects |
|
||||
| highlights | array | No | Highlight objects |
|
||||
|
||||
### Bookmark Object
|
||||
At least one of `book_uuid` or `book_sha256` is required; `book_sha256` resolves through the shared BookResolver.
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
| ---------------- | ------- | -------- | -------------------------- |
|
||||
| book | string | Yes | Book identifier |
|
||||
| chapter | string | No | Chapter title |
|
||||
| page | integer | No | Page number |
|
||||
| position | float | Yes | Position in document (0-1) |
|
||||
| notes | string | No | Bookmark notes |
|
||||
| highlighted_text | string | No | Highlighted text |
|
||||
| time | string | Yes | ISO 8601 timestamp |
|
||||
| created_at | string | Yes | ISO 8601 timestamp |
|
||||
### Bookmark / Note / Highlight Object
|
||||
|
||||
All three types share the same KOReader annotation shape:
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
| ------------ | ------- | -------- | ---------------------------------------------------- |
|
||||
| chapter | int | No | Chapter index |
|
||||
| datetime | string | No | ISO 8601 creation/edit timestamp |
|
||||
| pos0 / pos1 | string | No | Start/end xpointer (or `page:N` / bare page) |
|
||||
| page | int | No | Page number (fallback location when `pos0` is empty) |
|
||||
| text | string | No | Highlighted text |
|
||||
| notes | string | No | Note text attached to the annotation |
|
||||
| type | string | No | Annotation type (`highlight`, `note`, `bookmark`) |
|
||||
| color | string | No | Highlight color (highlights only) — KOReader palette name, see below |
|
||||
| percentage | float | No | Position within the book (0-1) |
|
||||
| book_sha256 | string | No | Per-annotation SHA-256; overrides the request-level book match |
|
||||
| dedup_key | string | No | Stable echo key; an entry whose content is unchanged from what the server previously served is recognized as an echo rather than a new edit |
|
||||
|
||||
### Color Semantics
|
||||
|
||||
KOReader paints highlights from a fixed palette of color names; the web reader uses hex swatches. Colors are mapped at the boundary (unmappable values fall back to yellow on both sides):
|
||||
|
||||
| KOReader name | Web hex |
|
||||
| ------------- | --------- |
|
||||
| yellow, orange | `#ffd54f` |
|
||||
| green, olive | `#a5d6a7` |
|
||||
| cyan, blue | `#90caf9` |
|
||||
| purple | `#ce93d8` |
|
||||
| red | `#f48fb1` |
|
||||
|
||||
- An echo (device re-reporting an annotation it received from the server) carries **no color**, so the stored web color is never clobbered.
|
||||
- A non-empty color means the user edited the highlight on the device; it is mapped to the nearest web swatch.
|
||||
|
||||
### Example Request
|
||||
|
||||
```json
|
||||
{
|
||||
"device_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"book_sha256": "64-hex-char-sha256",
|
||||
"bookmarks": [
|
||||
{
|
||||
"book": "book.epub",
|
||||
"chapter": "Chapter 1",
|
||||
"chapter": 3,
|
||||
"datetime": "2026-08-20T10:00:00Z",
|
||||
"pos0": "/body/Doc[4]/Sec[2]",
|
||||
"page": 25,
|
||||
"position": 0.125,
|
||||
"notes": "Important section",
|
||||
"highlighted_text": "Text to remember",
|
||||
"time": "2026-02-08T10:00:00Z",
|
||||
"created_at": "2026-02-08T10:00:00Z"
|
||||
"text": "",
|
||||
"type": "bookmark",
|
||||
"percentage": 0.125
|
||||
}
|
||||
],
|
||||
"highlights": [
|
||||
{
|
||||
"datetime": "2026-08-20T10:05:00Z",
|
||||
"pos0": "/body/Doc[4]/Sec[2]/text()[3]:0",
|
||||
"pos1": "/body/Doc[4]/Sec[2]/text()[3]:42",
|
||||
"text": "Text to remember",
|
||||
"notes": "Why this matters",
|
||||
"type": "highlight",
|
||||
"color": "blue",
|
||||
"dedup_key": "echo-key-from-server"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -53,15 +85,17 @@ This endpoint requires device authentication (not user JWT). Devices authenticat
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "Bookmarks synced successfully",
|
||||
"synced_count": 1
|
||||
"sync_status": "ok",
|
||||
"bookmarks_synced": 1,
|
||||
"notes_synced": 0,
|
||||
"highlights_synced": 1
|
||||
}
|
||||
```
|
||||
|
||||
## Error Responses
|
||||
|
||||
| Code | Description |
|
||||
| ---- | ---------------------------- |
|
||||
| 401 | Device authentication failed |
|
||||
| 400 | Invalid request data |
|
||||
| 404 | Device not found |
|
||||
| Code | Description |
|
||||
| ---- | -------------------------------------------------- |
|
||||
| 400 | Invalid request, or neither uuid nor SHA provided |
|
||||
| 401 | Missing/invalid device token |
|
||||
| 404 | Book not found by SHA-256 |
|
||||
|
||||
@@ -32,7 +32,7 @@ KOReader uses a custom JSON-based sync protocol.
|
||||
| books[].chapter | integer | No | Current chapter |
|
||||
| books[].epubcfi | string | No | EPUB CFI location |
|
||||
| books[].character | integer | No | Character offset |
|
||||
| books[].bookmarks | array | No | Array of bookmarks/highlights |
|
||||
| books[].bookmarks | array | No | Array of bookmarks/highlights (shape, color mapping, and echo/dedup rules: see [Sync Bookmarks](../koreader/sync_bookmarks.md)) |
|
||||
|
||||
\* At least one of `uuid` or `sha256` should be present. The server resolves the
|
||||
book through the shared `BookResolver` with this priority: `uuid` → `sha256` →
|
||||
|
||||
Reference in New Issue
Block a user