source-19hz
Electronic music events from 19hz.info for one region, between two dates. Regions: BayArea, LosAngeles, Seattle, ORE (Portland), Denver, Phoenix, Texas, CHI, Detroit, Iowa, Atlanta, Miami, DC, Massachusetts. Code, no model: free and complete.
Agent monad: other monads start it. · version 2 · 1 steps
Shared code
const text = s => s.replace(/<br\s*\/?>/gi, ' ').replace(/<[^>]+>/g, '')
.replace(/&/g, '&').replace(/�?39;|'/g, "'").replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>')
.replace(/\s+/g, ' ').trim();
// "(9pm-2am)" or "(Fri: 8am-Sun: 4pm)" -> "21:00" / "08:00"; null when no time is printed.
function startTime(s) {
const m = s.match(/\((?:[A-Za-z]+:\s*)?(\d{1,2})(?::(\d{2}))?\s*(am|pm)/i);
if (!m) return null;
const h = (+m[1] % 12) + (m[3].toLowerCase() === 'pm' ? 12 : 0);
return String(h).padStart(2, '0') + ':' + (m[2] || '00');
}
parse
uses web · timeout 2m, 2 retries
const { region, from, to } = ctx.config;
const url = 'https://19hz.info/eventlisting_' + region + '.php';
const events = await ctx.cache.use(region + ':' + new Date().toISOString().slice(0, 10), 864e5, async () => {
const html = await ctx.web.text(url);
const out = [];
for (const [, row] of html.matchAll(/<tr[^>]*>([\s\S]*?)<\/tr>/g)) {
const cells = row.split(/<td[^>]*>/i).slice(1).map(c => c.replace(/<\/td>\s*$/i, ''));
if (cells.length < 7) continue;
const day = text(cells[6]).replace(/\//g, '-');
if (!/^\d{4}-\d{2}-\d{2}$/.test(day)) continue;
const [title, place = ''] = text(cells[1]).split(/ @ (?=[^@]*$)/);
const venueCity = place.match(/^(.*?)\s*\(([^)]+)\)\s*$/);
const time = startTime(text(cells[0]));
out.push({
title,
start: time ? day + 'T' + time : day,
venue: venueCity ? venueCity[1] : place,
city: venueCity ? venueCity[2] : '',
url: cells[1].match(/href=['"]([^'"]+)/)?.[1] || url,
price: text(cells[3]),
tags: text(cells[2]),
});
}
return out;
});
const inWindow = events.filter(e => e.start.slice(0, 10) >= from && e.start.slice(0, 10) <= to);
await ctx.log(url + ': ' + inWindow.length + ' of ' + events.length + ' events between ' + from + ' and ' + to);
return { events: inWindow };