/** * Utility to add a Spotify track to the download queue by converting it to Deezer * Uses ISRC matching to find the equivalent Deezer track */ import { deezerAPI } from '../deezer'; import { addToQueue } from '$lib/stores/downloadQueue'; import { settings } from '$lib/stores/settings'; import { deezerAuth } from '$lib/stores/deezer'; import { trackExists } from '../deezer/downloader'; import { setInfo, setWarning, setError } from '$lib/stores/status'; import { get } from 'svelte/store'; import { convertSpotifyTrackToDeezer, type SpotifyTrackInput } from './converter'; import type { DeezerTrack } from '$lib/types/deezer'; export interface SpotifyTrackData { id: string; name: string; artist_name: string; album_name: string; duration_ms: number; isrc?: string | null; } /** * Add a Spotify track to the download queue by converting it to Deezer * @param spotifyTrack - Spotify track data (from cache or API) * @returns Result object with success status and details */ export async function addSpotifyTrackToQueue( spotifyTrack: SpotifyTrackData ): Promise<{ success: boolean; deezerId?: string; matchMethod?: string; reason?: string; }> { // Ensure Deezer authentication const authState = get(deezerAuth); if (!authState.loggedIn || !authState.arl) { setError('Deezer login required for downloads'); return { success: false, reason: 'deezer_auth_required' }; } deezerAPI.setArl(authState.arl); try { // Convert Spotify track to Deezer console.log(`[AddSpotifyToQueue] Converting: ${spotifyTrack.name} by ${spotifyTrack.artist_name}`); const conversionInput: SpotifyTrackInput = { id: spotifyTrack.id, name: spotifyTrack.name, artists: [spotifyTrack.artist_name], album: spotifyTrack.album_name, duration_ms: spotifyTrack.duration_ms, isrc: spotifyTrack.isrc }; const conversionResult = await convertSpotifyTrackToDeezer(conversionInput); if (!conversionResult.success || !conversionResult.deezerTrack) { const errorMsg = `Could not find "${spotifyTrack.name}" on Deezer`; console.warn(`[AddSpotifyToQueue] ${errorMsg}`); setWarning(errorMsg); return { success: false, reason: conversionResult.error || 'conversion_failed' }; } const deezerPublicTrack = conversionResult.deezerTrack; const deezerTrackId = deezerPublicTrack.id.toString(); console.log( `[AddSpotifyToQueue] Matched to Deezer track: ${deezerTrackId} via ${conversionResult.matchMethod}` ); // Fetch full track data from Deezer GW API const deezerFullTrack = await deezerAPI.getTrack(deezerTrackId); if (!deezerFullTrack || !deezerFullTrack.SNG_ID) { const errorMsg = 'Failed to fetch full Deezer track data'; console.error(`[AddSpotifyToQueue] ${errorMsg}`); setError(errorMsg); return { success: false, reason: 'deezer_fetch_failed' }; } // Fetch album data for cover art let albumData = null; try { albumData = await deezerAPI.getAlbumData(deezerFullTrack.ALB_ID.toString()); } catch (error) { console.warn('[AddSpotifyToQueue] Could not fetch album data:', error); } // Fetch lyrics let lyricsData = null; try { lyricsData = await deezerAPI.getLyrics(deezerFullTrack.SNG_ID.toString()); } catch (error) { console.warn('[AddSpotifyToQueue] Could not fetch lyrics:', error); } // Parse lyrics if available let lyrics = undefined; if (lyricsData) { let syncLrc = ''; if (lyricsData.LYRICS_SYNC_JSON) { for (const line of lyricsData.LYRICS_SYNC_JSON) { const text = line.line || ''; const timestamp = line.lrc_timestamp || '[00:00.00]'; syncLrc += `${timestamp}${text}\n`; } } lyrics = { sync: syncLrc || undefined, unsync: lyricsData.LYRICS_TEXT || undefined, syncID3: undefined }; } // Build full DeezerTrack object const deezerTrack: DeezerTrack = { id: parseInt(deezerFullTrack.SNG_ID, 10), title: deezerFullTrack.SNG_TITLE, artist: deezerFullTrack.ART_NAME, artistId: parseInt(deezerFullTrack.ART_ID, 10), artists: [deezerFullTrack.ART_NAME], album: deezerFullTrack.ALB_TITLE, albumId: parseInt(deezerFullTrack.ALB_ID, 10), albumArtist: deezerFullTrack.ART_NAME, albumArtistId: parseInt(deezerFullTrack.ART_ID, 10), trackNumber: typeof deezerFullTrack.TRACK_NUMBER === 'number' ? deezerFullTrack.TRACK_NUMBER : parseInt(deezerFullTrack.TRACK_NUMBER, 10), discNumber: typeof deezerFullTrack.DISK_NUMBER === 'number' ? deezerFullTrack.DISK_NUMBER : parseInt(deezerFullTrack.DISK_NUMBER, 10), duration: typeof deezerFullTrack.DURATION === 'number' ? deezerFullTrack.DURATION : parseInt(deezerFullTrack.DURATION, 10), explicit: deezerFullTrack.EXPLICIT_LYRICS === 1, md5Origin: deezerFullTrack.MD5_ORIGIN, mediaVersion: deezerFullTrack.MEDIA_VERSION, trackToken: deezerFullTrack.TRACK_TOKEN, // Enhanced metadata lyrics, albumCoverUrl: albumData?.ALB_PICTURE ? `https://e-cdns-images.dzcdn.net/images/cover/${albumData.ALB_PICTURE}/500x500-000000-80-0-0.jpg` : undefined, albumCoverXlUrl: albumData?.ALB_PICTURE ? `https://e-cdns-images.dzcdn.net/images/cover/${albumData.ALB_PICTURE}/1000x1000-000000-80-0-0.jpg` : undefined, label: albumData?.LABEL_NAME, barcode: albumData?.UPC, releaseDate: deezerFullTrack.PHYSICAL_RELEASE_DATE, genre: deezerFullTrack.GENRE ? [deezerFullTrack.GENRE] : undefined, copyright: deezerFullTrack.COPYRIGHT }; // Check if we should skip this track (if it exists and overwrite is false) const appSettings = get(settings); if (!appSettings.deezerOverwrite && appSettings.musicFolder) { const exists = await trackExists(deezerTrack, appSettings.musicFolder, appSettings.deezerFormat); if (exists) { console.log(`[AddSpotifyToQueue] Skipping "${deezerTrack.title}" - already exists`); setWarning(`Skipped: ${deezerTrack.title} (already exists)`); return { success: false, deezerId: deezerTrackId, matchMethod: conversionResult.matchMethod, reason: 'already_exists' }; } } // Add to queue await addToQueue({ source: 'deezer', type: 'track', title: deezerTrack.title, artist: deezerTrack.artist, totalTracks: 1, downloadObject: deezerTrack }); setInfo(`Queued: ${deezerTrack.title}`); return { success: true, deezerId: deezerTrackId, matchMethod: conversionResult.matchMethod }; } catch (error) { const errorMsg = `Error adding track to queue: ${error instanceof Error ? error.message : 'Unknown error'}`; console.error('[AddSpotifyToQueue]', errorMsg); setError(errorMsg); return { success: false, reason: 'queue_error' }; } }