184 lines
6.3 KiB
Markdown
184 lines
6.3 KiB
Markdown
# BookFusion Back Sync - Calibre Plugin
|
|
|
|
**English version:** `README_en.md` (this file)
|
|
**Russian version:** [readme.md](readme.md)
|
|
|
|
Calibre plugin for reverse sync: reads last-read dates from BookFusion
|
|
and writes them into a selected Calibre custom date column.
|
|
|
|
It works alongside the official BookFusion Calibre plugin (no conflicts).
|
|
|
|
## About the author, etc
|
|
(c) Dmitriy Kazimirov <dmitriy.kazimirov@viorsan.com>
|
|
This project also uses ideas and findings from the Calibre BookFusion plugin,
|
|
the Calibre Obsidian plugin, and my earlier private API research
|
|
(as far as I remember, done for compatibility purposes).
|
|
|
|
BookFusion (c)
|
|
|
|
## AI usage
|
|
Used during development:
|
|
- Claude Code
|
|
- OpenCode with OpenAI Codex
|
|
|
|
---
|
|
|
|
## What it does
|
|
|
|
1. Authenticates to BookFusion using email and password (Private API).
|
|
2. Fetches the full list of books from the BookFusion library.
|
|
3. Matches books to Calibre by the `bookfusion` identifier (written by the
|
|
official plugin into Calibre Identifiers when a book is uploaded): first by
|
|
`BookV3.book_id`, then with fallback to `BookV3.id`.
|
|
4. Writes the last-read date for each matched book into the selected
|
|
Calibre custom Date column.
|
|
5. Sorts processing order so the most recently read books are written first;
|
|
books without dates are processed last.
|
|
6. Optionally sets a Completed flag in a selected Calibre Yes/No column when
|
|
`reading_position.percentage` is above the configured threshold.
|
|
|
|
Date source priority:
|
|
- `last_read_at` from BookFusion API (when present)
|
|
- `reading_position.updated_at` (used most often, because `last_read_at`
|
|
is usually populated only by the mobile app)
|
|
|
|
The dialog log shows, for each book, which date was written, from which field,
|
|
and which id mapping was used (`match=book_id` or `match=id`).
|
|
|
|
---
|
|
|
|
## Requirements
|
|
|
|
- Calibre 6.2.1 or newer
|
|
- Official BookFusion plugin installed and synced at least once
|
|
(so books already have the `bookfusion` identifier)
|
|
- BookFusion account with email/password (not Facebook/Twitter OAuth)
|
|
- A custom Date column in Calibre (for example `#dateread`)
|
|
|
|
---
|
|
|
|
## Installation
|
|
|
|
1. Download `BookfusionBackSync.zip` from the repository root.
|
|
|
|
2. In Calibre: **Preferences -> Plugins -> Load plugin from file** and select the ZIP.
|
|
|
|
3. Restart Calibre.
|
|
|
|
4. The **BookFusion Back Sync** button appears in the toolbar.
|
|
|
|
---
|
|
|
|
## Configuration
|
|
|
|
Open settings using the **Settings** button in the plugin dialog, or:
|
|
**Preferences -> Plugins -> BookFusion Back Sync -> Customize**.
|
|
|
|
| Field | Description |
|
|
|------|-------------|
|
|
| BookFusion Email | BookFusion account email |
|
|
| Password | Account password |
|
|
| Last Read Column | Calibre custom Date column to write into |
|
|
| Completed Column (bool) | Optional Calibre Yes/No column for completed books |
|
|
| Completed Threshold (%) | Completion threshold in percent (default `99.9`) |
|
|
|
|
Password is stored as plain text in Calibre config
|
|
(`plugins/bookfusionbacksync.json`), similar to how the official plugin stores the API key.
|
|
|
|
Device ID is generated automatically on the first sync and then persisted.
|
|
|
|
---
|
|
|
|
## Usage
|
|
|
|
You do **not** need to select books. The plugin syncs the whole library.
|
|
|
|
1. Click **BookFusion Back Sync** in the toolbar.
|
|
2. Click **Sync Now**.
|
|
3. Watch progress in the log area.
|
|
4. After completion, Calibre refreshes the book list automatically.
|
|
|
|
### What you see during sync
|
|
|
|
The status line above the progress bar shows these stages:
|
|
|
|
| Status | Meaning |
|
|
|--------|---------|
|
|
| `Authenticating...` | Login with email/password, token retrieval |
|
|
| `Fetching BookFusion library...` | Downloading all books from BookFusion (paged) |
|
|
| `Scanning Calibre library...` | Matching books with `bookfusion` identifiers - progress bar fills here |
|
|
| `Writing N dates to Calibre...` | Writing all found dates in one call |
|
|
| `Done - N updated, M skipped.` | Finished |
|
|
|
|
The progress bar fills during Calibre-to-BookFusion matching.
|
|
The first two stages (auth + network fetch) are indeterminate.
|
|
|
|
### Log
|
|
|
|
For each updated book, lines look like:
|
|
```text
|
|
OK The Name of the Wind -> 2024-11-03 (from reading_position.updated_at)
|
|
OK Dune -> 2025-01-15 (from last_read_at, match=book_id)
|
|
OK Dune -> Completed=True (percentage=100.000, threshold=99.90, match=book_id)
|
|
```
|
|
|
|
Books without a match in BookFusion are skipped silently (counted in `M skipped`).
|
|
|
|
A book gets a visible `SKIP` line only when a date exists in BookFusion but cannot be parsed.
|
|
|
|
### Cancel
|
|
|
|
Click **Close** during sync - worker stops gracefully (waits up to 3 seconds).
|
|
|
|
---
|
|
|
|
## How it works
|
|
|
|
### API
|
|
|
|
The plugin uses BookFusion Private API (`https://bookfusion.com/api`) - the same API
|
|
used by the mobile app. There is no official public documentation; the API was
|
|
reconstructed from Android client (2014) and fusionfixer (2018) source analysis.
|
|
|
|
Auth: `POST /v1/auth.json` with `device`, `login`, `password` returns `token`.
|
|
The token is then used as query params (`?device=...&token=...`) in GET requests.
|
|
|
|
Library: `GET /v3/library/books.json?page=N&per_page=100` for paginated export.
|
|
Each `BookV3` item includes:
|
|
- `book_id` - global book id (this is usually what `identifiers['bookfusion']` stores)
|
|
- `id` - user-library record id
|
|
- `last_read_at` - last read timestamp (often `null`)
|
|
- `reading_position.updated_at` - last reading position update timestamp
|
|
- `reading_position.percentage` - read percentage (used for Completed flag)
|
|
|
|
HTTP calls are made with Python standard library (`urllib.request`),
|
|
without external dependencies.
|
|
|
|
### Book matching
|
|
|
|
The official BookFusion plugin stores the remote id in Calibre:
|
|
```text
|
|
Identifiers -> bookfusion: 66816
|
|
```
|
|
|
|
Back Sync reads this value and compares it with `BookV3.book_id` from API
|
|
(with fallback to `BookV3.id` for compatibility with older records).
|
|
If matched, it writes the date to the selected column.
|
|
|
|
### Writing into Calibre
|
|
|
|
All updates are written in one Calibre `new_api` call:
|
|
```python
|
|
db.set_field('#dateread', {calibre_book_id: datetime_object, ...})
|
|
```
|
|
|
|
### File structure
|
|
|
|
| File | Purpose |
|
|
|------|---------|
|
|
| `__init__.py` | Plugin registration, metadata, entry point |
|
|
| `plugin-import-name-bookfusionbacksync.txt` | Import name for Calibre plugin loader |
|
|
| `config.py` | JSONConfig settings and ConfigWidget form |
|
|
| `ui.py` | InterfacePlugin - menu action and toolbar button |
|
|
| `main.py` | MainDialog - dialog with progress bar and log |
|
|
| `sync_worker.py` | SyncWorker (QThread) - sync logic |
|