#!/usr/bin/env node /** * CLI スクレイパー * * 使い方: * npm run scrape # 全会場 * npm run scrape liquid-room # 特定会場 * npm run scrape -- --list # 登録済み会場を表示 */ import { styleText } from "node:util"; import { runAllScrapers, runScraper } from "../app/lib/scraper-runner.server.js"; import { ALL_SCRAPERS } from "../app/scrapers/index.js"; const args = process.argv.slice(2); const venueId = args.find((a) => !a.startsWith("--")); const flagList = args.includes("--list"); if (flagList) { console.log(styleText("bold", "\n登録済みスクレイパー一覧:\n")); for (const s of ALL_SCRAPERS) { console.log(` ${styleText("cyan", s.venue.id.padEnd(25))} ${s.venue.name} (${s.venue.area})`); } console.log(); process.exit(0); } const startTime = Date.now(); console.log( styleText("bold", "\n🎸 東京ライブハウス スクレイパー\n") + (venueId ? ` 対象: ${styleText("cyan", venueId)}\n` : ` 対象: ${styleText("cyan", `全 ${ALL_SCRAPERS.length} 会場`)}\n`) ); const results = venueId ? [await runScraper(venueId)] : await runAllScrapers(); const elapsed = ((Date.now() - startTime) / 1000).toFixed(1); let totalSaved = 0; let hasError = false; for (const r of results) { const icon = r.status === "ok" ? styleText("green", "✔") : styleText("red", "✖"); const name = r.venue_name.padEnd(24); if (r.status === "ok") { const count = styleText("green", `${r.events_saved} 件`); console.log(` ${icon} ${name} ${count}`); totalSaved += r.events_saved; } else { hasError = true; console.log(` ${icon} ${styleText("red", name)}`); if (r.error) { const lines = r.error.split("\n"); for (const line of lines) { console.log(` ${styleText("dim", line)}`); } } } } const summary = `\n 合計 ${styleText("bold", `${totalSaved} 件`)} 保存 (${elapsed}s)`; console.log(summary + "\n"); process.exit(hasError ? 1 : 0);