Skip to content

Releases: markbyrne/libris

v0.4.1 — Config endpoint for path auto-detection

Choose a tag to compare

@markbyrne markbyrne released this 03 Jul 00:15

Adds authenticated GET /api/v1/config to the directive API, returning {incoming_dir, state_db, review_dir, version} so Librarr's new Detect paths button can auto-fill its Libris plugin settings instead of the user hand-typing paths from another machine.

Same fail-closed X-Api-Key auth as the rest of the API. 530 tests green, CI green on Python 3.10/3.11/3.12.

🤖 Generated with Claude Code

v0.4.0 — Directive API

Choose a tag to compare

@markbyrne markbyrne released this 02 Jul 19:30

Directive API

External tools (e.g. Librarr) can now pre-register the correct match for an incoming file, so Libris imports it with exact, already-verified metadata instead of re-matching from scratch.

  • POST /api/v1/directives — register a match (title, author, ISBN, year, cover, series) keyed on the incoming filename. API-key auth (X-Api-Key), disabled by default (api.enabled + api.api_key in config).
  • GET /api/v1/ping — connectivity/auth check.
  • The watcher applies a matching directive at pickup and skips all metadata lookups (Google Books / Open Library / DuckDuckGo); files without a directive are matched exactly as before.
  • Directives are one-shot (consumed on use) and purged after 48h if the file never arrives.
  • Structured double-dash filenames remain the zero-config fallback path.

Everything downstream — duplicate detection, format conversion, tagging, calibredb import — is unchanged.

v0.3.17b0

Choose a tag to compare

@markbyrne markbyrne released this 13 Jun 14:23

Bug fixes

  • revert-import broken on split-library setups_export_from_book_files had its own calibredb list call that always returned empty formats for relocated books. Now falls back to reading metadata.db directly, the same pattern used elsewhere in the codebase.

  • Duplicate not caught when series prefix differs_find_calibre_duplicates used exact-title search only, so importing "The Merchant of Death" would create a duplicate alongside an existing "Pendragon: The Merchant of Death". A secondary contains-mode search now runs when the primary returns empty, catching mismatches in both directions.

Feature

  • Richer Anna's Archive filename parsing — the title -- author -- year -- publisher -- md5 convention is now fully parsed: author initials (D_ J_D.J.), narrator split from author field, ISBN from isbn13/isbn10 fields, series name + ordinal from Series_ Book N, Title, and attribution strings (Anna's Archive, etc.) consumed silently. All fields are wired into the metadata lookup, yielding near-perfect confidence scores for AA filenames.

v0.3.16b0

v0.3.16b0 Pre-release
Pre-release

Choose a tag to compare

@markbyrne markbyrne released this 13 Jun 12:37

What's new

