diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-09-19 17:22:20 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-09-19 17:22:20 +0200 |
| commit | 34d74cba0421ac88505ac620158882f90cf5db7e (patch) | |
| tree | 1d427579dfa55118e4281cf894492a0d3d73c0fd /packages/meshbay-hub/src/meshbay_hub | |
| parent | 2eaf6887295614509f8d0bc24a9b77ccd915ef88 (diff) | |
| download | meshbay-34d74cba0421ac88505ac620158882f90cf5db7e.tar.gz | |
fix(hub): a track with no artist tag reaches the Music grid
The grid's unit is an album, so a track whose artist tag is empty was
drawn nowhere — while `empty` counted it and stayed false, so no message
appeared either. An untagged library rendered a toolbar over a blank
page, with every track one mode-switch away and nothing saying so.
It gets a card, the same shape the singleton folding already mints. No
cover is looked up for it, or for any album this file invented: the
release name is one the browser wrote, and the request cannot match.
music_untagged_probe.py renders the real grid and reads the page back.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub')
11 files changed, 45 insertions, 4 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js index 2d925e1..aea1196 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js @@ -302,6 +302,7 @@ export default { 'music.mode_flat': 'Flache Liste', 'music.empty': 'Keine Musik gefunden.', 'music.unknown_album': 'Unbekanntes Album', + 'music.unknown_artist': 'Unbekannter Künstler', 'music.various': 'Verschiedenes', 'music.play_all': 'Alle abspielen', 'music.menu_more': 'Mehr…', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js index 690a70b..d9172a6 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js @@ -300,6 +300,7 @@ export default { 'music.mode_flat': 'Flat list', 'music.empty': 'No music found.', 'music.unknown_album': 'Unknown album', + 'music.unknown_artist': 'Unknown artist', 'music.various': 'Various', 'music.play_all': 'Play all', 'music.menu_more': 'More…', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js index c48f942..fc0e639 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js @@ -300,6 +300,7 @@ export default { 'music.mode_flat': 'Lista plana', 'music.empty': 'No se encontró música.', 'music.unknown_album': 'Álbum desconocido', + 'music.unknown_artist': 'Artista desconocido', 'music.various': 'Varios', 'music.play_all': 'Reproducir todo', 'music.menu_more': 'Más…', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js index 6d148d6..5eb8d60 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js @@ -301,6 +301,7 @@ export default { 'music.mode_flat': 'Liste à plat', 'music.empty': 'Aucune musique trouvée.', 'music.unknown_album': 'Album inconnu', + 'music.unknown_artist': 'Artiste inconnu', 'music.various': 'Divers', 'music.play_all': 'Tout lire', 'music.menu_more': 'Plus…', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js index c619d61..6d12cb3 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js @@ -301,6 +301,7 @@ export default { 'music.mode_flat': 'Elenco semplice', 'music.empty': 'Nessuna musica trovata.', 'music.unknown_album': 'Album sconosciuto', + 'music.unknown_artist': 'Artista sconosciuto', 'music.various': 'Vari', 'music.play_all': 'Riproduci tutto', 'music.menu_more': 'Altro…', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js index eac90dc..f4ced88 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js @@ -298,6 +298,7 @@ export default { 'music.mode_flat': 'フラットリスト', 'music.empty': '音楽が見つかりません。', 'music.unknown_album': '不明なアルバム', + 'music.unknown_artist': '不明なアーティスト', 'music.various': 'その他', 'music.play_all': 'すべて再生', 'music.menu_more': 'その他…', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js index 65bf4f5..31a2321 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js @@ -302,6 +302,7 @@ export default { 'music.mode_flat': 'Platte lijst', 'music.empty': 'Geen muziek gevonden.', 'music.unknown_album': 'Onbekend album', + 'music.unknown_artist': 'Onbekende artiest', 'music.various': 'Diversen', 'music.play_all': 'Alles afspelen', 'music.menu_more': 'Meer…', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js index 264d442..463a6a9 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js @@ -309,6 +309,7 @@ export default { 'music.mode_flat': 'Lista płaska', 'music.empty': 'Nie znaleziono muzyki.', 'music.unknown_album': 'Nieznany album', + 'music.unknown_artist': 'Nieznany wykonawca', 'music.various': 'Różne', 'music.play_all': 'Odtwórz wszystko', 'music.menu_more': 'Więcej…', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js index 0fbfb1e..20822ee 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js @@ -302,6 +302,7 @@ export default { 'music.mode_flat': 'Lista simples', 'music.empty': 'Nenhuma música encontrada.', 'music.unknown_album': 'Álbum desconhecido', + 'music.unknown_artist': 'Artista desconhecido', 'music.various': 'Diversos', 'music.play_all': 'Reproduzir tudo', 'music.menu_more': 'Mais…', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js index ab75b11..b2d7591 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js @@ -295,6 +295,7 @@ export default { 'music.mode_flat': '平铺列表', 'music.empty': '未找到音乐。', 'music.unknown_album': '未知专辑', + 'music.unknown_artist': '未知艺术家', 'music.various': '其他', 'music.play_all': '全部播放', 'music.menu_more': '更多…', 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 e0308cb..1dbecbb 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/music-app.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/music-app.js @@ -256,7 +256,13 @@ function AlbumCard({ album, transportRef, gekRef, musicbrainzEnabled, onOpen, on const repTrack = album.tracks.find((tr) => tr.thumb_hash) || album.tracks[0]; const tRef = repTrack._tRef || transportRef; const gRef = repTrack._gRef || gekRef; - const needsLookup = musicbrainzEnabled && !repTrack.thumb_hash; + // Never for an album this file minted itself (`isUnknown`): the release + // name is one we wrote -- '<artist> - Various', or the untagged pile + // below -- so the lookup is a third-party request, on the operator's + // connection, that cannot match anything. Confirmed in a node's log: + // queries went out naming a placeholder as both the artist and the + // release, once per card. + const needsLookup = musicbrainzEnabled && !repTrack.thumb_hash && !album.isUnknown; const meta = useMusicMeta(tRef, repTrack.id, needsLookup); const coverHash = repTrack.thumb_hash || (meta && meta.cover_thumb_hash) || null; @@ -287,7 +293,7 @@ function MusicDetailModal({ album, transportRef, gekRef, musicbrainzEnabled, onC const repTrack = album.tracks.find((tr) => tr.thumb_hash) || album.tracks[0]; const tRef = repTrack._tRef || transportRef; const gRef = repTrack._gRef || gekRef; - const needsLookup = musicbrainzEnabled && !repTrack.thumb_hash; + const needsLookup = musicbrainzEnabled && !repTrack.thumb_hash && !album.isUnknown; const meta = useMusicMeta(tRef, repTrack.id, needsLookup); const coverHash = repTrack.thumb_hash || (meta && meta.cover_thumb_hash) || null; @@ -672,17 +678,39 @@ function MusicApp({ const filteredTracks = useMemo(() => (!needle ? tracks : tracks.filter( (tr) => (tr.display_title || tr.name).toLowerCase().includes(needle))), [tracks, needle]); + // A track whose artist tag is empty *and* whose folder gave nothing to fall + // back on. The flat list has always drawn these as its own top-level rows; + // the grid, whose unit is an album, drew them nowhere at all -- and `empty` + // below counts them, so it stayed false and no message appeared either. A + // library nothing has tagged therefore rendered a toolbar over a blank page, + // with every one of its tracks one mode-switch away and nothing saying so. + // Reported after a node restart, where the index is briefly served without + // the tags it re-reads at start-up, and true of a genuinely untagged library + // with no restart involved. + // + // One card, the same shape the singleton folding above already mints for an + // artist's leftovers: it names what it is, and the tracks are playable from + // it. Not a card each -- that is the wall of one-track tiles this file + // exists to avoid -- and not sorted in among the artists, because it is not + // a name anybody chose and the alphabet is no place for it. + const untagged = useMemo(() => (filteredTracks.length + ? { artist: t('music.unknown_artist'), album: t('music.unknown_album'), + isUnknown: true, tracks: filteredTracks } + : null), [filteredTracks]); + // What this mode draws, in drawing order: albums under their artists in the // grid, and in the flat list loose tracks and artist folders sorted together. const units = useMemo(() => { if (mode === 'grid') { - return filteredArtists.flatMap((a) => a.albums.map((album) => ({ artist: a.artist, album }))); + const byArtist = filteredArtists.flatMap( + (a) => a.albums.map((album) => ({ artist: a.artist, album }))); + return untagged ? [...byArtist, { artist: untagged.artist, album: untagged }] : byArtist; } return [ ...filteredTracks.map((tr) => ({ key: tr.display_title || tr.name, kind: 'track', track: tr })), ...filteredArtists.map((a) => ({ key: a.artist, kind: 'artist', artist: a })), ].sort((a, b) => a.key.localeCompare(b.key)); - }, [mode, filteredArtists, filteredTracks]); + }, [mode, filteredArtists, filteredTracks, untagged]); const pager = usePager(units.length, pageSizeFrom(userPrefs), `${groupId}|${mode}|${needle}|${pageResetKey || ''}`); @@ -690,6 +718,9 @@ function MusicApp({ return units.slice(pager.start, pager.end); }, [units, pager.start, pager.end]); + // True exactly when the library has nothing, and — now that every track + // reaches a card — exactly when the grid has nothing to draw either. The two + // used to disagree, which is the whole of the defect above. const empty = albums.length === 0 && tracks.length === 0; return html` |