// main.js import { downloadQueue } from './queue.js'; document.addEventListener('DOMContentLoaded', () => { const searchButton = document.getElementById('searchButton'); const searchInput = document.getElementById('searchInput'); const queueIcon = document.getElementById('queueIcon'); // Initialize queue icon queueIcon.addEventListener('click', () => downloadQueue.toggleVisibility()); // Search functionality searchButton.addEventListener('click', performSearch); searchInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') performSearch(); }); }); async function performSearch() { const query = document.getElementById('searchInput').value.trim(); const searchType = document.getElementById('searchType').value; const resultsContainer = document.getElementById('resultsContainer'); if (!query) { showError('Please enter a search term'); return; } // If the query is a Spotify URL for a supported resource, redirect to our route. if (isSpotifyUrl(query)) { try { const { type, id } = getSpotifyResourceDetails(query); const supportedTypes = ['track', 'album', 'playlist', 'artist']; if (!supportedTypes.includes(type)) throw new Error('Unsupported URL type'); // Redirect to {base_url}/{type}/{id} window.location.href = `${window.location.origin}/${type}/${id}`; return; } catch (error) { showError(`Invalid Spotify URL: ${error.message}`); return; } } resultsContainer.innerHTML = '
Searching...
'; try { const response = await fetch(`/api/search?q=${encodeURIComponent(query)}&search_type=${searchType}&limit=50`); const data = await response.json(); if (data.error) throw new Error(data.error); const items = data.data[`${searchType}s`]?.items; if (!items?.length) { resultsContainer.innerHTML = '
No results found
'; return; } resultsContainer.innerHTML = items.map(item => createResultCard(item, searchType)).join(''); attachDownloadListeners(items); } catch (error) { showError(error.message); } } function attachDownloadListeners(items) { document.querySelectorAll('.download-btn').forEach((btn, index) => { btn.addEventListener('click', (e) => { e.stopPropagation(); const url = e.currentTarget.dataset.url; const type = e.currentTarget.dataset.type; const albumType = e.currentTarget.dataset.albumType; if (e.currentTarget.classList.contains('main-download')) { e.currentTarget.closest('.result-card').remove(); } else { e.currentTarget.remove(); } startDownload(url, type, items[index], albumType); }); }); } async function startDownload(url, type, item, albumType) { const config = JSON.parse(localStorage.getItem('activeConfig')) || {}; const { fallback = false, spotify = '', deezer = '', spotifyQuality = 'NORMAL', deezerQuality = 'MP3_128', realTime = false } = config; let service = url.includes('open.spotify.com') ? 'spotify' : 'deezer'; let apiUrl = `/api/${type}/download?service=${service}&url=${encodeURIComponent(url)}`; if (type === 'artist') { apiUrl = `/api/artist/download?service=${service}&artist_url=${encodeURIComponent(url)}&album_type=${encodeURIComponent(albumType || 'album,single,compilation')}`; } if (fallback && service === 'spotify') { apiUrl += `&main=${deezer}&fallback=${spotify}`; apiUrl += `&quality=${deezerQuality}&fall_quality=${spotifyQuality}`; } else { const mainAccount = service === 'spotify' ? spotify : deezer; apiUrl += `&main=${mainAccount}&quality=${service === 'spotify' ? spotifyQuality : deezerQuality}`; } if (realTime) apiUrl += '&real_time=true'; try { const response = await fetch(apiUrl); const data = await response.json(); downloadQueue.addDownload(item, type, data.prg_file); } catch (error) { showError('Download failed: ' + error.message); } } // UI Helper Functions function showError(message) { document.getElementById('resultsContainer').innerHTML = `
${message}
`; } function isSpotifyUrl(url) { return url.startsWith('https://open.spotify.com/'); } /** * Extracts the resource type and ID from a Spotify URL. * Expected URL format: https://open.spotify.com/{type}/{id} */ function getSpotifyResourceDetails(url) { const urlObj = new URL(url); const pathParts = urlObj.pathname.split('/'); // Expecting ['', type, id, ...] if (pathParts.length < 3 || !pathParts[1] || !pathParts[2]) { throw new Error('Invalid Spotify URL'); } return { type: pathParts[1], id: pathParts[2] }; } function msToMinutesSeconds(ms) { const minutes = Math.floor(ms / 60000); const seconds = ((ms % 60000) / 1000).toFixed(0); return `${minutes}:${seconds.padStart(2, '0')}`; } function createResultCard(item, type) { let newUrl = '#'; try { const spotifyUrl = item.external_urls.spotify; const parsedUrl = new URL(spotifyUrl); newUrl = window.location.origin + parsedUrl.pathname; } catch (e) { console.error('Error parsing URL:', e); } let imageUrl, title, subtitle, details; switch(type) { case 'track': imageUrl = item.album.images[0]?.url || ''; title = item.name; subtitle = item.artists.map(a => a.name).join(', '); details = `${item.album.name} ${msToMinutesSeconds(item.duration_ms)}`; return `
${type} cover
${title}
${subtitle}
${details}
`; case 'playlist': imageUrl = item.images[0]?.url || ''; title = item.name; subtitle = item.owner.display_name; details = `${item.tracks.total} tracks ${item.description || 'No description'}`; return `
${type} cover
${title}
${subtitle}
${details}
`; case 'album': imageUrl = item.images[0]?.url || ''; title = item.name; subtitle = item.artists.map(a => a.name).join(', '); details = `${item.release_date} ${item.total_tracks} tracks`; return `
${type} cover
${title}
${subtitle}
${details}
`; case 'artist': imageUrl = item.images && item.images.length ? item.images[0].url : ''; title = item.name; subtitle = item.genres && item.genres.length ? item.genres.join(', ') : 'Unknown genres'; details = `Followers: ${item.followers?.total || 'N/A'}`; return `
${type} cover
${title}
${subtitle}
${details}
`; default: title = item.name || 'Unknown'; subtitle = ''; details = ''; return `
${type} cover
${title}
${subtitle}
${details}
`; } }