diff --git a/webui/static/script.js b/webui/static/script.js index 86513f85..2f72fe28 100644 --- a/webui/static/script.js +++ b/webui/static/script.js @@ -5035,6 +5035,106 @@ async function closeDownloadMissingModal(playlistId) { } } +/** + * Extract unique album cover images from tracks + */ +function extractUniqueCoverImages(tracks, maxCovers = 20) { + const uniqueCovers = new Set(); + const covers = []; + + for (const track of tracks) { + if (covers.length >= maxCovers) break; + + let coverUrl = null; + let spotifyData = track.spotify_data; + + // Parse spotify_data if it's a string + if (typeof spotifyData === 'string') { + try { + spotifyData = JSON.parse(spotifyData); + } catch (e) { + continue; + } + } + + // Extract cover URL + coverUrl = spotifyData?.album?.images?.[0]?.url; + + // Add to list if unique and valid + if (coverUrl && !uniqueCovers.has(coverUrl)) { + uniqueCovers.add(coverUrl); + covers.push(coverUrl); + } + } + + return covers; +} + +/** + * Shuffle array using Fisher-Yates algorithm + */ +function shuffleArray(array) { + const shuffled = [...array]; + for (let i = shuffled.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; + } + return shuffled; +} + +/** + * Generate mosaic grid background HTML with continuous scrolling rows + */ +function generateMosaicBackground(coverUrls) { + // If less than 3 covers, use gradient fallback + if (!coverUrls || coverUrls.length < 3) { + return ` +
+ + `; + } + + const rows = 4; + let mosaicHTML = '