feat: replace Bandsintown with Ticketmaster for concert events
Backend: swap /api/events/:artist to use Ticketmaster Discovery API
(startDateTime=now, classificationName=music, size=20, sort by date).
Maps TM response to the same {datetime, venue, offers} shape the modal
expects. Returns [] if TICKETMASTER_API_KEY is not set.
Frontend: remove all debug console.logs from events.js; drop redundant
client-side date filter (TM already filters by startDateTime server-side).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
def625a622
commit
adcbb2e883
3 files changed
+40
-18
No files matched your search
@@ -9,7 +9,8 @@ const CONFIG = {
|
||||
// 'ytdlp' | 'squid' | 'auto' (squid first, yt-dlp fallback)
|
||||
downloadBackend: (process.env.DOWNLOAD_BACKEND || 'auto').toLowerCase(),
|
||||
// squid.wtf quality: 'mp3' | 'flac' | 'hi-res'
|
||||
squidQuality: (process.env.SQUID_QUALITY || 'mp3').toLowerCase(),
|
||||
squidQuality: (process.env.SQUID_QUALITY || 'mp3').toLowerCase(),
|
||||
ticketmasterKey: process.env.TICKETMASTER_API_KEY || '',
|
||||
};
|
||||
|
||||
module.exports = CONFIG;
|
||||
+36
-10
@@ -316,17 +316,43 @@ app.get('/api/itunes/:endpoint', (req, res) => {
|
||||
});
|
||||
|
||||
app.get('/api/events/:artist', (req, res) => {
|
||||
const key = CONFIG.ticketmasterKey;
|
||||
if (!key) return res.json([]);
|
||||
const artist = req.params.artist;
|
||||
const target = `https://rest.bandsintown.com/artists/${encodeURIComponent(artist)}/events?app_id=novasonic`;
|
||||
https.get(target, {
|
||||
headers: {
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
|
||||
'Accept': 'application/json',
|
||||
}
|
||||
}, upstream => {
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
upstream.pipe(res);
|
||||
}).on('error', e => res.status(502).json({ error: e.message }));
|
||||
const now = new Date().toISOString().replace(/\.\d{3}Z$/, 'Z');
|
||||
const url = `https://app.ticketmaster.com/discovery/v2/events.json` +
|
||||
`?keyword=${encodeURIComponent(artist)}&classificationName=music` +
|
||||
`&startDateTime=${encodeURIComponent(now)}&size=20&sort=date%2Casc` +
|
||||
`&apikey=${encodeURIComponent(key)}`;
|
||||
https.get(url, { headers: { 'Accept': 'application/json' } }, upstream => {
|
||||
let body = '';
|
||||
upstream.on('data', d => body += d);
|
||||
upstream.on('end', () => {
|
||||
try {
|
||||
const data = JSON.parse(body);
|
||||
const raw = data?._embedded?.events || [];
|
||||
const events = raw.map(e => {
|
||||
const venue = e._embedded?.venues?.[0] || {};
|
||||
const start = e.dates?.start || {};
|
||||
const dt = start.dateTime || (start.localDate
|
||||
? `${start.localDate}T${start.localTime || '00:00:00'}`
|
||||
: null);
|
||||
const soldOut = e.dates?.status?.code === 'offsale' ||
|
||||
e.dates?.status?.code === 'cancelled';
|
||||
return {
|
||||
datetime: dt,
|
||||
venue: {
|
||||
name: venue.name || '',
|
||||
city: venue.city?.name || '',
|
||||
country: venue.country?.name || '',
|
||||
},
|
||||
offers: e.url ? [{ url: e.url, status: soldOut ? 'unavailable' : 'available' }] : [],
|
||||
};
|
||||
}).filter(e => e.datetime);
|
||||
res.json(events);
|
||||
} catch { res.json([]); }
|
||||
});
|
||||
}).on('error', () => res.json([]));
|
||||
});
|
||||
|
||||
app.get('/api/health', (req, res) => {
|
||||
|
||||
+2
-7
@@ -3,19 +3,14 @@ import { S } from '../state.js';
|
||||
|
||||
export async function fetchEvents(artistName) {
|
||||
const base = S.cfg?.backendUrl;
|
||||
console.log('[events] base:', base, '| artist:', artistName);
|
||||
if(!base) return [];
|
||||
try {
|
||||
const r = await fetch(`${base}/api/events/${encodeURIComponent(artistName)}`);
|
||||
console.log('[events] status:', r.status);
|
||||
if(!r.ok) return [];
|
||||
const data = await r.json();
|
||||
console.log('[events] data:', data);
|
||||
if(!Array.isArray(data)) return [];
|
||||
const filtered = data.filter(e => new Date(e.datetime) > new Date());
|
||||
console.log('[events] upcoming:', filtered.length);
|
||||
return filtered;
|
||||
} catch(e) { console.log('[events] error:', e); return []; }
|
||||
return data;
|
||||
} catch { return []; }
|
||||
}
|
||||
|
||||
export function openEventsModal(events, artistName) {
|
||||
|
||||
Reference in new issue
Block a user