feat(web): switch the Apple/Erkunden search to Deezer
Apple's iTunes /search returns 403 from server IPs, killing the Erkunden search. Deezer is free, needs no key, and is actually better: it returns explicit versions, artist images, track durations and 30s previews. - Backend: /api/deezer/* proxy (CORS-safe); album download accepts a `tracks` array in the request body (Deezer albums send their tracks directly, pre-mapped). - api.js: itunesSearch / itunesLookupAlbum / itunesLookupArtistSongs|Albums now hit Deezer and map results to the existing ItunesItem shape, so the UI + download flow are unchanged. Function names kept. - downloads.js: album downloads fetch the Deezer album's tracks and send them in the body. iTunes /lookup (by ID) and the charts RSS proxy stay. Known follow-ups: artist-type download still calls iTunes /search server-side; the metadata-editor iTunes autofill; and the Android client — all still on iTunes for now. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
ccf704754a
commit
68a9cdd7d1
4 files changed
+103
-22
No files matched your search
@@ -324,6 +324,20 @@ app.get('/api/itunes/:endpoint', (req, res) => {
|
||||
}).on('error', e => res.status(502).json({ error: e.message }));
|
||||
});
|
||||
|
||||
// Deezer API proxy — the search source (Apple's /search returns 403 from server IPs). Deezer is
|
||||
// free, needs no key, and returns explicit versions + artist images + track durations. Covers
|
||||
// /search, /search/album, /search/artist, /album/{id}, /album/{id}/tracks, /artist/{id}/albums, …
|
||||
app.get('/api/deezer/*', (req, res) => {
|
||||
const sub = String(req.params[0] || '').replace(/[^a-zA-Z0-9/_-]/g, '');
|
||||
const qs = new URLSearchParams(req.query).toString();
|
||||
const target = `https://api.deezer.com/${sub}${qs ? '?' + qs : ''}`;
|
||||
https.get(target, { headers: { 'User-Agent': 'NovaSonic/1.0' } }, upstream => {
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.status(upstream.statusCode || 200);
|
||||
upstream.pipe(res);
|
||||
}).on('error', e => res.status(502).json({ error: e.message }));
|
||||
});
|
||||
|
||||
// Apple "most-played" charts RSS — the browser can't fetch rss.applemarketingtools.com directly
|
||||
// (no CORS header), so proxy it. kind = albums | songs; country defaults to de.
|
||||
app.get('/api/itunes-charts/:kind', (req, res) => {
|
||||
@@ -626,8 +640,11 @@ app.post('/api/download', async (req, res) => {
|
||||
}
|
||||
|
||||
} else if (type === 'album') {
|
||||
let tracks = [];
|
||||
if (collectionId) {
|
||||
// Prefer a track list supplied in the request body (Deezer albums send their tracks directly,
|
||||
// pre-mapped to the iTunes track shape). Fall back to the iTunes lookup for legacy callers.
|
||||
let tracks = Array.isArray(req.body.tracks) ? req.body.tracks : [];
|
||||
if (tracks.length) log(jobId, `📋 ${tracks.length} Tracks aus Anfrage (Deezer)`);
|
||||
if (!tracks.length && collectionId) {
|
||||
log(jobId, '📋 Lade Track-Liste von iTunes…');
|
||||
tracks = await fetchItunesTracks(collectionId);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user