From 1d4fc3b96e2d308d7664d0808f5517e7aa34cd7a Mon Sep 17 00:00:00 2001 From: Broque Thomas Date: Mon, 29 Dec 2025 11:02:37 -0800 Subject: [PATCH] include duration in unified search single results. --- webui/static/script.js | 129 ++++++++++++++++++++++++++++++++++++++--- webui/static/style.css | 8 +++ 2 files changed, 129 insertions(+), 8 deletions(-) diff --git a/webui/static/script.js b/webui/static/script.js index 013c2461..f46d13fa 100644 --- a/webui/static/script.js +++ b/webui/static/script.js @@ -2563,7 +2563,7 @@ function initializeSearchModeToggle() { placeholder: '💿', name: album.name, meta: `${album.artist} • ${album.release_date ? album.release_date.substring(0, 4) : 'N/A'}`, - onClick: () => searchSlskdFor('album', album) + onClick: () => handleEnhancedSearchAlbumClick(album) }) ); @@ -2573,16 +2573,28 @@ function initializeSearchModeToggle() { 'enh-tracks-list', 'enh-tracks-count', data.spotify_tracks || [], - (track) => ({ - image: track.image_url, - placeholder: '🎵', - name: track.name, - meta: `${track.artist} • ${track.album}`, - onClick: () => searchSlskdFor('track', track) - }) + (track) => { + const duration = formatDuration(track.duration_ms); + return { + image: track.image_url, + placeholder: '🎵', + name: track.name, + meta: `${track.artist} • ${track.album}`, + duration: duration, + onClick: () => searchSlskdFor('track', track) + }; + } ); } + function formatDuration(durationMs) { + if (!durationMs) return ''; + const totalSeconds = Math.floor(durationMs / 1000); + const minutes = Math.floor(totalSeconds / 60); + const seconds = totalSeconds % 60; + return `${minutes}:${seconds.toString().padStart(2, '0')}`; + } + function renderCompactSection(sectionId, listId, countId, items, mapItem) { const section = document.getElementById(sectionId); const list = document.getElementById(listId); @@ -2650,12 +2662,17 @@ function initializeSearchModeToggle() { ? `
${config.badge.text}
` : ''; + const durationHtml = config.duration && isTrack + ? `
${escapeHtml(config.duration)}
` + : ''; + elem.innerHTML = ` ${imageHtml}
${escapeHtml(config.name)}
${escapeHtml(config.meta)}
+ ${durationHtml} ${badgeHtml} `; @@ -2664,6 +2681,102 @@ function initializeSearchModeToggle() { }); } + async function handleEnhancedSearchAlbumClick(album) { + console.log(`💿 Enhanced search album clicked: ${album.name} by ${album.artist}`); + + hideDropdown(); + showLoadingOverlay('Loading album...'); + + try { + // Fetch full album data with tracks from Spotify + const response = await fetch(`/api/spotify/album/${album.id}`); + + if (!response.ok) { + if (response.status === 401) { + throw new Error('Spotify not authenticated. Please check your API settings.'); + } + throw new Error(`Failed to load album: ${response.status}`); + } + + const albumData = await response.json(); + + if (!albumData || !albumData.tracks || albumData.tracks.length === 0) { + throw new Error('No tracks found for this album'); + } + + console.log(`✅ Loaded ${albumData.tracks.length} tracks for ${albumData.name}`); + + // Create virtual playlist ID for enhanced search albums + const virtualPlaylistId = `enhanced_search_album_${album.id}`; + + // Check if modal already exists and show it + if (activeDownloadProcesses[virtualPlaylistId]) { + console.log(`📱 Reopening existing modal for ${album.name}`); + const process = activeDownloadProcesses[virtualPlaylistId]; + if (process.modalElement) { + if (process.status === 'complete') { + showToast('Showing previous results. Close this modal to start a new analysis.', 'info'); + } + process.modalElement.style.display = 'flex'; + hideLoadingOverlay(); + return; + } + } + + // Enrich each track with full album object (needed for wishlist functionality) + const enrichedTracks = albumData.tracks.map(track => ({ + ...track, + album: { + name: albumData.name, + id: albumData.id, + album_type: albumData.album_type || 'album', + images: albumData.images || [], + release_date: albumData.release_date, + total_tracks: albumData.total_tracks + } + })); + + console.log(`📦 Enriched ${enrichedTracks.length} tracks with album metadata`); + + // Format playlist name + const playlistName = `[${album.artist}] ${albumData.name}`; + + // Create minimal artist object for the modal + const artistObject = { + id: null, // No artist ID from enhanced search + name: album.artist + }; + + // Prepare full album object for modal + const fullAlbumObject = { + name: albumData.name, + id: albumData.id, + album_type: albumData.album_type || 'album', + images: albumData.images || [], + release_date: albumData.release_date, + total_tracks: albumData.total_tracks, + artists: albumData.artists || [{ name: album.artist }] + }; + + // Open download missing tracks modal + await openDownloadMissingModalForArtistAlbum( + virtualPlaylistId, + playlistName, + enrichedTracks, + fullAlbumObject, + artistObject, + false // Don't show loading overlay, we already have one + ); + + hideLoadingOverlay(); + + } catch (error) { + hideLoadingOverlay(); + console.error('❌ Error handling enhanced search album click:', error); + showToast(`Error opening album: ${error.message}`, 'error'); + } + } + async function searchSlskdFor(type, item) { const mainResultsArea = document.getElementById('enhanced-main-results-area'); if (!mainResultsArea) return; diff --git a/webui/static/style.css b/webui/static/style.css index 249db82d..7ec79f4e 100644 --- a/webui/static/style.css +++ b/webui/static/style.css @@ -19485,6 +19485,14 @@ body { white-space: nowrap; } +.enh-item-duration { + font-size: 13px; + font-weight: 500; + color: rgba(255, 255, 255, 0.5); + flex-shrink: 0; + margin-left: 8px; +} + /* ========================================= */ /* ENHANCED SEARCH CATEGORIZED RESULTS */ /* (OLD - REMOVE IF NOT NEEDED) */