Commit Graph

689 Commits

Author SHA1 Message Date
neon c48c4a0dad fix(metadata): persist Hardcover edition ID during automatic refresh
During an automatic metadata refresh, the fetched Hardcover edition ID was not being copied to the update payload. This ensures the value is persisted to the database alongside the other metadata fields.

Closes #440
2026-07-01 15:44:10 +00:00
neon e7aa6f1efe fix(authors): wire author book delete action
Open the existing delete confirmation from author book cards and keep the author detail counts in sync after deletion.

Closes #211
2026-07-01 15:27:45 +00:00
neon 219815941b feat(reader): support single-page mode for fixed-layout epubs
Adds a fixedLayoutSpread preference to EPUB reader settings to allow forcing single-page display. This is particularly useful for image-based EPUBs like comics and manga.

Closes #487
2026-07-01 15:16:17 +00:00
neon 68a395095a feat(kobo): add catalog bulk download and selection to KOReader plugin
Add support for selecting multiple books and downloading entire lists, pages, or selections in bulk. This includes long-press actions, a dedicated bulk download queue with retry and cancel support, and format preset configurations.

The commit also simplifies the OPDS random book query using ORDER BY random() and adjusts the Nest dev CLI config to disable the Swagger plugin for faster restarts.

Closes #478
2026-07-01 00:21:40 +00:00
neon 6f2fe8612e feat(books): add rich text description formatting bar
Replace the plain textarea for book descriptions with a Tiptap-based rich text editor in the metadata edit tab. Include HTML sanitization utilities to ensure safe rendering of user-provided descriptions.

Closes #460
2026-06-30 20:21:25 +00:00
neon cfc48b6218 feat(stats): demote reading source in default charts and breakdowns
Reading source is often less relevant for day-to-day analytics than
book format or reading pace. Shift the source distribution chart to the
end of the user dashboard and change the default breakdown dimension
from source to format to prioritize more useful metrics. Include a
migration to update existing users who have not customized their chart
order.
2026-06-30 19:56:48 +00:00
neon 117bc82de6 fix(reader): prevent tracking progress out of bounds or non-finite
Progress values computed during reader events could occasionally result in NaN, Infinity, or out-of-bounds numbers, which caused API validation failures or corrupted state. Progress values are now clamped to valid ranges and sanitized to omit non-finite numbers before tracking.

Closes #479
2026-06-30 19:51:06 +00:00
neon e6c24bc608 fix: exclude active and read books from discover something new
The "Discover Something New" dashboard section previously only excluded
books based on reading progress. It now joins the userBookStatus table
to correctly exclude books that are actively being read, finished, or
abandoned.

Closes #484
2026-06-30 19:19:00 +00:00
neon 14b262fa35 feat(server): import Booklore reading state
Import Booklore and Grimmory reading sessions, personal ratings, and progress state through the migration pipeline.

Preserve book-level progress as primary-file progress when source file progress rows are absent.

Closes #482

Closes #483
2026-06-30 18:54:06 +00:00
neon 2dad3eea0b feat(client): expand global search to display all matching books
The search dropdown previously limited results to a fixed number. This change implements virtual scrolling and lazy loading in the AppHeader search bar to allow users to scroll through an unlimited number of matching books efficiently.

Closes #480
2026-06-30 05:56:12 +00:00
neon 47f16fc678 feat(smart-scope): support provider filtering for community ratings
The group rule validator now accepts an optional `provider` field for
`communityRating` rules. This enables smart scopes to filter books based
on community ratings from specific providers (e.g., Goodreads, Amazon)
rather than just the aggregate rating.

The validator ensures that the provider field is only applied to community
rating rules and rejects unsupported providers.

Closes #477
2026-06-29 23:46:33 +00:00
Jadehawk de388392e7 feat(metadata): parse Calibre #extra_tags custom column into book tags (#476)
* feat(metadata): parse Calibre #extra_tags custom column into book tags

Extract #extra_tags from Calibre user metadata and populate ParsedOpf.tags when
the custom column is present.

