Skip to content

feat(reader): add R2Progression support for cross-device EPUB reading…#7

Open
AshDevFr wants to merge 7 commits intomainfrom
r2-progression
Open

feat(reader): add R2Progression support for cross-device EPUB reading…#7
AshDevFr wants to merge 7 commits intomainfrom
r2-progression

Conversation

@AshDevFr
Copy link
Owner

… sync

Add R2Progression (Readium/OPDS 2.0 standard) storage and endpoints to enable EPUB reading position sync between Codex web reader and third-party apps like Komic.

  • Add r2_progression TEXT column to read_progress table
  • Add GET/PUT /books/{id}/progression endpoints to both Komga-compatible and V1 APIs, storing the full R2Progression JSON alongside legacy fields (current_page, progress_percentage, completed) for backwards compatibility
  • Update the EPUB reader frontend to save and restore R2Progression with CFI locations for precise cross-device position restoration
  • Restore priority: localStorage CFI > R2Progression CFI >
    R2Progression totalProgression > legacy progress_percentage

Includes integration tests for both Komga and V1 progression endpoints.

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Mar 14, 2026

Deploying codex with  Cloudflare Pages  Cloudflare Pages

Latest commit: b5279d3
Status: ✅  Deploy successful!
Preview URL: https://f3abbad6.codex-asm.pages.dev
Branch Preview URL: https://r2-progression.codex-asm.pages.dev

View logs

… sync

Add R2Progression (Readium/OPDS 2.0 standard) storage and endpoints to
enable EPUB reading position sync between Codex web reader and third-party
apps like Komic.

- Add r2_progression TEXT column to read_progress table
- Add GET/PUT /books/{id}/progression endpoints to both Komga-compatible
  and V1 APIs, storing the full R2Progression JSON alongside legacy fields
  (current_page, progress_percentage, completed) for backwards compatibility
- Update the EPUB reader frontend to save and restore R2Progression with
  CFI locations for precise cross-device position restoration
- Restore priority: localStorage CFI > R2Progression CFI >
  R2Progression totalProgression > legacy progress_percentage

Includes integration tests for both Komga and V1 progression endpoints.
Fix EPUB parser failing to detect namespace-prefixed OPF tags (e.g.,
<opf:item>, <opf:spine>), which caused incorrect spine/page counts.
Add helpers that match both bare and prefixed XML tag names, with tests.

Fix cross-app sync with Readium-based readers (e.g., Komic) by resolving
epub.js relative hrefs to full EPUB-internal paths when saving
R2Progression locators (e.g., "contents.xhtml" -> "OEBPS/contents.xhtml").

Fix progress display on BookDetail and MediaCard to prefer R2Progression's
totalProgression over the misleading currentPage/pageCount ratio for EPUBs.

Remove dead startPercent URL parameter code from the EPUB reader, since
position is now restored from R2Progression CFI automatically.

Remove percentage change threshold from EPUB progress saving so every
page turn triggers a save (still debounced at 1s), matching the behavior
of comic/PDF readers.
…ss-device sync

Previously, the EPUB reader always used the localStorage CFI on load,
ignoring any newer progress from the API (e.g., updated via Komic or
another device). This meant cross-device sync effectively never worked
when the user had previously opened the same book on the current device.

Now localStorage saves a timestamp alongside the CFI, and on load the
reader compares it with the R2Progression `modified` timestamp from the
API. Whichever is newer wins, so progress updated on another app or
device is correctly restored.
…on sync

Implement the Readium positions algorithm (1 position per 1024 bytes of
each spine resource) to normalize totalProgression values from any client,
matching Komga's approach. This fixes cross-app EPUB reading sync between
Codex web (epub.js) and Readium-based readers like Komic, which calculate
totalProgression differently.

- Extend EPUB parser to extract spine item file sizes and media types
- Add compute_epub_positions() and normalize_progression() functions
- Add epub_positions column to books table with migration
- Store positions as JSON during book analysis
- Update both native API and Komga API PUT progression handlers to
  normalize client totalProgression against server positions before storing
- Fall back to client values for books without positions (non-EPUB or
  not yet re-analyzed)
- Add unit tests for positions computation and normalization logic
…normalized values

The server was mutating the stored R2Progression JSON by replacing
totalProgression and position in the locator with server-computed values.
This broke cross-device sync because each client relies on its own locator
(href + progression/CFI) for navigation, and the server-normalized values
could differ from what the client originally sent.

Now the R2Progression body is stored as-is from the client. The server's
normalized values are still used internally for current_page and percentage
tracking but no longer overwrite the client's locator data.
… editing

Convert between KOReader's DocFragment format and Codex's R2Progression
so EPUB reading progress is shared across all clients (web reader,
KOReader, OPDS apps). Previously, KOReader sync only stored raw page
numbers, breaking cross-client EPUB progress.

Fix the KOReader partial hash algorithm to match LuaJIT's bit.lshift
32-bit wrapping semantics (i=-1 offset is 0, not 256). Recompute
koreader_hash during both scan and analysis, and expose it in the
book API/DTOs.

Add ProgressRead/ProgressWrite permissions so API keys can be scoped
for progress sync without granting broader access. Include these in
all role presets (reader, maintainer, admin) and the OPDS preset.

Add x-auth-user header authentication for KOReader's login flow, which
sends the API key as a username rather than a Bearer token.

Add API key permission editing UI with an edit modal on the profile
settings page. Update OPDS preset to include progress permissions.

Add KOReader setup documentation with troubleshooting guide.
…s-device sync

epub.js computes reading position based on character offsets while
KOReader and Readium clients use byte offsets. For multi-byte content
(CJK, accented text), the same percentage from each client points to
different physical locations, causing cross-device sync drift.

The server now acts as the conversion authority:
- During EPUB analysis, count text characters per spine item alongside
  byte sizes (stripping HTML markup, scripts, and styles)
- Store spine items with both char_count and file_size in a new
  epub_spine_items column
- At the PUT progression API boundary, detect char-based clients by
  presence of CFI in the locator and convert their totalProgression
  from character-weighted to byte-weighted (Readium canonical) before
  normalization
- Byte-based clients (KOReader, Komic) pass through unchanged

Existing books need a rescan to populate epub_spine_items; without it,
behavior falls back to the previous no-conversion path.

Includes unit tests for character counting, char/byte conversion
(ASCII, CJK, mixed content, roundtrip accuracy), and edge cases.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant