From 5b59341987391f52a628c9e7dda808aa51100578 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 11 Nov 2026 07:19:30 +0000 Subject: [PATCH] Match a Tito event with a typed location by name An organiser can type a location instead of picking a venue, which leaves the page with no venue record: no address list, and a maps link without coordinates. findEvents threw on it, failing transform-prepare for every venue. Such an event is now matched on the typed name alone. Co-Authored-By: Claude Fable 5.2 Claude-Session: https://claude.ai/code/session_01GBhsxWaH5bizzTJXocRH48 --- sources/ti.to/find-events.js | 26 ++++++++++--- sources/ti.to/tests/index.test.js | 61 +++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 6 deletions(-) diff --git a/sources/ti.to/find-events.js b/sources/ti.to/find-events.js index 8f7ffcc8d..03b0729b2 100644 --- a/sources/ti.to/find-events.js +++ b/sources/ti.to/find-events.js @@ -143,20 +143,34 @@ function extractEventDetails(html, url) { throw new Error(`No calendar info found on ${url}`); } - const locationHref = $(".tito-event-homepage--basic-info-location a").attr( - "href", - ); + const locationLink = $(".tito-event-homepage--basic-info-location a"); + const locationHref = locationLink.attr("href"); if (!locationHref) { throw new Error(`No location link found on ${url}`); } + const description = getText($(".tito-description")); + + // An organiser can type a location instead of picking a venue. The page + // then has no venue record: no address list, and a maps link that searches + // for the typed text rather than pointing at coordinates. The typed text is + // all there is, so the event is matched on that name alone. const venueAddress = getText($(".tito-venues li a span")); if (!venueAddress) { - throw new Error(`No .tito-venues address found on ${url}`); + const locationText = getText(locationLink); + if (!locationText) { + throw new Error(`No .tito-venues address or location text on ${url}`); + } + return { + title, + venueName: locationText, + venueAddress: locationText, + coordinates: null, + parsedDate: parseCalText(calText), + description, + }; } - const description = getText($(".tito-description")); - const coordinates = extractCoordinatesFromMapsUrl(locationHref); // First segment of venue address is the venue name diff --git a/sources/ti.to/tests/index.test.js b/sources/ti.to/tests/index.test.js index 05c24c1da..dcdca8751 100644 --- a/sources/ti.to/tests/index.test.js +++ b/sources/ti.to/tests/index.test.js @@ -98,4 +98,65 @@ describe(`${attributes.name}`, () => { expect(output).toHaveLength(0); }); + + // Markup from https://ti.to/queereast/girlfriends_uea_2026, whose organiser + // typed a location rather than picking a venue + describe("an event with a typed location and no venue record", () => { + const html = ` +

Girlfriends (University of East Anglia)

+ + +

Join Queer East for a screening of Girlfriends (2025)

+ `; + const venues = { + queereast: { + moviePages: { + "https://ti.to/queereast/girlfriends_uea_2026": html, + }, + }, + }; + const geo = { lat: 51.5162287, lon: -0.1213371 }; + + it("is matched to a cinema of that name", async () => { + readJSON.mockImplementation(() => venues); + + const output = await findEvents({ + name: "University of East Anglia", + alternativeNames: [], + address: "39-41 Parker St, London WC2B 5PQ, UK", + geo, + }); + + expect(output).toHaveLength(1); + expect(output[0].title).toBe("Girlfriends (University of East Anglia)"); + expect(output[0].performances).toHaveLength(1); + expect(output[0].performances[0].time).toBe( + new Date("2026-11-18T18:30:00Z").getTime(), + ); + }); + + it("is not matched to any other cinema", async () => { + readJSON.mockImplementation(() => venues); + + const output = await findEvents({ + name: "The Garden Cinema", + alternativeNames: [], + address: "39-41 Parker St, London WC2B 5PQ, UK", + geo, + }); + + expect(output).toHaveLength(0); + }); + }); });