Commit Graph

1389 Commits

Author SHA1 Message Date
neon 8810d47658 feat(books): remember a default read-aloud narration language
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.
2026-10-22 00:42:06 +00:00
neon 0320210767 fix: say which download client is not responding
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".
2026-10-22 00:05:10 +00:00
neon 992224f2e8 feat(books): add read-aloud generation
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.
2026-10-21 23:44:37 +00:00
neon 99aa7dce70 fix(reader): preserve third-party KOReader menu entries
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
2026-10-21 19:51:36 +00:00
neon 8f289c295a fix(series): load complete KOReader series lists
Fetch every bounded API page before applying local pagination so large series remain complete in KOReader and available offline.

Closes #1883
2026-10-21 19:33:26 +00:00
neon d31bf392f4 fix(reader): discard unmatched lifecycle entries
Treat an unmatched lifecycle response as terminal so built-in help documents do not retry until parked. Record the skipped title in sync activity and clear the stale parked warning.

Closes #1886
2026-10-21 19:17:38 +00:00
neon 9ba01498cf fix(reader): enable D-pad dashboard setup
Expose each starter preset and the Start button as separate focus targets so non-touch KOReader devices can complete first-run setup.

Closes #1880
2026-10-21 19:05:02 +00:00
neon 9d1319ef8b fix(reader): preserve KOReader covers in night mode
Counter-invert custom cover pixels before KOReader applies its screen-wide night mode so artwork retains its original colors.

Closes #1885
2026-10-21 18:51:54 +00:00
neon fbd7a2e125 feat(metadata): clean pasted identifiers instead of truncating them
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
2026-10-21 05:47:03 +00:00
neon edc5e41105 feat(books): filter read-along titles
Expose read-along EPUBs as an additive format filter while preserving ordinary EPUB matching and existing client compatibility.

