Skip to content

feat(genres): canonical book genres in a Calibre custom column - #168

Merged
phildenhoff merged 1 commit into
mainfrom
opds-port-genres
Sep 24, 2026
Merged

phildenhoff merged 1 commit into
mainfrom
opds-port-genres

Conversation

@phildenhoff

Copy link
Copy Markdown
Member

Why

Calibre tags are freeform, which makes them a noisy basis for a genre facet. Citadel wants a small canonical genre list — distinct from tags, and distinct from whatever a metadata provider happens to file under "subject" — that books can be assigned and readers can browse. ADR 0006 has the full reasoning.

This is #153 ported onto current main. The old stack predates the sharing rewrite (#160/#161/#165), so it couldn't be rebased; I carried the feature over by hand.

What changed

  • libcalibre stores genres in a #citadel_genres custom column. set_book_genres / add_book_genres normalize names, list_genres returns genres with book counts, and BookQuery can filter by genre.
  • Metadata providers carry genre candidates separately from subjects. Only Hardcover search genres (hardcover:genre) and MARC 655 genre/form terms (marc:655) are authoritative; Open Library subjects and other MARC 6XX fields stay suggestions and are never promoted silently.
  • EditBook shows stored genres and provider genre suggestions next to subjects, with add / add-all.

The column is only created when someone accepts a genre — reads never create schema — so a library shows no genres until the first assignment.

Not in this PR

#153 also filled in the OPDS Genres facet, but that facet lives in the catalog navigation layer from #152, which main doesn't have. It moves to the search/navigation port, which comes next. The ADR is renumbered 0003 → 0006 since 0003 is now the credentials ADR.

Validation

cargo test --workspace (0 failures, including the genre tests in libcalibre and the provider normalization tests), vitest run (250 passed), tsc --noEmit clean. bun lint shows the same 3 warnings as main, in files this doesn't touch.

I haven't clicked through the EditBook genre UI in the running app, and bindings.ts is hand-merged rather than regenerated — it matches the one new command and type, but the first debug run will confirm.

Calibre tags are freeform, so they make a noisy basis for a genre facet.
Genres now live in a `#citadel_genres` multi-value text column (ADR 0006),
created only when a user first accepts a genre and never by reads.

- libcalibre: set_book_genres / add_book_genres normalize and store
  genres, list_genres returns them with book counts, and BookQuery can
  filter by genre.
- Metadata providers carry genre candidates separately from subjects.
  Only Hardcover search genres and MARC 655 genre/form terms count; Open
  Library subjects and other MARC 6XX fields stay suggestions.
- EditBook shows stored genres and provider genre suggestions, with add
  and add-all.

Ported from #153 onto the current main. The OPDS Genres facet from that
PR needs the catalog navigation layer, so it moves to the search port.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@github-actions

Copy link
Copy Markdown

libcalibre Test Coverage Report

Overall coverage: 80.59%

📊 Download HTML Report

Coverage breakdown available in the artifacts.

@phildenhoff
phildenhoff merged commit d52a55d into main Sep 24, 2026
7 checks passed
@phildenhoff
phildenhoff deleted the opds-port-genres branch September 24, 2026 06:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant