import * as cheerio from "cheerio"; import type { Scraper, VenueMeta } from "./base"; import type { EventInput } from "~/lib/db.server"; export const venue: VenueMeta = { id: "shinjuku-loft", name: "新宿 LOFT", url: "https://www.loft-prj.co.jp", area: "新宿", capacity: 500, }; export const scraper: Scraper = { venue, async scrape(): Promise { const res = await fetch("https://www.loft-prj.co.jp/schedule/loft"); if (!res.ok) throw new Error(`HTTP ${res.status}`); const html = await res.text(); const $ = cheerio.load(html); const events: EventInput[] = []; const seen = new Set(); $("section.block_schedule_list a[href*='/schedule/loft/schedule/']").each( (_, el) => { const $el = $(el); const href = $el.attr("href") ?? ""; if (seen.has(href)) return; seen.add(href); const year = $el.find("time div.year").text().trim(); const month = $el.find("time div.month").text().trim(); const day = $el.find("time div.day").text().trim(); if (!year || !month || !day) return; const date = `${year}-${month.padStart(2, "0")}-${day.padStart(2, "0")}`; const title = $el.find(".c_title span").text().trim(); if (!title) return; const timeText = $el.find(".open").text().trim(); const openMatch = timeText.match(/OPEN\s*(\d{2}:\d{2})/i); const startMatch = timeText.match(/START\s*(\d{2}:\d{2})/i); const artists = $el .find("ul.artist_tag li") .map((_, li) => $(li).text().trim()) .get() .filter((a) => a !== "..."); const artist = artists.join(" / ") || null; events.push({ venue_id: venue.id, title, artist, date, open_time: openMatch?.[1] ?? null, start_time: startMatch?.[1] ?? null, image_url: $el.find("span.bg").attr("data-bg") ?? null, source_url: href, }); } ); return events; }, };