Six feature drops plus a sweep of fixes for default-install breaks that v1.14.2 didn't catch.
Added
-
Editable Quality Profiles (#768) — quality profiles existed in the model and had a read-only Settings tab, but no way to create, edit, or delete them existed. The
/api/v1/qualityprofileendpoint gains CRUD behindRequireAdmin, and Settings → Quality Profiles is now a full editor: reorderable preference list, per-format allow toggle, cutoff<select>restricted to allowed items, audiobook badge on m4b/mp3/flac/m4a entries. Delete-in-use returns 409 with the conflicting author count. Closes the obvious migration story from *arr that the read-only UI implied but didn't deliver. -
Configurable author monitor modes (#792, #809, #810) — adding a prolific author no longer floods Wanted with the full back catalogue. The Edit Author dialog gains a
MonitorModeselector with five values:all(existing behaviour),future(only books with a future release date),latest N,none, andseries— the last lets the user pick one or more of that author's series and monitor only books in those series.BINDERY_AUTHOR_DEFAULT_MONITOR_MODEsets the global default for new authors (any mode exceptseries, which is per-author by design). Existing books can be retroactively re-monitored to match the new mode via an "apply to existing" checkbox. Thanks to @anthonysnyder for the original ask and @magrhino for implementing #809. -
Bulk monitor/exclude/delete on Author detail (#791) — per-row checkboxes + select-all header with indeterminate state + sticky bulk-action bar. Monitor / Unmonitor immediate, Exclude / Delete behind a confirm. Selection auto-prunes when filters hide rows. Pairs with the new monitor modes for a coherent "add prolific author" workflow. Thanks to @anthonysnyder.
-
Per-media-type download client category (#700) — a download client now accepts a second
CategoryAudiobookfield. When set, audiobook grabs go under that category and validate againstBINDERY_AUDIOBOOK_DOWNLOAD_DIR; ebook grabs use the existingCategoryagainstBINDERY_DOWNLOAD_DIR. The fuzzystrings.Contains(category, "audio")heuristic is gone. Multi-client setups can now usePickClientForMediaTypeto route by explicit field. Thanks to @strenkml. -
Calibre import/sync rollback (#643) — Calibre import gains run tracking + entity snapshots + rollback preview + rollback execute, modelled on the existing ABS rollback. A bad Calibre import is now revertible without a database-level snapshot. Metadata-only revert — on-disk files are not touched. New endpoints under
/api/v1/calibre/runs[/{id}/rollback[/preview]]behindRequireAdmin; Settings → Calibre gains a "Recent imports" panel with per-run Rollback. Sync-side (push to Calibre) intentionally excluded — it only mutatescalibre_id, no data damage. Thanks to @magrhino. -
Edit existing Prowlarr connection (#820) — the Prowlarr card in Settings → Indexers gained Add/Test/Sync/Delete but never Edit, so rotating the Prowlarr API key required deleting and recreating the instance (which cascade-deleted every synced indexer row and the user's local toggles/priorities). New Edit form is wired to the existing
PUT /api/v1/prowlarr/{id}. Key rotation propagates immediately to every indexer row managed by the instance, so synced indexers never authenticate with a stale key. URL changes still need a manual Sync to rebuild per-indexer torznab URLs; the form warns when URL is edited. Thanks to @magrhino. -
Hardcover series refs populated during list sync (#805) — Hardcover GraphQL queries for custom lists and built-in shelves now request
featured_seriesfields; the list syncer wires those refs intoSeriesRepo.LinkSeriesRefsbest-effort after each book create. Series links appear immediately for books imported from Hardcover lists rather than requiring a later manual rebind. Thanks to @magrhino.
Fixed
-
OpenLibrary name/title searches no longer fail with HTTP 403 (#834) — OpenLibrary's API now blocks requests whose
User-Agentdoes not include a contact pointer (email or URL), and Bindery's previousbindery/<version> (<os>)UA matched that block. Name/title book additions failed immediately against OpenLibrary while ISBN lookups (which fall through to Hardcover enrichment) still worked, so the breakage was easy to miss in smoke checks. The User-Agent now appends the project URL —bindery/<version> (<os>; https://github.com/vavallee/bindery)— which satisfies OpenLibrary's policy and unlocks its higher rate limit. Thanks to @thetic for the precise repro and root-cause analysis. -
qBittorrent on Windows reports paths with backslashes; Bindery now normalizes them (#800 follow-up) — a qBittorrent instance running on Windows reports
SavePath,ContentPath, and categorySavePathwith backslash separators (e.g.N:\Torrents\complete\library\book). Bindery's downstream Linux path code (filepath.Walk,PathRemap.Apply,pathIsAtOrUnder) cannot process those, so a Docker-on-Windows user's import failed withno book files found in N:\...even though their PathRemap looked right. Backslashes are now normalized to forward slashes at the qBit API boundary; PathRemap configuration becomes predictable across deployments. Reported by PixieApples on Discord. -
qBittorrent path remap is now verified against the on-disk filesystem (#800 follow-up) — the PathRemap suggestion shipped in v1.14.2 only checked whether the remapped string was at-or-under
BINDERY_DOWNLOAD_DIR, never that the resolved path actually existed. Linux is case-sensitive, so a Windows/WSL/Docker user with their drive mounted at/N/but Bindery configured with/n/(or vice versa) got a textually-correct remap suggestion that silently pointed at nothing. The health check now stat()s the resolved path; if missing but a case-variant exists, the error names the divergent segment so the user knows exactly which letter to fix. Also reported by PixieApples. -
qBittorrent import diagnostics now surface real failure modes (#824) — three gaps made torrent import failures near-invisible even with debug logging on:
GetTorrentsAPI errors were debug-level, loop-level skip-on-no-match was silent, and the "no book files found" error didn't distinguish path-doesn't-exist from path-exists-but-empty. API errors now log at Warn, the poll loop emits per-skip and per-match debug context, and when the download path doesn't exist a separate Warn names PathRemap as the likely fix. Thanks to @statte. -
Docker → bare-metal host firewall is now named in the ECONNREFUSED hint —
(service may not be running on that port)was misleading when the service was running and a host firewall was REJECTing traffic from the Docker bridge subnet (REJECT sends RST → ECONNREFUSED). The hint now reads(connection refused — service may not be listening on that port, or a host firewall is rejecting traffic from the Docker subnet). Reported by Daize on Discord. -
Grimmory integration is now honest about being configuration-only (#818) — Grimmory v3.x has no API keys (confirmed upstream at grimmory-tools/grimmory#1487), but the Bindery UI implied the API key was required, sending users on wild-goose chases when they tried to find one. The field is now optional with a clear note, the empty
Authorizationheader is suppressed when no key is set, and a banner at the top of the tab acknowledges that Bindery does not yet push books to Grimmory — that's tracked in #826. Reported by Merijeek. -
Migration version collision (043) (#832) — internal hotfix during the v1.15.0 cycle: two unrelated PRs both shipped a
043_*.sqlmigration and the runner skips any version it has already applied, so on existing installs only the first 043 would run and the second's schema change would be silently lost. The author-monitor-mode migration was renumbered to 045 before any release shipped the collision.
Changed
- Per-media-type expected-dir resolution (#700 ripple) —
ExpectedDownloadDirForClientnow takes amediaTypeparameter rather than guessing from category name. The fuzzystrings.Contains(category, "audio")heuristic is gone.
Chores
- Dependency bumps via dependabot (#815, #817) and vulnerable Go modules update (#819).
- IndexersTab i18n migration (#838) — Settings → Indexers strings now route through
t()so they translate per locale. - Sweep of 113
t.Fatalnil-check sites in test files to satisfy staticcheck's SA5011 analyzer (#841); no runtime behaviour change.