mirror of
https://github.com/markuryy/shark.git
synced 2026-07-28 07:41:02 +00:00
feat(spotify): hook existing download queue
This commit is contained in:
216
src/lib/services/spotify/addToQueue.ts
Normal file
216
src/lib/services/spotify/addToQueue.ts
Normal file
@@ -0,0 +1,216 @@
|
||||
/**
|
||||
* 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'
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user