Cover placeholder rejection (#71)

Two-layer defence against junk cover images being saved to the Calibre library:

Layer 1 — OpenLibrary ?default=false

OpenLibrary's cover CDN serves a full-size "image not available" placeholder JPEG with HTTP 200 when a cover is missing. Content inspection cannot reliably distinguish it from a real cover. Adding ?default=false to all OL cover URLs converts these into 404 responses, which are already rejected.

Layer 2 — _download_cover validation

Even when a URL returns HTTP 200 with image/*, the body is validated before saving:

  • Content-type must start with image/ (rejects HTML error pages, JSON, etc.)
  • Body must be ≥ 1024 bytes (rejects blank GIF stubs)
  • Pixel dimensions must be ≥ 100px in both axes (rejects 1×1 tracking pixels); unknown formats (WebP, etc.) fail open

The pixel sniffer parses PNG IHDR, GIF header, and JPEG SOF0/SOF2 segments from the first ~500 bytes — no imaging library required.

New tests

12 tests in tests/unit/test_cover_validation.py covering _image_dimensions(), _download_cover() validation paths, and OL URL ?default=false on both cover_i and ISBN fallback URLs.


Full diff: v0.3.15b0...v0.3.16b0

v0.3.15b0

v0.3.15b0 Pre-release
Pre-release

Choose a tag to compare

@markbyrne markbyrne released this 13 Jun 00:13

What's new

Shadow-library filename convention (#70)

Files named {title} -- {author} -- {year} -- {publisher} -- {md5}.ext (Anna's Archive et al) are now parsed as structured fields: title and author drive the search directly, the year becomes a scoring hint, and the publisher and content hash are discarded as query noise. Previously these filenames produced garbage queries with no author hint.

Also: trailing md5/sha1/sha256 content hashes are stripped from any filename, and stray -- separators collapse like single dashes.

Full Changelog: v0.3.14b0...v0.3.15b0

v0.3.14b0

v0.3.14b0 Pre-release
Pre-release

Choose a tag to compare

@markbyrne markbyrne released this 12 Jun 18:48

Fixed

  • set_cover failures are now surfaced (#68)get-covers previously reported ✓ even when calibredb could not write the cover (e.g. permission errors); it now prints ✗ with a hint, counts failures, and exits non-zero
  • Cover relocation, rename sync, and split-mode export no longer silently no-op for relocated books (#69)_get_format_paths falls back to reading books.path + data from metadata.db when calibredb list reports no formats (the same split-mode blindness fixed for list_books in v0.3.13b0, one layer deeper). Production symptom: covers written to the metadata.db side never moved to the book-files side, breaking calibre-web thumbnail generation.

Full Changelog: v0.3.13b0...v0.3.14b0

v0.3.13b0

v0.3.13b0 Pre-release
Pre-release

Choose a tag to compare

@markbyrne markbyrne released this 12 Jun 18:28

Fixed

Split-library mode libraries were invisible to get-covers and clean-library (#67). calibredb list --fields formats only reports a format when the file exists under the metadata.db directory — true for no properly-relocated book in split mode, so 84 of 90 books in the reporting library were silently skipped. list_books now falls back to reading books.path + data directly from metadata.db (read-only) when calibredb reports no formats.

Full Changelog: v0.3.12b0...v0.3.13b0

v0.3.12b0

v0.3.12b0 Pre-release
Pre-release

Choose a tag to compare

@markbyrne markbyrne released this 12 Jun 17:58

What's new

Covers (#66)

  • The matched cover is now always saved as cover.jpg in the book's library directory on import (via calibredb set_cover, split-library aware) — previously gated behind output.embed_cover_art
  • output.embed_cover_art now does exactly what its name says: embed the art inside the audio file as album art (it was never actually passed to the tagger before)
  • New libris get-covers command backfills cover.jpg for books already in the library: uses the cover URL recorded at import time, falls back to a fresh Google Books / OpenLibrary lookup by title/author; --dry-run to preview

Full Changelog: v0.3.11b0...v0.3.12b0

v0.3.11b0

v0.3.11b0 Pre-release
Pre-release

Choose a tag to compare

@markbyrne markbyrne released this 12 Jun 13:56

What's new

Fixed

  • libris --version crashed for PyPI installs — it still looked up the old libris distribution name after the pylibris rename (#65)
  • Standalone install.sh runs no longer install a stale hardcoded git tag (v0.2.0-beta) — remote installs now pull the latest release from PyPI (#64)
  • install.sh local-mode detection updated for the pylibris rename (#64)

Added

  • Donation links: Ko-fi and GitHub Sponsors — repo Sponsor button, README Support section, --help epilog, installer finale (#65)

Full Changelog: v0.3.10b0...v0.3.11b0

v0.3.10b0 — first public release

Pre-release

Choose a tag to compare

@markbyrne markbyrne released this 12 Jun 13:07

First public release 🎉

Libris is now open source (MIT) and published on PyPI as pylibris:

pip install pylibris   # installs the libris command

What's new since v0.3.9b0

calibre-web coexistence (#62)

New calibre.reconnect_url config — libris pings calibre-web's /reconnect endpoint after every import and removal so calibre-web reopens its database connection instead of holding a stale view across external calibredb writes (the mechanism behind database disk image is malformed). Best-effort: failures never affect a completed import. See the new Coexisting with calibre-web README section for the full corruption-prevention guide.

Packaging

  • Distribution renamed to pylibris (libris was taken on PyPI); import package and CLI are unchanged
  • __version__ now derives from package metadata
  • CI publishes to PyPI via trusted publishing on every GitHub release

Full Changelog: v0.3.9b0...v0.3.10b0