soulsync/core/metadata
BoulderBadgeDad 2d2ee34df8 #758: a manual album match pins + locks the canonical version
Users manually match an album to the regular edition, but enrichment/
repair keeps treating it as the deluxe (missing songs, renumbered tracks).
Root cause: an album has TWO identities — the enrichment match
(spotify_album_id, which manual-match sets and the worker already honors)
and a SEPARATE canonical version pin (canonical_album_id, added by #777).
The canonical pin is what track-number repair / reorganize / missing-track
detection actually read, and library_manual_match never wrote it — so it
was resolved independently and landed on the deluxe edition.

(So #777 did NOT solve #758: it added canonical pinning, but manual
matches didn't write the pin.)

Fix: a manual ALBUM match on a canonical-recognised source now also pins
AND locks the canonical version to the chosen release:
- new canonical_locked column (same migration pattern as the other
  canonical cols).
- set_album_canonical(..., locked=False) gains an atomic WHERE-clause
  guard: an auto write can't overwrite a locked pin; a manual write
  (locked=True) always wins. get_album_canonical exposes `locked`.
- library_manual_match pins canonical for album matches via the pure
  should_pin_manual_canonical(entity_type, source).

The auto resolve job already skips already-pinned albums, so the lock is
protected on two fronts; the new guard also covers any future
re-resolution. A new manual match still overrides.

18 tests: the pure gate (+ a sync-invariant test vs _ALBUM_ID_COLUMNS)
and the DB lock seam (auto can't clobber a manual lock; manual overrides;
auto-over-auto still works). Additive — locked defaults False, so the
auto path is unchanged unless a manual lock exists. Full suite clean.
2026-06-05 23:28:19 -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_apply.py Cover Art Filler: skip files that already have art (keep apply purely additive) 2026-06-03 22:17:20 -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_resolver.py Canonical: fix ruff lint (B023 loop-bound lambda, S110 bare except-pass) 2026-06-02 15:42:14 -07:00
canonical_version.py #758: a manual album match pins + locks the canonical version 2026-06-05 23:28:19 -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 Fix: MusicMap 404s miscounted as errors in similar-artists worker 2026-06-03 18:04:31 -07:00
source.py Tagging: write source IDs too (Write Tags button + library re-tag now complete) 2026-06-04 09:20:34 -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