/**
* Liquid Room (恵比寿) — https://www.liquidroom.net/schedule
*
* The schedule page lists events with JSON-LD or HTML data.
* Structure:
contains date, title, etc.
*/
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.p-schedule__item, .schedule-list__item, .c-event-item").each(
(_, el) => {
const $el = $(el);
const title =
$el.find(".p-schedule__title, .event-title, h3, h2").first().text().trim();
if (!title) return;
const dateStr =
$el.find(".p-schedule__date, .event-date, time").first().text().trim() ||
$el.find("time").attr("datetime") ||
"";
const date = parseJapaneseDate(dateStr);
if (!date) return;
const artist =
$el.find(".p-schedule__artist, .artist").first().text().trim() || null;
const startTime =
$el.find(".p-schedule__time, .open-time").first().text().trim().match(/\d{2}:\d{2}/)?.[0] ?? null;
const ticketUrl =
$el.find("a[href*='ticket'], a[href*='eplus'], a[href*='pia']").first().attr("href") ?? null;
const imageUrl =
$el.find("img").first().attr("src") ?? null;
const sourceUrl =
$el.find("a").first().attr("href") ?? null;
events.push({
venue_id: venue.id,
title,
artist,
date,
start_time: startTime,
ticket_url: ticketUrl,
image_url: imageUrl ? absoluteUrl(imageUrl, venue.url) : null,
source_url: sourceUrl ? absoluteUrl(sourceUrl, venue.url) : null,
});
}
);
return events;
},
};
function parseJapaneseDate(raw: string): string | null {
// Handles "2025.06.15" "2025/06/15" "2025年06月15日" "06.15" formats
const m =
raw.match(/(\d{4})[./年](\d{1,2})[./月](\d{1,2})/) ||
raw.match(/(\d{1,2})[./月](\d{1,2})/);
if (!m) return null;
if (m.length === 4) {
return `${m[1]}-${m[2].padStart(2, "0")}-${m[3].padStart(2, "0")}`;
}
const year = new Date().getFullYear();
return `${year}-${m[1].padStart(2, "0")}-${m[2].padStart(2, "0")}`;
}
function absoluteUrl(url: string, base: string): string {
if (url.startsWith("http")) return url;
if (url.startsWith("/")) return base + url;
return base + "/" + url;
}