revert: restore iTunes direct calls — remove broken backend proxy
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
ad8ba08a03
commit
a2f2f4f6c3
2 files changed
+48
-86
No files matched your search
@@ -483,48 +483,6 @@ function _spTrackItem(t, defaultAlbum, defaultArt, defaultAlbumArtist) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── ITUNES PROXY ────────────────────────────────────────────────────────────
|
|
||||||
// Browser origin is blocked by Apple (403). All iTunes/Apple RSS calls go through here.
|
|
||||||
|
|
||||||
async function itunesProxy(url) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
https.get(url, res => {
|
|
||||||
let d = '';
|
|
||||||
res.on('data', c => d += c);
|
|
||||||
res.on('end', () => { try { resolve(JSON.parse(d)); } catch { resolve({}); } });
|
|
||||||
}).on('error', reject);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
app.get('/api/itunes/search', async (req, res) => {
|
|
||||||
try {
|
|
||||||
const { term, entity = 'musicTrack', limit = 25, country = 'us' } = req.query;
|
|
||||||
if (!term) return res.json({ results: [] });
|
|
||||||
const url = `https://itunes.apple.com/search?term=${encodeURIComponent(term)}&media=music&entity=${entity}&limit=${limit}&country=${country}&explicit=Yes`;
|
|
||||||
const d = await itunesProxy(url);
|
|
||||||
res.json({ results: d.results || [] });
|
|
||||||
} catch { res.json({ results: [] }); }
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get('/api/itunes/lookup', async (req, res) => {
|
|
||||||
try {
|
|
||||||
const { id, entity = 'song', limit = 200, country = 'us' } = req.query;
|
|
||||||
if (!id) return res.json({ results: [] });
|
|
||||||
const url = `https://itunes.apple.com/lookup?id=${id}&entity=${entity}&limit=${limit}&country=${country}&explicit=Yes`;
|
|
||||||
const d = await itunesProxy(url);
|
|
||||||
res.json({ results: d.results || [] });
|
|
||||||
} catch { res.json({ results: [] }); }
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get('/api/itunes/charts', async (req, res) => {
|
|
||||||
try {
|
|
||||||
const { type = 'albums', country = 'de' } = req.query;
|
|
||||||
const url = `https://rss.applemarketingtools.com/api/v2/${country}/music/most-played/50/${type}.json`;
|
|
||||||
const d = await itunesProxy(url);
|
|
||||||
res.json(d.feed ? { results: d.feed.results || [] } : { results: [] });
|
|
||||||
} catch { res.json({ results: [] }); }
|
|
||||||
});
|
|
||||||
|
|
||||||
// ─── ROUTES ──────────────────────────────────────────────────────────────────
|
// ─── ROUTES ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
app.post('/api/spotify/track-artwork', async (req, res) => {
|
app.post('/api/spotify/track-artwork', async (req, res) => {
|
||||||
|
|||||||
+48
-44
@@ -40,71 +40,75 @@ export async function ndAuth() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ─── ITUNES ───
|
// ─── ITUNES ───
|
||||||
// Try backend proxy first (avoids Apple's 403 browser-origin block).
|
|
||||||
// Fall back to direct Apple API if no backend is configured or proxy fails.
|
|
||||||
const ITUNES = 'https://itunes.apple.com';
|
const ITUNES = 'https://itunes.apple.com';
|
||||||
const APPLE_RSS = 'https://rss.applemarketingtools.com';
|
|
||||||
|
|
||||||
async function _fetch(url) {
|
|
||||||
try {
|
|
||||||
const r = await fetch(url);
|
|
||||||
const text = await r.text();
|
|
||||||
if (!text) return {};
|
|
||||||
return JSON.parse(text);
|
|
||||||
} catch { return {}; }
|
|
||||||
}
|
|
||||||
|
|
||||||
async function _iGet(proxyPath, directUrl) {
|
|
||||||
const base = S.cfg.backendUrl;
|
|
||||||
if (base) {
|
|
||||||
const d = await _fetch(base + proxyPath);
|
|
||||||
if (d?.results?.length) return d; // proxy returned real results
|
|
||||||
if (d?.results !== undefined) return d; // proxy responded but iTunes had no results (don't double-fetch)
|
|
||||||
}
|
|
||||||
return directUrl ? _fetch(directUrl) : {};
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function itunesSearch(query, entity='musicTrack', limit=25) {
|
export async function itunesSearch(query, entity='musicTrack', limit=25) {
|
||||||
const qs = `term=${encodeURIComponent(query)}&media=music&entity=${entity}&limit=${limit}&country=us&explicit=Yes`;
|
try {
|
||||||
const d = await _iGet(`/api/itunes/search?term=${encodeURIComponent(query)}&entity=${entity}&limit=${limit}`,
|
const r = await fetch(`${ITUNES}/search?term=${encodeURIComponent(query)}&media=music&entity=${entity}&limit=${limit}&country=us&explicit=Yes`);
|
||||||
`${ITUNES}/search?${qs}`);
|
const text = await r.text();
|
||||||
return d.results || [];
|
if (!text) return [];
|
||||||
|
return JSON.parse(text).results || [];
|
||||||
|
} catch { return []; }
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function itunesLookupAlbum(collectionId) {
|
export async function itunesLookupAlbum(collectionId) {
|
||||||
const d = await _iGet(`/api/itunes/lookup?id=${collectionId}&entity=song`,
|
const r = await fetch(`${ITUNES}/lookup?id=${collectionId}&entity=song&explicit=Yes`);
|
||||||
`${ITUNES}/lookup?id=${collectionId}&entity=song&explicit=Yes`);
|
const d = await r.json();
|
||||||
return (d.results || []).filter(x => x.wrapperType === 'track');
|
return (d.results || []).filter(x => x.wrapperType === 'track');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns actual explicit tracks for an artist via lookup (search endpoint only returns cleaned versions)
|
||||||
export async function itunesLookupArtistSongs(artistId, limit=15) {
|
export async function itunesLookupArtistSongs(artistId, limit=15) {
|
||||||
const d = await _iGet(`/api/itunes/lookup?id=${artistId}&entity=song&limit=${limit}`,
|
const r = await fetch(`${ITUNES}/lookup?id=${artistId}&entity=song&limit=${limit}&country=us&explicit=Yes`);
|
||||||
`${ITUNES}/lookup?id=${artistId}&entity=song&limit=${limit}&country=us&explicit=Yes`);
|
const d = await r.json();
|
||||||
return (d.results || []).filter(x => x.wrapperType === 'track');
|
return (d.results || []).filter(x => x.wrapperType === 'track');
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function itunesLookupArtistAlbums(artistId, limit=200) {
|
export async function itunesLookupArtistAlbums(artistId, limit=200) {
|
||||||
const d = await _iGet(`/api/itunes/lookup?id=${artistId}&entity=album&limit=${limit}`,
|
const r = await fetch(`${ITUNES}/lookup?id=${artistId}&entity=album&limit=${limit}&country=us`);
|
||||||
`${ITUNES}/lookup?id=${artistId}&entity=album&limit=${limit}&country=us`);
|
const d = await r.json();
|
||||||
return (d.results || []).filter(x => x.wrapperType === 'collection' && x.collectionType === 'Album');
|
return (d.results || []).filter(x => x.wrapperType === 'collection' && x.collectionType === 'Album');
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function itunesTopCharts() {
|
export async function itunesTopCharts() {
|
||||||
for (const country of ['de', 'us']) {
|
const urls = [
|
||||||
const d = await _iGet(`/api/itunes/charts?type=albums&country=${country}`,
|
'https://rss.applemarketingtools.com/api/v2/de/music/most-played/50/albums.json',
|
||||||
`${APPLE_RSS}/api/v2/${country}/music/most-played/50/albums.json`);
|
'https://rss.applemarketingtools.com/api/v2/us/music/most-played/50/albums.json',
|
||||||
const results = d.results || d.feed?.results;
|
];
|
||||||
if (results?.length) return results;
|
for(const url of urls) {
|
||||||
|
try {
|
||||||
|
const r = await fetch(url, { signal: AbortSignal.timeout(6000) });
|
||||||
|
if(!r.ok) continue;
|
||||||
|
const d = await r.json();
|
||||||
|
if(d.feed?.results?.length) return d.feed.results;
|
||||||
|
} catch {}
|
||||||
}
|
}
|
||||||
return [];
|
try {
|
||||||
|
const r = await fetch('https://itunes.apple.com/search?term=new+music&media=music&entity=album&limit=50&country=de');
|
||||||
|
const d = await r.json();
|
||||||
|
return (d.results||[]).map(a => ({
|
||||||
|
name: a.collectionName, artistName: a.artistName,
|
||||||
|
artworkUrl: a.artworkUrl100?.replace('100x100','300x300'), artworkUrl100: a.artworkUrl100,
|
||||||
|
}));
|
||||||
|
} catch { return []; }
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function itunesTopSongs() {
|
export async function itunesTopSongs() {
|
||||||
for (const country of ['de', 'us']) {
|
const urls = [
|
||||||
const d = await _iGet(`/api/itunes/charts?type=songs&country=${country}`,
|
'https://rss.applemarketingtools.com/api/v2/de/music/most-played/50/songs.json',
|
||||||
`${APPLE_RSS}/api/v2/${country}/music/most-played/50/songs.json`);
|
'https://rss.applemarketingtools.com/api/v2/us/music/most-played/50/songs.json',
|
||||||
const results = d.results || d.feed?.results;
|
];
|
||||||
if (results?.length) return results;
|
for(const url of urls) {
|
||||||
|
try {
|
||||||
|
const r = await fetch(url, { signal: AbortSignal.timeout(6000) });
|
||||||
|
if(!r.ok) continue;
|
||||||
|
const d = await r.json();
|
||||||
|
if(d.feed?.results?.length) return d.feed.results;
|
||||||
|
} catch {}
|
||||||
}
|
}
|
||||||
return [];
|
try {
|
||||||
|
const r = await fetch('https://itunes.apple.com/search?term=top+hits&media=music&entity=musicTrack&limit=50&country=de');
|
||||||
|
const d = await r.json();
|
||||||
|
return (d.results||[]).map(t => ({ name: t.trackName, artistName: t.artistName, artworkUrl: t.artworkUrl100 }));
|
||||||
|
} catch { return []; }
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user