diff --git a/drizzle/0001_magenta_naoko.sql b/drizzle/0001_magenta_naoko.sql new file mode 100644 index 0000000..60c6d0a --- /dev/null +++ b/drizzle/0001_magenta_naoko.sql @@ -0,0 +1 @@ +ALTER TABLE "eventsTable" ADD COLUMN "location" text; \ No newline at end of file diff --git a/drizzle/meta/0001_snapshot.json b/drizzle/meta/0001_snapshot.json new file mode 100644 index 0000000..6b0b663 --- /dev/null +++ b/drizzle/meta/0001_snapshot.json @@ -0,0 +1,131 @@ +{ + "id": "f3f30a9d-d72d-4e62-86b4-031282707007", + "prevId": "c0102a7e-5d09-41ca-88d1-c66f2f299e65", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.eventsTable": { + "name": "eventsTable", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "startTime": { + "name": "startTime", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "endTime": { + "name": "endTime", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lineId": { + "name": "lineId", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "eventsTable_lineId_linesTable_id_fk": { + "name": "eventsTable_lineId_linesTable_id_fk", + "tableFrom": "eventsTable", + "tableTo": "linesTable", + "columnsFrom": [ + "lineId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.linesTable": { + "name": "linesTable", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 7b7dbdc..e936560 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -8,6 +8,13 @@ "when": 1750786732013, "tag": "0000_noisy_omega_red", "breakpoints": true + }, + { + "idx": 1, + "version": "7", + "when": 1751371464551, + "tag": "0001_magenta_naoko", + "breakpoints": true } ] } \ No newline at end of file diff --git a/package.json b/package.json index bf1d4a5..7ace9ab 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "build": "next build", "start": "next start", "lint": "next lint", - "push-db": "drizzle-kit push" + "db-generate": "drizzle-kit generate" }, "dependencies": { "@electric-sql/pglite": "^0.3.3", diff --git a/src/common/parser.ts b/src/common/parser.ts index 2b1f988..5b0c9e5 100644 --- a/src/common/parser.ts +++ b/src/common/parser.ts @@ -6,14 +6,16 @@ export interface Line { description: string; } +const userAgentHeader = { + "User-Agent": + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3", +}; + export async function getLines() { const annotation = await fetch( "https://amber.festivalfantazie.cz/porady.php", { - headers: { - "User-Agent": - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3", - }, + headers: userAgentHeader, }, ); @@ -68,16 +70,14 @@ export interface Event { description: string; type: string; lineId: number; + location?: string; // Optional location field } export async function getScheduleForLine(lineId: number) { const response = await fetch( `https://amber.festivalfantazie.cz/program_linie.php?linie=${lineId}`, { - headers: { - "User-Agent": - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3", - }, + headers: userAgentHeader, }, ); @@ -162,8 +162,7 @@ export async function getScheduleForLine(lineId: number) { "https://amber.festivalfantazie.cz/ajax/anotace.php", { headers: { - "User-Agent": - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0", + ...userAgentHeader, "Content-Type": "application/x-www-form-urlencoded", }, referrer: `https://amber.festivalfantazie.cz/program_linie.php?linie=${lineId}`, @@ -182,6 +181,25 @@ export async function getScheduleForLine(lineId: number) { const description$ = cheerio.load(descriptionHtml); const description = description$("description").text().trim(); + const locationRes = await fetch( + `https://app.festivalfantazie.cz/program-detail/${eventIdNumber}`, + { + headers: userAgentHeader, + }, + ); + + if (!locationRes.ok) { + throw new Error( + `Failed to fetch location for event ID ${eventIdNumber}`, + ); + } + + const locationHtml = await locationRes.text(); + const location$ = cheerio.load(locationHtml); + const location = location$("div.row.mb-5 > div.col-6.mb-2:nth-of-type(2)") + .text() + .trim(); + events.push({ id: eventIdNumber, startTime, @@ -191,6 +209,7 @@ export async function getScheduleForLine(lineId: number) { description, type: $(td).find("td.program_typ").text().trim(), lineId, + location: location || undefined, // Optional location field }); currentTime = new Date(endTime); diff --git a/src/components/EventCard.tsx b/src/components/EventCard.tsx index bb6616d..7b1d99d 100644 --- a/src/components/EventCard.tsx +++ b/src/components/EventCard.tsx @@ -2,10 +2,7 @@ import type { Event } from "@/common/parser"; import { isEventFavorite } from "@/common/utils"; import { Button } from "./ui/button"; import { toggleFavoriteEvent } from "@/app/actions"; -import { - HeartMinusIcon, - HeartPlusIcon, -} from "lucide-react"; +import { HeartMinusIcon, HeartPlusIcon } from "lucide-react"; import { getLineById } from "@/db"; import { Badge } from "./ui/badge"; import Link from "next/link"; @@ -24,8 +21,15 @@ export async function EventCard({ return (
-
-

{event.title}

+
+
+

{event.title}

+ {line && ( + + {line.name} + + )} +
-
- {line && ( - - {line.name} - - )}
{event.name}
{showDate ? `${event.startTime.toLocaleDateString(["cs-CZ"], { month: "numeric", day: "numeric", - weekday: 'short', + weekday: "short", })} ` : ""} {event.startTime.toLocaleTimeString(["cs-CZ"], { @@ -65,6 +67,11 @@ export async function EventCard({ hour12: false, })}
+ {event.location && ( +
+ {event.location} +
+ )}

{event.description}

diff --git a/src/components/EventList.tsx b/src/components/EventList.tsx index 9a3f487..a6d3d5a 100644 --- a/src/components/EventList.tsx +++ b/src/components/EventList.tsx @@ -1,4 +1,4 @@ -import { Event } from "@/common/parser"; +import type { Event } from "@/common/parser"; import { EventCard } from "./EventCard"; export function EventList({ diff --git a/src/db/schema.ts b/src/db/schema.ts index dfbbbc5..a3c87c3 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -14,6 +14,7 @@ export const eventsTable = pgTable("eventsTable", { title: text().notNull(), description: text().notNull(), type: text().notNull(), + location: text(), // Optional location field lineId: integer() .notNull() .references(() => linesTable.id, {