import * as cheerio from "cheerio"; import type { Scraper, VenueMeta } from "./base"; import type { EventInput } from "~/lib/db.server"; export const venue: VenueMeta = { id: "club-quattro", name: "CLUB QUATTRO", url: "https://www.club-quattro.com", area: "渋谷", capacity: 750, }; export const scraper: Scraper = { venue, async scrape(): Promise { const res = await fetch("https://www.club-quattro.com/shibuya/schedule/"); if (!res.ok) throw new Error(`HTTP ${res.status}`); const html = await res.text(); const $ = cheerio.load(html); const events: EventInput[] = []; $("li[data-event-date]").each((_, el) => { const $el = $(el); const date = $el.attr("data-event-date") ?? ""; if (!date) return; const title = $el.find("p.txt-02").text().trim(); if (!title) return; const artist = $el.find("p.txt-01 span").text().trim() || null; let openTime: string | null = null; let startTime: string | null = null; $el.find("dl.detail-list .bundle").each((_, bundle) => { const label = $(bundle).find("dt").text().trim(); if (label.includes("開場") || label.includes("開演")) { const times = $(bundle).find("dd").text().trim().match(/\d{2}:\d{2}/g) ?? []; openTime = times[0] ?? null; startTime = times[1] ?? null; } }); const href = $el.find("a").first().attr("href") ?? null; const imageSrc = $el.find(".front img").attr("src") ?? null; events.push({ venue_id: venue.id, title, artist, date, open_time: openTime, start_time: startTime, image_url: imageSrc ? absoluteUrl(imageSrc, venue.url) : null, source_url: href ? absoluteUrl(href, venue.url) : null, }); }); return events; }, }; function absoluteUrl(url: string, base: string): string { if (url.startsWith("http")) return url; return url.startsWith("/") ? base + url : `${base}/${url}`; }