Support JSON array, pipe-delimited, and comma-delimited string values. Merge
tags from #extra_tags with bookorbit:tags and deduplicate them.

* fix(opf-parser): prefer pipe over comma when splitting extra tags string

Previously, coerceExtraTags used the regex /[|,]/ which splits on both
delimiters simultaneously. A value like "History, Modern|Fantasy" was
parsed into three tags instead of two because every comma and pipe was
treated as a separator regardless of which was the primary delimiter.

Fix: check if the string contains a pipe first; if so, split only on "|",
otherwise fall back to splitting on ",". This preserves commas embedded
within pipe-delimited tags (e.g. "History, Modern" stays intact).

Also adds a regression test covering the mixed-delimiter case.
2026-06-29 17:18:05 -06:00
neon 9e40a3c86a feat(books): surface custom metadata fields in table view
Adds support for displaying custom metadata as columns in the book
table view. The frontend now fetches active fields and dynamically
renders the corresponding cells, backed by book card API updates.

Closes #465
2026-06-29 22:46:22 +00:00
neon a4b39542a0 feat(koreader): add option to skip auto-sync when offline
When enabled, automatic sync on book open will be skipped if the
device is not online, preventing stalls from Wi-Fi connection attempts.

Closes #471
2026-06-29 13:47:11 -06:00
Kyungwook Park b0c6ffb8f7 fix(aladin): use ItemLookUp for ISBN search instead of keyword lookup (#470)
ISBN queries were sent to ItemSearch.aspx as keywords, missing ~24%
of valid Korean ISBNs. Route ISBN searches through ItemLookUp.aspx
with ItemIdType=ISBN13 (13-digit) or ISBN (10-digit), falling back
to keyword search for unrecognised formats.

Closes #437

Co-authored-by: neon <18559658+neonsolstice@users.noreply.github.com>
2026-06-29 13:43:17 -06:00
neon d8e8765518 feat(koreader): add system action to open dashboard in koreader plugin
Closes #449
2026-06-29 19:17:31 +00:00
neon f46824211e feat(metadata): allow manual search with providers outside field rules
Previously, the explicit metadata search modal for a book was restricted
to only the providers configured in the library's field rules. This
prevented users from manually searching a specific provider if it
was not part of their default automated fetch pipeline.

This change allows all globally enabled providers to be used during
a manual metadata search, while still defaulting the selected UI
checkboxes to the providers specified in the field rules.
2026-06-29 19:03:07 +00:00
neon 82c77c72b9 fix(metadata): avoid search reruns on provider filters
Keep provider tabs as local result filters so switching providers does not restart metadata streams.
2026-06-29 18:23:50 +00:00
neon db3d1a07af fix(series): resolve missing series in koreader catalog by id
The KOReader plugin previously matched series by name, which caused
issues for series with identical names or special characters. This
updates the plugin and backend to use the exact series ID for
navigation and filtering, ensuring all series load reliably. Also
bumps the KOReader plugin version to 1.0.1.

Closes #466
2026-06-29 18:08:58 +00:00
neon a88d27a0d5 chore: add funding link 2026-06-29 04:22:13 +00:00
neon 965dcca21e fix(reader): support loading cbz files larger than 2gb
Extracts the ZIP index parser into a dedicated utility that avoids
loading the entire file into a single memory buffer. This fixes an
issue where CBZ files larger than 2GB would fail to load due to
Node.js buffer limits. Also extracts CBR error handling into helper
functions for readability.

Closes #457
2026-06-29 03:30:09 +00:00
neon 2065af41f9 feat(server): add swagger setup for public api documentation
Introduces Swagger and OpenAPI documentation generation for the
backend API. Configures bearer authentication globally while
allowing explicit bypasses for public endpoints. Exports nested
DTO classes across several modules so the NestJS Swagger plugin
can correctly reflect their schema definitions.

Closes #399
2026-06-29 01:52:30 +00:00
neon 246389b050 fix(books): restore metadata editing for multiple selected books
Routes multiple book selection to the edit tab instead of details.
Fixes a bug where deactivated kept-alive sources would passively
clobber the active navigation context by tracking component
activation state. Converts route param computed refs to shallow
refs to prevent reactivity issues during navigation.

Closes #455
2026-06-29 00:49:09 +00:00
neon e0a2c94854 fix(authors): chunk inserts to prevent call stack overflow on backfill
The drizzle-orm query builder hits a RangeError when generating a single
bulk INSERT for tens of thousands of authors during backfill. Chunking
the insertions into smaller batches resolves the issue.

Closes #452
2026-06-28 17:40:47 +00:00
neon e453618463 feat(scanner): add work queue for book dock pipeline and auto-size node heap
Introduce BookDockWorkQueue to serialize metadata extraction and auto-finalize
jobs. Files process newest-first with a 250 ms drain window and 500-1000 ms
inter-job spacing to avoid hammering metadata providers. Watched-folder rescans
now requeue files stuck in extracting or fetching status after a server restart
rather than silently dropping them.

Replace the global ThrottlerGuard with SensitiveEndpointThrottlerGuard, which
only activates on endpoints that carry explicit @Throttle() metadata. This
eliminates all @SkipThrottle() decorators from non-auth controllers and adds
missing throttle coverage for oidcGenerateLinkState and oidcGeneratePreviewState.
Update the login page to match the simplified 429 error message.

Fix countsByStatus to aggregate extracting and fetching rows into the pending
bucket so the UI counter stays accurate mid-pipeline. Add a secondary id sort
to findAll for deterministic pagination ordering.

Auto-detect the container memory limit via cgroup v1/v2 in entrypoint.sh and
compute --max-old-space-size proportionally (50-75 % of limit) instead of
defaulting to a fixed 2048 MB that OOMs small containers.

Closes #441
2026-06-28 17:01:55 +00:00
neon 9951a107af fix(reader): refresh access token when loading epub chapters
Pass the authenticated API fetcher to the streaming book loader instead of a static token. This ensures the token is automatically refreshed if it expires while reading, preventing 401 Unauthorized errors when navigating between chapters.

Closes #446
2026-06-28 05:53:35 +00:00
neon 6956fa95a3 fix(server): encode non-ASCII filenames in download headers
Extract content disposition header logic into a shared utility and use it consistently across book and koreader downloads, fixing failures when filenames contain non-ASCII characters.

Closes #448
2026-06-28 05:30:32 +00:00
neon d809cf71c4 fix(reader): respect epub page progression direction
The reader previously hardcoded left/right interactions (arrows, clicks,
swipes) to prev/next actions. This change uses physical left/right
navigation methods to respect the EPUB's resolved text direction (RTL).

Closes #444
2026-06-28 05:08:53 +00:00
neon ce2d49af3d feat: add custom icons support
Add full-stack support for uploading, managing, and using custom SVG icons for libraries and smart scopes. Includes database schema, server module for storage and sanitization, and client UI under Appearance settings.
2026-06-28 04:53:01 +00:00
neon 9a58e07a56 docs: overhaul README with accurate features, screenshots, and plugin setup 2026-06-27 19:25:13 +00:00
neon 301de9e014 fix(release-notes): stop treating horizontal rules as section terminators in highlights parser
Horizontal rules (`---`) are used in GitHub release notes as visual separators
between individual highlights, not as section boundaries. Treating them as
terminators caused only the first highlight to be parsed when a release body
used the multi-item format.

Remove the HR_RE guard from extractHighlightsBlock and stripHighlightsSection
so that only ATX headings end the Highlights block.
2026-06-27 17:14:06 +00:00
neon 553d584458 chore(release): mark v2 release boundary
Record the major release boundary so semantic-release publishes the next release as v2.0.0 after the unreleased KOReader and Kobo sync work.

BREAKING CHANGE: establish the v2 database migration baseline for KOReader and Kobo sync.

Deploy the v2 image normally and allow startup migrations to complete before serving traffic.
2026-06-27 15:33:50 +00:00
neon e3c0b73067 db(server): fix migration journal ordering
Keep unreleased migration timestamps monotonic so Drizzle applies the pending schema changes for existing installs. Add a backend test guard to catch any future journal timestamp regressions before release.
2026-06-27 15:12:05 +00:00
neon a72ca1155a feat(kobo): add KOReader catalog plugin integration
Add the KOReader catalog surfaces, plugin package metadata, and device progress handling needed for the plugin workflow. Keep progress status updates non-blocking so secondary status failures do not reject successful sync writes.
2026-06-27 14:28:36 +00:00
Savvas Dalkitsis 842923044b fix(kobo): support syncing native kepub files (#428)
* feat(kobo): support syncing native kepub files

* test(kobo): cover native kepub delivery paths

Add coverage for native kepub downloads and delivery hashes so CI validates the sync behavior changed by the PR.

---------

Co-authored-by: neon <18559658+neonsolstice@users.noreply.github.com>
2026-06-26 15:04:22 -06:00
neon a7805f7c14 fix(library): lock organization mode after creation
The organization mode determines how the scanner maps files to books. Changing it after creation leaves the library in an inconsistent state. This change disables the mode selector in the library edit modal and enforces the restriction in the backend service.

Additionally:
- Fixed TooltipContent z-index so it renders above modals.
- Bypassed rate limiting on the cover proxy endpoint to prevent 429 errors when loading large lists.
2026-06-26 16:00:56 +00:00
dependabot[bot] dc8f5822f6 chore(deps): bump the github-actions group across 1 directory with 2 updates (#381)
Bumps the github-actions group with 2 updates in the / directory: [actions/checkout](https://github.com/actions/checkout) and [EnricoMi/publish-unit-test-result-action](https://github.com/enricomi/publish-unit-test-result-action).


Updates `actions/checkout` from 6.0.3 to 7.0.0
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/df4cb1c069e1874edd31b4311f1884172cec0e10...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

Updates `EnricoMi/publish-unit-test-result-action` from 2.23.0 to 2.24.0
- [Release notes](https://github.com/enricomi/publish-unit-test-result-action/releases)
- [Commits](https://github.com/enricomi/publish-unit-test-result-action/compare/c950f6fb443cb5af20a377fd0dfaa78838901040...d0a4676d0e0b938bc201470d88276b7c74c712b3)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: EnricoMi/publish-unit-test-result-action
  dependency-version: 2.24.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-25 21:16:35 -06:00
neon c28ea29671 feat(sync): three way (kobo, koreader and bookorbit) progress and annotation sync (#338)
Introduces a custom KOReader plugin to synchronize reading progress, stats, and annotations. Adds backend controllers, services, and database schema updates to ingest plugin data. Updates the frontend to display KOReader annotations alongside built-in reader highlights.

Closes #337
2026-06-25 20:13:04 -06:00
neon 58031c1c83 feat(metadata): add community rating and rating count
Users rely on community feedback when evaluating books in their library. This change
introduces aggregated community ratings and rating counts from external metadata
providers, allowing users to see popular opinion directly within the book details
without needing to leave the application.

Closes #414
2026-06-25 21:50:17 +00:00
neon 9b946fc83e fix(metadata): preserve Book Dock epub metadata
Reuse the shared OPF mapper for Book Dock EPUB staging so embedded subject genres and Calibre page counts stay aligned with library imports.

Closes #423
2026-06-25 19:12:02 +00:00
neon 2af4895e69 feat(metadata): store and write hardcover edition IDs
Adds hardcoverEditionId to the database schema and shared types, enabling it to be stored as first-class metadata. Updates the file metadata parsers and writers (EPUB, PDF, CBZ) to read and embed this ID, and exposes it in the frontend metadata editor.

Closes #420
2026-06-25 18:55:22 +00:00
neon d1442b6d44 fix(metadata): prioritize edition release date over book release year
Previously, the edition release_date was ignored if the book had a release_year, due to the nullish coalescing operator treating the book release_year as a match for the first argument to parseYear. The new logic resolves the edition published year using both of its own date fields before falling back to the book date fields.

Closes #413
2026-06-25 18:03:21 +00:00
neon 113eeefeaf feat(metadata): add configuration for custom book metadata fields
Allow users to define new custom fields for books beyond the standard
metadata properties. This provides flexibility for cataloging arbitrary
data like reading formats, editions, or personal ratings.

Closes #419
2026-06-25 17:34:24 +00:00
neon c4ffa2c85e fix(client): preserve scroll position when navigating back to book grid views
- Cache view state (DOM, scroll, virtual lists) using KeepAlive during navigation.
- Restore scroll position on view activation using requestAnimationFrame for grids and TanStack scrollToIndex for tables.
- Wrap multi-root views in single elements to support Transition animations.
- Blur focused descendants when aria-hidden is applied to fix Reka UI Chrome 124+ warnings.

Closes #417
2026-06-25 03:28:35 +00:00
neon 9bfd134092 fix(server): resolve bulk selection queries for collections and email
Both AddToCollection and SendBook previously only operated on the explicitly provided list of book IDs. This prevented them from working correctly when a user selected all items across multiple pages in the UI. They now inherit from BulkSelectionDto and resolve queries to apply to all matching books.

Closes #416
2026-06-25 01:13:45 +00:00
neon 8f79e92053 feat(hardcover): add per-book sync controls and book sync scope
Introduce a per-book sync toggle in the book detail view that lets
users include or exclude individual books from Hardcover sync.

Add a book sync scope setting with two modes:
- all_eligible: sync all books unless explicitly excluded (default)
- selected_only: sync only books the user explicitly includes

Extract sync eligibility logic into hardcover-sync-policy.ts for
testability. The toggle handles unread books correctly: sync is skipped
while the book is unread, but the user's inclusion intent is stored so
sync activates automatically once reading starts.

Closes #412
2026-06-25 00:32:27 +00:00
neon e7956dadee fix(metadata): use ISBN context for Hardcover slug lookup
Pass metadata search params into stored provider lookups so providers can reject cached work identifiers that resolve to the wrong edition. Match Hardcover editions by normalized ISBN before falling back to provider search.

Closes #411
2026-06-23 22:33:44 +00:00
neon 0fb7e46913 feat(book-dock): allow importing duplicates and same-path collisions
Add two per-file override flags to the Book Dock finalize flow:

- skipDuplicateCheck: bypasses ISBN/title+author duplicate detection for
  a specific file. In book_per_folder mode the file naturally lands in
  the same folder as the existing book, so createBookRecord auto-attaches
  it as a second format on the same record.

- targetFileName: lets the user rename the file at the destination to
  resolve a same-path collision. The extension is always appended from
  the file format; if the user includes the extension it is stripped
  first to avoid doubling. Setting targetFileName also implicitly skips
  the duplicate check. A directory-traversal guard rejects any name
  whose resolved path escapes the intended destination folder.

Frontend: results panel shows an "Import anyway" button for isDuplicate
rows and an inline rename input for file-exists-at-target rows. Both
retry calls forward defaultLibraryId/defaultFolderId so files that rely
on dialog-level defaults get the right destination. Concurrent double-
click is guarded. reimportingIds uses reactive(Set) so spinner and
disabled states update correctly.

Closes #213
2026-06-23 21:13:16 +00:00
neon 78e02770a1 fix(metadata): always unpause queue on manual trigger
triggerGlobal and triggerForLibrary skipped unpauseIfNeeded when no new
books were eligible (queued = 0), leaving a previously paused queue
permanently stuck on re-trigger.

Also add a live statusLabel to the auto-fetch settings components that
shows remaining queue count when active, replacing the misleading
"~0 eligible" count that appeared immediately after triggering a run.

Closes #408
2026-06-23 19:16:57 +00:00
neon 484ad0b9be fix(metadata): persist auto-fetch library overrides
Ensure the settings UI saves inherit toggles immediately and guards eligible-count previews against stale async responses. Align library override DTO validation with the shared partial override contract, and record manual library runs even when nothing queues.
2026-06-23 16:37:59 +00:00