github vavallee/bindery v1.24.0

3 hours ago

The Grimmory integration goes from a settings tab nothing read to a working
push pipeline, and the UI can now be embedded in a dashboard iframe when an
operator explicitly opts in. A new per-author "Monitor newly discovered
books" policy defuses the refresh-mass-monitors-the-back-catalogue trap.
Three community bug reports filed this week are fixed in the same cut: books
silently stranded under the wrong author, the author-page filters mishandling
dual-format books, and unreadable NZB grab failures. Repo-side, the CI
pipeline gains AI triage/review bots and a hardening pass on the fork-facing
workflows.

Added

  • Grimmory push pipeline — BookDrop upload on import plus bulk sync
    (#1392, closes #826) — the Grimmory integration was configuration-only: a
    Ping() client and a Settings toggle no code ever read. It now works end to
    end. A new internal/grimmory client does JWT auth against Grimmory's API
    (login → access/refresh pair, rotation, one 401 retry after re-auth; a set
    api_key is honoured as a static Bearer token and bypasses login), and
    streams a multipart upload to POST /api/v1/files/upload/bookdrop on a
    dedicated 5-minute timeout. A Pusher is hooked into the importer alongside
    the Calibre/CWA handoffs — settings are read live per push (no restart) and
    pushes are best-effort by contract, so a Grimmory failure never fails the
    import. BookDrop has no server-side dedup, so idempotency is Bindery's:
    migration 059 adds a grimmory_pushes table keyed by file path, consulted
    on every push. Bulk sync (grimmory.Syncer, admin-only
    POST /grimmory/sync + GET /grimmory/sync/status) mirrors the Calibre
    single-job pattern (409 on concurrent start, polled progress, capped error
    list). Settings → Grimmory gains username/password fields, a real
    Test Connection login check, a "Push all to Grimmory" button with live
    progress, and the experimental banner is gone. Ebook files only for now —
    BookDrop takes one file per upload, which multi-part audiobook folders don't
    reduce to.
  • Opt-in iframe embedding via BINDERY_FRAME_ANCESTORS (#1367) — the UI
    could not be embedded in an <iframe> because SecurityHeaders hard-coded
    X-Frame-Options: DENY and CSP frame-ancestors 'none', blocking use inside
    dashboards like Organizr. That clickjacking lockdown stays the default; an
    operator can now set BINDERY_FRAME_ANCESTORS to a CSP frame-ancestors
    source list ('self' for same-origin, or a specific origin such as
    https://organizr.example.com) to allow framing per trusted origin. When
    set, X-Frame-Options is dropped, since it can't express an origin allowlist
    and DENY/SAMEORIGIN would override the more expressive CSP directive.
    Documented in docs/DEPLOYMENT.md and charts/bindery/values.yaml.
  • Per-author "Monitor newly discovered books" setting (#1348) — "Refresh
    Metadata" shares the add code path, so on a default-config author (monitor
    mode all) a refresh that discovered the provider's full back-catalogue
    created every work monitored + Wanted, queueing a search storm the user
    never asked for. Each author now has a Monitor New Items policy in the edit
    modal: Follow monitor mode (default, previous behaviour) or Add as
    unmonitored
    , which applies to works discovered after the initial sync —
    the add flow and migrations still honour the monitor mode, and a genuinely
    wanted back-catalogue is one bulk-monitor away. Import-created authors
    (Calibre, Audiobookshelf) default to Add as unmonitored: they start
    with a partial catalogue, which made the first refresh after an import the
    classic detonation point.

Fixed

  • Author sync no longer strands books under the wrong author (#1405) — a
    work fetched during an author's sync could match an existing row created
    under a different author record (a duplicate OpenLibrary author key, a
    Calibre shell author, or an earlier book-level add). The sync refreshed that
    row's ratings forever but never re-linked it, and since the author page
    filters by author_id the book was permanently invisible under the real
    author — the reported case being Elantris missing from Brandon Sanderson
    despite being fetched on every sync. The sync now re-links such rows to the
    author being synced, using the provider's credited-author list as the
    safety check: a genuinely co-authored work (credited to both authors, e.g.
    the Wheel of Time books Sanderson finished for Robert Jordan) stays with
    its current owner so it can't ping-pong between authors on alternating
    syncs, and when authorship can't be determined the row is left untouched.
  • Author-page filters now respect the selected media type for dual-format
    books
    (#1406) — a book wanted as Both was invisible under the
    Type: Ebook / Type: Audiobook chips (the filter compared mediaType
    exactly), and the Status chips judged the combined status, so a Both book
    whose ebook was already imported never showed under Type: Ebook +
    Status: Imported while its audiobook was still wanted. Both books now match
    either type chip, and with a type selected the status filter judges that
    format's own state (its file on disk → imported), so each side of a
    dual-format book filters correctly.
  • NZB grab failures now explain themselves — including the NZBFinder
    "error 203" case
    (#1404) — when an indexer refuses the NZB download, the
    error now surfaces the parsed newznab code and description instead of raw
    XML, and when the grab was redirected off its original host (Prowlarr's
    per-indexer Redirect setting handing the fetch to an app-whitelisting
    indexer that rejects Bindery's identity) the error names both hosts and
    points at the Prowlarr setting to disable. Applies to both SABnzbd and
    NZBGet grabs; a matching entry was added to the troubleshooting guide.

CI

  • AI triage, PR review, and nightly backlog-sweep bots (#1222) — new
    ai-triage, PR-review, and ai-sweep workflows automate issue triage and
    first-pass PR review. The same change hardens the fork-facing notify
    workflows: the pull_request_target path no longer interpolates
    github.event.* inline (script-injection vector), permissions are cut to
    the minimum each job needs, and the ESLint security scan is pinned in the
    lockfile so the SARIF step actually runs instead of silently no-opping.

Don't miss a new bindery release

NewReleases is sending notifications on new releases.