soulsync/webui/static
BoulderBadgeDad 28850672a6 Fix: duplicate detector kept lossy over lossless (rank format first)
The Duplicate Detector's 'Keep Best' auto-selection ranked copies by highest
bitrate -> duration -> track number, with no notion of format. A FLAC whose
bitrate the library scan never populated (a common gap) therefore lost to a
282 kbps MP3: 282 > 0, so the MP3 was kept and the FLAC deleted (reported on
Havok 'Prepare For Attack', and again on Kendrick GNX).

Fix: rank by format/lossless tier FIRST, then bitrate, duration, track number.
A lossless file now always beats a lossy one regardless of the recorded
bitrate; bitrate/duration/track# only break ties within the same format.

- core/library/duplicate_keep.py (new): pure, importable pick_duplicate_to_keep
  + duplicate_keep_sort_key + format_rank_for_path (extension rank mirroring
  auto_import_worker._quality_rank: flac=10 ... mp3=5 ... unknown=1).
- core/repair_worker.py: _fix_duplicates auto-pick now calls
  pick_duplicate_to_keep instead of the bitrate-first max().
- webui/static/enrichment.js: the KEEP/REMOVE recommendation mirrors the same
  format-first ranking so the badge matches what the backend will delete.

Parity: Python uses '.ext' keys (os.path.splitext), JS uses 'ext'
(split('.').pop()) -> identical results; both keep the first copy on a full
tie. Verified the only other dedup path (the standalone Duplicate Cleaner
automation, core/library/duplicate_cleaner.py) was already format-priority-first
and correct -- no change needed there.

Tests: tests/test_duplicate_keep.py (11 -- incl. the exact FLAC-with-missing-
bitrate vs 282 kbps MP3 case, format ranking, within-format tie-breakers, and
edge cases). 147 repair/duplicate tests still pass.

Note: why FLAC bitrate is NULL in the DB is a separate library-scan gap;
format-first ranking makes the keep decision correct regardless.
2026-06-01 12:49:34 -07:00
..
docs
vendor
amazon.svg
api-monitor.js Dashboard: round Last.fm avatar, invert dark-mark logos (Tidal/Qobuz/Discogs/Amazon) 2026-05-27 19:32:32 -07:00
artists.png
audiodb.png Dashboard: equalizer bars now show real brand logos in the avatar disc 2026-05-27 19:22:28 -07:00
auto-sync.js Auto-Sync sidebar: brand logo on each source-group header 2026-05-27 20:23:12 -07:00
automation.png
basic-search-v2.css Basic search: visual overhaul + per-source picker in hybrid mode 2026-05-28 10:22:07 -07:00
beatport-ui.js
core.js
dashboard.png
discover-section-controller.js
discover.js UI: migrate remaining compact button families to the .btn--sm tier 2026-05-29 11:40:31 -07:00
discover.png
docs-images-needed.md
docs.js
downloads.js Track-detail modal: click any download row for a rich, status-aware view 2026-05-31 20:24:37 -07:00
enrichment.js Fix: duplicate detector kept lossy over lossless (rank format first) 2026-06-01 12:49:34 -07:00
explorer.png
favicon.png
help.png
helper.js Basic search: visual overhaul + per-source picker in hybrid mode 2026-05-28 10:22:07 -07:00
hydrabase.png
import.png
init.js UI: migrate remaining compact button families to the .btn--sm tier 2026-05-29 11:40:31 -07:00
library.js Player: 'Play next' action + accent-correct queue buttons 2026-05-30 14:54:14 -07:00
library.png
manifest.json
media-player.js Player: seek hover tooltip on the Now Playing progress bar 2026-05-30 15:15:58 -07:00
mobile.css Standardize artist-detail hero action buttons 2026-05-28 12:35:02 -07:00
pages-extra.js Batches panel: redesign expanded track rows + fix scrollbar clipping 2026-05-31 22:44:58 -07:00
particles.js
placeholder-album.png
pwa-icon-192.png
pwa-icon-512.png
search.js Search: bump live-search debounce 300ms -> 600ms (#751) 2026-05-30 15:34:34 -07:00
search.png
settings.js Feature: preferred album-art source selection (opt-in, ordered, with fallback) 2026-06-01 11:45:07 -07:00
settings.png
setup-wizard.css
setup-wizard.js
shared-helpers.js
shell-bridge.js
stats-automations.js Mirrored playlist modal: revamp + fix header clipping on long playlists 2026-06-01 09:31:20 -07:00
style.css Mirrored playlist modal: revamp + fix header clipping on long playlists 2026-06-01 09:31:20 -07:00
sw.js
sync-lastfm.js Add Last.fm Radio tab to Sync page (Phase 1c.2) 2026-05-26 15:24:23 -07:00
sync-listenbrainz.js Add Last.fm Radio tab to Sync page (Phase 1c.2) 2026-05-26 15:24:23 -07:00
sync-services.js Tools: add Deep Scan option to the Database Updater 2026-05-29 10:47:02 -07:00
sync-soulsync-discovery.js SoulSync Discovery tab: open mirror detail modal after refresh 2026-05-26 20:02:50 -07:00
sync-spotify.js
sync.png
track-detail.js Track-detail modal: click any download row for a rich, status-aware view 2026-05-31 20:24:37 -07:00
trans.png
trans2.png
whisoul.png
wishlist-tools.js Merge pull request #594 from Skowll/telegram_thread_id 2026-05-26 11:47:08 -07:00
worker-orbs.js