Commit Graph

550 Commits

Author SHA1 Message Date
neon d182d913c6 fix(books): support accent-insensitive search
Install the PostgreSQL unaccent extension and use a custom immutable wrapper function to perform accent-insensitive ILIKE comparisons when searching for books, authors, series, and narrators. Add database indexes on the unaccented values to maintain query performance.

Closes #640
2026-07-11 17:47:29 +00:00
neon 89545e6c69 fix(storygraph): skip progress sync on read transition
Updating status to read on StoryGraph automatically logs the remaining page count. Sending a
subsequent progress update for the full book page count would create a duplicate journal entry,
inflating the logged page count. Skip the progress update when transitioning to the read status.

Closes #643
2026-07-11 17:06:49 +00:00
neon 568bf9dd6f fix(kobo): strip leading v from server version in koreader plugin
The KOReader plugin's update check response provides the server version,
which already contains a leading "v". The update and plugin popups in
the KOReader plugin format the version with an explicit "v" prefix,
resulting in a "vv" typo (e.g. "vv1.2.0").

Strip the leading "v" from the server version in the updater script
if present to ensure it is displayed with a single "v" prefix.

Closes #644
2026-07-11 17:02:32 +00:00
neon 870abecb01 feat(reader): polish the PDF reading experience
Make long PDF reading sessions easier to navigate across desktop and mobile. Keep document loading recoverable when EmbedPDF initializes or tears down out of order, without enabling document-editing capabilities.
2026-07-11 15:29:34 +00:00
neon 4a8b793c65 fix(metadata): preserve embedded metadata on new book imports
Ensure that new book imports via scanner or file upload prioritize and
preserve embedded metadata. Pass a new `preserveExisting` option to the
metadata fetch pipeline to prevent external providers from overwriting
existing fields or covers that were extracted from the book files during
import.

Closes #612
2026-07-10 05:44:17 +00:00
neon 56e0a554f3 feat(settings): allow users to disable achievements and gamification
Introduce user-specific settings to toggle achievements. Avoid evaluation
and awarding of achievements on the backend when disabled. Render a toggle
in the Account Settings interface on the frontend.

