import * as cheerio from "cheerio"; import type { Scraper, VenueMeta } from "./base"; import type { EventInput } from "~/lib/db.server"; export const venue: VenueMeta = { id: "liquid-room", name: "LIQUID ROOM", url: "https://www.liquidroom.net", area: "恵比寿", }; export const scraper: Scraper = { venue, async scrape(): Promise { const res = await fetch("https://www.liquidroom.net/schedule"); if (!res.ok) throw new Error(`HTTP ${res.status}`); const html = await res.text(); const $ = cheerio.load(html); const events: EventInput[] = []; $("article").each((_, el) => { const $el = $(el); const href = $el.find("a.s_link").attr("href") ?? ""; // Date is encoded in the URL: e.g. /schedule/eventname_20260501 const dateMatch = href.match(/_(\d{4})(\d{2})(\d{2})$/); if (!dateMatch) return; const date = `${dateMatch[1]}-${dateMatch[2]}-${dateMatch[3]}`; const h2 = $el.find("h2").first().text().trim(); if (!h2) return; const subtitle = $el.find("p.subtitle").first().text().trim(); // h2 is the artist/band name; subtitle (if present) is the event title const title = subtitle || h2; const artist = subtitle ? h2 : null; const openTime = $el .find("dl") .filter((_, dl) => $(dl).find("dt").text().includes("OPEN")) .find("dd") .text() .trim() .match(/\d{2}:\d{2}/)?.[0] ?? null; const startTime = $el .find("dl") .filter((_, dl) => $(dl).find("dt").text().includes("START")) .find("dd") .text() .trim() .match(/\d{2}:\d{2}/)?.[0] ?? null; events.push({ venue_id: venue.id, title, artist, date, open_time: openTime, start_time: startTime, image_url: $el.find("div.left img").attr("src") ?? null, source_url: href, }); }); return events; }, };