Skip to content

## v1.24.0 (2026-10-02)

Choose a tag to compare

@github-actions github-actions released this 02 Oct 13:55
· 72 commits to main since this release

v1.24.0 (2026-10-02)

The parity program's first release: Phases 14 and 15 land

The 2026-09-29 reopen (roadmap.md) committed five parity phases; this release ships the first two, the completion wave and the maintenance ring, plus the Dependabot bookkeeping. The Cross-Repo Rule consumers affected this round are CalibreQuarry (adopts get_dirtied_formats() in its Phase 20 lane) and Carrel-calibre-web (the residue trio; adoption waived this round per the decision record).

  • Restore-from-trash verbs (cquarry.write): the trash stops being a dead end. copy_format_from_trash/copy_book_from_trash rescue entries out of .caltrash (pure filesystem), move_format_from_trash undeletes one format into its live book, delete_trash_entry removes one entry, and move_book_from_trash rebuilds the row from the entry's sidecar metadata.opf (Calibre's own backup, read with stdlib ElementTree and never generated, keeping the OPF-generation decline intact): core metadata restored, every extension-bearing file re-registered in data, the cover flag from the file, OPF resync and FTS extraction queued, and the directory moved back only after the rows commit. Custom columns, annotations, and plugin data are not restored (the OPF carries none of them).
  • get_dirtied_formats() (read side): the FTS sidecar's extraction queue as sorted (book_id, format) pairs, the formats sibling of get_dirtied_books(). Retires CalibreQuarry's raw sidecar read when its lane adopts it.
  • set_custom_column_metadata (write side): name/editable/display edits on an existing column, upstream backend.py:1407's shape. The headline use: display={"enum_values": [...]} populates an enumeration without Calibre open. Honest no-op on equal values; a change sets update_all_last_mod_dates_on_start like upstream's wrapper; datatype and label changes are refused by omission (type is baked into the storage layout; the label is the #label search token).
  • The Carrel residue trio (read side): get_book_by_uuid (the Calibre-Companion lookup; case-insensitive, None on miss), get_entity_book_ids(kind, name) (the id-set half of get_entities; tags resolve the engine's anchored-subtree rule so a browse node's set covers its children), and get_all_formats() (a cached {book_id: [FMT]} map).
  • facet_counts / facet_counts_for_ids (read side): per-value counts over a restricted result set (the builtin browse fields plus custom columns by #label; ratings surfaced as stars exactly as field() yields them). The _for_ids half is the restriction plumbing Phase 18's restricted tag browser reuses.
  • WritableCalibreDB.maintain(): vacuum, analyze, and integrity_check over the library DB and the attached FTS sidecar in one standalone pass (upstream backend.py vacuum extended with the two statements calibredb users run by hand; the notes DB stays out of scope). Refuses batch() and mid-transaction states; reports the check rows (["ok"] on a healthy file).
  • check_library_disk() (cquarry.integrity): the extra-side disk checks of upstream's check_library in one tree walk: extra titles/authors, malformed paths and formats, extra formats, unknown files, extra covers, failed folders. The missing side is deliberately not re-answered (find_missing_format_files/find_missing_cover_files own it). The format-vs-unknown-file classifier is structural (a token extension minus the image/OPF/junk set), not upstream's curated BOOK_EXTENSIONS; POSIX case sensitivity assumed.
  • FTS queue verbs (cquarry.write): fts_reindex_book, fts_reindex_all, and fts_queue_clear are the queue half of upstream's fts_unindex family as pure sidecar SQL. One boundary named rather than faked: upstream's index-row deletion runs books_text delete triggers that tokenize through Calibre's custom FTS5 tokenizer, which does not exist outside Calibre, so removing indexed text stays Calibre's job (an O-lane calibredb fts_unindex wrap if a consumer asks).
  • Typed preference writes (cquarry.write): set_preference(key, value) over a five-key whitelist (saved_searches, virtual_libraries, user_categories, grouped_search_terms, fts_enabled), each payload validated by key before anything lands and stored as one JSON row in Calibre's shape; saved_search_add/saved_search_delete/saved_search_rename are the calibredb saved_searches parity item (rename resolves case-insensitively to the stored spelling and refuses to overwrite an existing name where upstream silently overwrites).
  • Housekeeping: the Dependabot gate ticked (both major-bump PRs merged 2026-09-30), the import skills swept per the skill-sync rule, and an API.md table row the maintain() insert had split was repaired in the same window.
  • Suite: 500 -> 556 tests (pytest; 550 collected by CI's unittest discover).