Scanned books are commonly distributed as DjVu, which the scanner only stored as a supplement.
Decode pages on the server with DjVuLibre so DjVu imports with a cover and page count, opens in
the image reader with its embedded OCR text, and can be the preferred format of an ordinary email
recipient. Migration 0113 widens the recipient format check to allow it.
Decoding runs on validated private snapshots behind a bounded, reader-first queue, and
revalidation and cached pages are answered without a decoder. Released iOS builds cannot open
DjVu, so series continuation offers it only to callers sending includeDjvu=true, and the
dashboard and notebook read file never name it. The first full scan after upgrading promotes
stored .djvu supplements to content.
Library access roles had no effect on metadata editing: only the global Edit metadata permission
was checked, so a Viewer could edit every library they could see and an Editor could not be
limited to one library. Require Editor or Owner on the book's library for every metadata write,
including bulk edits, locks, covers, file renames, entity manager changes and author edits, and
hide the matching actions in the web client.
Keep existing access levels when an admin saves a user's library list. The old replace reset every
row to Viewer, which would now silently revoke editing on each user save.
Existing grants are Viewer, so after upgrading, admins must set Editor on each library where a
non-superuser should keep editing metadata. The released iOS app already gates editing on Editor.
Closes#1778
The seed names the icon for Epic Voyager (long_book_3) book-marked,
which lucide ships only as an alias of book-bookmark. The web's icon
map had every other seeded name but not this one, so the badge drew the
HelpCircle fallback. The map now has it, and a test reads the seed and
fails if any seeded icon would fall back.
Fix the server and web gaps found while porting the PDF and comic
readers and offline downloads to Android, checked against the iOS app:
- KOReader: answer a pull for a PDF or comic with the page number when a
BookOrbit client wrote last, instead of a percentage with no page
- comics: key the zip offset, RAR and CB7 caches by file id and
modification time, so pages are not served from stale offsets after a
metadata write-back rewrites the archive
- comics: answer an unreadable archive with 422 rather than a page count
of 0, and extract CB7 with its folders so same-named pages in
different folders no longer overwrite each other
- book files: send an ETag and Last-Modified on serve and download and
honour If-None-Match and If-Range, clamp a range that runs past the
end, answer suffix ranges with 206, and honour Range on download,
which advertised it but sent the whole file
- book files: leave raw file routes uncompressed, so FB2 and unknown
formats keep their Content-Length
- progress: reject a fractional or negative page number with 400 rather
than failing with 500 at the database
- types: declare the comic page-count response shared by the web and
the apps
- web: open a percentage-only PDF position at its estimated page, and
flush pending PDF and comic progress before the reading session ends
Preserve reset-hold notices when recovery requests fail and prevent
stale responses from replacing another book's progress. Refresh holds
after permission changes and rereads, and cover recovery and session
deletion with regression tests.
Ref #1355
Deleting a KOReader session left its page events stored, so the next upload within the
session gap re-derived it. Record the deleted span in koreader_session_tombstones and skip
those events during derivation. Show the reset-hold notice on the Reading Log as well,
since Reset is reachable there and a held device otherwise reads as unexplained progress.
Refs #1355
The Book Dock discarded every upload failure, so a rejected file looked like nothing happened.
List failed files with a translated reason, offer retry where it can help, and stop a failed
token refresh, abort or timeout from stalling the upload queue. Dropped files of unsupported
types are reported instead of ignored.
Refs #1739
A voice or speed change invalidated the playing block's end handler, so the next block was never
requested, and a change during loading left the player loading forever. Hand the playing block over
to the new settings, refetch any block playback was waiting on, and cancel superseded requests in
the client and at the provider. Audio that arrives during a pause no longer resumes playback.
Fixes#1734
Fold six tabs into Recipients, Mail servers, Templates and History, where only problems get
colour and one star sets each default through email_preferences. Replace the send dialog with
a sheet that preselects the default recipient and follows the send. Server additions are
optional fields and routes, so released iOS builds keep working.
Rewrite absolute publisher font sizes (keywords and px/pt lengths) as each EPUB loads so they
follow the reader's root size, falling back to the original value when the reader applies no
styles. Skip fixed-layout books and SVG content, and cover the rewrite and the foliate loader
hook with regression tests.
Fixes#1276
Inset and center hover metadata within the circular portrait so the book count and last-added date remain readable. Add regression coverage for both shapes and metadata states.
Fixes#1413
Keep EPUB loading functional on browsers without Object.groupBy or Map.groupBy.
Replace the native calls with local helpers and cover missing APIs with EPUB 2
and EPUB 3 metadata regression tests.
Closes#1683
Use English Wiktionary's multilingual definitions endpoint to avoid HTTP 501
responses from language-specific hosts. Keep filtering definitions by the
book's language and cover host selection, language aliases, inflection
lookups, URL encoding, and popover retry recovery with regression tests.
Closes#1439
The table rendered a dash in every column of a collapsed series row,
including Authors, although the row already carries the first
volume's authors and the grid and list views show them. Render them
as the same read-only author chips book rows use.
Closes#1645
epubBuilder writes the OPF root as `mlns=` for `xmlns=`. Once a metadata save
bound `opf:` on <package>, foliate matched OPF elements by namespace, found no
spine, and failed with "reading 'children'". Match by namespace only when the
root is in it, and have the writer add the missing default namespace so
rewritten files also open in released iOS builds.
Closes#1680
Paint the glow from a zero-height element in the main inset instead of the full-width shell, so it
centers on the content column and follows the sidebar as it resizes or collapses. The shell's own
background patterns (dots, mesh, aurora and others) were hidden under the old glow and now render.
Bump every direct dependency to its newest same-major release and move the
node base image to 26.11.1, which invalidates the cached apk layer and picks
up tiff 4.7.2 for CVE-2026-4775. Rebuild all six kepubify binaries from the
pinned upstream commit with Go 1.27.2 for CVE-2026-78667, CVE-2026-78669 and
CVE-2026-97031, keeping v4-dev so rendition hashes stay stable.
Create client IDs with createUuid instead of crypto.randomUUID, which
browsers only expose in secure contexts, so Add tier, saved views, table
presets and podcast sessions work on HTTP installs. Lint now rejects raw
randomUUID calls.
Fixes#1722
Add original and text-only EPUB delivery choices across Kobo, KOReader,
OPDS, web downloads and email while keeping source files unchanged.
Persist server defaults, device overrides and captured artifact identities
so retries and guided replacement retain the selected rendition.
Preserve saved progress across first opens, replacement resets and stale
updates, and project annotations against the copy delivered to each device.
Handle XML resource references, large inodes, retry backoff and background
worker state correctly. Consolidate the schema changes into one migration
and retain released-client request and response compatibility.
Verify with the full server and client checks, delivery and sync HTTP suites,
independent code reviews, and live KOReader download/settings/reader proof.
Fixes#1496Closes#1603Closes#1714Fixes#1723
Ref #1633
Ref #1508
Ref #1621
Ref #1136
Repeated searches and refreshes asked every provider again, which is slow and runs into provider
rate limits. Keep each provider's answer in Postgres, shared by all users, and reuse it until an
admin-set lifetime runs out. Search screens mark saved results and offer a fresh search, and
explicit refreshes stay live, falling back to a saved answer only when a provider fails.
Empty answers are only kept for API providers that report failures reliably, and a cache-only
request never calls a provider in cooldown. Book Dock's manual refetch stays live-only because
the dock has nowhere to report a fallback. Released iOS builds keep working: every new request
field is optional and newer clients gate it on the metadata-search-cache server feature.
Keep books and comic archives under the existing ebook request kind so searches, ownership checks,
format preferences and release profiles agree without changing the API contract. Cover the combined
formats and library access rules with unit and end-to-end tests.
Ref #1748
Share the saved upload limit with validation so changes apply without a page reload.
Seed new installations with a 4096 MB limit while preserving existing values.
Fixes#1733
Give the missing-format action a wand and subtle accent glow to make it
more inviting. Keep its status readable in narrow cards and honor reduced motion.
Add a Style setting beside mode, accent, radius and background. Nostalgia restyles the app through
tokens only (neutral surface palettes, system type, flat shell, top glow) and pins background,
opacity and brightness. Add a top glow reach slider for both styles. Tidy the theme layer first:
charts read live tokens, bare `rounded` follows the radius scale, one shared sign-in toolbar.
Also carries the settings page header redesign.
Lead fetches with the primary file, give Book Dock its library's rules and read-along media,
default audiobook fields to sources that carry them, and flag providers that never fill a field.
Joining a format now fills gaps only and respects locks; a move picks its primary as a scan would.
Make the field rules table one grid with each row as a subgrid, so the
merge and status columns size to the widest control instead of fixed
272px and 116px tracks. The four-option genres control and translated
labels now fit, and the global scope drops the empty status gap.
Link two ordinary requests as companions, filed into one book, with a Both search,
grouped list rows, a tabbed release picker and a book-page request for the missing
format. Retire comic requests, keeping old rows readable. File Book Dock covers by the
field rules with a shape check, and re-pick a moved book's primary by its new library.
Pick the face slot from the primary file's medium instead of the library's
ratio, and send that frame per book on cards, detail, notebook labels and
recommendations. Grids keep uniform tiles, and the library setting becomes
Grid shape.
Keep text and narration bookmarks independent when opening and navigating EPUBs.
Use the narrated section for sentence bookmarks and attribute delayed page moves to narration.
Recover stalled WebKit blob frames with a bounded fallback that preserves XHTML location structure.
Cover restoration, narration attribution, and iframe fallback behavior with regression tests.
Closes#1708
Return matching item counts with create and update responses so the sidebar
can display the saved scope count without requiring a page reload.
Reuse the listing count logic and verify visibility and shared sidebar state.
Carry ISBN-10 and ISBN-13 independently from accepted providers through
Auto-fill previews, saves and background refreshes. Preserve existing
identifiers and field locks, validate provider checksums and avoid
combining different editions.
Classify embedded ISBNs correctly and support ISBN-10 fallback in
search, Book Dock confidence and file naming.
Add account-scoped reminders for the metadata fields users care about.
Treat either ISBN as complete, accept a publication year or date, and
skip page-count reminders for audio-only books.
Place reminders after the genre blocklist and allow customization from
the editor without discarding unsaved changes.
Let users inspect missing fields without launching provider searches.
Keep metadata searches manual by default and expose a remembered
opt-in beside Search.
The walker sorted a book's files by file name, so tracks in disc folders that reuse names, such as
CD 1/01.mp3 and CD 2/01.mp3, interleaved in playback, merged chapters, and written track numbers.
Order them by their whole path instead, everywhere a track order is chosen.
A rescan that only reorders a book's existing tracks now moves every audio bookmark and the
listening progress percentage to the same moment of the same track, and rebuilds merged chapters.
Disc folders are never skipped by incremental scans, so existing libraries are fixed by their next
scan.
Also inventory the notebook and dashboard highlights routes in the authorization matrix, and pin the
app locale in two date tests that depended on the machine's locale.
Books often carry several files, such as an EPUB beside a PDF or an audiobook split into tracks.
Write-back reached only the primary file and the audio tracks, so the other formats kept stale
metadata.
Add an opt-in per-library mode that writes every content file of a book. Target selection,
capability reporting and the Files tab now share one role-aware selector, covers resolve per
medium, and results report per-format counts. Read-along EPUBs get their own toggle, off by
default, and their own size limit of 1000 MB, so they no longer follow the EPUB settings.
Live testing on copies of real libraries surfaced write-path bugs, fixed here as well:
- tag-only writes to chaptered M4B files failed because FFmpeg copied the chapter data track
- an undecodable cover failed every file instead of falling back to metadata only
- failure reasons stored the FFmpeg command line and absolute paths; they are now sanitized
- size limits used the stored size, so an unknown size bypassed them; writes now check the disk
- atomic replace dropped the file mode and ownership
- the EPUB temp name failed on long file names and could clash with a user file
- tracks in disc folders got duplicate titles
- a PDF worker that exited without a result hung its write forever; it now fails and times out
- null on a NOT NULL library setting returned 500 instead of 400
Generates migrations 0104 and 0105. Every new API field is optional, and released iOS builds
decode none of them.
Ref #694
Remove redundant destination choices when only one book library or folder is available.
Preserve existing Dock targets and cover loading, selection, and failure states with unit tests.
Closes#1362
Register the identity of delivered bytes so Kobo conversions and audio
removal continue matching their source books in KOReader. Keep download
registration recoverable without allowing database lock waits to stall it.
Preserve manual corrections across cached device identities, make review
semantics explicit, and keep cache invalidation bounded for large libraries.
Add unit, database, lifecycle and scale regressions with one generated migration.
Closes#1697
Convert complete source CFIs on the server to avoid chapter-start resets
and browser-generated locators that disagree with CREngine. Preserve
engine whitespace, Unicode offsets, element anchors and long text nodes.
Handle prefixed OPF namespaces and retry outdated annotation conversions.
Closes#1621Closes#1288
Keep dashboard layouts browser-local by default and let users opt in to account storage.
Group library selection and shelf sync in one dropdown, preserve legacy client settings,
and block sync changes for demo-restricted accounts.
Fixes#1646
The three-column layout left the editor 270px wide at 1280 and broke resolved paths mid-word.
Draw the pattern one folder per line, preview it against long-name books, and list only what
missing metadata changes. Add resolvePlaceholders to the shared resolver to report fallbacks.
Keep TypeScript at 6.0.3 while moving the remaining server and client packages onto their current supported releases.
Allow the PWA asset generator 2 peer range because vite-plugin-pwa still declares the compatible version 1 range.
Keep long request titles from widening the auto-layout table and hiding row actions. Guard the intrinsic-sizing chain with focused component regression tests.
Fixes#1613
Generate RFC 4122 UUIDs with getRandomValues when randomUUID is unavailable so plain HTTP deployments can save playback state and bookmarks.
Fixes#1608
Cut long paths from the start, name layouts and schedules the way the editor
does, and switch to cards when the ledger would scroll sideways. Rows now
toggle automation in place with undo, open recent scan history, show live
scan counts, flag failed or never-scanned libraries, and can seed a new
library from another's settings.
Bump every direct dependency to its newest same-major release, moving the
embedpdf patches and packageExtensions to 2.15.1 and deduping the lockfile.
Hold jsdom at 30.0 because 30.1 breaks vitest's URL.createObjectURL shim,
and skip releases still inside pnpm's minimum release age.