Closes #614
2026-07-10 04:24:26 +00:00
dependabot[bot] 3ba405729d chore(deps): bump the development-dependencies group with 21 updates (#617)
Bumps the development-dependencies group with 21 updates:

| Package | From | To |
| --- | --- | --- |
| [@commitlint/cli](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/cli) | `21.0.2` | `21.2.1` |
| [@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/config-conventional) | `21.0.2` | `21.2.0` |
| [@semantic-release/github](https://github.com/semantic-release/github) | `12.0.8` | `12.0.9` |
| [prettier](https://github.com/prettier/prettier) | `3.8.4` | `3.9.5` |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `26.0.0` | `26.1.1` |
| [@vitest/coverage-v8](https://github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8) | `4.1.9` | `4.1.10` |
| [@vitest/eslint-plugin](https://github.com/vitest-dev/eslint-plugin-vitest) | `1.6.20` | `1.6.22` |
| [@vue/eslint-config-typescript](https://github.com/vuejs/eslint-config-typescript) | `14.8.0` | `14.9.0` |
| [eslint](https://github.com/eslint/eslint) | `10.5.0` | `10.6.0` |
| [eslint-plugin-oxlint](https://github.com/oxc-project/eslint-plugin-oxlint) | `1.70.0` | `1.73.0` |
| [oxlint](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint) | `1.70.0` | `1.73.0` |
| [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) | `8.0.16` | `8.1.4` |
| [vite-plugin-vue-devtools](https://github.com/vuejs/devtools/tree/HEAD/packages/vite) | `8.1.3` | `8.1.5` |
| [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) | `4.1.9` | `4.1.10` |
| [vue-tsc](https://github.com/vuejs/language-tools/tree/HEAD/packages/tsc) | `3.3.5` | `3.3.7` |
| [@nestjs/testing](https://github.com/nestjs/nest/tree/HEAD/packages/testing) | `11.1.27` | `11.1.28` |
| [@swc/core](https://github.com/swc-project/swc/tree/HEAD/packages/core) | `1.15.41` | `1.15.43` |
| [globals](https://github.com/sindresorhus/globals) | `17.6.0` | `17.7.0` |
| [ts-loader](https://github.com/TypeStrong/ts-loader) | `9.6.1` | `9.6.2` |
| [tsx](https://github.com/privatenumber/tsx) | `4.22.4` | `4.23.0` |
| [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.61.1` | `8.63.0` |

Updates `@commitlint/cli` from 21.0.2 to 21.2.1
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/cli/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v21.2.1/@commitlint/cli)

Updates `@commitlint/config-conventional` from 21.0.2 to 21.2.0
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/config-conventional/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v21.2.0/@commitlint/config-conventional)

Updates `@semantic-release/github` from 12.0.8 to 12.0.9
- [Release notes](https://github.com/semantic-release/github/releases)
- [Commits](https://github.com/semantic-release/github/compare/v12.0.8...v12.0.9)

Updates `prettier` from 3.8.4 to 3.9.5
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.8.4...3.9.5)

Updates `@types/node` from 26.0.0 to 26.1.1
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `@vitest/coverage-v8` from 4.1.9 to 4.1.10
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.10/packages/coverage-v8)

Updates `@vitest/eslint-plugin` from 1.6.20 to 1.6.22
- [Release notes](https://github.com/vitest-dev/eslint-plugin-vitest/releases)
- [Commits](https://github.com/vitest-dev/eslint-plugin-vitest/compare/v1.6.20...v1.6.22)

Updates `@vue/eslint-config-typescript` from 14.8.0 to 14.9.0
- [Release notes](https://github.com/vuejs/eslint-config-typescript/releases)
- [Commits](https://github.com/vuejs/eslint-config-typescript/compare/v14.8.0...v14.9.0)

Updates `eslint` from 10.5.0 to 10.6.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v10.5.0...v10.6.0)

Updates `eslint-plugin-oxlint` from 1.70.0 to 1.73.0
- [Release notes](https://github.com/oxc-project/eslint-plugin-oxlint/releases)
- [Commits](https://github.com/oxc-project/eslint-plugin-oxlint/compare/v1.70.0...v1.73.0)

Updates `oxlint` from 1.70.0 to 1.73.0
- [Release notes](https://github.com/oxc-project/oxc/releases)
- [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxlint/CHANGELOG.md)
- [Commits](https://github.com/oxc-project/oxc/commits/oxlint_v1.73.0/npm/oxlint)

Updates `vite` from 8.0.16 to 8.1.4
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v8.1.4/packages/vite)

Updates `vite-plugin-vue-devtools` from 8.1.3 to 8.1.5
- [Release notes](https://github.com/vuejs/devtools/releases)
- [Commits](https://github.com/vuejs/devtools/commits/v8.1.5/packages/vite)

Updates `vitest` from 4.1.9 to 4.1.10
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.10/packages/vitest)

Updates `vue-tsc` from 3.3.5 to 3.3.7
- [Release notes](https://github.com/vuejs/language-tools/releases)
- [Changelog](https://github.com/vuejs/language-tools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/vuejs/language-tools/commits/v3.3.7/packages/tsc)

Updates `@nestjs/testing` from 11.1.27 to 11.1.28
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.1.28/packages/testing)

Updates `@swc/core` from 1.15.41 to 1.15.43
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/commits/v1.15.43/packages/core)

Updates `globals` from 17.6.0 to 17.7.0
- [Release notes](https://github.com/sindresorhus/globals/releases)
- [Commits](https://github.com/sindresorhus/globals/compare/v17.6.0...v17.7.0)

Updates `ts-loader` from 9.6.1 to 9.6.2
- [Release notes](https://github.com/TypeStrong/ts-loader/releases)
- [Changelog](https://github.com/TypeStrong/ts-loader/blob/main/CHANGELOG.md)
- [Commits](https://github.com/TypeStrong/ts-loader/compare/v9.6.1...v9.6.2)

Updates `tsx` from 4.22.4 to 4.23.0
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs)
- [Commits](https://github.com/privatenumber/tsx/compare/v4.22.4...v4.23.0)

Updates `typescript-eslint` from 8.61.1 to 8.63.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.63.0/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: "@commitlint/cli"
  dependency-version: 21.2.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: "@commitlint/config-conventional"
  dependency-version: 21.2.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: "@semantic-release/github"
  dependency-version: 12.0.9
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: prettier
  dependency-version: 3.9.5
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: "@types/node"
  dependency-version: 26.1.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: "@vitest/coverage-v8"
  dependency-version: 4.1.10
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: "@vitest/eslint-plugin"
  dependency-version: 1.6.22
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: "@vue/eslint-config-typescript"
  dependency-version: 14.9.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: eslint
  dependency-version: 10.6.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: eslint-plugin-oxlint
  dependency-version: 1.73.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: oxlint
  dependency-version: 1.73.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: vite
  dependency-version: 8.1.4
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: vite-plugin-vue-devtools
  dependency-version: 8.1.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: vitest
  dependency-version: 4.1.10
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: vue-tsc
  dependency-version: 3.3.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: "@nestjs/testing"
  dependency-version: 11.1.28
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: "@swc/core"
  dependency-version: 1.15.43
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: globals
  dependency-version: 17.7.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: ts-loader
  dependency-version: 9.6.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: tsx
  dependency-version: 4.23.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: typescript-eslint
  dependency-version: 8.63.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-09 21:53:14 -06:00
dependabot[bot] f3a2689e21 chore(deps): bump the production-dependencies group with 16 updates (#616)
Bumps the production-dependencies group with 16 updates:

| Package | From | To |
| --- | --- | --- |
| [@lucide/vue](https://github.com/lucide-icons/lucide/tree/HEAD/packages/vue) | `1.23.0` | `1.24.0` |
| [@tiptap/extension-link](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-link) | `3.27.1` | `3.27.3` |
| [@tiptap/pm](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/pm) | `3.27.1` | `3.27.3` |
| [@tiptap/starter-kit](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/starter-kit) | `3.27.1` | `3.27.3` |
| [@tiptap/vue-3](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/vue-3) | `3.27.1` | `3.27.3` |
| [@fastify/cookie](https://github.com/fastify/fastify-cookie) | `11.0.2` | `11.1.1` |
| [@fastify/helmet](https://github.com/fastify/fastify-helmet) | `13.0.2` | `13.1.0` |
| [@fastify/multipart](https://github.com/fastify/fastify-multipart) | `10.0.0` | `10.1.0` |
| [@fastify/static](https://github.com/fastify/fastify-static) | `9.1.3` | `9.3.0` |
| [@nestjs/common](https://github.com/nestjs/nest/tree/HEAD/packages/common) | `11.1.27` | `11.1.28` |
| [@nestjs/core](https://github.com/nestjs/nest/tree/HEAD/packages/core) | `11.1.27` | `11.1.28` |
| [@nestjs/platform-fastify](https://github.com/nestjs/nest/tree/HEAD/packages/platform-fastify) | `11.1.27` | `11.1.28` |
| [@nestjs/platform-socket.io](https://github.com/nestjs/nest/tree/HEAD/packages/platform-socket.io) | `11.1.27` | `11.1.28` |
| [@nestjs/websockets](https://github.com/nestjs/nest/tree/HEAD/packages/websockets) | `11.1.27` | `11.1.28` |
| [fastify](https://github.com/fastify/fastify) | `5.9.0` | `5.10.0` |
| [mysql2](https://github.com/sidorares/node-mysql2) | `3.22.5` | `3.22.6` |


Updates `@lucide/vue` from 1.23.0 to 1.24.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/1.24.0/packages/vue)

Updates `@tiptap/extension-link` from 3.27.1 to 3.27.3
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-link/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.27.3/packages/extension-link)

Updates `@tiptap/pm` from 3.27.1 to 3.27.3
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/pm/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.27.3/packages/pm)

Updates `@tiptap/starter-kit` from 3.27.1 to 3.27.3
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/starter-kit/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.27.3/packages/starter-kit)

Updates `@tiptap/vue-3` from 3.27.1 to 3.27.3
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/vue-3/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.27.3/packages/vue-3)

Updates `@fastify/cookie` from 11.0.2 to 11.1.1
- [Release notes](https://github.com/fastify/fastify-cookie/releases)
- [Commits](https://github.com/fastify/fastify-cookie/compare/v11.0.2...v11.1.1)

Updates `@fastify/helmet` from 13.0.2 to 13.1.0
- [Release notes](https://github.com/fastify/fastify-helmet/releases)
- [Commits](https://github.com/fastify/fastify-helmet/compare/v13.0.2...v13.1.0)

Updates `@fastify/multipart` from 10.0.0 to 10.1.0
- [Release notes](https://github.com/fastify/fastify-multipart/releases)
- [Commits](https://github.com/fastify/fastify-multipart/compare/v10.0.0...v10.1.0)

Updates `@fastify/static` from 9.1.3 to 9.3.0
- [Release notes](https://github.com/fastify/fastify-static/releases)
- [Commits](https://github.com/fastify/fastify-static/compare/v9.1.3...v9.3.0)

Updates `@nestjs/common` from 11.1.27 to 11.1.28
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.1.28/packages/common)

Updates `@nestjs/core` from 11.1.27 to 11.1.28
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.1.28/packages/core)

Updates `@nestjs/platform-fastify` from 11.1.27 to 11.1.28
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.1.28/packages/platform-fastify)

Updates `@nestjs/platform-socket.io` from 11.1.27 to 11.1.28
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.1.28/packages/platform-socket.io)

Updates `@nestjs/websockets` from 11.1.27 to 11.1.28
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.1.28/packages/websockets)

Updates `fastify` from 5.9.0 to 5.10.0
- [Release notes](https://github.com/fastify/fastify/releases)
- [Commits](https://github.com/fastify/fastify/compare/v5.9.0...v5.10.0)

Updates `mysql2` from 3.22.5 to 3.22.6
- [Release notes](https://github.com/sidorares/node-mysql2/releases)
- [Changelog](https://github.com/sidorares/node-mysql2/blob/master/Changelog.md)
- [Commits](https://github.com/sidorares/node-mysql2/compare/v3.22.5...v3.22.6)

---
updated-dependencies:
- dependency-name: "@lucide/vue"
  dependency-version: 1.24.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: "@tiptap/extension-link"
  dependency-version: 3.27.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: "@tiptap/pm"
  dependency-version: 3.27.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: "@tiptap/starter-kit"
  dependency-version: 3.27.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: "@tiptap/vue-3"
  dependency-version: 3.27.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: "@fastify/cookie"
  dependency-version: 11.1.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: "@fastify/helmet"
  dependency-version: 13.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: "@fastify/multipart"
  dependency-version: 10.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: "@fastify/static"
  dependency-version: 9.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: "@nestjs/common"
  dependency-version: 11.1.28
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: "@nestjs/core"
  dependency-version: 11.1.28
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: "@nestjs/platform-fastify"
  dependency-version: 11.1.28
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: "@nestjs/platform-socket.io"
  dependency-version: 11.1.28
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: "@nestjs/websockets"
  dependency-version: 11.1.28
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: fastify
  dependency-version: 5.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: mysql2
  dependency-version: 3.22.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-09 21:28:49 -06:00
neon 910ce5a92e feat(smart-scope): add toggle to collapse series in smart scopes
Provide a series-collapse toggle in the Smart Scopes view to reduce clutter from large series. Use the existing series collapse preferences logic to allow users to toggle this setting per Smart Scope, maintaining consistency with Library and Collection views.

Closes #609
2026-07-09 22:20:54 +00:00
neon 0f37e197ad fix(books): clear reading progress and history when resetting book
Provide a new endpoint and user interface to reset the reading state of a book. Delete all related reading sessions, progress, audiobook progress, and KOReader progress. Clear lifecycle dates when setting the book status back to unread or want to read, preventing the book from persisting on the continue reading shelf.

Closes #606
2026-07-09 21:54:18 +00:00
neon 5541e1acdc fix(kobo): track library snapshots and sync progress per device
Migrate the snapshot and snapshot books tables to be per-device instead
of per-user. This resolves a sync race condition where only the first
registered Kobo device for a user receives the library delta, leaving the
others empty.

Closes #314
2026-07-09 20:57:02 +00:00
Chris Mansell 729b221427 feat(metadata): add full publication date support (#598)
- Add a persisted publication date while continuing to derive publication years.

- Thread full dates through metadata import, editing, filters, sorting, write-back, and sync payloads.

- Cover provider parsing, DTO validation, jump buckets, and date formatting with tests.

Closes #596
2026-07-09 08:17:53 -06:00
neon 7154a6c0d3 feat(library): allow discarding duplicate books from finalize dialog
Provide an action in the Book Dock finalize dialog to discard files that
are identified as duplicate candidates. This saves users from having to close
the finalize window, re-select the duplicates in the main queue, and manually
discard them.

Closes #591
2026-07-09 04:01:11 +00:00
neon 7e66f61dae feat(library): allow pausing book dock import task
Introduce a control mechanism to pause and resume the Book Dock import task.
Suspend resource-intensive background file ingestion and metadata finalization
to avoid system overhead or database locks during large-scale operations.

Closes #593
2026-07-09 02:22:09 +00:00
blinkidy ec4e5e2fc4 feat(storygraph): add StoryGraph sync integration (#568)
* feat(storygraph): add StoryGraph sync integration

Sync reading status and progress from BookOrbit to StoryGraph using
session-cookie authentication. Includes book matching with manual
rematch and edition selection, per-user sync toggles, a throttled
request queue, a storygraph_sync permission, and a StoryGraph
settings page.

Closes #274

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vi6e8ZJRAGMmRSmSj9sWXR

* fix(storygraph): address automated review findings

- clear stale cookie validation badge when inputs change
- reject saves with only one cookie value instead of silently dropping it
- handle linked-books load failures with a retryable error state so post-action
  reloads cannot misattribute failures to the action
- add a 30s request timeout so a hung StoryGraph response cannot stall a user's
  sync queue indefinitely
- reserve throttle slots synchronously so concurrent requests serialize
- reject empty cookie strings that would overwrite stored credentials
- report the retry response status when the rereading fallback fails
- add component specs for the connection card and linked books, plus regression
  tests for the throttle, settings, and retry-status fixes

* test(storygraph): fully restore setTimeout spies between queue tests

* feat(storygraph): add sync-scope option

Adds the book sync scope selector (all eligible books vs selected books
only) and the per-book StoryGraph sync control on the book details page,
mirroring the Hardcover integration's controls. All books with a syncable
status are now eligible — the previous connected-at cutoff that skipped
books finished before connecting is removed in favor of explicit scope
and per-book selection.

* fix(storygraph): stream live sync progress and surface failed books

The manual sync progress stream emitted the same mutated status object,
so distinctUntilChanged dropped every update after the first and the bar
never moved. Progress snapshots are now immutable and track processed
books (synced + skipped + failed), so the bar advances for every book.

Adds live synced/skipped/failed counters, a persistent last-run summary
from a terminal completed/cancelled event, and a failed-books list under
Manual sync backed by GET /storygraph/sync/failures. Details-page sync
state now refetches when the global sync scope changes.

(cherry picked from commit 075eeaa0c8678d4ebbba5dcce7aa15df42d64f51)

* feat(storygraph): show distinguishing identifiers in the editions list

Edition rows previously showed only format, pages, and language, making
identical-looking editions impossible to tell apart. The editions parser
now also scrapes ISBN, publisher, publication date, and cover image when
present, and each row shows the edition title, those identifiers, a cover
thumbnail, and the StoryGraph edition id linking to the edition page for
verification before switching.

(cherry picked from commit 09ec111613e9be149b556f64eb242cd3d82b296c)

* fix(storygraph): auto-refresh linked books after sync and add unmatched hint

The Linked books list only loaded on mount, so a book linked during a manual
sync run stayed on 'Not linked yet' until a manual page refresh. It now
silently reloads when a run completes (no spinner flash, keeps expanded rows),
guarded by a request id so overlapping reloads can't resolve out of order.
Books StoryGraph couldn't match automatically now show a short hint pointing
at 'Try auto-match' or pasting the StoryGraph URL.

(cherry picked from commit 1d0cfa1e06f3e211258620f7ad5e61373df7a310)

* feat(storygraph): compact edition link and wrap identifiers on mobile

The editions list showed the raw StoryGraph edition UUID as a long
monospace hyperlink, which was hard to read and cluttered the row. Replace
it with a small icon-only 'Open on StoryGraph' button (aria-labelled) next
to 'Use this', and let the publisher/date/ISBN line wrap instead of
truncating so the ISBN is no longer cut off on narrow/mobile screens.

(cherry picked from commit cdf4d64063534adeea9daafad11cd58a0cbc9a00)

* fix(storygraph): re-sync books re-added after being marked unread, clarify labels

A book synced while 'read' then marked 'unread' left its synced markers in
place, so re-marking it read compared against a stale lastSyncedStatus and was
silently skipped (shown as Linked but never re-pushed) until 'Try auto-match'.
On the unread transition the synced markers are now cleared while keeping the
match, so re-adding the book re-syncs.

Also present match methods with real casing (ISBN/Title/Cached/Manual) and
clarify that the manual-link field takes a StoryGraph URL or book ID (the code
after /books/), not an ISBN.

(cherry picked from commit 771e7e4fe793afbc30800717ae1c017ca307e5e1)

* refactor(storygraph): compare skippedBooks explicitly in sync-status dedup

The active-sync stream's equality check omitted skippedBooks, relying on
processedBooks (synced+skipped+failed) to capture skip-only changes. That is
correct but non-obvious; comparing every counter explicitly makes the intent
clear. No behavior change.

(cherry picked from commit f267088939f0d2493caadab8eb90f24ab4b28c0b)

* fix(storygraph): encode manually-entered book id in the lookup path

The manual-link id can be pasted directly, so a stray ?, #, or / would change
which /books/ URL is requested. Encode it before interpolating; a bad id now
404s cleanly instead of being stored.

* style(storygraph): fix prettier formatting and remove em dashes

The CI Lint (Client) job runs prettier --check, which flagged
StorygraphLinkedBooks.vue and StorygraphSyncProgress.vue. Reflow both
to prettier style and replace em dashes in UI strings and comments with
regular punctuation per project convention.

* fix(storygraph): harden sync access and batching

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: neon <18559658+neonsolstice@users.noreply.github.com>
2026-07-08 17:15:17 -06:00
neon 36bfc6e83d feat(reader): serialize and coordinate sync jobs in KOReader plugin
Introduce a sync coordinator to serialize and prioritize KOReader sync jobs (such as progress sync, highlight sync, library sweep, and update check). Coalesce pending jobs by family to prevent race conditions and redundant network requests. Use a dedicated scheduler for open annotations to handle highlight sync and matching asynchronously on book open. Add a diagnostics menu in the settings to track detailed sync status and provide clear error messages for debugging.
2026-07-08 17:55:40 +00:00
neon 512f16e288 fix(notifications): keep notifications visible when marking all as read
Emit a notification:all-read event instead of notification:cleared when marking all notifications as read, and handle this event on the client by updating the read status rather than emptying the notification list.

Closes #587
2026-07-08 16:23:38 +00:00
neon 9f19fa8126 fix(kobo): sync reading progress when two-way sync is disabled
Ensure Kobo reading progress is written to internal progress records even if two-way progress sync is disabled, allowing reading sessions to be recorded correctly. Also add analytics endpoints to the Kobo device resources template to support tracking.

Closes #584
2026-07-08 15:58:12 +00:00
Oscar Nogueira Neto 4eed87a6fd feat(readwise): sync highlights to Readwise (#578)
* feat(types): add readwise_sync permission

* feat(types): add readwise settings types

* feat(db): add readwise_user_settings table

* feat(readwise): add constants

* feat(readwise): add throttle queue

* feat(readwise): add REST client

* feat(readwise): add repository

* feat(readwise): add settings service

* feat(readwise): add sync service with watermark

* refactor(readwise): drop unused logger in settings service

* feat(readwise): add debounced scheduler

* feat(readwise): add event listener

* feat(readwise): add controller and dtos

* feat(readwise): wire module into app

* test(readwise): add readwise_sync to authorization matrix

* feat(annotation): emit annotation.created on device sync ingest

* feat(client): add readwise api client

* feat(client): add useReadwiseSettings composable

* feat(client): add Readwise settings page

* feat(client): add Readwise settings route

* feat(client): add Readwise to settings nav

* fix(readwise): avoid typeof this in settings type annotation

Production build (nest build → tsc via tsconfig.build.json) rejects
`Parameters<typeof this.repo.upsertSettings>[1]` with TS2304. Reference
the repository class type directly so the prod image compiles.

* fix(client): add Readwise sync to admin permission list

The admin UserFormDrawer renders a hardcoded permission list, not an
iteration of PERMISSION_LABELS, so readwise_sync was ungrantable via UI.
Add it to the Devices & Access group and the Standard preset, mirroring
Hardcover sync.

* feat(readwise): send book cover to Readwise via OpenLibrary ISBN

Populate the Readwise highlight image_url with the OpenLibrary cover URL
built from the book's isbn13 (falling back to isbn10). Uses ?default=false
so a missing cover yields 404 (no image) rather than a blank placeholder.
Books without an ISBN send no image_url. BookOrbit's own cover endpoint is
auth-gated and stores no public source URL, so a public third-party cover
URL is the zero-infra way to give Readwise an image.

* test(readwise): add type parameters to vi.fn mocks

oxlint (vitest/require-mock-type-parameters) fails on untyped vi.fn()
in the settings page test; the pre-commit hook only runs eslint, so
this surfaced under full lint:check.

* docs(readme): add Readwise Sync to features list

* fix(readwise): surface settings fetch errors in the UI

The settings page destructured error from useReadwiseSettings but never
rendered it. A failed fetch on mount left the user with an empty default
form and no indication that loading failed. Render an error banner when
error is set, and cover it with component tests.

* test(readwise): cover settings page token test, save, and visibility toggle

Adds component tests for the Test-connection flow (empty/valid/invalid
token), Save (success clears the field, failure surfaces an error toast),
and the show/hide token toggle — the interactive handlers Codecov flagged
as uncovered in ReadwiseSettings.vue.

* test(readwise): match settings buttons by exact label in test helper

Use an exact text match instead of substring matching in the testButton
helper so it can never return the wrong button if labels overlap.

* fix(readwise): harden highlight sync

---------

Co-authored-by: neon <18559658+neonsolstice@users.noreply.github.com>
Co-authored-by: Oscar Nogueira Neto <oscarnogueira@gmail.com>
2026-07-07 19:09:28 -06:00
Bernard Potocki d82a018d1e feat(kobo): sync smart scopes to kobo (#570)
Allow smart scopes to opt into Kobo sync while preserving collection sync behavior. Generate the current Drizzle migration and keep smart-scope matching aligned with the normal book query path.

Fixes #202

Co-authored-by: neon <18559658+neonsolstice@users.noreply.github.com>
Co-authored-by: Jane Doe <janedoe@users.noreply.github.com>
2026-07-07 15:55:08 -06:00
neon 2b8b8962fa feat(reader): adapt KOReader catalog layouts for tall screens
Use measured screen surplus to show additional dashboard and related book rows on tall devices.

Preserve compact one-row layouts when space is tight.

Closes #526
2026-07-07 17:51:31 +00:00
neon 0a4dcc192f feat(books): export single-book zip with pattern-based filename
Use the user's configured download pattern to name single-book zip archives instead of defaulting to a generic 'books.zip'. Update the frontend reader cover card to download multi-track audiobooks as zip files directly.
2026-07-07 17:27:25 +00:00
neon 39d9be30f9 feat(library): support LIBRARY_BROWSE_ROOT to restrict file path access
Introduce a LIBRARY_BROWSE_ROOT environment variable (defaulting to the
current directory) to restrict directories users can browse and select when
setting up library paths. Add a PathPolicyService on the backend to enforce
path containment checks securely, and update the frontend FolderPickerModal
to prevent navigating above the configured browse root.

Closes #567
2026-07-07 17:02:46 +00:00
neon efd3c9f487 fix(types): truncate pattern path segments to 255 bytes
Truncate long path segments generated during upload pattern resolution and
download filename generation to prevent ENAMETOOLONG errors. Keep the file
extension and suffix of the segment, and use a deterministic hash of the
original segment to ensure unique truncations.

Closes #549
2026-07-07 15:35:49 +00:00
neon 55b99858fc feat(books): allow progress updates for manual want_to_read books
Allow the system to automatically update the reading status of a book from 'want_to_read' to 'reading' or 'read' when reading progress is recorded, even if the status was originally set manually. Ensure that manually cataloged books are updated when the user begins reading them, while still protecting other manually set statuses from being overridden by auto-updates.

Closes #566
2026-07-07 04:56:24 +00:00
neon bf4102e8df feat(books): support reviews, rating removal, and plugin enhancements
Add support for user-scoped personal notes (reviews) and allow clearing book ratings to null,
providing full sync capabilities for these actions in the KOReader plugin. Modularize the
KOReader plugin codebase into dedicated mixin files to improve maintainability and expand
the catalog detail view with related sections (series, author, similar books).

Closes #552
2026-07-07 04:31:53 +00:00
neon db5f17f8de feat(opds): honor configured download file pattern for downloads
Update KOReader and OPDS catalog download endpoints to resolve filenames using the settings-configured download file pattern via BookService. This ensures consistent filenames across all download paths.

Closes #569
2026-07-06 23:03:39 +00:00
neon 55967fe977 fix(kobo): stop proxying delete requests for bookorbit collection tags
The DELETE handler for a synced BookOrbit collection tag (col-*) was
missing, so requests fell through to the catch-all proxy and were
forwarded to Kobo's real cloud API. That API rejects the unknown
locally-generated tag id with a 400, so the device never receives a
success ack and endlessly retries the deletion during two-way sync,
keeping sync from ever completing.

Add a dedicated DELETE route that acknowledges col-* tag ids locally,
mirroring the existing add/remove items handlers, and proxies through
unrecognized tag ids as before.

Closes #536
2026-07-06 16:50:02 +00:00
neon 000c380f4f fix(opds): include public smart scopes in OPDS feeds
Allow users to access public smart scopes through the OPDS endpoint. Currently, the service only queries smart scopes owned by the requesting user, which prevents public smart scopes created by other users from showing up on devices like KOReader.

Fixes #560
2026-07-06 04:27:15 +00:00
Dukko fe57701f51 feat(books): add AudiobookCovers.com as a cover search source (#565)
AudiobookCovers.com hosts community-submitted audiobook cover art and has
no public API, so results are scraped from its server-rendered search
page. Since it only indexes square audiobook fan covers (no title/author
metadata), the provider is scoped to audiobook searches and surfaced in
the cover picker only when the audiobook toggle is on.

Closes: #564

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-05 22:21:14 -06:00
neon bc7f1f58bc fix(server): normalize and deduplicate narrator names
Narrator names and sort names are now normalized to remove duplicate
internal whitespace and leading/trailing spaces. Names are deduplicated
case-insensitively when saved to prevent duplicate metadata entries that
are visually identical or very similar in the UI.

Closes #553
2026-07-05 21:12:34 +00:00
neon 947e88c2bc fix(stats): fix peak reading hour queries with timezone parameters
Drizzle generates redundant parameters when grouping by an expression with parameters (like AT TIME ZONE). Wrapping the expression in a subquery ensures the timezone parameter is only injected once.

Closes #558
2026-07-05 20:14:21 +00:00
neon 7f28b40d1f feat(library): use configurable max upload size limit
Replace the hardcoded 500MB limit with a dynamic value from the application
settings to allow uploading files larger than 500MB.

Closes #550
2026-07-04 18:43:18 +00:00
neon 7ae98d3000 fix(metadata): fail hardcover progress sync when page count is missing
When a matched Hardcover edition was missing page counts, progress sync
would silently skip sending updates and save a null progress value to the
local DB. This caused the book to be perpetually stuck in a "Pending sync"
state and triggered endless redundant sync attempts.

We now throw a clear missing pages error during progress calculation to
correctly register the sync error state, log the failure, and trigger background
sync immediately when sync is first enabled for a book.

Closes #544
2026-07-04 17:12:18 +00:00
neon 66f85cf6af fix(books): skip rename if metadata write fails
When saving metadata synchronously, if the file write operation fails, the rename operation is now skipped to avoid renaming a file that could be left in an inconsistent state.

Closes #532
2026-07-04 05:10:04 +00:00
neon 3db72ced64 fix(dashboard): exclude finished books from continue reading
The dashboard query now joins user book statuses to exclude books marked as 'read', 'skimmed', or 'abandoned' from the continue reading list.

Closes #528
2026-07-04 04:50:14 +00:00
dependabot[bot] 8cf88fb547 chore(deps): bump the production-dependencies group with 13 updates (#515)
Bumps the production-dependencies group with 13 updates:

| Package | From | To |
| --- | --- | --- |
| [@lucide/vue](https://github.com/lucide-icons/lucide/tree/HEAD/packages/vue) | `1.21.0` | `1.23.0` |
| [@tailwindcss/vite](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/@tailwindcss-vite) | `4.3.1` | `4.3.2` |
| [@tanstack/vue-virtual](https://github.com/TanStack/virtual/tree/HEAD/packages/vue-virtual) | `3.13.29` | `3.13.31` |
| [cronstrue](https://github.com/bradymholt/cRonstrue) | `3.20.0` | `3.24.0` |
| [driver.js](https://github.com/nilbuild/driver.js) | `1.4.0` | `1.6.0` |
| [reka-ui](https://github.com/unovue/reka-ui) | `2.10.0` | `2.10.1` |
| [tailwindcss](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss) | `4.3.1` | `4.3.2` |
| [vue](https://github.com/vuejs/core) | `3.5.38` | `3.5.39` |
| [@nestjs/swagger](https://github.com/nestjs/swagger) | `11.4.4` | `11.4.5` |
| [fastify](https://github.com/fastify/fastify) | `5.8.5` | `5.9.0` |
| [nodemailer](https://github.com/nodemailer/nodemailer) | `9.0.1` | `9.0.3` |
| [sharp](https://github.com/lovell/sharp) | `0.35.2` | `0.35.3` |
| [unzipper](https://github.com/ZJONSSON/node-unzipper) | `0.12.3` | `0.12.5` |


Updates `@lucide/vue` from 1.21.0 to 1.23.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/1.23.0/packages/vue)

Updates `@tailwindcss/vite` from 4.3.1 to 4.3.2
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.3.2/packages/@tailwindcss-vite)

Updates `@tanstack/vue-virtual` from 3.13.29 to 3.13.31
- [Release notes](https://github.com/TanStack/virtual/releases)
- [Changelog](https://github.com/TanStack/virtual/blob/main/packages/vue-virtual/CHANGELOG.md)
- [Commits](https://github.com/TanStack/virtual/commits/@tanstack/vue-virtual@3.13.31/packages/vue-virtual)

Updates `cronstrue` from 3.20.0 to 3.24.0
- [Release notes](https://github.com/bradymholt/cRonstrue/releases)
- [Changelog](https://github.com/bradymholt/cRonstrue/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bradymholt/cRonstrue/compare/v3.20.0...v3.24.0)

Updates `driver.js` from 1.4.0 to 1.6.0
- [Release notes](https://github.com/nilbuild/driver.js/releases)
- [Commits](https://github.com/nilbuild/driver.js/compare/1.4.0...1.6.0)

Updates `reka-ui` from 2.10.0 to 2.10.1
- [Release notes](https://github.com/unovue/reka-ui/releases)
- [Commits](https://github.com/unovue/reka-ui/compare/v2.10.0...v2.10.1)

Updates `tailwindcss` from 4.3.1 to 4.3.2
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.3.2/packages/tailwindcss)

Updates `vue` from 3.5.38 to 3.5.39
- [Release notes](https://github.com/vuejs/core/releases)
- [Changelog](https://github.com/vuejs/core/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/core/compare/v3.5.38...v3.5.39)

Updates `@nestjs/swagger` from 11.4.4 to 11.4.5
- [Release notes](https://github.com/nestjs/swagger/releases)
- [Commits](https://github.com/nestjs/swagger/compare/11.4.4...11.4.5)

Updates `fastify` from 5.8.5 to 5.9.0
- [Release notes](https://github.com/fastify/fastify/releases)
- [Commits](https://github.com/fastify/fastify/compare/v5.8.5...v5.9.0)

Updates `nodemailer` from 9.0.1 to 9.0.3
- [Release notes](https://github.com/nodemailer/nodemailer/releases)
- [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodemailer/nodemailer/compare/v9.0.1...v9.0.3)

Updates `sharp` from 0.35.2 to 0.35.3
- [Release notes](https://github.com/lovell/sharp/releases)
- [Commits](https://github.com/lovell/sharp/compare/v0.35.2...v0.35.3)

Updates `unzipper` from 0.12.3 to 0.12.5
- [Release notes](https://github.com/ZJONSSON/node-unzipper/releases)
- [Commits](https://github.com/ZJONSSON/node-unzipper/commits)

---
updated-dependencies:
- dependency-name: "@lucide/vue"
  dependency-version: 1.23.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: "@tailwindcss/vite"
  dependency-version: 4.3.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: "@tanstack/vue-virtual"
  dependency-version: 3.13.31
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: cronstrue
  dependency-version: 3.24.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: driver.js
  dependency-version: 1.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: reka-ui
  dependency-version: 2.10.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: tailwindcss
  dependency-version: 4.3.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: vue
  dependency-version: 3.5.39
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: "@nestjs/swagger"
  dependency-version: 11.4.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: fastify
  dependency-version: 5.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: nodemailer
  dependency-version: 9.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: sharp
  dependency-version: 0.35.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: unzipper
  dependency-version: 0.12.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-03 10:05:31 -06:00
neon 1b46c7f3a0 feat(library): allow renaming books into existing folders
Previously, attempting to rename a book into a folder that already existed on disk was blocked. This change allows users to move books into existing folders, enabling scenarios where different formats (like ebooks and audiobooks) can coexist in the same directory. If a book is already tracked at the target path, the files are merged.

Closes #523
2026-07-03 15:29:51 +00:00
neon 8a36f62b59 fix(library): rename all audio tracks of multi-file audiobooks
When renaming/organizing a book with multiple audio content files (such as
a multi-track audiobook), only the primary file was renamed to the new
destination path, leaving the other track files to collide or remain
unprocessed.

Retrieve the `sortOrder` for all files in the file-rename repository. When
renaming, detect if a book has multiple audio content tracks and resolve any
target path collisions among them by appending `-PartXX` suffixes, ordered by
their sort position.

Closes #521
2026-07-03 14:55:25 +00:00
neon 0f6656a303 feat(users): allow setting default library access for new users
Add configuration settings and backend support to define default library access permissions for newly created users. Newly registered users automatically receive these default library access permissions upon creation. Also align current users page controls and magic link options in the admin tab settings UI, adding styled callout cards for empty states.

Closes #486
2026-07-03 06:28:57 +00:00
neon e97d54953b fix(reader): reconcile remote progress before manual KOReader book sync
When manually syncing a book, the plugin uploaded progress without checking if another device had written a newer progress state to the server first, causing reading progress to be overwritten and lost.

Fetch the remote progress from the server before starting a manual sync. If a newer progress state is found on the server, prompt the user to apply the remote progress locally first. If the prompt is rejected, proceed with the sync of annotations and reading history while bypassing the progress upload.

Closes #461
2026-07-03 05:52:22 +00:00
neon 68134a9f85 fix(stats): use local timezone for peak reading hours
Pass the user's timezone settings to the database query to ensure the
Reading Clock widget groups peak reading hours correctly, instead of
defaulting to UTC.

Also includes a minor UI update to the KOReader plugin to expose a
download options button when multiple formats are available.

Closes #513
2026-07-03 05:11:14 +00:00
neon 6fdd2569e1 feat(kobo): add device sync history tracking
Records success and failure events for Kobo device syncs in the database, including operation duration, event types, and error details. Surfaces the recent sync history in the Kobo Settings UI for users to monitor their device activity.
2026-07-03 00:54:01 +00:00
neon 283d9ba6d9 fix(books): cast date offset to int to fix smart scope crash
When using date operators in smart scopes or book queries, PostgreSQL
threw an "operator does not exist: date >= integer" error. This explicitly
casts the days offset to an integer in the SQL generated by the book query
builder to prevent the crash.

Closes #508
2026-07-02 18:29:02 +00:00
neon c63ac6cc6c fix(metadata): add poppler pdfinfo fallback for pdf metadata parsing
Some PDF files contain malformed metadata that pdf-lib cannot correctly
decode, despite being readable by external tools. This introduces a
fallback to extract metadata using Poppler's pdfinfo utility, and improves
XMP keyword parsing as a fallback for missing tags.

Closes #491
2026-07-02 18:15:55 +00:00
neon f0374f01c3 fix(server): batch migration queries to prevent parameter limit errors
Processes database queries in chunks of 500 within the migration
repository. This prevents exceeding PostgreSQL maximum bind parameter
limits when importing or querying large datasets.

Closes #504
2026-07-02 17:50:56 +00:00
neon fb35e232ed feat(reader): add KOReader device removal and unmatched book tracking
Introduces the koreader_unmatched_book_devices schema to track unmatched
books per device. Adds API endpoints and UI in settings for dismissing
unmatched books and completely removing a KOReader device.

Closes #504
2026-07-02 17:36:55 +00:00
neon a0dad23087 feat(kobo): add manual KOReader book hash linking and unmatched tracking
When KOReader syncs progress or annotations, it identifies books by a document
hash. BookOrbit normally resolves this automatically, but some books cannot be
matched. This adds a tracking table for unmatched hashes and a UI to manually
link them to existing library books.

Closes #458
2026-07-02 00:34:16 +00:00
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 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