/** The rows a record's own artist anchor found no count for — a feature's * guest, a compilation's stranger — asked by each row's OWN Deezer artist * id, the rule the desktop's search results follow. A row the backend has * no count for keeps nothing: unknown is not a zero. */ export function loadTrackListeners(albumId, tracks) { if (albumId == null || catalog.listeners[albumId]) return; const want = (tracks || []).filter((t) => t.listeners == null && t.artist_id != null); if (!want.length) return; catalog.listeners[albumId] = { status: "loading", counts: {} }; api.catalogTrackListeners(want.map((t) => ({ artistId: t.artist_id, title: t.title }))).then( (counts) => { const out = {}; (counts || []).forEach((n, i) => { if (n != null && want[i]) out[want[i].id] = n; }); catalog.listeners[albumId] = { status: "ready", counts: out }; }, () => { catalog.listeners[albumId] = { status: "error", counts: {} }; }, ); } /** Forget an artist's page and ask for it again — the desktop's * `invalidateCatalogArtist`, for a hero that has just changed. The seed is * kept, so the identity search does not run a second time. */ export function forgetArtist(name) { const k = artistKey(name); const cur = catalog.artists[k]; if (!cur) return; delete catalog.artists[k]; lookupArtist(name, cur.seed ?? null); } /** A photo from this phone as an artist's hero — the desktop's Choose hero, * through the phone's own image picker. The page is asked again, so the * banner, the ground and the accent all follow. */ export async function heroFromPhoto(id, name) { let r; try { r = await api.pickImage(); } catch (e) { if (/cancelled/.test(String(e))) return false; throw e; } if (!r?.path) return false; const hero = await api.setArtistHero(id, r.path); forgetArtist(name); note(`Hero set · ${hero.width}×${hero.height}`); return true; } /** The chosen hero removed — the catalog's own photos come back. */ export async function heroClear(id, name) { const removed = await api.clearArtistHero(id); forgetArtist(name); note(removed ? "The catalog's own photos are back" : "No custom hero here"); } export function lookupAlbum(id) {