Hardcover metadata source, request/UI fixes, and QA/CI pipeline#1
Merged
Conversation
When Libreseerr POSTs a new author, Bookshelf's metadata refresh
immediately creates unmonitored edition records for all that author's
books. The subsequent book POST then collides (duplicate edition), so
add_book fell into its "already exists" paths and returned the record
without ever monitoring it or triggering a search — the book just sat
there and never downloaded.
Add _monitor_and_search(), used by both existing-book paths, which sets
the book monitored via the dedicated /book/monitor endpoint (the full
PUT /book/{id} crashes server-side on null editions) and fires a
BookSearch command. Also ensure the author is monitored via
/author/editor so RSS auto-grabs work for future books, without
touching individual book monitoring.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019QAAfNHLFzwaD5CXerQU8g
Three issues on the Discover page: - Flicker / results coming and going: the book cover onerror fallback pointed at via.placeholder.com, which is now defunct. Each broken cover re-set src to that dead host, which failed and re-fired onerror in an infinite loop, thrashing the network. Replace it with a self-contained inline SVG data-URI and null onerror so it can't loop. - Same book shown several times: a popular work appears in multiple category rows. Dedupe across rows in loadDiscovery(), keeping the first occurrence. - Foreign-language editions: add language=eng to the search.json discovery categories so sort=rating stops surfacing translations. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019QAAfNHLFzwaD5CXerQU8g
The completion check relied solely on book.statistics.bookFileCount, which Readarr/Bookshelf cache and don't recompute until an author refresh runs. A freshly imported file therefore showed bookFileCount=0, leaving the request stuck on "Processing" even though the file was on disk and mapped to the book. Add get_book_files() (GET /bookfile?bookId=) to the Readarr and Bookshelf clients and consult it in refresh_requests: a request is completed if the cached count is positive OR a book file actually exists. LazyLibrarian returns an empty list (no per-book file endpoint; it still completes via its snatched-list status check). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019QAAfNHLFzwaD5CXerQU8g
Add an optional Hardcover API token in Settings. When a token is configured, Hardcover replaces Open Library for the Discover search box and the discovery carousels; with no token, behaviour is unchanged. Hardcover is the same provider Bookshelf uses for its own metadata, so results line up better with what the backend can add, and it offers real popularity/rating signals for the trending rows. - hardcover.py: HardcoverClient (GraphQL) with test_connection, search_books (Typesense search), discover (Hasura books order_by for trending/best/new, search-term queries for genre rows), normalized into the shared book schema. - app.py: get_metadata_client() gate; /api/search and /api/discover branch on the configured token; 5-minute discover cache keyed by (source, category) to stay under Hardcover's 60 req/min; admin GET/POST /api/hardcover and /api/hardcover/test endpoints. - Settings UI: "Metadata Source - Hardcover" card with token, Test, Save. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019QAAfNHLFzwaD5CXerQU8g
Protect against ever pushing sensitive or noise files: - /data/ holds the Hardcover token, server API keys, hashed passwords, and the session secret_key — must never be committed. - __pycache__/*.pyc were tracked from upstream; untrack them. - Ignore Claude/AI assistant files (CLAUDE.md, .claude/, .mcp.json) and common env/secret files. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019QAAfNHLFzwaD5CXerQU8g
These were committed upstream; they are build artifacts and now ignored via .gitignore. Removing from the index (kept on disk locally). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019QAAfNHLFzwaD5CXerQU8g
Book/author lookups and library listings proxy the server's upstream metadata provider and are slow — several seconds when idle and well over the previous 15s while the instance is busy refreshing (e.g. right after a new, prolific author is added). Requesting a book by a not-yet-added author could fail at the lookup stage with a read timeout before the author was ever created. Introduce LOOKUP_TIMEOUT (60s) in the Readarr and Bookshelf clients and apply it to the lookup/list calls. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019QAAfNHLFzwaD5CXerQU8g
renderRequest() used the same defunct via.placeholder.com fallback with a self-referential onerror as the Discover cards did, so a broken/missing request cover looped onerror against a dead host — the bouncing-text flicker. Use the inline NO_COVER SVG and null onerror, matching the Discover fix. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019QAAfNHLFzwaD5CXerQU8g
A stalled torrent ("download is stalled with no connections") marked a
request as error, and refresh then skipped any error request forever, so
it never recovered even after the file imported.
Re-evaluate everything except completed requests, and check for an
imported file first: if the book has a file, mark it completed
regardless of a stalled/seeding queue entry or a prior error. Clear the
stored error when a request moves to downloading/completed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019QAAfNHLFzwaD5CXerQU8g
Introduce a pytest suite that exercises our own logic without mocking the external book servers: a boot/smoke check, Open Library normalizer units, the client factory, JSON persistence round-trips, and a duck-typed client contract check. Tests redirect the data dir via a new LIBRESEERR_DATA_DIR env override so they never touch real data/. Add a GitHub Actions workflow (ruff + pytest on Python 3.12, plus a Docker build-and-boot job) and a ruff config. Apply the resulting lint fixes: modern union type hints, sorted imports, and timezone-aware datetimes (datetime.now(UTC) instead of deprecated utcnow()). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019QAAfNHLFzwaD5CXerQU8g
Use a host bind mount at /opt/appdata/libreseerr instead of a named volume, set an explicit container_name, make the host port configurable via PORT, and pass through an optional SECRET_KEY (auto-generated when blank). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019QAAfNHLFzwaD5CXerQU8g
The pytest console script (used in CI) does not put the repo root on sys.path, so conftest's 'import app' failed with ModuleNotFoundError — only 'python -m pytest' had been exercised locally. Set pythonpath=['.'] so imports work under either invocation. Also bump checkout@v4->v5 and setup-python@v5->v6 to clear the Node 20 runner deprecation, and run pytest verbosely for clearer CI output. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019QAAfNHLFzwaD5CXerQU8g
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an optional Hardcover metadata source, fixes several request/discover/UI issues, and introduces the project's first automated QA gate (tests + GitHub Actions CI).
Features
Fixes
QA / CI (new)
LIBRESEERR_DATA_DIRenv override so tests never touch realdata/.Verification
CI green on the latest commit: Lint & test and Docker build & boot both pass.
🤖 Generated with Claude Code