Closes #1583
2026-10-21 05:18:37 +00:00
neon d1cc18dc51 feat(smart-scope): add rolling older-than date filters
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
2026-10-21 03:59:41 +00:00
neon 95fa2c77fb docs: refresh KOReader plugin preview 2026-10-20 18:39:25 -06:00
neon c59dd0118f i18n(client): sync translations from Crowdin (#1844)
Keep sparse translated catalogs synchronized with reviewed Crowdin content.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
v3.5.0
2026-10-20 17:07:14 -06:00
neon cecbdc44ce security(docker): require patched libexpat version
Prevent cached image layers from retaining the vulnerable Alpine package.
2026-10-20 22:33:08 +00:00
neon a41d3da63e fix(email): use shared default provider as fallback
Preserve explicit and per-user provider precedence while allowing multi-user installations to use a configured shared default deterministically.

Closes #1387
2026-10-20 16:05:33 -06:00
neon 2b8917afb6 feat(authors): allow correcting metadata matches
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
2026-10-20 16:05:32 -06:00
neon 0d695a7ea0 feat(metadata): expand book request discovery
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
2026-10-20 16:05:32 -06:00
neon 2be79af880 fix(stats): read session archetype hours in the user's time zone
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
2026-10-20 16:05:32 -06:00
neon cc6bac696b fix(book-requests): tell a plugin indexer when its connection changes
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
2026-10-20 16:05:32 -06:00
neon b95a285c1b feat(koreader): hide audiobook-only books from the KOReader plugin
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
2026-10-20 16:05:32 -06:00
neon a728bf293b feat(book-requests): link releases to their source page and hold imports for review
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
2026-10-20 16:05:31 -06:00
neon 3c87418416 feat(client): let users hide and reorder sidebar rows and groups
Add a Customize sidebar mode, opened from any section menu or Cmd+K, that hides rows and groups
and reorders them by drag or keyboard, with reset and undo. The layout saves to a new
sidebarLayout setting; a hidden Book Dock or Requests row returns while work is waiting.
Also line up the Rows shown menu labels, which sat 3px apart.

Closes #1434
2026-10-20 16:05:31 -06:00
neon 568614265f fix(books): list series stacks before standalones when sorting by series
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
2026-10-20 16:05:31 -06:00
neon 192c205299 fix(email): pick the only recipient when none is starred
Creating a recipient never makes it the default, so a single-Kindle setup
opened the send sheet with nobody ticked and Send disabled. Tick the lone
recipient in that case; a starred default still wins.

Ref #1443
2026-10-20 16:05:30 -06:00
neon 2e454cc846 fix(koreader): rank synced books by when the device read them
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
2026-10-20 16:05:30 -06:00
neon 98415b90cc fix(dashboard): keep abandoned books out of up next in series
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
2026-10-20 16:05:30 -06:00
neon 8762c4e628 fix(book-requests): show the book's cover on a request made from its page
Asking for a book's missing format sends no cover, so the request list, drawer and release
picker drew a blank placeholder. Fall back to the cover of the book the request joins or
became, in the requested format's slot when it has one. The request's own art still wins.
2026-10-20 16:05:30 -06:00
neon 435a60288d fix(books): show read-aloud sync as a compact pill only when it applies
Show the sync control only for a read-along EPUB with audio files or another
EPUB copy, so lone read-along EPUBs and plain audiobooks no longer show an
Unavailable card. Fold the card into a pill in the format row whose two-track
meter shows the state, with the details and toggle in a popover. Your Reading
now lists three recent sessions.

Fixes #1845
2026-10-20 16:05:29 -06:00
neon 58f258a5fc fix(reader): reopen a PDF at its saved page instead of the first
The PDF reader restored the saved page with a smooth scroll, which Chrome
starts about a second after it is asked. With Fit page, Fit width or
Automatic zoom, the first viewport resize refits the page from the
current offset with an instant scroll, and a classic scrollbar appearing
as the document loads is such a resize. The refit cancelled the restore
before it moved, so the reader stayed on page 1 while the toolbar showed
the saved page, and the next scroll saved page 1 or 2 over the position.

Restore the opening page with an instant scroll, so the refit starts from
the saved page and keeps it. Page turns still scroll smoothly.

Fixes #1857
2026-10-20 16:05:29 -06:00
neon a5c4dfac16 fix(series): stop counting unreleased and extra books as missing
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
2026-10-20 16:05:29 -06:00
neon 1214152bd5 fix(series): group books by media from content files only
Count only primary and content files when profiling a book's media, so a
side image, cover or .nfo in an audiobook folder no longer adds it to the
Books group on the series page.

Fixes #1862
2026-10-20 16:05:29 -06:00
neon 5878c96254 fix(books): fit series card covers to the grid instead of cropping them
First volume, Latest volume and First unread series cards drew their cover
with a cropping img, so a square audiobook cover in a 2:3 grid lost a third
of its width. They now render through BookCoverArtwork like single tiles and
follow the cover display setting, while the grid keeps one tile shape.

Fixes #1863
2026-10-20 16:05:29 -06:00
neon e0ce6d3348 chore(annotations): add KOReader highlight drawer
Keep long dashboard highlights readable while preserving quick access to book details.
2026-10-20 16:05:28 -06:00
neon c01022aeb2 chore(client): default new users to Nostalgia and teal
Give users without stored appearance choices the Nostalgia style and teal accent while preserving saved selections and BookOrbit styling on standalone pages.
2026-10-20 16:05:28 -06:00
neon 1a2ffb68c0 fix(koreader): offer a further place before a fresh copy pushes its own
A device that had not weighed its open book against BookOrbit since
opening dated its place by its last page turn. One turn from a fresh
download's cover made another device's further place look older, so
Sync now pushed the cover over it without asking, and the phone then
reopened the book at its title page.

Until the open book's place has been compared with BookOrbit's (a pull
or push went through, or Jump or Stay was answered), a further BookOrbit
place now also counts as newer, so Sync now and pulls offer Jump instead
of overwriting it. Once compared, page-turn times decide as before, and a
reset stamped after the last page turn still prompts.

Ref #1806
2026-10-20 16:05:28 -06:00
neon 6440281e65 fix(books): count played read-along narration in continue listening
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
2026-10-20 16:05:28 -06:00
neon 80244ea4e2 feat(koreader): rebuild the plugin interface and fix four-way annotation sync
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.
2026-10-20 16:05:27 -06:00
neon 7202a59afc fix(auth): wait for an unreachable server instead of signing out
A page load during a server restart read the failed session refresh as
signed out and stranded the reader on /login. Boot now holds the first
navigation behind a reconnect screen and retries until the server
answers, and api() keeps the session when a refresh gets a 5xx or no
connection.
2026-10-20 16:05:27 -06:00
neon ce118e3890 i18n(client): sync translations from Crowdin (#1843)
Keep sparse translated catalogs synchronized with reviewed Crowdin content.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-16 23:47:46 -06:00
neon c1b4627e0d fix(kobo): take the Kobo's port from APP_URL when Host omits it
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
2026-10-17 05:19:20 +00:00
neon 7f172fd8fb fix(kobo): convert chapters listed by percent-encoded hrefs
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
2026-10-17 05:01:43 +00:00
neon 06cfc2a074 fix(kobo): hand the Kobo a sync token in a shape it recognises
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 #1175
Fixes #435
2026-10-17 04:23:23 +00:00
neon edfa20e19f fix(kobo): deliver a changed cover to a Kobo that already has the book
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.
2026-10-17 04:03:34 +00:00
neon 2dca4bc85c fix(kobo): send Kobo covers at the device's screen size
A Kobo asks for each cover at its screen size but always got the
400x600 grid thumbnail, so sleep screens and full-screen covers were
blurry. Scale the full cover to fit the requested box instead, behind a
new Full-size covers setting that is on by default. Drop the stale
UserStorage entries from the authorization matrix gap baseline.

Fixes #924
2026-10-17 03:34:18 +00:00
neon 51b53d4581 fix(kobo): relay Kobo notebooks to Kobo when store titles are on
A Kobo syncs its notebooks through Kobo's reading services, which
BookOrbit answered with an empty listing, so notebooks never synced.
When Include Kobo store titles is on, relay every UserStorage call to
readingservices.kobo.com; otherwise keep the empty listing.

Fixes #1787
2026-10-17 03:05:40 +00:00
neon b189bf2edd fix(reading): file late Kobo sessions into the read they finished
When a Kobo syncs after finishing a book, its finished state closes the
reading attempt before the offline sessions arrive, and those sessions
opened a second attempt, marking the book as reread or read twice.
Activity dated within the finished read now joins it, activity from
before it began joins nothing, and an undated start never widens the
range. Update the reading-attempts E2E Kobo test to use a real device
that holds the book.

Fixes #1224
Fixes #1182
2026-10-17 02:55:13 +00:00
neon 058cd6e7e4 fix(kobo): sync Kobo bookmarks as bookmarks and keep annotation sync converging
A Kobo dogear was stored as an empty yellow highlight. Land it as a
BookOrbit bookmark linked to the device's id, serve it back as a dogear,
sync deletions both ways, and move dogears already stored as highlights.
Report only books on that Kobo from checkforchanges: a re-registered
device was told about books it never had, so real changes were fetched
outside its sync and never applied. Serve a known annotation at its last
location instead of failing the whole pull when this copy cannot place it.

Fixes #1374
2026-10-17 02:42:42 +00:00
neon 5397825003 fix(kobo): open a newly downloaded book at its saved position
A book's first announcement went to the Kobo before it was downloaded,
when no Kobo location could be computed, so it carried a bare percentage
and the book opened at its start. Compute the location from the copy
being offered for bookmarks sent to the device; positions it reports
still wait for the transfer.
2026-10-17 01:20:03 +00:00
neon ff8759b9a0 fix(kobo): keep a Kobo's progress on the file its copy was built from
When a book's primary file changed, the Kobo kept the copy built from the
old file but its positions no longer converted, so it was sent a bare
percentage, reset to the start, and that reset was saved over real
progress. Sync each device against its copy's source file instead, which
BookOrbit's sibling sync carries to the other EPUBs, and serve the new
primary when the device downloads the book again.

Fixes #1786
2026-10-17 01:02:57 +00:00
neon 60ad70ff5c fix(kobo): trust a confirmed replacement for positions and annotations
A replacement stayed ambiguous after the user confirmed it, so every
position the Kobo sent was discarded and annotation pulls failed with 409.
Confirming now makes it the device's copy and moves the stored bookmark
onto it. It still cannot drag saved progress backwards, and that is
judged by position, since its percentages run on a different scale.

Ref #1786
2026-10-17 00:45:21 +00:00