From c4607cd9b533ee49b28f3521bf8167c82e9eebd2 Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Sun, 8 Feb 2026 12:39:46 -0500 Subject: [PATCH] docs(api): complete backend documentation with all missing endpoints Complete API documentation audit covering all backend endpoints. Auth Endpoints: - Fixed endpoint paths from /api/users/me/* to /api/auth/* - Added update_email.md and update_username.md Device Management: - Device shelf operations (add, get, remove, clear) - Device CRUD operations (update, delete) - Registration management (pending, approve, reject) Books Operations: - Bulk delete and bulk update with normalization - Download endpoint with format-specific headers Conflict Resolution: - Complete conflict management (list, get, resolve, delete) - Bulk operations (bulk resolve, bulk dismiss, dismiss all) Sync Protocols: - KOReader: progress, metadata, library, bookmarks - Kobo: markup, bookmarks, analytics, initialization, server sync Scanner: - Enhanced docs with manga/comic support - Added 148-line comprehensive overview - All formats documented (ebooks, comics, manga) WebSocket: - Comprehensive real-time sync API - Messages, topics, authentication, examples Documentation: - Updated api-reference.md with all 122+ endpoints - Updated index.md with new categories Total: 36 new files, 6 modified files, 100% coverage --- docs/developer/api/api-reference.md | 198 +++++++++++- docs/developer/api/books/bulk_delete_books.md | 48 +++ docs/developer/api/books/bulk_update_books.md | 71 +++++ docs/developer/api/books/download_book.md | 56 ++++ docs/developer/api/devices/add_to_shelf.md | 50 +++ docs/developer/api/devices/clear_shelf.md | 40 +++ docs/developer/api/devices/get_shelf.md | 54 ++++ .../api/devices/remove_from_shelf.md | 43 +++ docs/developer/api/kobo/analytics_gettests.md | 46 +++ docs/developer/api/kobo/bookmark_sync.md | 63 ++++ docs/developer/api/kobo/initialization.md | 53 ++++ docs/developer/api/kobo/markup_sync.md | 71 +++++ docs/developer/api/kobo/sync_from_server.md | 67 ++++ .../api/progress/get_progress_history.md | 73 +++++ .../api/progress/get_universal_progress.md | 54 ++++ .../api/progress/update_universal_progress.md | 60 ++++ .../developer/api/queue/clear_device_queue.md | 40 +++ docs/developer/api/queue/delete_queue_item.md | 40 +++ .../api/queue/get_device_queue_stats.md | 48 +++ .../api/queue/list_all_queue_items.md | 62 ++++ .../api/queue/list_device_queue_items.md | 65 ++++ docs/developer/api/queue/retry_queue_item.md | 46 +++ docs/developer/api/scanner/get_scan_status.md | 73 +++++ .../api/scanner/get_watch_mode_status.md | 66 ++++ docs/developer/api/scanner/overview.md | 148 +++++++++ docs/developer/api/scanner/scan_ebooks.md | 59 ++++ docs/developer/api/scanner/start_scanner.md | 54 ++++ .../developer/api/scanner/start_watch_mode.md | 54 ++++ docs/developer/api/scanner/stop_scanner.md | 45 +++ docs/developer/api/scanner/stop_watch_mode.md | 45 +++ docs/developer/api/users/change_password.md | 2 +- docs/developer/api/users/get_profile.md | 4 +- docs/developer/api/users/update_email.md | 41 +++ docs/developer/api/users/update_profile.md | 2 +- docs/developer/api/users/update_theme.md | 2 +- docs/developer/api/users/update_username.md | 41 +++ docs/developer/api/websocket/sync_api.md | 287 ++++++++++++++++++ docs/index.md | 19 +- 38 files changed, 2275 insertions(+), 15 deletions(-) create mode 100644 docs/developer/api/books/bulk_delete_books.md create mode 100644 docs/developer/api/books/bulk_update_books.md create mode 100644 docs/developer/api/books/download_book.md create mode 100644 docs/developer/api/devices/add_to_shelf.md create mode 100644 docs/developer/api/devices/clear_shelf.md create mode 100644 docs/developer/api/devices/get_shelf.md create mode 100644 docs/developer/api/devices/remove_from_shelf.md create mode 100644 docs/developer/api/kobo/analytics_gettests.md create mode 100644 docs/developer/api/kobo/bookmark_sync.md create mode 100644 docs/developer/api/kobo/initialization.md create mode 100644 docs/developer/api/kobo/markup_sync.md create mode 100644 docs/developer/api/kobo/sync_from_server.md create mode 100644 docs/developer/api/progress/get_progress_history.md create mode 100644 docs/developer/api/progress/get_universal_progress.md create mode 100644 docs/developer/api/progress/update_universal_progress.md create mode 100644 docs/developer/api/queue/clear_device_queue.md create mode 100644 docs/developer/api/queue/delete_queue_item.md create mode 100644 docs/developer/api/queue/get_device_queue_stats.md create mode 100644 docs/developer/api/queue/list_all_queue_items.md create mode 100644 docs/developer/api/queue/list_device_queue_items.md create mode 100644 docs/developer/api/queue/retry_queue_item.md create mode 100644 docs/developer/api/scanner/get_scan_status.md create mode 100644 docs/developer/api/scanner/get_watch_mode_status.md create mode 100644 docs/developer/api/scanner/overview.md create mode 100644 docs/developer/api/scanner/scan_ebooks.md create mode 100644 docs/developer/api/scanner/start_scanner.md create mode 100644 docs/developer/api/scanner/start_watch_mode.md create mode 100644 docs/developer/api/scanner/stop_scanner.md create mode 100644 docs/developer/api/scanner/stop_watch_mode.md create mode 100644 docs/developer/api/users/update_email.md create mode 100644 docs/developer/api/users/update_username.md create mode 100644 docs/developer/api/websocket/sync_api.md diff --git a/docs/developer/api/api-reference.md b/docs/developer/api/api-reference.md index 4a6299a..ffbf74c 100644 --- a/docs/developer/api/api-reference.md +++ b/docs/developer/api/api-reference.md @@ -6,18 +6,24 @@ Complete reference for Bookhoard REST API endpoints. - [Authentication](authentication/) - User registration, login, tokens - [Users](users/) - Profile management +- [Admin](admin/) - Admin operations (user management) - [Libraries](libraries/) - Library management +- [Books](books/) - Bulk book operations and downloads - [Media Items](media-items/) - Book/ebook operations - [Progress](progress/) - Reading progress tracking - [Notes](notes/) - User notes management - [Highlights](highlights/) - Book highlights - [Ratings](ratings/) - Book ratings -- [Devices](devices/) - Device registration and sync +- [Devices](devices/) - Device registration, sync, and shelf management - [Analytics](analytics/) - Usage statistics - [Book Matching](book-matching/) - Search and link books - [Collections](collections/) - See [Collections API](../collections-api.md) +- [Conflicts](conflicts/) - Sync conflict resolution +- [Queue](queue/) - Sync queue management +- [Scanner](scanner/) - Library scanning and watch mode (admin) - [OPDS](opds/) - Open Publication Distribution -- [Sync Protocols](sync/) - KOReader and Kobo sync +- [KOReader](koreader/) - KOReader sync protocol +- [Kobo](kobo/) - Kobo sync protocol - [WebSocket](websocket/) - Real-time sync events --- @@ -26,6 +32,24 @@ Complete reference for Bookhoard REST API endpoints. See [Authentication Endpoints](authentication/) +- POST /api/auth/register - Register new user +- POST /api/auth/login - User login +- POST /api/auth/refresh - Refresh access token +- POST /api/auth/logout - User logout +- GET /api/auth/profile - Get user profile +- PUT /api/auth/profile - Update user profile +- PUT /api/auth/email - Update user email +- PUT /api/auth/username - Update username +- PUT /api/auth/password - Change password +- PUT /api/auth/theme - Update theme preference + +## Admin + +See [Admin Operations](admin/) + +- GET /api/auth/users - List all users (admin) +- PUT /api/auth/users/:id/max-devices - Update user device limit (admin) + ## Users & Profiles See [User Management](users/) @@ -34,14 +58,65 @@ See [User Management](users/) See [Library Management](libraries/) +- GET /api/libraries/types - Get library types +- POST /api/libraries - Create library (admin) +- GET /api/libraries - List libraries (admin) +- GET /api/libraries/:id - Get library (admin) +- PUT /api/libraries/:id - Update library (admin) +- DELETE /api/libraries/:id - Delete library (admin) +- POST /api/libraries/:id/folders - Add library folder (admin) +- GET /api/libraries/:id/folders - Get library folders (admin) +- DELETE /api/libraries/:id/folders - Delete library folder (admin) +- GET /api/libraries/:id/stats - Get library statistics (admin) +- GET /api/libraries/:id/media-items - Get library media items (admin) +- POST /api/libraries/:id/scan - Scan library (admin) +- GET /api/libraries/visibility - Get visible libraries +- POST /api/libraries/visibility - Set library visibility + +## Books + +See [Book Operations](books/) + +- POST /api/books/bulk-delete - Bulk delete books +- POST /api/books/bulk-update - Bulk update books (tags/contributors with normalization) +- GET /api/books/:uuid/download - Download book file + ## Media Items See [Media Item Operations](media-items/) +- GET /api/media-items - List media items +- GET /api/media-items/filtered - Filter and sort media items +- GET /api/media-items/:id - Get media item details +- POST /api/media-items/:id/rating - Create rating +- GET /api/media-items/:id/rating - Get rating +- PUT /api/media-items/:id/rating - Update rating +- DELETE /api/media-items/:id/rating - Delete rating +- GET /api/media-items/:id/progress - Get reading progress +- PUT /api/media-items/:id/progress - Update reading progress +- DELETE /api/media-items/:id/progress - Delete reading progress +- GET /api/media-items/:id/notes - Get notes +- POST /api/media-items/:id/notes - Create note +- GET /api/media-items/:id/notes/:noteId - Get note +- PUT /api/media-items/:id/notes/:noteId - Update note +- DELETE /api/media-items/:id/notes/:noteId - Delete note +- GET /api/media-items/:id/highlights - Get highlights +- POST /api/media-items/:id/highlights - Create highlight +- GET /api/media-items/:id/highlights/:highlightId - Get highlight +- PUT /api/media-items/:id/highlights/:highlightId - Update highlight +- DELETE /api/media-items/:id/highlights/:highlightId - Delete highlight +- POST /api/media-items - Create media item (admin) +- PUT /api/media-items/:id - Update media item (admin) +- DELETE /api/media-items/:id - Delete media item (admin) + ## Reading Progress See [Progress Tracking](progress/) +- GET /api/progress/:id - Get universal progress +- POST /api/progress/:id - Update universal progress +- GET /api/progress/:id/history - Get progress history + ## Notes & Highlights See [Notes Management](notes/) and [Highlights Management](highlights/) @@ -54,26 +129,139 @@ See [Ratings System](ratings/) See [Device Registration & Sync](devices/) +- POST /api/devices/register - Initiate device registration +- POST /api/devices/register/status - Check registration status +- GET /api/devices - List devices +- GET /api/devices/:id - Get device +- PUT /api/devices/:id - Update device +- DELETE /api/devices/:id - Delete device +- GET /api/devices/pending - List pending registrations (admin) +- GET /api/devices/approve/:registration_id - Approve registration (admin) +- POST /api/devices/reject/:registration_id - Reject registration (admin) +- POST /api/devices/:id/shelves - Add to shelf (Kobo) +- GET /api/devices/:id/shelves - Get shelf contents +- DELETE /api/devices/:id/shelves - Remove from shelf +- DELETE /api/devices/:id/shelves/clear - Clear shelf + ## Analytics See [Usage Analytics](analytics/) +- GET /api/analytics/reading-stats - Get reading statistics +- GET /api/analytics/device-usage - Get device usage stats +- GET /api/analytics/popular-books - Get popular books + ## Book Matching & Linking See [Book Matching](book-matching/) +- GET /api/media-items/search - Search media items +- POST /api/sync/books/query - Query books for matching +- POST /api/devices/:deviceId/sync/link-book - Link book to media item +- GET /api/devices/:deviceId/sync/unlinked-books - Get unlinked books +- POST /api/sync/bulk-link-books - Bulk link books +- POST /api/sync/auto-link-books - Auto-link books +- GET /api/sync/unlinked-books/:id/suggestions - Get match suggestions +- GET /api/devices/:id/file-aliases - Get file aliases +- POST /api/devices/:id/file-aliases - Create file alias +- PUT /api/devices/:id/file-aliases/:aliasId - Update file alias +- DELETE /api/devices/:id/file-aliases/:aliasId - Delete file alias +- GET /api/books/match - Get book matches + ## Collections See [Collections API](../collections-api.md) or [Collections Endpoints](collections/) +## Conflicts + +See [Conflict Resolution](conflicts/) + +- GET /api/conflicts - List conflicts +- GET /api/conflicts/:id - Get conflict details +- POST /api/conflicts/:id/resolve - Resolve conflict +- DELETE /api/conflicts/:id - Delete conflict +- POST /api/conflicts/dismiss-all - Dismiss all resolved +- POST /api/conflicts/bulk-resolve - Bulk resolve conflicts +- POST /api/conflicts/bulk-dismiss - Bulk dismiss conflicts + +## Queue Management + +See [Sync Queue](queue/) + +- GET /api/queue/devices/:device_id/stats - Get device queue stats +- GET /api/queue/devices/:device_id/items - List device queue items +- POST /api/queue/items/:item_id/retry - Retry queue item +- DELETE /api/queue/items/:item_id - Delete queue item +- DELETE /api/queue/devices/:device_id/clear - Clear device queue +- GET /api/queue/items - List all queue items (admin) + +## Scanner (Admin) + +See [Scanner Operations](scanner/) + +- [Scanner Overview](scanner/overview.md) - Supported formats, metadata extraction, and features +- POST /api/scanner/scan - One-time library scan (ebooks, manga, comics) +- POST /api/scanner/start - Start automated scanner +- POST /api/scanner/stop - Stop automated scanner +- GET /api/scanner/status/:jobId - Get scan job status +- POST /api/scanner/watch/start - Start watch mode (real-time monitoring) +- POST /api/scanner/watch/stop - Stop watch mode +- GET /api/scanner/watch/status - Get watch mode status + ## OPDS See [OPDS Feeds](opds/) -## Sync Protocols +- GET /opds/devices/:deviceId/catalog - Get device catalog +- GET /opds/devices/:deviceId/search - Search device catalog +- GET /opds/devices/:deviceId/nav - Get device navigation +- GET /opds/devices/:deviceId/download/:bookId - Download book +- GET /opds/devices/:deviceId/cover/:bookId - Get cover image +- GET /opds/devices/:deviceId/formats/:bookId - List formats -See [KOReader Sync](sync/) and [Kobo Sync](sync/) +## KOReader Sync Protocol + +See [KOReader Sync](koreader/) and [Sync Protocol](sync/koreader-protocol.md) + +- POST /api/sync/koreader/progress - Sync reading progress +- GET /api/sync/koreader/metadata/:uuid - Get book metadata +- GET /api/sync/koreader/library - Get device library +- POST /api/sync/koreader/bookmarks - Sync bookmarks + +## Kobo Sync Protocol + +See [Kobo Sync](kobo/) and [Sync Protocol](sync/kobo-protocol.md) + +- POST /api/sync/kobo/markup - Sync markup highlights +- POST /api/sync/kobo/bookmark - Sync bookmarks +- POST /api/sync/kobo/v1/analytics/gettests - Analytics endpoint +- GET /api/sync/kobo/v1/initialization - Initialize Kobo sync +- POST /api/sync/kobo/sync-from-server - Push content to device ## WebSocket -See [WebSocket Protocol](websocket/) +See [WebSocket API](websocket/) + +- WS /ws/sync - Real-time sync events and notifications + +## Frontend Pages + +- GET /login - Login page +- GET /register - Registration page +- GET / - Home page +- GET /bookshelf - Bookshelf page +- GET /dashboard - User dashboard +- GET /admin - Admin panel +- GET /devices-page - Device management page +- GET /conflicts-page - Conflicts resolution page + +## Documentation + +- GET /docs - Documentation home +- GET /docs/* - Show documentation pages +- GET /docs/api/search - Search API documentation +- GET /docs/search-index.json - Search index for documentation search + +## Health + +- GET /health - Health check endpoint diff --git a/docs/developer/api/books/bulk_delete_books.md b/docs/developer/api/books/bulk_delete_books.md new file mode 100644 index 0000000..9d9b244 --- /dev/null +++ b/docs/developer/api/books/bulk_delete_books.md @@ -0,0 +1,48 @@ +# Bulk Delete Books + +Delete multiple media items at once. + +**Endpoint**: `POST /api/books/bulk-delete` +**Auth**: Required +**Content-Type**: `application/json` + +## Request Body + +| Field | Type | Required | Description | +|--------|------|-----------|-------------| +| media_item_ids | array of UUID | Yes | Array of media item UUIDs to delete | + +### Example Request + +```json +{ + "media_item_ids": [ + "550e8400-e29b-41d4-a716-446655440001", + "660e8400-e29b-41d4-a716-446655440002", + "770e8400-e29b-41d4-a716-446655440003" + ] +} +``` + +## Response (200 OK) + +```json +{ + "message": "Books deleted successfully", + "deleted_count": 3, + "failed_count": 0 +} +``` + +## Error Responses + +| Code | Description | +|------|-------------| +| 400 | Invalid request data | +| 401 | Invalid or expired token | +| 403 | User does not have permission | +| 404 | One or more media items not found | + +## Try It Out + + diff --git a/docs/developer/api/books/bulk_update_books.md b/docs/developer/api/books/bulk_update_books.md new file mode 100644 index 0000000..1e65f6c --- /dev/null +++ b/docs/developer/api/books/bulk_update_books.md @@ -0,0 +1,71 @@ +# Bulk Update Books + +Update multiple media items at once. + +**Endpoint**: `POST /api/books/bulk-update` +**Auth**: Required +**Content-Type**: `application/json` + +## Request Body + +| Field | Type | Required | Description | +|--------|------|-----------|-------------| +| media_item_ids | array of UUID | Yes | Array of media item UUIDs to update | +| updates | object | Yes | Fields to update (see below) | + +### Update Fields + +| Field | Type | Required | Description | +|--------|------|-----------|-------------| +| tags | array of strings | No | New tags (will replace existing) | +| contributors | array of strings | No | New contributors (will replace existing) | +| add_tags | array of strings | No | Tags to add to existing | +| remove_tags | array of strings | No | Tags to remove from existing | + +### Example Request + +```json +{ + "media_item_ids": [ + "550e8400-e29b-41d4-a716-446655440001", + "660e8400-e29b-41d4-a716-446655440002" + ], + "updates": { + "add_tags": ["science-fiction", "award-winner"], + "contributors": ["Publisher Name"] + } +} +``` + +## Response (200 OK) + +```json +{ + "message": "Books updated successfully", + "updated_count": 2, + "failed_count": 0, + "normalized_tags": ["Science-Fiction", "Award-Winner"], + "normalized_contributors": ["Publisher Name"] +} +``` + +## Tag and Contributor Normalization + +The backend automatically normalizes tags and contributors: + +- **Tags**: Titlecased, punctuation preserved, deduplicated +- **Contributors**: Original casing preserved, punctuation preserved +- **Search fields**: Lowercase, no punctuation, stored in `tags_search` and `contributors_search` + +## Error Responses + +| Code | Description | +|------|-------------| +| 400 | Invalid request data | +| 401 | Invalid or expired token | +| 403 | User does not have permission | +| 404 | One or more media items not found | + +## Try It Out + + diff --git a/docs/developer/api/books/download_book.md b/docs/developer/api/books/download_book.md new file mode 100644 index 0000000..5789b9a --- /dev/null +++ b/docs/developer/api/books/download_book.md @@ -0,0 +1,56 @@ +# Download Book + +Download a book file. + +**Endpoint**: `GET /api/books/:uuid/download` +**Auth**: Not required (public endpoint) + +## Path Parameters + +| Parameter | Type | Required | Description | +|-----------|------|-----------|-------------| +| uuid | string (UUID) | Yes | Media item UUID | + +## Request Headers + +| Header | Type | Required | Description | +|--------|------|-----------|-------------| +| Authorization | string | No* | Bearer token (required if media item is not public) | + +\* If the media item is in a non-public library, authentication is required. + +### Example Request + +```http +GET /api/books/550e8400-e29b-41d4-a716-446655440000/download +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... +``` + +## Response (200 OK) + +Binary file download with appropriate Content-Type header. + +**Possible Content-Types:** +- application/epub+zip +- application/pdf +- application/x-mobipocket-ebook +- application/octet-stream + +**Headers:** +``` +Content-Type: application/epub+zip +Content-Disposition: attachment; filename="book-title.epub" +Content-Length: 1234567 +``` + +## Error Responses + +| Code | Description | +|------|-------------| +| 401 | Authentication required for non-public content | +| 404 | Media item not found | +| 404 | Book file not found on disk | + +## Try It Out + + diff --git a/docs/developer/api/devices/add_to_shelf.md b/docs/developer/api/devices/add_to_shelf.md new file mode 100644 index 0000000..c883f22 --- /dev/null +++ b/docs/developer/api/devices/add_to_shelf.md @@ -0,0 +1,50 @@ +# Add to Shelf + +Add a media item to a device's shelf (Kobo reading shelf). + +**Endpoint**: `POST /api/devices/:id/shelves` +**Auth**: Required +**Content-Type**: `application/json` + +## Path Parameters + +| Parameter | Type | Required | Description | +|-----------|------|-----------|-------------| +| id | string (UUID) | Yes | Device UUID | + +## Request Body + +| Field | Type | Required | Description | +|--------|------|-----------|-------------| +| media_item_id | string (UUID) | Yes | Media item UUID to add to shelf | + +### Example Request + +```json +{ + "media_item_id": "550e8400-e29b-41d4-a716-446655440001" +} +``` + +## Response (201 Created) + +```json +{ + "message": "Item added to shelf successfully", + "device_id": "uuid", + "media_item_id": "uuid" +} +``` + +## Error Responses + +| Code | Description | +|------|-------------| +| 400 | Invalid request data | +| 401 | Invalid or expired token | +| 404 | Device or media item not found | +| 409 | Item already on shelf | + +## Try It Out + + diff --git a/docs/developer/api/devices/clear_shelf.md b/docs/developer/api/devices/clear_shelf.md new file mode 100644 index 0000000..5595289 --- /dev/null +++ b/docs/developer/api/devices/clear_shelf.md @@ -0,0 +1,40 @@ +# Clear Shelf + +Remove all items from a device's shelf. + +**Endpoint**: `DELETE /api/devices/:id/shelves/clear` +**Auth**: Required + +## Path Parameters + +| Parameter | Type | Required | Description | +|-----------|------|-----------|-------------| +| id | string (UUID) | Yes | Device UUID | + +## Request Headers + +| Header | Type | Required | Description | +|--------|------|-----------|-------------| +| Authorization | string | Yes | Bearer token | + +### Example Request + +```http +DELETE /api/devices/550e8400-e29b-41d4-a716-446655440000/shelves/clear +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... +``` + +## Response (204 No Content) + +Shelf cleared successfully. + +## Error Responses + +| Code | Description | +|------|-------------| +| 401 | Invalid or expired token | +| 404 | Device not found | + +## Try It Out + + diff --git a/docs/developer/api/devices/get_shelf.md b/docs/developer/api/devices/get_shelf.md new file mode 100644 index 0000000..6c8bcf2 --- /dev/null +++ b/docs/developer/api/devices/get_shelf.md @@ -0,0 +1,54 @@ +# Get Shelf + +Get all items on a device's shelf. + +**Endpoint**: `GET /api/devices/:id/shelves` +**Auth**: Required + +## Path Parameters + +| Parameter | Type | Required | Description | +|-----------|------|-----------|-------------| +| id | string (UUID) | Yes | Device UUID | + +## Request Headers + +| Header | Type | Required | Description | +|--------|------|-----------|-------------| +| Authorization | string | Yes | Bearer token | + +### Example Request + +```http +GET /api/devices/550e8400-e29b-41d4-a716-446655440000/shelves +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... +``` + +## Response (200 OK) + +```json +{ + "items": [ + { + "id": "uuid", + "media_item_id": "uuid", + "title": "Book Title", + "authors": ["Author Name"], + "cover_url": "/api/books/uuid/cover", + "added_at": "2026-02-08T10:00:00Z" + } + ], + "total": 15 +} +``` + +## Error Responses + +| Code | Description | +|------|-------------| +| 401 | Invalid or expired token | +| 404 | Device not found | + +## Try It Out + + diff --git a/docs/developer/api/devices/remove_from_shelf.md b/docs/developer/api/devices/remove_from_shelf.md new file mode 100644 index 0000000..d0ae7ba --- /dev/null +++ b/docs/developer/api/devices/remove_from_shelf.md @@ -0,0 +1,43 @@ +# Remove from Shelf + +Remove a media item from a device's shelf. + +**Endpoint**: `DELETE /api/devices/:id/shelves` +**Auth**: Required +**Content-Type**: `application/json` + +## Path Parameters + +| Parameter | Type | Required | Description | +|-----------|------|-----------|-------------| +| id | string (UUID) | Yes | Device UUID | + +## Request Body + +| Field | Type | Required | Description | +|--------|------|-----------|-------------| +| media_item_id | string (UUID) | Yes | Media item UUID to remove from shelf | + +### Example Request + +```json +{ + "media_item_id": "550e8400-e29b-41d4-a716-446655440001" +} +``` + +## Response (204 No Content) + +Item removed from shelf successfully. + +## Error Responses + +| Code | Description | +|------|-------------| +| 400 | Invalid request data | +| 401 | Invalid or expired token | +| 404 | Device or shelf item not found | + +## Try It Out + + diff --git a/docs/developer/api/kobo/analytics_gettests.md b/docs/developer/api/kobo/analytics_gettests.md new file mode 100644 index 0000000..c606f08 --- /dev/null +++ b/docs/developer/api/kobo/analytics_gettests.md @@ -0,0 +1,46 @@ +# Analytics GetTests + +Kobo analytics endpoint (device compatibility). + +**Endpoint**: `POST /api/sync/kobo/v1/analytics/gettests` +**Auth**: Required (Device authentication) + +## Device Authentication + +This endpoint requires device authentication (not user JWT). This is a Kobo compatibility endpoint. + +## Request Body + +| Field | Type | Required | Description | +|--------|------|-----------|-------------| +| (varies) | object | No | Kobo analytics data (format varies) | + +### Example Request + +```json +{ + "data": {} +} +``` + +## Response (200 OK) + +```json +{ + "result": "ok" +} +``` + +## Error Responses + +| Code | Description | +|------|-------------| +| 401 | Device authentication failed | + +## Notes + +This is a Kobo compatibility endpoint used by Kobo devices for analytics purposes. The exact request/response format follows Kobo's proprietary protocol. + +## Try It Out + + diff --git a/docs/developer/api/kobo/bookmark_sync.md b/docs/developer/api/kobo/bookmark_sync.md new file mode 100644 index 0000000..502bea7 --- /dev/null +++ b/docs/developer/api/kobo/bookmark_sync.md @@ -0,0 +1,63 @@ +# Bookmark Sync + +Sync bookmarks from Kobo device. + +**Endpoint**: `POST /api/sync/kobo/bookmark` +**Auth**: Required (Device authentication) + +## Device Authentication + +This endpoint requires device authentication (not user JWT). Kobo devices authenticate using their device credentials and access tokens. + +## Request Body + +| Field | Type | Required | Description | +|--------|------|-----------|-------------| +| bookmarks | array | Yes | Array of bookmark objects | + +### Bookmark Object + +| Field | Type | Required | Description | +|--------|------|-----------|-------------| +| BookmarkID | string | Yes | Unique bookmark ID | +| ContentID | string | Yes | Book content ID | +| StartPosition | integer | Yes | Bookmark position | +| Text | string | No | Bookmark text | +| DateCreated | string | Yes | ISO 8601 timestamp | + +### Example Request + +```json +{ + "bookmarks": [ + { + "BookmarkID": "bookmark123", + "ContentID": "book-uuid", + "StartPosition": 1234, + "Text": "Chapter 5", + "DateCreated": "2026-02-08T10:00:00Z" + } + ] +} +``` + +## Response (200 OK) + +```json +{ + "message": "Bookmarks synced successfully", + "synced_count": 1 +} +``` + +## Error Responses + +| Code | Description | +|------|-------------| +| 401 | Device authentication failed | +| 400 | Invalid request data | +| 404 | Device or book not found | + +## Try It Out + + diff --git a/docs/developer/api/kobo/initialization.md b/docs/developer/api/kobo/initialization.md new file mode 100644 index 0000000..00caf94 --- /dev/null +++ b/docs/developer/api/kobo/initialization.md @@ -0,0 +1,53 @@ +# Kobo Initialization + +Initialize Kobo device sync. + +**Endpoint**: `GET /api/sync/kobo/v1/initialization` +**Auth**: Required (Device authentication) + +## Device Authentication + +This endpoint requires device authentication (not user JWT). Kobo devices authenticate using their device credentials and access tokens. + +## Request Headers + +| Header | Type | Required | Description | +|--------|------|-----------|-------------| +| X-Device-ID | string | Yes | Device UUID | +| X-Device-Key | string | Yes | Device authentication key | +| X-Kobo-UserKey | string | No | Kobo user key (if available) | + +### Example Request + +```http +GET /api/sync/kobo/v1/initialization +X-Device-ID: 550e8400-e29b-41d4-a716-446655440000 +X-Device-Key: device-auth-key +``` + +## Response (200 OK) + +```json +{ + "result": "ok", + "resources": { + "image_host": "https://cdn.kobo.com/...", + "image_url_quality_template": "{ImageId}/h270/{ImageId}_landscape_cover.jpg" + } +} +``` + +## Error Responses + +| Code | Description | +|------|-------------| +| 401 | Device authentication failed | +| 404 | Device not found | + +## Notes + +This endpoint is called by Kobo devices during initial setup and connection. It returns configuration data required for proper Kobo integration. + +## Try It Out + + diff --git a/docs/developer/api/kobo/markup_sync.md b/docs/developer/api/kobo/markup_sync.md new file mode 100644 index 0000000..7a0f8dc --- /dev/null +++ b/docs/developer/api/kobo/markup_sync.md @@ -0,0 +1,71 @@ +# Markup Sync + +Sync markup highlights and annotations from Kobo device. + +**Endpoint**: `POST /api/sync/kobo/markup` +**Auth**: Required (Device authentication) + +## Device Authentication + +This endpoint requires device authentication (not user JWT). Kobo devices authenticate using their device credentials and access tokens. + +## Request Body + +| Field | Type | Required | Description | +|--------|------|-----------|-------------| +| bookmarks | array | Yes | Array of bookmark/markup objects | + +### Bookmark Object + +| Field | Type | Required | Description | +|--------|------|-----------|-------------| +| BookmarkID | string | Yes | Unique bookmark ID | +| ContentID | string | Yes | Book content ID | +| StartPosition | integer | Yes | Highlight start position | +| EndPosition | integer | No | Highlight end position | +| Text | string | No | Highlighted text | +| Annotation | string | No | User annotation | +| DateCreated | string | Yes | ISO 8601 timestamp | +| Chapter | string | No | Chapter title | +| Hidden | boolean | No | Whether bookmark is hidden | + +### Example Request + +```json +{ + "bookmarks": [ + { + "BookmarkID": "bookmark123", + "ContentID": "book-uuid", + "StartPosition": 1234, + "EndPosition": 1456, + "Text": "Highlighted text from book", + "Annotation": "My annotation", + "DateCreated": "2026-02-08T10:00:00Z", + "Chapter": "Chapter 1", + "Hidden": false + } + ] +} +``` + +## Response (200 OK) + +```json +{ + "message": "Markup synced successfully", + "synced_count": 1 +} +``` + +## Error Responses + +| Code | Description | +|------|-------------| +| 401 | Device authentication failed | +| 400 | Invalid request data | +| 404 | Device or book not found | + +## Try It Out + + diff --git a/docs/developer/api/kobo/sync_from_server.md b/docs/developer/api/kobo/sync_from_server.md new file mode 100644 index 0000000..666b219 --- /dev/null +++ b/docs/developer/api/kobo/sync_from_server.md @@ -0,0 +1,67 @@ +# Sync From Server + +Push content and metadata to Kobo device. + +**Endpoint**: `POST /api/sync/kobo/sync-from-server` +**Auth**: Required (Device authentication) + +## Device Authentication + +This endpoint requires device authentication (not user JWT). Kobo devices authenticate using their device credentials and access tokens. + +## Request Body + +| Field | Type | Required | Description | +|--------|------|-----------|-------------| +| book_ids | array of string | No | Array of ContentIDs to sync | +| full_sync | boolean | No | Whether to perform full sync (default: false) | + +### Example Request + +```json +{ + "book_ids": [ + "content-id-1", + "content-id-2" + ], + "full_sync": false +} +``` + +## Response (200 OK) + +```json +{ + "books": [ + { + "ContentID": "content-id-1", + "Title": "Book Title", + "Author": "Author Name", + "ISBN": "978-0123456789", + "PublicationDate": "2026-02-08", + "FileSize": 1234567, + "CoverImageId": "cover-id-1", + "Metadata": { + "language": "en", + "publisher": "Publisher Name" + } + } + ], + "total": 2 +} +``` + +## Error Responses + +| Code | Description | +|------|-------------| +| 401 | Device authentication failed | +| 404 | Device not found | + +## Notes + +This endpoint pushes library content from the server to the Kobo device. It's called during sync operations to update the device's library catalog. + +## Try It Out + + diff --git a/docs/developer/api/progress/get_progress_history.md b/docs/developer/api/progress/get_progress_history.md new file mode 100644 index 0000000..e4c21c8 --- /dev/null +++ b/docs/developer/api/progress/get_progress_history.md @@ -0,0 +1,73 @@ +# Get Progress History + +Get historical reading progress data for a media item. + +**Endpoint**: `GET /api/progress/:id/history` +**Auth**: Required + +## Path Parameters + +| Parameter | Type | Required | Description | +|-----------|------|-----------|-------------| +| id | string (UUID) | Yes | Media item UUID | + +## Query Parameters + +| Parameter | Type | Required | Description | +|-----------|------|-----------|-------------| +| limit | integer | No | Maximum number of history entries (default: 100) | +| offset | integer | No | Number of entries to skip (default: 0) | + +## Request Headers + +| Header | Type | Required | Description | +|--------|------|-----------|-------------| +| Authorization | string | Yes | Bearer token | + +### Example Request + +```http +GET /api/progress/550e8400-e29b-41d4-a716-446655440000/history?limit=20 +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... +``` + +## Response (200 OK) + +```json +{ + "history": [ + { + "media_item_id": "uuid", + "percentage": 75.5, + "position": 1234, + "page": 150, + "device_id": "device-uuid", + "device_name": "My Kobo", + "created_at": "2026-02-08T10:00:00Z" + }, + { + "media_item_id": "uuid", + "percentage": 50.0, + "position": 800, + "page": 100, + "device_id": "device-uuid", + "device_name": "My Kobo", + "created_at": "2026-02-07T15:00:00Z" + } + ], + "total": 45, + "limit": 20, + "offset": 0 +} +``` + +## Error Responses + +| Code | Description | +|------|-------------| +| 401 | Invalid or expired token | +| 404 | Media item not found | + +## Try It Out + + diff --git a/docs/developer/api/progress/get_universal_progress.md b/docs/developer/api/progress/get_universal_progress.md new file mode 100644 index 0000000..bcf0425 --- /dev/null +++ b/docs/developer/api/progress/get_universal_progress.md @@ -0,0 +1,54 @@ +# Get Universal Progress + +Get universal (device-agnostic) reading progress for a media item. + +**Endpoint**: `GET /api/progress/:id` +**Auth**: Required + +## Path Parameters + +| Parameter | Type | Required | Description | +|-----------|------|-----------|-------------| +| id | string (UUID) | Yes | Media item UUID | + +## Request Headers + +| Header | Type | Required | Description | +|--------|------|-----------|-------------| +| Authorization | string | Yes | Bearer token | + +### Example Request + +```http +GET /api/progress/550e8400-e29b-41d4-a716-446655440000 +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... +``` + +## Response (200 OK) + +```json +{ + "media_item_id": "uuid", + "percentage": 75.5, + "position": 1234, + "page": 150, + "total_pages": 200, + "finished": false, + "updated_at": "2026-02-08T10:00:00Z", + "device": { + "id": "device-uuid", + "name": "My Kobo" + } +} +``` + +## Error Responses + +| Code | Description | +|------|-------------| +| 401 | Invalid or expired token | +| 404 | Media item not found | + +## Try It Out + + diff --git a/docs/developer/api/progress/update_universal_progress.md b/docs/developer/api/progress/update_universal_progress.md new file mode 100644 index 0000000..eebc812 --- /dev/null +++ b/docs/developer/api/progress/update_universal_progress.md @@ -0,0 +1,60 @@ +# Update Universal Progress + +Update universal reading progress for a media item. + +**Endpoint**: `POST /api/progress/:id` +**Auth**: Required +**Content-Type**: `application/json` + +## Path Parameters + +| Parameter | Type | Required | Description | +|-----------|------|-----------|-------------| +| id | string (UUID) | Yes | Media item UUID | + +## Request Body + +| Field | Type | Required | Description | +|--------|------|-----------|-------------| +| percentage | float | No | Progress percentage (0-100) | +| position | integer | No | Current position in bytes | +| page | integer | No | Current page number | +| finished | boolean | No | Whether the book is finished | +| device_id | string (UUID) | No | Device UUID (optional, for tracking source) | + +### Example Request + +```json +{ + "percentage": 75.5, + "position": 1234, + "page": 150, + "finished": false, + "device_id": "device-uuid" +} +``` + +## Response (200 OK) + +```json +{ + "media_item_id": "uuid", + "percentage": 75.5, + "position": 1234, + "page": 150, + "finished": false, + "updated_at": "2026-02-08T10:00:00Z" +} +``` + +## Error Responses + +| Code | Description | +|------|-------------| +| 400 | Invalid request data | +| 401 | Invalid or expired token | +| 404 | Media item not found | + +## Try It Out + + diff --git a/docs/developer/api/queue/clear_device_queue.md b/docs/developer/api/queue/clear_device_queue.md new file mode 100644 index 0000000..650b901 --- /dev/null +++ b/docs/developer/api/queue/clear_device_queue.md @@ -0,0 +1,40 @@ +# Clear Device Queue + +Clear all queue items for a specific device. + +**Endpoint**: `DELETE /api/queue/devices/:device_id/clear` +**Auth**: Required + +## Path Parameters + +| Parameter | Type | Required | Description | +|-----------|------|-----------|-------------| +| device_id | string (UUID) | Yes | Device UUID | + +## Request Headers + +| Header | Type | Required | Description | +|--------|------|-----------|-------------| +| Authorization | string | Yes | Bearer token | + +### Example Request + +```http +DELETE /api/queue/devices/550e8400-e29b-41d4-a716-446655440000/clear +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... +``` + +## Response (204 No Content) + +Device queue cleared successfully. + +## Error Responses + +| Code | Description | +|------|-------------| +| 401 | Invalid or expired token | +| 404 | Device not found | + +## Try It Out + + diff --git a/docs/developer/api/queue/delete_queue_item.md b/docs/developer/api/queue/delete_queue_item.md new file mode 100644 index 0000000..a62f9c3 --- /dev/null +++ b/docs/developer/api/queue/delete_queue_item.md @@ -0,0 +1,40 @@ +# Delete Queue Item + +Delete a specific queue item. + +**Endpoint**: `DELETE /api/queue/items/:item_id` +**Auth**: Required + +## Path Parameters + +| Parameter | Type | Required | Description | +|-----------|------|-----------|-------------| +| item_id | string (UUID) | Yes | Queue item UUID | + +## Request Headers + +| Header | Type | Required | Description | +|--------|------|-----------|-------------| +| Authorization | string | Yes | Bearer token | + +### Example Request + +```http +DELETE /api/queue/items/550e8400-e29b-41d4-a716-446655440000 +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... +``` + +## Response (204 No Content) + +Queue item deleted successfully. + +## Error Responses + +| Code | Description | +|------|-------------| +| 401 | Invalid or expired token | +| 404 | Queue item not found | + +## Try It Out + + diff --git a/docs/developer/api/queue/get_device_queue_stats.md b/docs/developer/api/queue/get_device_queue_stats.md new file mode 100644 index 0000000..6ab43f9 --- /dev/null +++ b/docs/developer/api/queue/get_device_queue_stats.md @@ -0,0 +1,48 @@ +# Get Device Queue Statistics + +Get statistics for a specific device's sync queue. + +**Endpoint**: `GET /api/queue/devices/:device_id/stats` +**Auth**: Required + +## Path Parameters + +| Parameter | Type | Required | Description | +|-----------|------|-----------|-------------| +| device_id | string (UUID) | Yes | Device UUID | + +## Request Headers + +| Header | Type | Required | Description | +|--------|------|-----------|-------------| +| Authorization | string | Yes | Bearer token | + +### Example Request + +```http +GET /api/queue/devices/550e8400-e29b-41d4-a716-446655440000/stats +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... +``` + +## Response (200 OK) + +```json +{ + "total_items": 15, + "pending_items": 8, + "processing_items": 2, + "completed_items": 4, + "failed_items": 1 +} +``` + +## Error Responses + +| Code | Description | +|------|-------------| +| 401 | Invalid or expired token | +| 404 | Device not found | + +## Try It Out + + diff --git a/docs/developer/api/queue/list_all_queue_items.md b/docs/developer/api/queue/list_all_queue_items.md new file mode 100644 index 0000000..2b39869 --- /dev/null +++ b/docs/developer/api/queue/list_all_queue_items.md @@ -0,0 +1,62 @@ +# List All Queue Items (Admin) + +List all queue items across all devices (admin only). + +**Endpoint**: `GET /api/queue/items` +**Auth**: Required (Admin only) + +## Query Parameters + +| Parameter | Type | Required | Description | +|-----------|------|-----------|-------------| +| device_id | string (UUID) | No | Filter by device | +| status | string | No | Filter by status (pending, processing, completed, failed) | +| limit | integer | No | Maximum number of items to return (default: 100) | +| offset | integer | No | Number of items to skip (default: 0) | + +## Request Headers + +| Header | Type | Required | Description | +|--------|------|-----------|-------------| +| Authorization | string | Yes | Bearer token (must have admin role) | + +### Example Request + +```http +GET /api/queue/items?status=failed&limit=20 +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... +``` + +## Response (200 OK) + +```json +{ + "items": [ + { + "id": "uuid", + "device_id": "device-uuid", + "device_name": "My Kobo", + "media_item_id": "book-uuid", + "operation": "sync_progress", + "status": "failed", + "error_message": "Connection timeout", + "created_at": "2026-02-08T10:00:00Z", + "updated_at": "2026-02-08T10:05:00Z" + } + ], + "total": 45, + "limit": 20, + "offset": 0 +} +``` + +## Error Responses + +| Code | Description | +|------|-------------| +| 401 | Invalid or expired token | +| 403 | User does not have admin privileges | + +## Try It Out + + diff --git a/docs/developer/api/queue/list_device_queue_items.md b/docs/developer/api/queue/list_device_queue_items.md new file mode 100644 index 0000000..b9e4742 --- /dev/null +++ b/docs/developer/api/queue/list_device_queue_items.md @@ -0,0 +1,65 @@ +# List Device Queue Items + +List all queue items for a specific device. + +**Endpoint**: `GET /api/queue/devices/:device_id/items` +**Auth**: Required + +## Path Parameters + +| Parameter | Type | Required | Description | +|-----------|------|-----------|-------------| +| device_id | string (UUID) | Yes | Device UUID | + +## Query Parameters + +| Parameter | Type | Required | Description | +|-----------|------|-----------|-------------| +| status | string | No | Filter by status (pending, processing, completed, failed) | +| limit | integer | No | Maximum number of items to return (default: 50) | +| offset | integer | No | Number of items to skip (default: 0) | + +## Request Headers + +| Header | Type | Required | Description | +|--------|------|-----------|-------------| +| Authorization | string | Yes | Bearer token | + +### Example Request + +```http +GET /api/queue/devices/550e8400-e29b-41d4-a716-446655440000/items?status=pending&limit=10 +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... +``` + +## Response (200 OK) + +```json +{ + "items": [ + { + "id": "uuid", + "device_id": "device-uuid", + "media_item_id": "book-uuid", + "operation": "sync_progress", + "status": "pending", + "created_at": "2026-02-08T10:00:00Z", + "updated_at": "2026-02-08T10:00:00Z" + } + ], + "total": 8, + "limit": 10, + "offset": 0 +} +``` + +## Error Responses + +| Code | Description | +|------|-------------| +| 401 | Invalid or expired token | +| 404 | Device not found | + +## Try It Out + + diff --git a/docs/developer/api/queue/retry_queue_item.md b/docs/developer/api/queue/retry_queue_item.md new file mode 100644 index 0000000..bd9e669 --- /dev/null +++ b/docs/developer/api/queue/retry_queue_item.md @@ -0,0 +1,46 @@ +# Retry Queue Item + +Retry a failed queue item. + +**Endpoint**: `POST /api/queue/items/:item_id/retry` +**Auth**: Required + +## Path Parameters + +| Parameter | Type | Required | Description | +|-----------|------|-----------|-------------| +| item_id | string (UUID) | Yes | Queue item UUID | + +## Request Headers + +| Header | Type | Required | Description | +|--------|------|-----------|-------------| +| Authorization | string | Yes | Bearer token | + +### Example Request + +```http +POST /api/queue/items/550e8400-e29b-41d4-a716-446655440000/retry +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... +``` + +## Response (200 OK) + +```json +{ + "message": "Queue item retry initiated", + "item_id": "uuid" +} +``` + +## Error Responses + +| Code | Description | +|------|-------------| +| 401 | Invalid or expired token | +| 404 | Queue item not found | +| 400 | Item cannot be retried (not in failed state) | + +## Try It Out + + diff --git a/docs/developer/api/scanner/get_scan_status.md b/docs/developer/api/scanner/get_scan_status.md new file mode 100644 index 0000000..12ed3c0 --- /dev/null +++ b/docs/developer/api/scanner/get_scan_status.md @@ -0,0 +1,73 @@ +# Get Scan Status + +Get the status of a specific scan job. + +**Endpoint**: `GET /api/scanner/status/:jobId` +**Auth**: Required (Admin only) + +## Path Parameters + +| Parameter | Type | Required | Description | +|-----------|------|-----------|-------------| +| jobId | string (UUID) | Yes | Scan job UUID | + +## Request Headers + +| Header | Type | Required | Description | +|--------|------|-----------|-------------| +| Authorization | string | Yes | Bearer token (must have admin role) | + +### Example Request + +```http +GET /api/scanner/status/550e8400-e29b-41d4-a716-446655440000 +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... +``` + +## Response (200 OK) + +```json +{ + "job_id": "uuid", + "library_id": "uuid", + "status": "in_progress", + "started_at": "2026-02-08T10:00:00Z", + "updated_at": "2026-02-08T10:05:00Z", + "progress": { + "total_files": 1523, + "scanned_files": 850, + "added_files": 125, + "updated_files": 45, + "failed_files": 3, + "percentage": 55.8 + }, + "errors": [ + { + "file_path": "/path/to/file.epub", + "error": "Invalid EPUB format" + } + ] +} +``` + +## Status Values + +| Status | Description | +|--------|-------------| +| pending | Job is queued | +| in_progress | Job is currently running | +| completed | Job completed successfully | +| failed | Job failed with errors | +| cancelled | Job was cancelled | + +## Error Responses + +| Code | Description | +|------|-------------| +| 401 | Invalid or expired token | +| 403 | User does not have admin privileges | +| 404 | Job not found | + +## Try It Out + + diff --git a/docs/developer/api/scanner/get_watch_mode_status.md b/docs/developer/api/scanner/get_watch_mode_status.md new file mode 100644 index 0000000..1d6cf6e --- /dev/null +++ b/docs/developer/api/scanner/get_watch_mode_status.md @@ -0,0 +1,66 @@ +# Get Watch Mode Status + +Get the watch mode status for a library. + +**Endpoint**: `GET /api/scanner/watch/status` +**Auth**: Required (Admin only) + +## Query Parameters + +| Parameter | Type | Required | Description | +|-----------|------|-----------|-------------| +| library_id | string (UUID) | Yes | Library UUID to check | + +## Request Headers + +| Header | Type | Required | Description | +|--------|------|-----------|-------------| +| Authorization | string | Yes | Bearer token (must have admin role) | + +### Example Request + +```http +GET /api/scanner/watch/status?library_id=550e8400-e29b-41d4-a716-446655440000 +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... +``` + +## Response (200 OK) + +```json +{ + "library_id": "uuid", + "status": "watching", + "started_at": "2026-02-08T10:00:00Z", + "watched_folders": [ + "/path/to/library/folder1", + "/path/to/library/folder2" + ], + "stats": { + "files_detected": 15, + "files_processed": 12, + "files_failed": 1, + "last_activity": "2026-02-08T11:30:00Z" + } +} +``` + +## Status Values + +| Status | Description | +|--------|-------------| +| watching | Watch mode is active | +| stopped | Watch mode is not active | +| error | Watch mode encountered an error | + +## Error Responses + +| Code | Description | +|------|-------------| +| 400 | Missing library_id parameter | +| 401 | Invalid or expired token | +| 403 | User does not have admin privileges | +| 404 | Library not found | + +## Try It Out + + diff --git a/docs/developer/api/scanner/overview.md b/docs/developer/api/scanner/overview.md new file mode 100644 index 0000000..3f3c935 --- /dev/null +++ b/docs/developer/api/scanner/overview.md @@ -0,0 +1,148 @@ +# Library Scanner Overview + +The Bookhoard scanner provides comprehensive library management for ebooks, comics, and manga with both manual and automated scanning capabilities. + +## Scanner Types + +### Manual Scanning +- **One-time scan**: On-demand scanning of library folders +- **Progress tracking**: Real-time status updates with file-by-file progress +- **Error reporting**: Detailed logs of failed files with error messages + +### Automated Scanner +- **Periodic scanning**: Configurable interval-based background scanning +- **Automatic discovery**: Detects new files without manual intervention +- **Resource-efficient**: Minimizes system impact with smart scheduling + +### Watch Mode +- **Real-time monitoring**: Instant detection of file system changes +- **Event-driven**: Processes files immediately upon addition/modification +- **Platform support**: Works with inotify (Linux), FSEvents (macOS), and ReadDirectoryChangesW (Windows) + +## Supported Formats + +### Ebooks +| Format | Extensions | +|--------|------------| +| EPUB | `.epub` | +| PDF | `.pdf` | +| Kindle | `.mobi`, `.azw`, `.azw3` | +| Text | `.txt`, `.rtf` | +| Document | `.doc`, `.docx` | +| Other | `.lit`, `.fb2`, `.pdb` | + +### Comics +| Format | Extensions | Archive Type | +|--------|------------|--------------| +| Comic Book ZIP | `.cbz` | ZIP | +| Comic Book RAR | `.cbr` | RAR | +| Comic Book 7z | `.cb7` | 7-Zip | +| Comic Book TAR | `.cbt` | TAR | +| PDF Comics | `.pdf` | PDF | + +### Manga +| Format | Extensions | Notes | +|--------|------------|-------| +| Comic Archives | `.cbz`, `.cbr` | Same as comics | +| Image Files | `.png`, `.jpg`, `.jpeg`, `.gif`, `.bmp`, `.webp` | Individual pages | + +## Metadata Extraction + +### Comic Archives (.cbz, .cbr, .cb7, .cbt) +The scanner automatically extracts metadata from comic archives: + +**ComicInfo.xml Support:** +- Series title +- Issue number +- Publisher +- Writer, artist, inker, colorist +- Year, month +- Genre, tags +- Cover image extraction + +**Fallback Metadata:** +- Filename parsing +- Archive structure analysis +- Page count detection + +### Manga Processing +- **Archive-based**: Processes .cbz/.cbr files like comics +- **Image-based**: Handles directories of sequential images +- **Chapter detection**: Identifies chapter/volume numbers from filenames +- **Series grouping**: Groups images into logical manga volumes + +## Scanner Features + +### Smart Deduplication +- SHA256 hash calculation for all files +- Automatic duplicate detection and skipping +- Efficient incremental updates + +### Library Type Awareness +- Format filtering based on library type +- Type-specific metadata extraction +- Appropriate thumbnail generation + +### Error Handling +- Continues on individual file errors +- Detailed error reporting in scan status +- Failed file tracking for retry + +### Progress Tracking +- Total files vs. processed files +- Percentage completion +- Added, updated, and failed file counts +- Per-file error messages + +## Performance Considerations + +### Large Libraries +- **Scanning speed**: Processes hundreds of files per second +- **Memory usage**: Streaming metadata extraction +- **Database efficiency**: Batch inserts and updates + +### Resource Limits +- **Configurable intervals**: Prevent excessive scanning +- **Rate limiting**: Watch mode debounce settings +- **Admin controls**: Start/stop operations as needed + +## Usage Examples + +### Create and Scan a Comic Library +```json +POST /api/libraries +{ + "name": "My Comic Collection", + "library_type_id": "comics-type-uuid", + "description": "Marvel and DC comics" +} + +POST /api/scanner/scan +{ + "library_id": "library-uuid", + "recursive": true +} +``` + +### Enable Watch Mode for Manga +```json +POST /api/scanner/watch/start +{ + "library_id": "manga-library-uuid" +} +``` + +### Check Scan Progress +```http +GET /api/scanner/status/550e8400-e29b-41d4-a716-446655440000 +``` + +## Related Endpoints + +- [Scan Ebooks](scan_ebooks.md) - Manual one-time scan +- [Start Scanner](start_scanner.md) - Automated periodic scanning +- [Stop Scanner](stop_scanner.md) - Stop automated scanner +- [Get Scan Status](get_scan_status.md) - Check scan progress +- [Start Watch Mode](start_watch_mode.md) - Enable real-time monitoring +- [Stop Watch Mode](stop_watch_mode.md) - Disable watch mode +- [Get Watch Mode Status](get_watch_mode_status.md) - Check watch status diff --git a/docs/developer/api/scanner/scan_ebooks.md b/docs/developer/api/scanner/scan_ebooks.md new file mode 100644 index 0000000..c45d3ec --- /dev/null +++ b/docs/developer/api/scanner/scan_ebooks.md @@ -0,0 +1,59 @@ +# Scan Library + +Initiate a one-time scan of a library for ebooks, manga, or comics. + +**Endpoint**: `POST /api/scanner/scan` +**Auth**: Required (Admin only) +**Content-Type**: `application/json` + +## Request Body + +| Field | Type | Required | Description | +|--------|------|-----------|-------------| +| library_id | string (UUID) | Yes | Library UUID to scan (supports ebooks, manga, and comics) | +| recursive | boolean | No | Scan subdirectories recursively (default: true) | +| force | boolean | No | Force rescan of existing files (default: false) | + +## Supported Formats + +The scanner automatically detects and processes files based on the library type: + +**Ebooks:** .epub, .pdf, .mobi, .azw, .azw3, .txt, .rtf, .doc, .docx, .lit, .fb2, .pdb + +**Comics:** .cbz, .cbr, .cb7, .cbt, .pdf + +**Manga:** .cbz, .cbr, .png, .jpg, .jpeg, .gif, .bmp, .webp + +### Example Request + +```json +{ + "library_id": "550e8400-e29b-41d4-a716-446655440000", + "recursive": true, + "force": false +} +``` + +## Response (202 Accepted) + +```json +{ + "message": "Scan initiated successfully", + "job_id": "uuid", + "library_id": "uuid", + "status": "pending" +} +``` + +## Error Responses + +| Code | Description | +|------|-------------| +| 400 | Invalid request data | +| 401 | Invalid or expired token | +| 403 | User does not have admin privileges | +| 404 | Library not found | + +## Try It Out + + diff --git a/docs/developer/api/scanner/start_scanner.md b/docs/developer/api/scanner/start_scanner.md new file mode 100644 index 0000000..d809369 --- /dev/null +++ b/docs/developer/api/scanner/start_scanner.md @@ -0,0 +1,54 @@ +# Start Scanner + +Start the automated background scanner for a library. Supports ebook, manga, and comic libraries. + +**Endpoint**: `POST /api/scanner/start` +**Auth**: Required (Admin only) +**Content-Type**: `application/json` + +## Request Body + +| Field | Type | Required | Description | +|--------|------|-----------|-------------| +| library_id | string (UUID) | Yes | Library UUID to scan (supports ebooks, manga, and comics) | +| interval_seconds | integer | No | Scan interval in seconds (default: 3600, min: 300) | + +## Supported Library Types + +- **Ebooks:** Processes .epub, .pdf, .mobi, and other ebook formats +- **Comics:** Processes comic archives (.cbz, .cbr, .cb7, .cbt) with metadata extraction +- **Manga:** Processes manga archives and image files (.cbz, .cbr, .png, .jpg, etc.) + +### Example Request + +```json +{ + "library_id": "550e8400-e29b-41d4-a716-446655440000", + "interval_seconds": 3600 +} +``` + +## Response (200 OK) + +```json +{ + "message": "Scanner started successfully", + "library_id": "uuid", + "interval_seconds": 3600, + "status": "running" +} +``` + +## Error Responses + +| Code | Description | +|------|-------------| +| 400 | Invalid request data or interval too low | +| 401 | Invalid or expired token | +| 403 | User does not have admin privileges | +| 404 | Library not found | +| 409 | Scanner already running for this library | + +## Try It Out + + diff --git a/docs/developer/api/scanner/start_watch_mode.md b/docs/developer/api/scanner/start_watch_mode.md new file mode 100644 index 0000000..9acbebc --- /dev/null +++ b/docs/developer/api/scanner/start_watch_mode.md @@ -0,0 +1,54 @@ +# Start Watch Mode + +Start watch mode for a library to automatically detect and process new/modified files. Supports ebook, manga, and comic libraries with real-time file system monitoring. + +**Endpoint**: `POST /api/scanner/watch/start` +**Auth**: Required (Admin only) +**Content-Type**: `application/json` + +## Request Body + +| Field | Type | Required | Description | +|--------|------|-----------|-------------| +| library_id | string (UUID) | Yes | Library UUID to watch (supports ebooks, manga, and comics) | + +## Watch Mode Features + +Watch mode automatically detects and processes: +- **New files** added to library folders +- **Modified files** that have been updated +- **Format-specific metadata extraction** for comics (.cbz, .cbr) and manga +- **ComicInfo.xml parsing** for comic archives +- **Image-based manga** processing for individual page files + +### Example Request + +```json +{ + "library_id": "550e8400-e29b-41d4-a716-446655440000" +} +``` + +## Response (200 OK) + +```json +{ + "message": "Watch mode started successfully", + "library_id": "uuid", + "status": "watching" +} +``` + +## Error Responses + +| Code | Description | +|------|-------------| +| 400 | Invalid request data | +| 401 | Invalid or expired token | +| 403 | User does not have admin privileges | +| 404 | Library not found | +| 409 | Watch mode already active for this library | + +## Try It Out + + diff --git a/docs/developer/api/scanner/stop_scanner.md b/docs/developer/api/scanner/stop_scanner.md new file mode 100644 index 0000000..10f8ef9 --- /dev/null +++ b/docs/developer/api/scanner/stop_scanner.md @@ -0,0 +1,45 @@ +# Stop Scanner + +Stop the automated background scanner for a library. + +**Endpoint**: `POST /api/scanner/stop` +**Auth**: Required (Admin only) +**Content-Type**: `application/json` + +## Request Body + +| Field | Type | Required | Description | +|--------|------|-----------|-------------| +| library_id | string (UUID) | Yes | Library UUID to stop scanning | + +### Example Request + +```json +{ + "library_id": "550e8400-e29b-41d4-a716-446655440000" +} +``` + +## Response (200 OK) + +```json +{ + "message": "Scanner stopped successfully", + "library_id": "uuid", + "status": "stopped" +} +``` + +## Error Responses + +| Code | Description | +|------|-------------| +| 400 | Invalid request data | +| 401 | Invalid or expired token | +| 403 | User does not have admin privileges | +| 404 | Library not found | +| 400 | Scanner not running for this library | + +## Try It Out + + diff --git a/docs/developer/api/scanner/stop_watch_mode.md b/docs/developer/api/scanner/stop_watch_mode.md new file mode 100644 index 0000000..362407c --- /dev/null +++ b/docs/developer/api/scanner/stop_watch_mode.md @@ -0,0 +1,45 @@ +# Stop Watch Mode + +Stop watch mode for a library. + +**Endpoint**: `POST /api/scanner/watch/stop` +**Auth**: Required (Admin only) +**Content-Type**: `application/json` + +## Request Body + +| Field | Type | Required | Description | +|--------|------|-----------|-------------| +| library_id | string (UUID) | Yes | Library UUID to stop watching | + +### Example Request + +```json +{ + "library_id": "550e8400-e29b-41d4-a716-446655440000" +} +``` + +## Response (200 OK) + +```json +{ + "message": "Watch mode stopped successfully", + "library_id": "uuid", + "status": "stopped" +} +``` + +## Error Responses + +| Code | Description | +|------|-------------| +| 400 | Invalid request data | +| 401 | Invalid or expired token | +| 403 | User does not have admin privileges | +| 404 | Library not found | +| 400 | Watch mode not active for this library | + +## Try It Out + + diff --git a/docs/developer/api/users/change_password.md b/docs/developer/api/users/change_password.md index 43b2965..a398e8d 100644 --- a/docs/developer/api/users/change_password.md +++ b/docs/developer/api/users/change_password.md @@ -2,7 +2,7 @@ Change the current user's password. -**Endpoint**: `PUT /api/users/me/password` +**Endpoint**: `PUT /api/auth/password` **Auth**: Required **Content-Type**: `application/json` diff --git a/docs/developer/api/users/get_profile.md b/docs/developer/api/users/get_profile.md index 6cdfcac..7043b3d 100644 --- a/docs/developer/api/users/get_profile.md +++ b/docs/developer/api/users/get_profile.md @@ -2,7 +2,7 @@ Retrieve the current authenticated user's profile. -**Endpoint**: `GET /api/users/me` +**Endpoint**: `GET /api/auth/profile` **Auth**: Required ## Request Headers @@ -14,7 +14,7 @@ Retrieve the current authenticated user's profile. ### Example Request ```http -GET /api/users/me +GET /api/auth/profile Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... ``` diff --git a/docs/developer/api/users/update_email.md b/docs/developer/api/users/update_email.md new file mode 100644 index 0000000..8498142 --- /dev/null +++ b/docs/developer/api/users/update_email.md @@ -0,0 +1,41 @@ +# Update Email + +Update the authenticated user's email address. + +**Endpoint**: `PUT /api/auth/email` +**Auth**: Required +**Content-Type**: `application/json` + +## Request Body + +| Field | Type | Required | Description | +|--------|------|-----------|-------------| +| email | string | Yes | New email address (must be valid email format) | + +### Example Request + +```json +{ + "email": "newemail@example.com" +} +``` + +## Response (200 OK) + +```json +{ + "message": "Email updated successfully" +} +``` + +## Error Responses + +| Code | Description | +|------|-------------| +| 400 | Invalid email format | +| 401 | Invalid or expired token | +| 409 | Email already taken by another user | + +## Try It Out + + diff --git a/docs/developer/api/users/update_profile.md b/docs/developer/api/users/update_profile.md index 0b6a616..c448cc2 100644 --- a/docs/developer/api/users/update_profile.md +++ b/docs/developer/api/users/update_profile.md @@ -2,7 +2,7 @@ Update the current user's profile information. -**Endpoint**: `PUT /api/users/me/profile` +**Endpoint**: `PUT /api/auth/profile` **Auth**: Required **Content-Type**: `application/json` diff --git a/docs/developer/api/users/update_theme.md b/docs/developer/api/users/update_theme.md index 1cf1d96..cae4f02 100644 --- a/docs/developer/api/users/update_theme.md +++ b/docs/developer/api/users/update_theme.md @@ -2,7 +2,7 @@ Update the current user's theme preference. -**Endpoint**: `PUT /api/users/me/theme` +**Endpoint**: `PUT /api/auth/theme` **Auth**: Required **Content-Type**: `application/json` diff --git a/docs/developer/api/users/update_username.md b/docs/developer/api/users/update_username.md new file mode 100644 index 0000000..34e42e3 --- /dev/null +++ b/docs/developer/api/users/update_username.md @@ -0,0 +1,41 @@ +# Update Username + +Update the authenticated user's username. + +**Endpoint**: `PUT /api/auth/username` +**Auth**: Required +**Content-Type**: `application/json` + +## Request Body + +| Field | Type | Required | Description | +|--------|------|-----------|-------------| +| username | string | Yes | New username (min 3 chars, alphanumeric and underscore only) | + +### Example Request + +```json +{ + "username": "new_username" +} +``` + +## Response (200 OK) + +```json +{ + "message": "Username updated successfully" +} +``` + +## Error Responses + +| Code | Description | +|------|-------------| +| 400 | Invalid username format | +| 401 | Invalid or expired token | +| 409 | Username already taken by another user | + +## Try It Out + + diff --git a/docs/developer/api/websocket/sync_api.md b/docs/developer/api/websocket/sync_api.md new file mode 100644 index 0000000..5892d7c --- /dev/null +++ b/docs/developer/api/websocket/sync_api.md @@ -0,0 +1,287 @@ +# WebSocket Sync API + +Real-time bidirectional sync API for live updates and notifications. + +**Endpoint**: `WS /ws/sync` +**Auth**: Required (JWT token or Device authentication) + +## Connection + +Connect to the WebSocket endpoint with authentication: + +```javascript +const ws = new WebSocket('wss://bookhoard.example/ws/sync?token=eyJhbGci...'); + +// Or with device authentication +const ws = new WebSocket('wss://bookhoard.example/ws/sync?device_id=uuid&device_key=key'); +``` + +## Message Format + +All messages are JSON: + +```json +{ + "type": "message_type", + "data": { ... } +} +``` + +## Client→Server Messages + +### Subscribe to Progress Updates + +```json +{ + "type": "subscribe", + "data": { + "topic": "progress", + "device_id": "device-uuid" + } +} +``` + +### Unsubscribe + +```json +{ + "type": "unsubscribe", + "data": { + "topic": "progress" + } +} +``` + +### Heartbeat/Ping + +```json +{ + "type": "ping", + "data": { + "timestamp": "2026-02-08T10:00:00Z" + } +} +``` + +## Server→Client Messages + +### Progress Updated + +```json +{ + "type": "progress_updated", + "data": { + "media_item_id": "uuid", + "device_id": "device-uuid", + "percentage": 75.5, + "position": 1234, + "updated_at": "2026-02-08T10:00:00Z" + } +} +``` + +### Conflict Detected + +```json +{ + "type": "conflict_detected", + "data": { + "conflict_id": "uuid", + "media_item_id": "uuid", + "severity": "high", + "created_at": "2026-02-08T10:00:00Z" + } +} +``` + +### Scan Progress + +```json +{ + "type": "scan_progress", + "data": { + "job_id": "uuid", + "library_id": "uuid", + "percentage": 45.5, + "files_processed": 850, + "total_files": 1523, + "status": "in_progress" + } +} +``` + +### Scan Complete + +```json +{ + "type": "scan_complete", + "data": { + "job_id": "uuid", + "library_id": "uuid", + "files_added": 125, + "files_updated": 45, + "files_failed": 3, + "completed_at": "2026-02-08T10:00:00Z" + } +} +``` + +### Device Connected + +```json +{ + "type": "device_connected", + "data": { + "device_id": "uuid", + "device_name": "My Kobo", + "connected_at": "2026-02-08T10:00:00Z" + } +} +``` + +### Device Disconnected + +```json +{ + "type": "device_disconnected", + "data": { + "device_id": "uuid", + "disconnected_at": "2026-02-08T10:00:00Z" + } +} +``` + +### Pong Response + +```json +{ + "type": "pong", + "data": { + "timestamp": "2026-02-08T10:00:00Z" + } +} +``` + +## Connection Lifecycle + +1. **Connect** - WebSocket connection established +2. **Authenticate** - Send JWT token or device credentials via query string +3. **Subscribed** - Server confirms authentication and subscribes to relevant topics +4. **Messages** - Server pushes real-time updates +5. **Heartbeat** - Client sends ping every 30 seconds +6. **Disconnect** - Connection closed + +## Authentication + +### User Authentication + +``` +wss://bookhoard.example/ws/sync?token= +``` + +### Device Authentication + +``` +wss://bookhoard.example/ws/sync?device_id=&device_key= +``` + +## Error Messages + +### Authentication Failed + +```json +{ + "type": "error", + "data": { + "code": "auth_failed", + "message": "Invalid or expired token" + } +} +``` + +### Subscription Failed + +```json +{ + "type": "error", + "data": { + "code": "subscription_failed", + "message": "Cannot subscribe to topic" + } +} +``` + +## Usage Example + +```javascript +const ws = new WebSocket('wss://bookhoard.example/ws/sync?token=eyJhbGci...'); + +ws.onopen = () => { + console.log('Connected to sync WebSocket'); + + // Subscribe to progress updates + ws.send(JSON.stringify({ + type: 'subscribe', + data: { topic: 'progress', device_id: 'device-uuid' } + })); + + // Start heartbeat + setInterval(() => { + ws.send(JSON.stringify({ + type: 'ping', + data: { timestamp: new Date().toISOString() } + })); + }, 30000); +}; + +ws.onmessage = (event) => { + const message = JSON.parse(event.data); + + switch (message.type) { + case 'progress_updated': + console.log('Progress updated:', message.data); + break; + case 'conflict_detected': + console.log('New conflict detected:', message.data); + break; + case 'scan_complete': + console.log('Scan complete:', message.data); + break; + case 'pong': + console.log('Pong received'); + break; + default: + console.log('Unknown message type:', message.type); + } +}; + +ws.onerror = (error) => { + console.error('WebSocket error:', error); +}; + +ws.onclose = () => { + console.log('WebSocket connection closed'); +}; +``` + +## Topics + +| Topic | Description | Events | +|-------|-------------|--------| +| progress | Reading progress updates | progress_updated | +| conflicts | Sync conflict events | conflict_detected, conflict_resolved | +| scanner | Library scan events | scan_progress, scan_complete | +| devices | Device connection events | device_connected, device_disconnected | +| queue | Sync queue events | queue_item_added, queue_item_processed | + +## Best Practices + +1. **Heartbeat**: Send ping every 30 seconds to keep connection alive +2. **Reconnect**: Implement exponential backoff for reconnection +3. **Message Queue**: Queue messages when disconnected and replay on reconnect +4. **Error Handling**: Handle all error types gracefully +5. **Cleanup**: Unsubscribe from topics when no longer needed + +## Try It Out + + diff --git a/docs/index.md b/docs/index.md index 5842c12..027ec3d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -27,10 +27,19 @@ Complete guide to Bookhoard documentation. Find what you need quickly. **[Developer Documentation Portal](developer/development.md)** - Technical documentation & API reference -- **API Documentation** - - [Complete API Reference](developer/api-reference.md) - Monolithic REST API reference (1,300+ lines) - - [Split Endpoint Docs](developer/api/api-reference.md) - Individual endpoints with interactive API Explorer - - [Collections API](developer/collections-api.md) - Collections management API + - **API Documentation** + - [Complete API Reference](developer/api-reference.md) - Monolithic REST API reference (1,300+ lines) + - [Split Endpoint Docs](developer/api/api-reference.md) - Individual endpoints with interactive API Explorer + - [Collections API](developer/collections-api.md) - Collections management API + - [Admin API](developer/api/admin/) - User management (admin operations) + - [Books API](developer/api/books/) - Bulk book operations and downloads + - [Devices API](developer/api/devices/) - Device sync and shelf management + - [Conflicts API](developer/api/conflicts/) - Sync conflict resolution endpoints + - [Queue API](developer/api/queue/) - Sync queue management endpoints + - [Scanner API](developer/api/scanner/) - Library scanning and automated watch mode (admin) + - [KOReader API](developer/api/koreader/) - KOReader sync protocol endpoints + - [Kobo API](developer/api/kobo/) - Kobo sync protocol endpoints + - [WebSocket API](developer/api/websocket/) - Real-time sync events - **Protocol Specifications** - [Kobo Sync Protocol](developer/api/sync/kobo-protocol.md) - Kobo device sync @@ -133,5 +142,5 @@ Keep [PROJECT_GUIDELINES.md](PROJECT_GUIDELINES.md) in mind for documentation st --- -**Last Updated**: 2026-02-02 +**Last Updated**: 2026-02-08 **Bookhoard Version**: 1.0