feat(dl): online track search and add-to-queue utility

This commit is contained in:
2025-10-02 09:54:05 -04:00
parent 81ef5bac90
commit d1edc8b7f7
6 changed files with 115 additions and 94 deletions

View File

@@ -44,6 +44,9 @@
{
"url": "http://*.deezer.com/**"
},
{
"url": "https://api.deezer.com/**"
},
{
"url": "https://media.deezer.com/**"
},

View File

@@ -250,6 +250,35 @@ export class DeezerAPI {
return this.apiCall('song.getData', { SNG_ID: trackId });
}
// Search tracks using public API (no authentication required)
async searchTracks(query: string, limit: number = 25): Promise<any> {
const url = `https://api.deezer.com/search/track?q=${encodeURIComponent(query)}&limit=${limit}`;
console.log('[DEBUG] Searching Deezer API:', { query, limit, url });
try {
const response = await fetch(url, {
method: 'GET',
headers: {
...this.httpHeaders
},
connectTimeout: 30000
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
console.log('[DEBUG] Search results:', result);
return result;
} catch (error: any) {
console.error('[ERROR] Search failed:', error);
throw error;
}
}
// Get playlist data
async getPlaylist(playlistId: string): Promise<any> {
return this.apiCall('deezer.pagePlaylist', {

View File

@@ -0,0 +1,51 @@
/**
* Utility to add a Deezer track to the download queue
* Used by both search results and services/deezer pages
*/
import { deezerAPI } from '$lib/services/deezer';
import { addToQueue } from '$lib/stores/downloadQueue';
/**
* Fetch track metadata and add to download queue
* @param trackId - Deezer track ID
* @returns Promise that resolves when track is added to queue
*/
export async function addDeezerTrackToQueue(trackId: string): Promise<void> {
// Fetch full track data from GW API
const trackInfo = await deezerAPI.getTrack(trackId);
if (!trackInfo || !trackInfo.SNG_ID) {
throw new Error('Track not found or invalid track ID');
}
// Build track object
const track = {
id: trackInfo.SNG_ID,
title: trackInfo.SNG_TITLE,
artist: trackInfo.ART_NAME,
artistId: trackInfo.ART_ID,
artists: [trackInfo.ART_NAME],
album: trackInfo.ALB_TITLE,
albumId: trackInfo.ALB_ID,
albumArtist: trackInfo.ART_NAME,
albumArtistId: trackInfo.ART_ID,
trackNumber: trackInfo.TRACK_NUMBER || 1,
discNumber: trackInfo.DISK_NUMBER || 1,
duration: trackInfo.DURATION,
explicit: trackInfo.EXPLICIT_LYRICS === 1,
md5Origin: trackInfo.MD5_ORIGIN,
mediaVersion: trackInfo.MEDIA_VERSION,
trackToken: trackInfo.TRACK_TOKEN
};
// Add to queue
await addToQueue({
source: 'deezer',
type: 'track',
title: track.title,
artist: track.artist,
totalTracks: 1,
downloadObject: track
});
}

View File

@@ -1,71 +1,34 @@
import type { SearchResult } from '$lib/types/search';
import { deezerAPI } from '../deezer';
/**
* Search Deezer for tracks matching the query
* TODO: Implement actual Deezer search API
* For now, returns mock data
* Search Deezer for tracks using the public API
*/
export async function searchDeezerTracks(query: string): Promise<SearchResult[]> {
if (!query.trim()) {
return [];
}
// Simulate API delay
await new Promise(resolve => setTimeout(resolve, 300));
try {
const response = await deezerAPI.searchTracks(query, 50);
// Mock data - replace with actual Deezer API implementation
const mockResults: SearchResult[] = [
{
title: 'One More Time',
artist: 'Daft Punk',
album: 'Discovery',
duration: 320,
source: 'online',
trackId: '3135556',
coverArtUrl: 'https://e-cdns-images.dzcdn.net/images/cover/2e018122cb56986277102d2041a592c8/250x250-000000-80-0-0.jpg'
},
{
title: 'Get Lucky',
artist: 'Daft Punk',
album: 'Random Access Memories',
duration: 369,
source: 'online',
trackId: '67238732',
coverArtUrl: 'https://e-cdns-images.dzcdn.net/images/cover/b0b1e82769d1a1e452fd3f2f95d3bb0f/250x250-000000-80-0-0.jpg'
},
{
title: 'Around the World',
artist: 'Daft Punk',
album: 'Homework',
duration: 429,
source: 'online',
trackId: '3135553',
coverArtUrl: 'https://e-cdns-images.dzcdn.net/images/cover/d41d8cd98f00b204e9800998ecf8427e/250x250-000000-80-0-0.jpg'
},
{
title: 'Harder, Better, Faster, Stronger',
artist: 'Daft Punk',
album: 'Discovery',
duration: 224,
source: 'online',
trackId: '3135554',
coverArtUrl: 'https://e-cdns-images.dzcdn.net/images/cover/2e018122cb56986277102d2041a592c8/250x250-000000-80-0-0.jpg'
},
{
title: 'Digital Love',
artist: 'Daft Punk',
album: 'Discovery',
duration: 301,
source: 'online',
trackId: '3135555',
coverArtUrl: 'https://e-cdns-images.dzcdn.net/images/cover/2e018122cb56986277102d2041a592c8/250x250-000000-80-0-0.jpg'
// Map Deezer API response to SearchResult format
if (!response.data || !Array.isArray(response.data)) {
console.warn('[searchDeezerTracks] No data in response:', response);
return [];
}
];
// Filter mock results by query
return mockResults.filter(result =>
result.title.toLowerCase().includes(query.toLowerCase()) ||
result.artist.toLowerCase().includes(query.toLowerCase()) ||
result.album.toLowerCase().includes(query.toLowerCase())
);
return response.data.map((track: any): SearchResult => ({
title: track.title || 'Unknown Track',
artist: track.artist?.name || 'Unknown Artist',
album: track.album?.title || 'Unknown Album',
duration: track.duration,
source: 'online',
trackId: String(track.id),
coverArtUrl: track.album?.cover_medium || track.album?.cover_small
}));
} catch (error) {
console.error('[searchDeezerTracks] Search failed:', error);
throw error;
}
}

View File

@@ -4,7 +4,8 @@
import SearchResultsTable from '$lib/components/SearchResultsTable.svelte';
import { searchLocalTracks } from '$lib/library/search';
import { searchDeezerTracks } from '$lib/services/deezer/search';
import { downloadTrack } from '$lib/services/deezer/downloader';
import { deezerAPI } from '$lib/services/deezer';
import { addDeezerTrackToQueue } from '$lib/services/deezer/addToQueue';
import { settings, loadSettings } from '$lib/stores/settings';
import { deezerAuth } from '$lib/stores/deezer';
import type { SearchResult, SearchType } from '$lib/types/search';
@@ -69,7 +70,7 @@
return;
}
if (!$deezerAuth.user) {
if (!$deezerAuth.arl || !$deezerAuth.user) {
error = 'Please log in to Deezer first (Services → Deezer)';
return;
}
@@ -80,11 +81,12 @@
}
try {
console.log('Downloading track:', result.trackId);
await downloadTrack(result.trackId);
// TODO: Show success notification
deezerAPI.setArl($deezerAuth.arl);
await addDeezerTrackToQueue(result.trackId);
error = null; // Clear any previous error
} catch (e) {
error = 'Error downloading track: ' + (e as Error).message;
console.error('Queue error:', e);
error = 'Error adding to download queue: ' + (e as Error).message;
}
}
</script>

View File

@@ -3,7 +3,7 @@
import { goto } from '$app/navigation';
import { deezerAuth, loadDeezerAuth, saveArl, saveUser, clearDeezerAuth } from '$lib/stores/deezer';
import { deezerAPI } from '$lib/services/deezer';
import { addToQueue } from '$lib/stores/downloadQueue';
import { addDeezerTrackToQueue } from '$lib/services/deezer/addToQueue';
import { settings } from '$lib/stores/settings';
let arlInput = $state('');
@@ -129,35 +129,8 @@
queueError = '';
try {
// Build track object
const track = {
id: trackInfo.SNG_ID,
title: trackInfo.SNG_TITLE,
artist: trackInfo.ART_NAME,
artistId: trackInfo.ART_ID,
artists: [trackInfo.ART_NAME],
album: trackInfo.ALB_TITLE,
albumId: trackInfo.ALB_ID,
albumArtist: trackInfo.ART_NAME,
albumArtistId: trackInfo.ART_ID,
trackNumber: trackInfo.TRACK_NUMBER || 1,
discNumber: trackInfo.DISK_NUMBER || 1,
duration: trackInfo.DURATION,
explicit: trackInfo.EXPLICIT_LYRICS === 1,
md5Origin: trackInfo.MD5_ORIGIN,
mediaVersion: trackInfo.MEDIA_VERSION,
trackToken: trackInfo.TRACK_TOKEN
};
// Add to queue
await addToQueue({
source: 'deezer',
type: 'track',
title: track.title,
artist: track.artist,
totalTracks: 1,
downloadObject: track
});
// Use shared utility to add track to queue
await addDeezerTrackToQueue(trackIdInput);
queueStatus = '✓ Added to download queue!';