Commit Graph

829 Commits

Author SHA1 Message Date
neon c3abf6d9e1 fix(settings): add release tiers on plain-HTTP origins
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
2026-10-11 03:54:24 +00:00
neon cc50b55eeb feat(books): add read-aloud delivery policies and preserve sync
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 #1496
Closes #1603
Closes #1714
Fixes #1723
Ref #1633
Ref #1508
Ref #1621
Ref #1136
2026-10-11 03:11:27 +00:00
neon 7bb51a7a0b feat(metadata): cache metadata provider answers
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.
2026-10-10 22:26:26 +00:00
neon c91648eaac feat(books): include comics in non-audio requests
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
2026-10-10 21:47:09 +00:00
neon e6c0275fdf fix(settings): refresh upload limits and default to 4 GB
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
2026-10-10 17:30:22 +00:00
neon 3c6dd48b5b style(books): add enchanted request button effects
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.
2026-10-10 17:17:12 +00:00
neon af8f55976f feat(client): add the Nostalgia appearance style
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.
2026-10-09 01:01:57 +00:00
neon c63f3ab757 fix(metadata): choose sources by medium and stop overwriting book data
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.
2026-10-08 18:12:48 +00:00
neon 21b68685ad fix(settings): stop truncating the genres merge strategy control
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.
2026-10-08 17:55:12 +00:00
neon 39ac2f1efd feat: request an ebook and audiobook as a pair
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.
2026-10-08 16:12:30 +00:00
neon 01b5638ea7 feat(cover): show each book's cover in the shape of its primary format
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.
2026-10-08 06:19:00 +00:00
neon bd141802bd fix(reader): preserve reading progress across narration resume
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
2026-10-07 21:39:38 +00:00
neon 5c093333c5 fix(smart-scope): refresh sidebar counts after saving
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.
2026-10-07 20:12:54 +00:00
neon d719c4ee5b fix(metadata): preserve ISBN fields across metadata workflows
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.
2026-10-07 20:07:23 +00:00
neon 6c7d91eaac feat(metadata): add configurable missing-field reminders
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.
2026-10-07 20:06:50 +00:00
neon 8fef9d1633 feat(metadata): add empty-field details and optional auto-search
Let users inspect missing fields without launching provider searches.
Keep metadata searches manual by default and expose a remembered
opt-in beside Search.
2026-10-07 18:38:07 +00:00
neon 3ef4a22c37 fix(scanner): play disc-folder audiobook tracks disc by disc
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.
2026-10-07 18:31:33 +00:00
neon 100953ea1b feat(metadata): write metadata to every file of a multi-file book
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
2026-10-07 16:52:52 +00:00
neon e767358443 feat(library): auto-select sole upload destinations
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
2026-10-07 04:56:44 +00:00
neon 58756565fc fix(kobo): preserve koreader matching for converted downloads
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
2026-10-07 00:07:50 +00:00
neon 1adab58d4b i18n(client): sync translations from Crowdin (#1677)
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-06 11:23:11 -06:00
neon e253f7ffca fix(reader): preserve exact positions when syncing with koreader (#1694)
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 #1621
Closes #1288
2026-10-06 11:22:55 -06:00
neon 60fcbd9410 i18n(client): sync translations from Crowdin (#1632)
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-03 10:04:18 -06:00
neon cb8d1e3b2b fix(settings): add optional dashboard shelf sync
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
2026-10-02 04:11:47 +00:00
neon 4701412ef2 feat(settings): redesign file naming as a two-column workbench
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.
2026-09-30 22:12:02 -06:00
neon 2855dbb8a3 i18n(client): sync translations from Crowdin (#1611)
Keep sparse translated catalogs synchronized with reviewed Crowdin content.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-30 22:11:38 -06:00
neon 05a55d6b62 build(deps): upgrade non-TypeScript dependencies
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.
2026-09-30 21:37:55 +00:00
neon ef2bcced3b fix(client): constrain request title column width
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
2026-09-30 20:04:59 +00:00
neon 85927fb118 fix(reader): keep oversized comic pages within scroll bounds
Use safe flex alignment so pages stay centered when they fit and fall back to the scroll origin when they overflow.

Closes #1610
2026-09-30 19:35:10 +00:00
neon 951f3b702d fix(reader): persist audiobook progress on insecure origins
Generate RFC 4122 UUIDs with getRandomValues when randomUUID is unavailable so plain HTTP deployments can save playback state and bookmarks.

Fixes #1608
2026-09-30 19:31:05 +00:00
neon de0954e77f feat(settings): polish the libraries page and add in-place controls
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.
2026-09-30 06:53:37 +00:00
neon 09e4f1b5bd chore(deps): update minor and patch dependencies
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.
2026-09-30 00:57:24 +00:00
neon c0759ea1bf i18n(client): sync translations from Crowdin (#1574)
Keep sparse translated catalogs synchronized with reviewed Crowdin content.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-29 18:22:14 -06:00
neon 6f673e2e59 feat(metadata): merge search and compare into one match sheet
Search and Compare Metadata become one two-pane sheet, shared with the Book Dock. Results are
ranked by match strength beside a field-by-field comparison with Keep/Use per row, word-level
diffs, other sources inline, a cover strip, and picks that survive switching results.
The search runs on open and a single source can be retried. No API change.
2026-09-29 04:53:34 +00:00
neon 1dd389563c style(client): unify modal backdrops with dedicated scrim tokens
Replace tinted modal veils with dedicated scrim tokens so dialogs, sheets, and image lightboxes dim consistently in both light and dark themes. Enforce scrim token usage in style validation to prevent backdrops that wash out in dark mode.
2026-09-29 03:42:01 +00:00
neon e92cc36146 feat(library): rebuild the library editor as a settings sheet
Replace the seven-step wizard with a sheet whose section rail summarizes each
setting. Format priority shows only the formats a library holds, folders are
checked as they are added, and people can be granted access while creating.
2026-09-29 02:11:31 +00:00
neon d66f853cad feat(settings): add read-aloud filename token
Expose media-overlay-aware naming across file creation and delivery paths while preserving existing client contracts. Keep settings router mocks compatible with the application router exports.

Closes #1590
2026-09-29 01:29:25 +00:00
neon a572badecb fix(client): persist visible Book Dock destination on done
Save the library and folder shown in the metadata sheet before closing it. Avoid duplicate target updates after explicit selection and keep the sheet open when persistence fails.

Fixes #1573
2026-09-29 00:14:15 +00:00
neon 0085ffd11e fix(series): show mixed-media books in every group
Classify series entries from every available format so combined editions appear in each relevant section.

Fixes #1566
2026-09-29 00:00:13 +00:00
neon a35e222cb2 feat(types): expose the book file id on audiobook manifest assets
Clients that download an audiobook get its files from the book detail and its playback order from
the manifest, and had to pair the two by position. The two lists are ordered differently, so an
index pairing can attach the wrong file to an asset. Add the book_files id to each manifest asset
so the pairing can be made by id.

The field is additive and the server always sends it. The manifest revision is hashed from file
and chapter data, not from the response, so existing revisions do not change and clients holding
one do not see a 412 from this deploy.

The iOS app consumes this field to pair downloaded files with manifest assets; the web client
does not read it yet, so a grep of the client alone will show it as unused.
2026-09-28 22:38:47 +00:00
neon c76d9c01f9 fix(reader): stop web audiobook progress retrying rejected writes
Every non-OK playback-state response marked the write dirty and retried it every five seconds,
so a stale manifest (412) or a position past the rounded track duration (400) looped for the rest
of the session. Each attempt also minted a new operationId, which defeated the server's replay
protection, and capturedAt was stamped at flush time rather than when the position was taken.

Capture the timestamp and one operationId with each position and reuse them across retries.
Retry only network failures, 5xx and 429, and drop other rejected writes. On 409, reload the
server state and keep the local write only if it is newer. On 412, hold the write and have the
player reload the manifest, resending once the revision changes. Clamp positions to the track
duration from the manifest.
2026-09-28 22:23:06 +00:00
neon d8f68a0d38 fix(stats): record web audiobook sessions as listening
The web audiobook player shared the reading session composable, which never sent a
sessionType, so the server stored every listening session as a read session and counted
audiobook time as reading in activity stats. Let callers pass an optional sessionType and
have the audiobook view send listen. Other readers keep omitting the field, so their
payloads are unchanged. Existing rows are not backfilled.
2026-09-28 22:16:52 +00:00
neon 55110d5b44 feat(settings): add per-user Requests visibility toggle
Let users hide request entry points and notifications while existing requests continue running. Keep the setting optional so older clients retain their current behavior.

Ref #1476
2026-09-26 18:46:10 +00:00
neon 8dfb093301 feat(library): support fractional finished thresholds
Allow 0.05% adjustments while preserving existing integer settings and the 98% default. Keep finish detection stable when progress is stored as PostgreSQL real.
2026-09-26 18:22:02 +00:00
neon 244924a3aa fix(library): display reading start threshold accurately
Render the configured threshold in percentage points so library details agree with the editor for fractional values.

Fixes #1563
2026-09-26 18:04:25 +00:00
neon 2d9af5170b i18n(client): sync translations from Crowdin (#1565)
Keep sparse translated catalogs synchronized with reviewed Crowdin content.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-26 11:59:15 -06:00
S Heckler bc12143015 fix(auth): recover from expired edge auth proxy sessions (#1557)
* fix(auth): recover from expired edge auth proxy sessions

The service worker answered every page load from the precached app shell, so once an edge auth
proxy's session cookie (e.g. Cloudflare Access, OAuth2 Proxy) expired, the proxy never saw the
navigation and could not redirect to its login page. The app loaded from cache, its API calls were
redirected cross-origin and failed, and it fell back to its own login screen with no way out short
of clearing site data.

Serve navigations network-first and fall back to the precached shell only when offline.
vite-plugin-pwa defaults navigateFallback to index.html, which would register a cache-only route
ahead of this one, so disable it explicitly. Cache only 200 responses so a proxy's redirect is never
stored as the page.

Request API redirects manually so a proxy redirect is detected directly and triggers a full reload,
which the proxy can then send to its login page. Cap these reloads to one per 15 seconds so a reload
answered from cache again surfaces an error instead of looping.

Fixes #642

* fix(auth): harden edge auth proxy recovery against loops and cache growth

Force manual redirects after caller options so no request can opt back into following a proxy
redirect. Skip the reload when session storage is unavailable, since the loop guard cannot work
without it. Drop the navigation network timeout so a slow proxy redirect is never replaced by the
cached page, and cap the app shell cache, which stores one entry per visited URL.

Ref #642

* test(auth): isolate useLoginOptions module state between tests

The composable keeps its cached options, error, and in-flight request at module level, so they
carried over between tests in the file. Reset the module and import it fresh for each test so a
future test cannot pass or fail depending on what ran before it.

Ref #642

* fix(auth): let edge proxy handle sign-in navigations

Keep the offline shell under a distinct precache URL so Workbox cannot serve
root navigations before the proxy sees them. Route public auth requests through
the redirect-aware fetch path so expired edge sessions open reauthentication.

Ref #642

* fix(auth): keep offline shell off root URLs

Prevent Workbox from mapping root query variants to the precached offline shell so expired proxy sessions reach the network.

---------

Co-authored-by: neon <18559658+neonsolstice@users.noreply.github.com>
2026-09-26 08:55:42 -06:00
neon 6381f0221f fix(library): honor browser clock for cron schedules
Use the browser's hour cycle when its language matches the selected interface language, preserving regional time preferences.

Closes #1559
2026-09-26 13:41:33 +00:00
neon a52d005ff5 fix(reader): recover audiobook playback after media auth failure
Refresh the web access cookie after a media load failure and resume from the last observed position. Limit retries so unrelated media errors surface promptly.

Closes #1536
2026-09-26 05:52:28 +00:00
neon 5a108bbd5e fix(client): paginate Book Dock navigation
Add a reusable pagination control and cover Book Dock navigation with focused tests. Align the client PDF dependency and lockfile for clean CI verification.
2026-09-26 01:17:55 +00:00