The KOReader plugin sends md5(password) as x-auth-key, which can never match the bcrypt hash of the
plaintext password. The auth guard ran a cost-12 bcrypt compare first, so every authenticated
request spent about 250 ms of CPU and stalled other traffic before the md5 fallback accepted it.
Check md5 first and keep bcrypt as the fallback for clients that send the plaintext password, so the
accepted credentials are unchanged. A plugin auth call drops from about 285 ms to 8 ms.
Bump the KOReader plugin to 2.2.0. The plugin changed after the 2.1.0 release shipped in v3.6.0
(series sorting, series selection, file browser exit, paged Shelves list) without a version bump.
Also fix three metadata-fetch tests that were failing on main since cfcd4ca52 kept single-digit
tokens in tokenizeTitleText. A volume digit in the query diluted the word overlap in scoreTitleMatch,
so an ebook candidate fell under the relevance floor and a manga outranked it. scoreTitleMatch now
drops lone digits again; symmetricTitleSimilarity keeps them to tell volumes apart.
Also align the pnpm version installed in both Dockerfiles with the packageManager field. Since
f2a7a1118 the images installed pnpm 11.22.0 while package.json pinned 11.28.9, so pnpm tried to
switch itself and failed the arm64 image build with a missing @pnpm/exe lockfile entry.
Closes#1983
Ref #2037
A Kobo sends Host without a port, and Tailscale Funnel repeats that Host in X-Forwarded-Host
without adding X-Forwarded-Port. buildBaseUrl then skipped APP_URL because forwarded headers were
present, so cover, reading state and download links lost the public port and pointed at port 443.
The Kobo never reached the server for a download or a thumbnail, and the delivery stayed offered.
Use APP_URL's origin whenever no port is given and its host matches, and, when the proxy names a
scheme, only if the scheme matches too. Explicit ports, X-Forwarded-Port and hosts or schemes
APP_URL does not share keep their previous behaviour.
Fixes#2036
Normalize stored zero values at the shared write payload boundary so every format omits an unknown page count. Align collection repository tests with the query shape left after podcast removal.
Closes#2040
Compare release and imported metadata against the requested series position so unattended fulfillment cannot file a later volume under the request. Keep unnumbered candidates eligible and leave exact ISBN matches decisive.
Fixes#2037
Compare extensionless archive entry paths first so a cover named IMG.jpg precedes page names that extend IMG with a suffix. Keep cover extraction and CBZ/CBR page delivery aligned, and invalidate old page ETags.
Closes#2035
An EPUB with Media Overlays already carries its narration, yet the Chapters tab required audio files and the Editions panel offered to request an audiobook the book already had.
List the narration sections of such an EPUB as a read-only chapter list, and count the narration as the audio half when deciding which format is missing. The chapters state gains an optional readAlong flag, so released clients keep working. Editing stays tied to audio files, because an EPUB has no chapter metadata to write.
Ref #2032
Podcasts shipped switched off in every release and are now removed from the server,
web client and shared types. Migration 0132 drops the podcast tables and the media-type,
library-type and folder-role columns. Released iOS and Android builds read the removed
response fields as optional, so the API change stays compatible.
Achievement names, descriptions and category labels, the secret placeholder and the monthly
challenge are text the server writes and every client shows as is, so they stayed English in a
translated web page and will in the localized iOS app. The routes that return them now read
Accept-Language and answer in that language, with Vary: Accept-Language.
The translations live in the web's catalogs under serverText, so Crowdin, its checks and its
translators handle them through the existing pipeline; the server reads that subtree, and the image
copies the catalogs beside dist. English is never looked up: a request without the header, and
every client that predates it, gets the bytes it got before. A test keeps serverText in en.json
equal to the seed and the constants it mirrors. The web now sends its chosen language on every API
request, and the Accept-Language matcher moved into @bookorbit/types so the client and the server
negotiate the same way.
The iOS app is a consumer the web client does not show: it will send its own interface language.
App Store builds that predate that still send the device language, so on a non-English device they
will start getting translated achievement names inside English screens. That was accepted; the
response shape does not change.
Ref bookorbit/bookorbit-ios-support#79
Send logical volume counts on each series summary, tallied from the member
grouping the ladder already builds, and read them on the cards instead of
counting the 60-slot ladder. Long series no longer stop at 60 or show as
complete after their first 60 volumes are read.
Closes#1987
Upgrade fixable Node dependencies and rebuild the pinned kepubify binaries with Go 1.27.3 so multi-architecture image publication passes the security gate.
Report a linked copy whose bytes differ from the library file as positionIncompatible instead of
unmatched, so the plugin keeps the match, skips highlights without a failure and still syncs progress.
Treat a position-less place within a page as the same place and jump to the nearest page.
Suggest library books with the same title and authors for books to review, with bulk linking.
See docs/KOREADER_HASH_MATCHING.md.
Closes#1872Closes#1919
Plugins from 1.7.0 request a delivery variant, and every such download had
" - bo-<id>-<variant>" added to its name. Only copies that would share a path are
renamed now: a narrated EPUB's text-only copy and later same-format files. A new
<a|b|c> chained default also stops the default pattern naming authorless
standalone books "Standalone/ - <title>". See docs/READ_ALOUD_DELIVERY.md.
Closes#1893
Offer the one-folder-per-series layout Komga and Kavita use as a File as Book path-rule preset
rather than a new organization mode, so released iOS builds and both existing modes are unaffected.
Add a job that applies saved rules to books already in a library, and keep a run year such as
"Batman (2016)" when ComicInfo or a provider names the series without it.
Ref #1167
Ref #1918
Ref #839
Pick folders from a lazily loaded tree with find-by-name and typed paths, beside a plan showing
the books each organization mode would import, overlaps with other libraries, and folders no
library reads yet. New ManageLibraries /path routes walk folders within entry and time bounds.
Fix the scanner filing a disc-named library folder such as /mnt/disk1 under the folder above it,
and move books already filed there onto their primary file on the next scan.
Promote Viewer grants to Editor on book libraries for non-superusers who hold Edit metadata but
can edit nowhere, which is how every such account was left after the upgrade. Accounts with any
Editor or Owner role are left alone. The Users drawer now says where Edit metadata takes effect,
read from a new GET /users/:id/library-access route.
Closes#1902
Register book-metadata-provider services by address, with no code change per
provider; Settings lists ready-made ones to install. Ranking prefers a record of
the book's own medium. LubimyCzytac moves out to its own provider and Aladin,
whose API closes, is retired; their ids stay. Adds migrations 0129 and 0130.
See docs/METADATA_PROVIDERS.md.
Ref #1405
Add GET and PUT /reading-progress and DELETE /reading-progress/:fileId: positions grouped by
kind, up to 25 files saved in one request, and an optional editedAt so the server keeps the newer
of two saves. Move the web reader and book page to them, and list reading-progress in
SERVER_FEATURES for the iOS and Android apps, which adopt the routes in the same release.
A text-only download of an EPUB with no narration returned 400 when the
strict inspection tripped on a structural fault such as duplicate manifest
ids, a missing resource or a chapter over 16 MiB. Serve such books as stored,
and log why inspection failed or why a text-only delivery was refused.
Closes#1888
Add a History page listing every read newest first, rereads included, as a
virtualized ledger or month cover walls, with period totals, per-read ratings,
export and a share image. Deleting a book keeps its finished reads under a
title and author snapshot (migration 0128).
Closes#1630
Add a Chapters tab to edit chapter timing and titles, look chapters up on Audible or in the ebook,
snap starts to pauses, keep a version history and write chapters into M4B, M4A and MP3 files. Add a
chapter health tool with a bulk Audible lookup queue. Editing needs Editor access to the book's
library. Chapter edits keep released iOS playback working by accepting a stale manifest revision
while the track layout is unchanged.
Ref #1120
Resolve book languages stated as names ("English") to a code, so the
dialog and the server's fallback stop calling English unsupported. The
dialog groups the ebook and audiobook in a Sources card, shows
Highlighting without the Advanced disclosure, and saves a per-user
default under /user-preferences/read-aloud for books it cannot match.
Show one notice above the release picker naming the client, with a
Try again that resends the same release, instead of a chip on every
row. Grab failures carry the client name in errorMeta, and transport
errors read "connection refused" rather than "fetch failed".
Generate a synced read-aloud EPUB from a book's EPUB and audiobook through an
optional STAlign worker (Docker on CPU, or native with MLX on Apple Silicon),
with review, re-sync, replace in place, restart recovery and live progress on
the Files tab. Book details also stack the ebook and audiobook covers when
both exist. See docs/READ_ALOUD.md.
Extend KOReader's cached menu order instead of persisting complete
override lists. Remove marker-owned legacy overrides during upgrade
while preserving hand-maintained entries.
Closes#1878
ISBNs copied from Amazon carry hyphens, and copied ASINs often start with a zero-width space.
The edit tab cut a hyphenated ISBN at its maxlength, Book Dock sent it on for the server to
reject, and an invisible character left an ID that looked right but never matched.
Clean ISBN, ASIN and provider id input as it is typed or pasted, and apply the same shared
rule in the book metadata and Book Dock DTOs so every client, including released iOS builds,
gets it. Provider ids keep their punctuation because Google ids and slugs use it, and an
over-long value is kept whole so validation still names the field.
Closes#1624
Allow users to build bounded historical windows without hard-coded calendar dates. Keep the API additive and advertise support for independently released clients.
Closes#1751
Preserve explicit and per-user provider precedence while allowing multi-user installations to use a configured shared default deterministically.
Closes#1387
Let users edit author facts, choose and pin a provider record, and keep later refreshes tied to that record. Preserve compatibility for older clients through additive API fields and a discoverable server feature.
Keep server typechecking deterministic by breaking circular Drizzle inference and correcting the nullability mismatches it previously hid.
Closes#1464
Allow request searches to browse broader provider result sets, rerun queries by exact author, and submit unmatched titles for approval. Advertise the optional limit so older clients retain their existing contract.
Closes#1363
Convert started_at to the user's configured time zone before taking the
hour, minute and weekday, and include the zone in the cache key so a
changed setting is picked up.
Closes#716
Add an optional forget(indexerId) to the plugin contract and forward the
host's existing invalidation to it, so a plugin holding a live session
drops it when the credential, base URL or private-address setting changes.
A plugin that throws or rejects in forget is logged and never fails the save.
Fixes#1325
Add a per-login setting, on by default, that leaves books whose content files are all audio out
of the plugin catalog, its counts and bulk downloads, and drops a library holding nothing else.
The plugin detail page also names the first file the device can open as the main file.
Closes#1345
Torznab and Newznab read the feed's details page, plugins may send infoUrl, and the
search drops any link that could carry a credential. A grab can ask to be held for
review whatever its score, and the held review shows the file's own edition facts,
its cover and the release page. Torznab guids are now digested like Newznab's.
Refs #1438
With series collapsed, the series sort used the same key as title, so stacks
and standalone books interleaved alphabetically. Order stacks by series name
first, then standalones by title, in both directions. The jump rail now only
letters series, matching the flat view.
Fixes#1456
Stamp reading_progress.last_read_at with the device's reading time instead of the
server's write time. A sweep pushes a whole shelf at once, so sorting by last read
came out in processing order. Keep the later of the stored and pushed times, so a
late or offline push never sinks a newer read from another client.
Fixes#1433
Offer a series' next volume only when its read status is unset, unread or want to read, on the
shelf and in the is up next filter. Abandoned, on-hold and in-progress volumes at 0% no longer
show, and an abandoned volume ends its series there instead of surfacing the one after it.
Fixes#1649
Hardcover's books_count included novellas, split editions, box sets and announced books, so
series named books as missing that are not out yet. Count only released main books, clear the
inflated Hardcover totals once on boot, and stop a series row saved without a total from wiping
that series' total.
Fixes#1861
Continue Listening listed only books with audiobook progress, so a book
whose only audio is a read-along EPUB never appeared there, on the web,
on the phone or in the car, however much of its narration was heard.
The shelf now also takes read-along EPUBs whose narration progress was
saved by playing it, which is when its seconds are set. Reading on any
device carries the narration marker along and clears the seconds, so a
book only read stays in Continue Reading. A book with both an audiobook
and narration under way is listed once, at its latest listening.
The web and the iOS Home shelf show these as ordinary book cards. iOS
CarPlay builds before this change leave them out of Continue, which
there lists only audiobooks; the current build plays their narration.
Ref #1847
Squash of feat/koreader-widget-board. KOReader plugin 2.0.0 gets the widget-board dashboard, redesigned
book details, menus, lists and dialogs, Decor page and reading companions. Highlights, notes and bookmarks
now stay in step across web, Kobo, KOReader and the phones (defects D-00 to D-20, migration 0121), and
reading progress and narration hand off cleanly between devices.
A Kobo sends Host without a port, so cover, reading state and download
links got the port the server listens on. Behind a published mapping
such as 3010:3000 that is the internal port, which the Kobo cannot
reach. Use APP_URL's origin when it names the same host; other hosts,
explicit ports and forwarded headers behave as before.
Fixes#1281
kepubify looks a manifest href up exactly as written, so a chapter stored
as "chapter one.xhtml" but listed as "chapter%20one.xhtml" shipped with no
KoboSpans and every synced position snapped back to the chapter start.
Convert a copy with those hrefs decoded and restore the book's spelling
afterwards; only affected books get a new cache key. Position conversion
now matches and loads such chapters too.
Fixes#1136
Kobos on firmware 4.45 were reported failing every sync on BookOrbit's
prefixed PX tokens and passing on Calibre-Web's. Send Calibre-Web's base64
JSON token, or Kobo's own cursor verbatim when store titles carry one. The
token never located the snapshot, and PX and PX2 tokens still decode.
Fixes#1175Fixes#435
A Kobo downloads a cover only for a new entitlement, so a changed cover
sent as changed metadata left the book with a text-only cover until the
home screen fetched a small tile. Record the cover id on each device
snapshot row and re-announce the book when it moves; rows from before
the change learn it silently. Serve tile-size requests at a full-screen
size so the sleep screen never stretches a tile.