soulsync/core/metadata
BoulderBadgeDad 818c4f0bff Canonical album version — Stage 1: schema + pure scorer (dormant)
First stage of the canonical-album-version fix (#765 + #767-Bug2). Pins ONE
canonical (source, album_id) per album, chosen by best-fit to the user's actual
files, so the Reorganizer, Track Number Repair, and tagging stop re-resolving
independently and contradicting each other.

Ships DORMANT — nothing reads or writes the new data yet, so zero behavior
change. Later stages populate (Stage 2) and consume (Stages 3-4) it.

- core/metadata/canonical_version.py — pure scorer (the testable heart):
  score_release_against_files() rates a candidate release by track-count fit +
  duration alignment (greedy nearest within ±3s) + title overlap, dropping and
  renormalizing missing signals so it never crashes on sparse metadata.
  pick_canonical_release() takes candidates in source-priority order, picks the
  best fit, breaks ties toward the earlier (higher-priority) candidate so the
  choice is DETERMINISTIC — that determinism is what makes every tool agree
  (#765), while count/duration fit picks the right EDITION (#767-Bug2). A
  confidence floor (default 0.5) means a low-confidence guess is never pinned.

- database/music_database.py — additive, nullable columns on albums
  (canonical_source / canonical_album_id / canonical_score /
  canonical_resolved_at), guarded by the existing PRAGMA-table_info pattern.
  NULL = unresolved = every consumer falls back to today's behavior.

Tests: tests/test_canonical_version.py (11) — edition discrimination (11 files
-> standard, 17 -> deluxe), deterministic priority tiebreak, duration
disambiguation on count ties, graceful degradation (no durations / counts only /
fuzzy titles), confidence floor, empty-input safety. tests/test_canonical_
columns_migration.py (4) — fresh DB has the columns, they're nullable w/ NULL
default, migration is idempotent, and it ALTERs them onto an old albums table.
60 DB/schema regression tests still pass.
2026-06-02 11:30:58 -07:00
..
__init__.py Add MusicBrainz as a metadata source 2026-05-18 18:47:13 -07:00
album_mbid_cache.py Final silent-exception sweep + ruff S110 lint guardrail — ~45 sites 2026-05-07 11:16:06 -07:00
album_tracks.py Show MusicBrainz release variants in import 2026-05-24 09:33:19 -07:00
art_lookup.py Cover-art sources: skip low-res art (min-resolution guard) + max-res iTunes 2026-06-01 12:24:51 -07:00
art_preservation.py Fix #764: import no longer destroys embedded cover art 2026-06-02 08:40:05 -07:00
art_sources.py Feature: preferred album-art source selection (opt-in, ordered, with fallback) 2026-06-01 11:45:07 -07:00
artist_image.py Move metadata helpers into package modules 2026-04-29 11:28:42 +03:00
artist_resolution.py Fix Soulseek downloads losing collab artist tags 2026-05-13 22:11:20 -07:00
artwork.py Fix #764: import no longer destroys embedded cover art 2026-06-02 08:40:05 -07:00
cache.py Metadata cache: hard LRU row cap to stop unbounded growth (7.6GB incident) 2026-05-30 23:22:05 -07:00
canonical_version.py Canonical album version — Stage 1: schema + pure scorer (dormant) 2026-06-02 11:30:58 -07:00
common.py Surface silent exceptions across remaining modules — ~70 sites 2026-05-07 10:28:58 -07:00
completion.py Tighten artist discography soundtrack matching 2026-05-17 22:51:38 -07:00
discography.py feat: add explicit badges to discography modal and artist-detail cards 2026-05-12 23:35:08 -04:00
discography_filters.py Skip already-owned tracks during download discography 2026-05-12 15:10:41 -07:00
enrichment.py Fix #764: import no longer destroys embedded cover art 2026-06-02 08:40:05 -07:00
lookup.py Move metadata API into package 2026-04-29 08:10:18 +03:00
lyrics.py Move shared metadata helpers into package 2026-04-27 19:54:44 +03:00
multi_source_search.py Fix Download Discography pulling wrong artist + log routing 2026-05-06 13:03:43 -07:00
registry.py Add MusicBrainz as a metadata source 2026-05-18 18:47:13 -07:00
release_type.py fix(metadata): surface MusicBrainz 'Other' release-groups in discography (#650) 2026-05-19 20:20:28 -07:00
relevance.py MB rerank: prefer_known_duration is now a score boost, not a tiebreaker 2026-05-27 08:13:28 -07:00
service.py Move metadata API into package 2026-04-29 08:10:18 +03:00
similar_artists.py Move metadata helpers into package modules 2026-04-29 11:28:42 +03:00
source.py Fix: import overwrites album-artist tag to "Unknown Artist" (#735) 2026-05-29 09:30:38 -07:00
status.py Make Spotify status updates event-driven 2026-05-02 22:02:01 +03:00
track_number_format.py Stop writing TRCK as "6/0" when album total_tracks is unknown 2026-05-14 15:25:16 -07:00
types.py fix(metadata): surface MusicBrainz 'Other' release-groups in discography (#650) 2026-05-19 20:20:28 -07:00