diff options
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/music-app.js')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/music-app.js | 56 |
1 files changed, 41 insertions, 15 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 87e1319..7e16b53 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/music-app.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/music-app.js @@ -36,13 +36,15 @@ function saveViewMode(mode) { // -- grouping ------------------------------------------------------------- -// Two tags differing only in case ("Racing With The Sun" vs "Racing with -// the sun" -- found live, a single mistagged track split one real album -// into two cards) are the same artist/album for grouping purposes. Folded -// for the *key* only; the first-seen spelling is kept as the display -// string, so this never rewrites anyone's tags. +// Two tags differing only in case (found live: a single mistagged track +// split one real album into two cards) are the same artist/album for +// grouping purposes. Same for an "&" vs "and" spelling of the same act, +// tagged both ways across different rips of the same catalogue. Folded for +// the *key* only; the first-seen spelling is kept as the display string, so +// this never rewrites anyone's tags. function foldKey(s) { - return (s || '').trim().replace(/\s+/g, ' ').toLowerCase(); + return (s || '').trim().replace(/\s+/g, ' ').toLowerCase() + .replace(/\s*&\s*/g, ' and ').replace(/\s+/g, ' ').trim(); } function groupMusicEntries(entries) { @@ -74,18 +76,42 @@ function groupMusicEntries(entries) { tracks.sort(byTitle); const artists = [...byArtistKey.values()].map(({ artist, albumsByKey, loose }) => { - const albums = [...albumsByKey.values()].sort((a, b) => a.album.localeCompare(b.album)); - for (const album of albums) { + const sortedAlbums = [...albumsByKey.values()].sort((a, b) => a.album.localeCompare(b.album)); + + // A "singleton" -- an album bucket down to exactly one track, because + // that's a real album tag but this person only has one song from it, + // not the whole release -- clutters the grid exactly the way an + // untagged loose track does. Found live: one artist's folder listing a + // dozen near-empty one-track album cards alongside the genuine + // multi-track albums. Both kinds fold into a single "<artist> - + // Various" tile, unless there is only one leftover track overall, where + // relabeling away a real album name (or minting "Various" for one + // file) buys nothing. + const realAlbums = []; + const misc = [...loose]; + const miscSourceAlbums = []; + for (const album of sortedAlbums) { + if (album.tracks.length > 1) { realAlbums.push(album); continue; } + misc.push(...album.tracks); + miscSourceAlbums.push(album); + } + for (const album of realAlbums) { album.tracks.sort((a, b) => (trackNo(a) - trackNo(b)) || byTitle(a, b)); } - if (loose.length) { - // Appended after every real album, never sorted in among them by - // whatever the placeholder text happens to alphabetize to -- it - // isn't a release, and translating "Unknown album" must not move it. - loose.sort((a, b) => (trackNo(a) - trackNo(b)) || byTitle(a, b)); - albums.push({ artist, album: t('music.unknown_album'), isUnknown: true, tracks: loose }); + + if (misc.length === 1) { + // Keep the one leftover's own real album name if it had one; the + // generic placeholder only for a single untagged track with nothing + // else to call it. + realAlbums.push(miscSourceAlbums[0] + || { artist, album: t('music.unknown_album'), isUnknown: true, tracks: misc }); + } else if (misc.length > 1) { + misc.sort((a, b) => (trackNo(a) - trackNo(b)) || byTitle(a, b)); + realAlbums.push({ + artist, album: `${artist} - ${t('music.various')}`, isUnknown: true, tracks: misc, + }); } - return { artist, albums }; + return { artist, albums: realAlbums }; }).sort((a, b) => a.artist.localeCompare(b.artist)); const albums = artists.flatMap((a) => a.albums); |