* feat(kobo): support two-way progress sync
Implement two-way page-level reading progress synchronization between Kobo devices, KOReader, and BookOrbit. This ensures readers can switch between physical devices and the web app without losing their exact page-level reading position.
Closes#249
* fix(settings): restore thumbnail click preference
Restore the main branch thumbnail-click setting after the Kobo branch accidentally dropped it during the rebase. Keep the Kobo architecture allowlist aligned with the new identity service.
* fix(kobo): harden two-way progress sync fallbacks
Preserve Kobo bookmark metadata and tolerate null auth payloads/settings failures.
Cover review-reported regressions with focused tests.
Introduce a new display preference to control what happens when clicking
a book cover thumbnail. Users can now choose to open the book details
page instead of launching the reader directly.
Closes#240
Search results from Goodreads frequently surfaced study guides,
summaries, and companion books instead of the actual primary work
because they often shared similar titles.
To ensure the correct book is selected during metadata fetch, the
autocomplete ranking now heavily penalizes derivative works and boosts
items with high rating counts and strong author name overlap.
The genre blocklist allows users to define a global list of strings
that are prevented from being applied to a books genre field during
metadata fetches. Case-insensitive filtering executes directly inside
the metadata pipeline before merge strategies run, preventing unwanted
values from persisting across providers.
Closes#235
Remove the @IsNotEmpty() decorator from the author field in the
SearchCoversQueryDto so that empty strings do not trigger a validation
error on the cover search endpoint.
Update DEFAULT_SCROLLERS to enable 'Recently Added', 'Discover Something
New', 'Continue Reading', and 'Continue Listening' in that specific order.
Disable 'Want to Read' and 'Up Next in Series' by default to streamline the
initial user experience.
Add dedicated dashboard scrollers for audiobooks and want-to-read lists to improve content discoverability. Previously, audiobook progress was merged with reading progress, making it difficult to find active audiobooks. Separate the queries to ensure audiobooks are handled exclusively by the new listening scroller and expose the user's want-to-read queue directly on the dashboard for easier access.
Add a "Group by media" toggle in the series detail view to separate
books, audiobooks, and comics into distinct sections. Persist the
user's selection preference to localStorage.
Closes#154
Select all acquisition content files from the database for each book
instead of only the primary file. Return multiple acquisition links
in the OPDS catalog feed for books available in multiple formats,
and update unit and E2E tests to verify catalog output and downloads.
Closes#224
rearrange the fanned list of book covers so that the first book in a series sits in the center of the stack as the front-most hero, with subsequent books fanning out symmetrically on the sides. apply hardware-accelerated css 3d transforms for perspective, hover-container expansion, and hover-item lift, while keeping the scale transitions clean and subtle.
Introduces a new 'stack' cover mode for series cards, rendering the covers
as a stacked pile. This mode has also been made the default for series card
display preferences. Settings UI is updated to allow users to toggle this
mode alongside existing mosaic and single-cover options.
Closes#223
Preserve RanobeDB IDs through write-back, file import, and load-from-file so saved metadata survives round trips.
Expose saved RanobeDB sources in the book detail surfaces with the provider icon and database link.
Closes#207Closes#210
Square audiobook covers were using portrait dimensions for the background stack shadow, causing visual stretching. This updates the scale logic to detect square ratios and apply the correct 1:1 dimension.
Closes#221
Use useLocalStorage from @vueuse/core to persist the show paths toggle state on the book details files page so the preference is remembered across visits.
Closes#212
- Extend file write pipeline to write metadata fields directly into audiobook files.
- Map metadata fields to standard audio tag mappings and run ffmpeg to embed tags and cover art for M4B, M4A, MP3, and FLAC files.
- Update UI in edit metadata tab to show file write status and highlight written fields.
Handle edge cases in metadata extraction, file write-back status checks, and comic XML building. This ensures narrators are not incorrectly populated when they match the author/artist, preserves audibleId during chapter extraction, prevents whitespace issue numbers from breaking the XML, and improves the UI to clearly show why write-back is disabled.
Navigation sometimes treated stale rendered contents as successful navigation or failed silently when a section did not load properly. Ensure the target index matches the rendered content and throw on section load failure to prevent incorrect chapter display.
Closes#203
Transition from title-based to ID-based routing for series to prevent
404 errors when a series has an excessively long title. This introduces
numeric IDs for series, backed by schema updates, and updates the
frontend router to use seriesId instead of seriesName.
Closes#198
* feat(audio): embed cover image in audiobook files
- Add audio cover embedder
- Implement format writers for M4B, M4A, MP3, and FLAC using ffmpeg
- Add library settings for audio file writes
Closes#145
* fix(library): disable audio file write by default and add ffmpeg timeout
Change the default value of file_write_audio_enabled from true to false in the database schema and migrations. Automatically disable audio writing in the UI if cover writing is toggled off. Add a 60-second timeout to the ffmpeg child process to prevent hanging when embedding covers.
---------
Co-authored-by: neon <18559658+neonsolstice@users.noreply.github.com>
Allow public fork pull requests to upload coverage without repository secrets while keeping tokened uploads for trusted runs.
Use Codecov v6 so the action runs on Node 24.
Implement a 'peek mode' that allows users to open and read books without updating their reading progress or creating reading sessions. Add a 'Peeking' banner in the reader and disable API calls in 'useReaderProgress' and 'useReadingSession' when tracking is disabled.
Closes#161
Virtual sibling cleanup is now deferred until the final missing pass instead of being handled eagerly during upsert. This ensures that real nested book folders are not prematurely marked as missing before their own candidates can be processed. A new validation step against the disk state guarantees only genuinely missing books are removed.
Closes#170
The Book Dock directory inside the container was previously hardcoded to `/data/book-dock`. This adds support for the `BOOK_DOCK_PATH` environment variable, allowing users to customize the internal container directory where the Book Dock monitors for files. If the environment variable is not set, it defaults to the `book-dock` folder inside the configured app data path. This replaces the `BOOK_DROP_HOST_PATH` volume mapping in the compose file with a standard volume and environment variable approach.
Closes#178
The duplicate detection logic in BookDockFinalizeService previously identified books as duplicates if they had the same title, even if their authors differed, when ISBNs were unavailable. This has been updated to include author names in the duplicate check for books missing ISBNs to prevent incorrect duplicate identifications.
Closes#175
Refactors the virtual book table to render collapsed series as standard
inline cells (BookTableCollapsedSeriesCell) rather than full-width
rows, improving table alignment and layout consistency. Updates the
dashboard scroller and cover artwork components to support 1:1 aspect
ratios, ensuring audiobook covers are displayed correctly without
distortion.
Ensure the book context stays in sync when more books are loaded by lazy loading. Stop unregistering the loadMore handler when the list view unmounts so that metadata navigation continues working seamlessly after leaving the primary list view.
Closes#187
Remove stale smart-scope scrollers after smart scopes load so deleted shelves stop rendering and stop issuing invalid scroller requests.
Add dashboard config edge-case tests for prune, reset, and malformed storage normalization to prevent regressions.
Closes#189
Update the metadata fetch service to perform a fallback search when a provider's lookup by ID returns no result or an irrelevant candidate. This ensures that metadata retrieval can gracefully recover from incorrect or stale provider IDs.
Closes#182
Update the `updatedAt` field on the `books` table whenever book metadata is
automatically fetched or bulk updated. Additionally, filter codecov reports
to only include server coverage and ignore test/config files, and update
the README codecov badge to reflect this.
Closes#186
* feat(metadata): add RanobeDB metadata provider
Introduces a new metadata provider to fetch book metadata from RanobeDB.
- Adds `ranobedb.client` to interact with the RanobeDB API.
- Implements `ranobedb.mapper` to map RanobeDB data to the internal metadata schema.
- Maps native Japanese titles to subtitles.
- Uses the original language as a fallback for the language field.
- Parses specific release dates to determine the original publication year.
- Registers RanobeDB as an available provider in the preferences UI.
- Adds `ranobedb_id` to the metadata database schema.
- Removes the `bookId` override when manually performing a metadata search in the UI.
Closes#114
* ci: upload server and client coverage reports to codecov
Generate client lcov output and publish per-package coverage from CI so Codecov checks and README coverage artifacts stay up to date.
* ci: configure codecov project status and pr comments
* ci: isolate codecov coverage status by flags
* ci: add patch coverage status, per-flag badges, and client coverage scope
- Add patch coverage status in codecov.yml (server and client flags) so
Codecov checks coverage on changed lines in PRs, not just overall trend
- Add include/exclude patterns to client vitest coverage config so test
files and entry points are excluded from coverage stats
- Add per-flag server/client coverage badges to the README coverage section
* docs: restructure coverage map in README
Move the coverage map down to the documentation and contributing section, giving it its own heading. Remove the redundant server and client badges from the top.
Add a user preference to configure the label displayed underneath book
and series cards in grid view. The option allows users to select
between hiding labels completely, showing book titles, or showing series
titles.
Closes#159
Virtualization can incorrectly hide books in the series detail view, showing only a small subset until the page is interacted with. This adds a static rendering mode to the book grid component and uses it for the series detail view to ensure all books remain visible.
Closes#171
When a newly filled metadata field is locked before saving, the subsequent
save causes a 409 conflict because the field was locked but not yet saved.
This introduces a new endpoint and DTO to update both metadata and locks
simultaneously, resolving the conflict.
Closes#172
Allow readers to choose which representative series cover appears in collapsed grid view so browsing can prioritize first, latest, or first unread volumes.
Closes#160
Unify accent metadata in one client module to prevent hue and tone drift across theme picker, chart palettes, and cover placeholders.
Guard placeholder theme-store access when Pinia is unavailable so non-store mounts remain stable while preserving reactive updates when a store is active.
The database check constraint for the library reading threshold was previously enforcing a maximum value of 1, causing an internal server error when configuring a threshold greater than 1% via the UI. The constraint max has been increased to 100 to support percentage-based values and align with the mark_as_finished constraint.
Closes#142
Allow users to choose how cover artwork is fitted so mixed aspect ratios render cleanly in cards, lists, carousels, and widgets.
Persist display preferences through shared types and user-preferences endpoints so these settings stay consistent across sessions and devices.
Closes#152
When DATABASE_URL is not provided, entrypoint.sh assembled it by
interpolating POSTGRES_USER, POSTGRES_PASSWORD and POSTGRES_DB directly
into a postgres:// URL. Any reserved URL character in those values (for
example a '#', '/' or '?' in the password) produced an invalid URL, and
pg-connection-string's new URL() call then threw ERR_INVALID_URL,
preventing the container from starting.
Percent-encode the user, password and database components via
encodeURIComponent before building the URL so passwords containing
special characters work without manual escaping.
Co-authored-by: MohammadYusif <MohammadYusif@users.noreply.github.com>
The Content Security Policy was previously blocking external images,
preventing admin-configured OIDC provider icons from loading.
This allows https: images to be loaded while continuing to block
plain http: to maintain mixed-content protection.
Closes#131
Introduce a user-preferences module and database schema to sync theme
settings (theme, accent, radius, background, brightness) across devices.
Users can now enable theme sync to persist their customization choices.
Closes#146
Add a dialog to batch-edit metadata for multiple selected books.
Support add/remove/replace/clear modes for list fields, with clear as an explicit
frontend-only mode.
Fix scalar input border rendering, year field validation/coercion, tags replace
optimistic updates, and submit error handling.
Closes#150