diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-08-26 18:09:44 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-08-26 18:09:44 +0200 |
| commit | 6fb948045b9c563ce591da289cdac1df6bed360c (patch) | |
| tree | 1a783c4ed5dcf23bc65d6bb8825906473670d3aa /packages/meshbay-hub | |
| parent | 80cb6c4dc5f22336387f2ac74ef2cb85cf2cf7d4 (diff) | |
| download | meshbay-6fb948045b9c563ce591da289cdac1df6bed360c.tar.gz | |
fix(music): stop sharing one folder's cover across unrelated tracks, and merge various-artists compilations into one album
Two real, confirmed bugs in a large flat music library:
- enrich_audio.py's sibling-cover fallback assumed one folder is one
release. A large flat "chart ranking" folder mixing dozens of unrelated
artists carried several distinct WMP AlbumArt-cache guids (one per
original album a track was ripped from), and the fallback picked
whichever one WMP had copied to Folder.jpg — attaching one unrelated
release's cover to every other track in the folder. Now refuses to pick
a cover at all once 2+ distinct guids show up, rather than guess.
- music-app.js's groupMusicEntries grouped by artist first, album second,
so a various-artists compilation (many genuinely different per-track
artists, one shared album tag, no album-artist tag at all — a real
~20-track soundtrack rip has exactly this shape) could never be
recognized as one release: every track landed alone in its own artist's
bucket and got folded into a singleton pile. Now detects an album key
shared across 2+ distinct artist keys and merges those tracks into one
compilation card under a "Various" heading instead.
Both verified against real, previously-affected files and live in the
browser: the shared wrong cover is gone, and the compilation renders as
one card with all its tracks in order.
Diffstat (limited to 'packages/meshbay-hub')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/music-app.js | 45 |
1 files changed, 44 insertions, 1 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/music-app.js b/packages/meshbay-hub/src/meshbay_hub/static/music-app.js index ae482ad..dd354d9 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/music-app.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/music-app.js @@ -86,6 +86,46 @@ function groupMusicEntries(entries, audioRoot) { const byTitle = (a, b) => (a.display_title || a.name).localeCompare(b.display_title || b.name); tracks.sort(byTitle); + // A various-artists compilation (a real film/game soundtrack is the + // common shape: dozens of genuinely different per-track artists sharing + // one correctly-tagged album name, confirmed against a real ~20-track + // soundtrack rip with no separate "album artist" tag at all -- this era + // of rip never wrote one). Grouping by artist first, as above, can never + // recognize this: every track lands alone in its own artist's bucket as + // a one-track "album", each one then folded below into that artist's own + // singleton pile -- the same release rendered as a wall of disconnected + // one-track cards under a dozen different artist headings instead of one. + // Detected the only way the data actually supports here (no album-artist + // tag survived): the same album key reappears under two or more + // genuinely different artist keys. Pulled out and merged *before* the + // per-artist singleton folding below, so those tracks never reach it + // under their original artist bucket. + const albumKeyArtists = new Map(); // albumKey -> Set(artistKey) + for (const [artistKey, bucket] of byArtistKey) { + for (const albumKey of bucket.albumsByKey.keys()) { + if (!albumKeyArtists.has(albumKey)) albumKeyArtists.set(albumKey, new Set()); + albumKeyArtists.get(albumKey).add(artistKey); + } + } + const compilations = []; + for (const [albumKey, artistKeys] of albumKeyArtists) { + if (artistKeys.size < 2) continue; + const compTracks = []; + let albumDisplay = null; + for (const artistKey of artistKeys) { + const bucket = byArtistKey.get(artistKey); + const album = bucket.albumsByKey.get(albumKey); + if (albumDisplay == null) albumDisplay = album.album; + compTracks.push(...album.tracks); + bucket.albumsByKey.delete(albumKey); + } + compTracks.sort((a, b) => (trackNo(a) - trackNo(b)) || byTitle(a, b)); + compilations.push({ + artist: t('music.various'), album: albumDisplay, isUnknown: false, tracks: compTracks, + }); + } + compilations.sort((a, b) => a.album.localeCompare(b.album)); + const artists = [...byArtistKey.values()].map(({ artist, albumsByKey, loose }) => { const sortedAlbums = [...albumsByKey.values()].sort((a, b) => a.album.localeCompare(b.album)); @@ -123,7 +163,10 @@ function groupMusicEntries(entries, audioRoot) { }); } return { artist, albums: realAlbums }; - }).sort((a, b) => a.artist.localeCompare(b.artist)); + }).filter((a) => a.albums.length > 0); + + if (compilations.length > 0) artists.push({ artist: t('music.various'), albums: compilations }); + artists.sort((a, b) => a.artist.localeCompare(b.artist)); const albums = artists.flatMap((a) => a.albums); return { tracks, artists, albums }; |