soulsync/tests/metadata
Broque Thomas 8dbbf13c61 Branch cleanup: lift manual-match helpers, fix length-pref ordering, profile-scope view toggle
Self-review pass on the prior three commits — kettui-style cleanup
that should have landed first time.

**Length-preference sort ordering (real bug):**
The `search_tracks_with_artist` stable sort that promoted length-known
recordings ran in `core/musicbrainz_search.py`, but the MB endpoint in
`web_server.py:search_musicbrainz_tracks` runs `rerank_tracks` after
it — which re-sorts by relevance score and dropped the length-pref
ordering down to tiebreaker-only. For canonical-same-song MB duplicates
that all score identically the tiebreaker survived, but the
order-of-operations was wrong.

Moved into `rerank_tracks` itself via a new `prefer_known_duration`
flag. Sort key sits between relevance score and the stable-order
tiebreaker so relevance still wins (length only decides ties, never
overrides a higher-relevance match). The MB endpoint opts in via
`prefer_known_duration=True`; Spotify / iTunes / Deezer callers stay
on the default-off path since their search results always include
length. Pinned with three new `TestRerankTracks` cases:
ties-promote-length, relevance-still-wins, default-off-unchanged.

**Route logic lifted to `core/discovery/manual_match.py`:**
Two pieces lived as inline route logic in `web_server.py` — the
`derive_manual_match_provider` fallback chain (payload.source →
active source → 'spotify') used by `update_youtube_discovery_match`,
and the `is_drifted_for_redo` predicate (cached provider differs from
active AND not manual_match) used by `prepare_mirrored_discovery`.
Per kettui's "extract logic from web_server.py, don't AST-parse it"
standard, both helpers now live in `core/discovery/manual_match.py`
with 12 dedicated unit tests covering fallback resolution order,
non-dict payload defenses, manual_match exemption from drift,
absent-provider legacy default, and edge cases.

Side benefits from the lift:
- `match_source` now derived once before the cache-save try block
  instead of being duplicated in try + except (the except block existed
  only because the original used `match_source` later — pre-computing
  killed the duplication).
- `prepare_mirrored_discovery`'s `has_cached` check now reuses
  `is_drifted_for_redo` with inverted polarity instead of restating
  the field whitelist inline, so a future schema change only has to
  land in one place.
- The mirrored-DB persist block now gates on `matched_data is not None`
  to avoid a pre-existing latent NameError if the cache-save block
  raised before matched_data construction.

**Enhanced toggle localStorage key now profile-scoped:**
`soulsync-library-view-mode` was global — two admin profiles would
share one preference. Wrapped in `_libraryViewModeKey()` which appends
`:${currentProfile.id}` when a profile is loaded, falls back to the
unsuffixed key otherwise (preserves pre-multi-profile saved values).

Tests:
- 12 new in `tests/discovery/test_manual_match.py` pinning both helpers.
- 3 new in `tests/metadata/test_relevance.py` pinning the
  `prefer_known_duration` semantics.
- `test_search_tracks_with_artist_prefers_results_with_known_length`
  renamed to `_does_not_resort_by_length` since the sort moved out of
  this method. 664 tests pass across discovery + metadata suites.
2026-05-27 07:43:21 -07:00
..
test_album_mbid_cache.py Fix album MBID inconsistency: detector + persistent release-MBID cache 2026-05-03 17:16:39 -07:00
test_album_type_normalization.py Pin type='track' / type='artist' collision case for album-type normalizer 2026-05-12 21:15:58 -07:00
test_artist_resolution.py Fix Soulseek downloads losing collab artist tags 2026-05-13 22:11:20 -07:00
test_artist_source_detail.py feat: artist-detail deep linking — /artist-detail/:source/:id 2026-05-18 13:07:54 -07:00
test_artist_source_lookup.py Add amazon_id column to artists table for full source parity 2026-05-16 17:06:54 -07:00
test_build_album_info_typed_path.py Migrate album-info builders to typed Album path 2026-05-03 22:53:12 -07:00
test_cache_maintenance_retry.py Handle transient SQLite IO during maintenance 2026-05-21 17:50:30 -07:00
test_deezer_cover_url_upgrade.py Deezer cover-art download: fallback to original URL on CDN refusal 2026-05-10 18:29:36 -07:00
test_deezer_search_query.py Deezer search: free-text fallback when advanced query returns 0 2026-05-10 09:16:13 -07:00
test_deezer_track_cache_validity.py Fix Deezer contributors tagging silently dropping for cache-polluted tracks 2026-05-14 11:10:51 -07:00
test_discography_filters.py Skip already-owned tracks during download discography 2026-05-12 15:10:41 -07:00
test_discography_typed_path.py Migrate discography + quality scanner to typed Album path 2026-05-04 08:12:40 -07:00
test_enrichment_events.py Drop old per-service enrichment routes after registry cutover 2026-05-01 20:43:18 -07:00
test_image_url_normalization.py feat(webui): cache artwork images on disk 2026-05-20 10:43:47 -07:00
test_metadata_album_tracks.py Move metadata helpers into package modules 2026-04-29 11:28:42 +03:00
test_metadata_artist_image.py Move metadata helpers into package modules 2026-04-29 11:28:42 +03:00
test_metadata_cache.py Move profile Spotify cache into registry 2026-04-29 12:36:37 +03:00
test_metadata_cache_batch_lookup.py Move metadata API into package 2026-04-29 08:10:18 +03:00
test_metadata_discography.py Tighten artist discography soundtrack matching 2026-05-17 22:51:38 -07:00
test_metadata_enrichment.py Preserve full release dates in audio tags 2026-05-17 23:02:41 -07:00
test_metadata_gap_filler.py Fix: maintenance findings badge inflated when scan dedup-skipped 2026-05-04 08:55:13 -07:00
test_metadata_musicmap.py Move metadata helpers into package modules 2026-04-29 11:28:42 +03:00
test_metadata_registry.py Add MusicBrainz as a metadata source 2026-05-18 18:47:13 -07:00
test_multi_artist_tag_settings.py Fix Soulseek downloads losing collab artist tags 2026-05-13 22:11:20 -07:00
test_musicbrainz_search.py Branch cleanup: lift manual-match helpers, fix length-pref ordering, profile-scope view toggle 2026-05-27 07:43:21 -07:00
test_release_type.py fix(metadata): surface MusicBrainz 'Other' release-groups in discography (#650) 2026-05-19 20:20:28 -07:00
test_relevance.py Branch cleanup: lift manual-match helpers, fix length-pref ordering, profile-scope view toggle 2026-05-27 07:43:21 -07:00
test_runtime_bundle.py fix post-download tagging, and enable tagging for hifi 2026-05-02 07:50:12 -06:00
test_track_number_format.py Stop writing TRCK as "6/0" when album total_tracks is unknown 2026-05-14 15:25:16 -07:00
test_typed_metadata_types.py Add MusicBrainz as a metadata source 2026-05-18 18:47:13 -07:00