Skip to content

feat(library): effective genre falls back to MusicBrainz enrichment#949

Merged
byrongamatos merged 2 commits into
mainfrom
feat/enrichment-genre-fallback
Jul 13, 2026
Merged

feat(library): effective genre falls back to MusicBrainz enrichment#949
byrongamatos merged 2 commits into
mainfrom
feat/enrichment-genre-fallback

Conversation

@byrongamatos

@byrongamatos byrongamatos commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Converted packs rarely carry a genres manifest key — on the real library 1188 of 1190 songs had no genre, starving the library genre facet and the career passport rack (2 usable genres) while song_enrichment already held MusicBrainz genres for 636 matched songs.

The effective-genre expression now resolves: per-song override → pack genre → json_extract(enrichment.genres, '$[0]') for matched/manual rows only (review/failed candidates could carry the wrong recording's genres — and user-pinned manual matches are the most trusted, a Codex round-2 catch). Fast-path gating preserved: the plain indexed column when neither source has data; the override-only expression when overrides exist but the enrichment table doesn't (round-3 catch — stand-in DBs must never receive SQL referencing a missing table). Career passports and the library genre filter both pick this up automatically through _effective_genre_expr().

Measured on the real library: the passport rack went from 2 genres to 50 (heavy metal ×184, rock ×116, power metal, thrash, ska, prog…), with coverage growing as enrichment keeps matching.

Testing

Precedence matrix (override > pack > enrichment; review/failed never leak; manual counts), enriched-genre filtering finds the song, plain-column fast path, missing-table safety. Full suites at round 1: pytest 2485, JS 1170. Codex preflight clean (round 3).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Improved genre browsing and filtering by using a fallback chain: manual/field override → pack genre → trusted MusicBrainz enrichment primary genre (when available).
    • Genre browsing and “passport” coverage begin immediately and expand as enrichment runs.
  • Bug Fixes

    • Ensures only matched/manual, trusted enrichment contributes to the effective genre; unmatched/unreviewed rows no longer affect results.
    • Safely handles cases where enrichment data isn’t present, avoiding errors.
  • Tests

    • Added regression and precedence coverage for effective genre selection, including behavior without enrichment inputs.

Converted packs rarely carry a genres manifest key — on Byron's real
library 1188/1190 songs had no genre, starving the genre facet and the
career passport rack (2 usable genres) while song_enrichment already
held MB genres for 636 matched songs.

The effective-genre expression now resolves: per-song override → pack
genre → json_extract(enrichment.genres, '$[0]') for MATCHED rows only
(review/failed candidates could carry the wrong recording's genres).
Same fast-path gating as before: the plain indexed column is used
unless overrides or enrichment genres actually exist; stand-in DBs
without the table degrade via the OperationalError guard. Career
passports pick this up automatically through _effective_genre_expr().

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: cec148c9-3bbc-4468-b50a-8a087fd8c997

📥 Commits

Reviewing files that changed from the base of the PR and between 43423d4 and 3b4a250.

📒 Files selected for processing (1)
  • CHANGELOG.md

📝 Walkthrough

Walkthrough

Genre facet and filtering now compute an effective genre using override, pack genre, and trusted MusicBrainz enrichment precedence. MetadataDB safely handles absent enrichment tables, with tests covering precedence, fallback, filtering, and robustness.

Changes

Genre resolution

Layer / File(s) Summary
Effective genre SQL
lib/metadata_db.py
Genre selection now prioritizes non-empty song overrides, then pack genre, then primary genre from matched or manually enriched songs; enrichment lookup is disabled when the table is unavailable.
Genre resolution validation
tests/test_field_overrides.py, CHANGELOG.md
Tests cover precedence, facet and filter results, plain-column fallback, and missing-table safety; the changelog documents the fallback behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the change and testing, but it misses required template sections like What, feedpak surface, and the checklist. Add the missing template sections, including What, feedpak surface if applicable, and the checklist items for changelog, tests, and DCO sign-off.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: genre fallback to MusicBrainz enrichment.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/enrichment-genre-fallback

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
lib/metadata_db.py (1)

1099-1113: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Duplicated override SQL fragment across the two constants.

The override-lookup subquery (lines 1100-1102 vs 1105-1107) is copy-pasted verbatim between _EFFECTIVE_GENRE_OVERRIDE_SQL and _EFFECTIVE_GENRE_SQL. A future tweak to override matching (e.g. adding a new exclusion) risks being applied to only one constant.

♻️ Extract the shared override fragment
+    _EFFECTIVE_GENRE_OVERRIDE_FRAGMENT = (
+        "(SELECT o.value FROM song_field_override o "
+        "WHERE o.filename = songs.filename AND o.field = 'genre' "
+        "AND o.value IS NOT NULL AND o.value != '')"
+    )
     _EFFECTIVE_GENRE_OVERRIDE_SQL = (
-        "COALESCE((SELECT o.value FROM song_field_override o "
-        "WHERE o.filename = songs.filename AND o.field = 'genre' "
-        "AND o.value IS NOT NULL AND o.value != ''), genre)"
+        f"COALESCE({_EFFECTIVE_GENRE_OVERRIDE_FRAGMENT}, genre)"
     )
     _EFFECTIVE_GENRE_SQL = (
-        "COALESCE((SELECT o.value FROM song_field_override o "
-        "WHERE o.filename = songs.filename AND o.field = 'genre' "
-        "AND o.value IS NOT NULL AND o.value != ''), "
+        f"COALESCE({_EFFECTIVE_GENRE_OVERRIDE_FRAGMENT}, "
         "NULLIF(genre, ''), "
         "(SELECT json_extract(e.genres, '$[0]') FROM song_enrichment e "
         "WHERE e.filename = songs.filename AND e.match_state IN ('matched', 'manual') "
         "AND e.genres IS NOT NULL AND e.genres NOT IN ('', '[]')), "
         "'')"
     )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/metadata_db.py` around lines 1099 - 1113, Extract the duplicated genre
override lookup subquery into a shared SQL fragment near
_EFFECTIVE_GENRE_OVERRIDE_SQL and _EFFECTIVE_GENRE_SQL, then interpolate or
reuse it in both constants. Preserve the existing override matching conditions
and fallback behavior while ensuring future changes to the lookup apply
consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CHANGELOG.md`:
- Around line 11-16: Update the CHANGELOG entry’s scope wording to explicitly
include manual rows alongside matched rows, reflecting the implementation and
manual-state test assertion. Preserve the existing genre precedence and
enrichment behavior descriptions.

---

Nitpick comments:
In `@lib/metadata_db.py`:
- Around line 1099-1113: Extract the duplicated genre override lookup subquery
into a shared SQL fragment near _EFFECTIVE_GENRE_OVERRIDE_SQL and
_EFFECTIVE_GENRE_SQL, then interpolate or reuse it in both constants. Preserve
the existing override matching conditions and fallback behavior while ensuring
future changes to the lookup apply consistently.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ae3a50c4-a7b5-4b42-87aa-93e6db03b897

📥 Commits

Reviewing files that changed from the base of the PR and between 5921157 and 43423d4.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • lib/metadata_db.py
  • tests/test_field_overrides.py

Comment thread CHANGELOG.md
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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