From 8bb2729dc3c683b6fd50176d522ab62a0329d655 Mon Sep 17 00:00:00 2001 From: Broque Thomas <26755000+Nezreka@users.noreply.github.com> Date: Tue, 31 Mar 2026 09:33:52 -0700 Subject: [PATCH] Fix Tidal playlists showing 0 tracks and broken auto-mirror The metadata-only optimization broke two things: 1. Cards showed 0 tracks because tracks were no longer in the listing 2. Auto-mirror skipped all playlists because tracks array was empty Fix: cards render instantly from metadata, then tracks are fetched per-playlist in the background via /api/tidal/playlist/. As each playlist's tracks arrive, the card count updates and the playlist is auto-mirrored. Also tried multiple V2 attribute names for track count (numberOfTracks, numberOfItems, etc.) and fixed the card DOM selector for count updates. --- core/tidal_client.py | 9 ++++++++- webui/static/script.js | 31 ++++++++++++++++++++++++++++--- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/core/tidal_client.py b/core/tidal_client.py index 6d973d8f..a61953d8 100644 --- a/core/tidal_client.py +++ b/core/tidal_client.py @@ -647,7 +647,14 @@ class TidalClient: tracks=[], # Empty — fetched on-demand via get_playlist() ) # Store track count from metadata (no API call needed) - new_playlist.track_count = attributes.get('numberOfTracks', 0) + # V2 API may use different field names depending on version + new_playlist.track_count = ( + attributes.get('numberOfTracks') or + attributes.get('numberOfItems') or + attributes.get('totalNumberOfItems') or + attributes.get('nrOfTracks') or + 0 + ) if image_url: new_playlist.image_url = image_url diff --git a/webui/static/script.js b/webui/static/script.js index 64568c61..1eefa9ac 100644 --- a/webui/static/script.js +++ b/webui/static/script.js @@ -23168,16 +23168,41 @@ async function loadTidalPlaylists() { console.log(`🎵 Loaded ${tidalPlaylists.length} Tidal playlists`); - // Auto-mirror Tidal playlists that have tracks - tidalPlaylists.forEach(p => { + // Auto-mirror Tidal playlists: fetch tracks in background then mirror + // Cards render instantly from metadata; tracks load per-playlist without blocking UI + for (const p of tidalPlaylists) { + // Skip if already have tracks from a previous load if (p.tracks && p.tracks.length > 0) { mirrorPlaylist('tidal', p.id, p.name, p.tracks.map(t => ({ track_name: t.name || '', artist_name: Array.isArray(t.artists) ? t.artists[0] : (t.artists || ''), album_name: typeof t.album === 'string' ? t.album : '', duration_ms: t.duration_ms || 0, source_track_id: t.id || '' })), { owner: p.owner, image_url: p.image_url, description: p.description }); + continue; } - }); + // Fetch tracks on-demand for this playlist + try { + const fullResp = await fetch(`/api/tidal/playlist/${p.id}`); + if (fullResp.ok) { + const fullData = await fullResp.json(); + if (fullData.tracks && fullData.tracks.length > 0) { + p.tracks = fullData.tracks; + p.track_count = fullData.tracks.length; + // Update card track count in UI + const countEl = document.querySelector(`#tidal-card-${p.id} .playlist-card-track-count`); + if (countEl) countEl.textContent = `${fullData.tracks.length} tracks`; + // Mirror with full track data + mirrorPlaylist('tidal', p.id, p.name, fullData.tracks.map(t => ({ + track_name: t.name || '', artist_name: Array.isArray(t.artists) ? t.artists[0] : (t.artists || ''), + album_name: typeof t.album === 'string' ? t.album : '', duration_ms: t.duration_ms || 0, + source_track_id: t.id || '' + })), { owner: p.owner, image_url: p.image_url, description: p.description }); + } + } + } catch (e) { + console.warn(`Failed to fetch tracks for Tidal playlist ${p.name}: ${e.message}`); + } + } // Load and apply saved discovery states from backend (like YouTube) await loadTidalPlaylistStatesFromBackend();