fix: proxy Bandsintown events through backend to bypass 403, always show ticket link with sold-out badge
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
914297651e
commit
98c964ecae
3 files changed
+21
-4
No files matched your search
@@ -315,6 +315,20 @@ app.get('/api/itunes/:endpoint', (req, res) => {
|
||||
}).on('error', e => res.status(502).json({ error: e.message }));
|
||||
});
|
||||
|
||||
app.get('/api/events/:artist', (req, res) => {
|
||||
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 }));
|
||||
});
|
||||
|
||||
app.get('/api/health', (req, res) => {
|
||||
res.json({
|
||||
status: 'ok', ytdlpVersion,
|
||||
|
||||
+5
-2
@@ -1,8 +1,11 @@
|
||||
import { esc } from '../utils.js';
|
||||
import { S } from '../state.js';
|
||||
|
||||
export async function fetchEvents(artistName) {
|
||||
const base = S.cfg?.backendUrl;
|
||||
if(!base) return [];
|
||||
try {
|
||||
const r = await fetch(`https://rest.bandsintown.com/artists/${encodeURIComponent(artistName)}/events?app_id=novasonic`);
|
||||
const r = await fetch(`${base}/api/events/${encodeURIComponent(artistName)}`);
|
||||
if(!r.ok) return [];
|
||||
const data = await r.json();
|
||||
if(!Array.isArray(data)) return [];
|
||||
@@ -31,7 +34,7 @@ export function openEventsModal(events, artistName) {
|
||||
<div class="event-venue-name">${esc(venue)}</div>
|
||||
<div class="event-venue-city">${esc(city)}</div>
|
||||
</div>
|
||||
${offer ? `<a class="event-book-btn${soldOut ? ' sold-out' : ''}" href="${esc(offer.url)}" target="_blank" rel="noopener">${soldOut ? 'Ausverkauft' : 'Buchen →'}</a>` : ''}
|
||||
${offer ? `<a class="event-book-btn" href="${esc(offer.url)}" target="_blank" rel="noopener">${soldOut ? '<span class="event-sold-badge">Ausverkauft</span>' : ''}Tickets →</a>` : ''}
|
||||
</div>`;
|
||||
}).join('');
|
||||
|
||||
|
||||
+2
-2
@@ -855,9 +855,9 @@ button:focus{outline:none}
|
||||
.event-venue{flex:1;min-width:0}
|
||||
.event-venue-name{font-size:13px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||||
.event-venue-city{font-size:11px;color:var(--text2);margin-top:2px}
|
||||
.event-book-btn{flex-shrink:0;padding:7px 14px;border-radius:20px;background:var(--accent);color:#fff;font-family:var(--font);font-size:12px;font-weight:700;text-decoration:none;transition:opacity .15s}
|
||||
.event-book-btn{flex-shrink:0;padding:7px 14px;border-radius:20px;background:var(--accent);color:#fff;font-family:var(--font);font-size:12px;font-weight:700;text-decoration:none;transition:opacity .15s;display:flex;flex-direction:column;align-items:center;gap:2px}
|
||||
.event-book-btn:hover{opacity:.85}
|
||||
.event-book-btn.sold-out{background:var(--surface2);color:var(--text3)}
|
||||
.event-sold-badge{font-size:9px;font-weight:600;opacity:.8;letter-spacing:.3px}
|
||||
|
||||
/* ── MISC ── */
|
||||
.spinner{width:28px;height:28px;border:3px solid var(--surface2);border-top-color:var(--accent);border-radius:50%;animation:spin .8s linear infinite;margin:50px auto;display:block}
|
||||
|
||||
Reference in new issue
Block a user