Skip to content

Commit 295777d

Browse files
committed
add doclounge
1 parent 04801d1 commit 295777d

2 files changed

Lines changed: 115 additions & 0 deletions

File tree

mise.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ run = "uv run --group parse parse --output data/screenings.csv palladiumbio_se"
5050
[tasks."parse:osbyborgen_se"]
5151
run = "uv run --group parse parse --output data/screenings.csv osbyborgen_se"
5252

53+
[tasks."parse:doclounge_se"]
54+
run = "uv run --group parse parse --output data/screenings.csv doclounge_se"
55+
5356
[tasks.build]
5457
description = "Build the static site"
5558
run = "uv run --group build build www/allabiografer.se"

src/parse/parsers/doclounge_se.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
"""doclounge.se — Doc Lounge documentary screenings, Next.js SSR with __NEXT_DATA__."""
2+
3+
import json
4+
import logging
5+
import re
6+
from collections.abc import Iterator
7+
from datetime import date, time
8+
9+
import requests
10+
from bs4 import BeautifulSoup
11+
12+
from parse.parsers._tmdb_cache import lookup as _tmdb
13+
from store import Screening, Venue
14+
15+
log = logging.getLogger(__name__)
16+
17+
_URL = "https://www.doclounge.se/events-screenings"
18+
19+
# Only Swedish cities — Doc Lounge also operates in Finland but we skip those.
20+
_SWEDISH_CITIES = {
21+
"goteborg": "Göteborg",
22+
"helsingborg": "Helsingborg",
23+
"landskrona": "Landskrona",
24+
"lund": "Lund",
25+
"malmo": "Malmö",
26+
"ostersund": "Östersund",
27+
"umea": "Umeå",
28+
"varberg": "Varberg",
29+
}
30+
31+
32+
def parse() -> Iterator[Screening | Venue]:
33+
resp = requests.get(_URL, timeout=30, headers={"User-Agent": "Mozilla/5.0 (compatible; bio-parser/1.0)"})
34+
resp.raise_for_status()
35+
36+
soup = BeautifulSoup(resp.text, "html.parser")
37+
script = soup.find("script", id="__NEXT_DATA__")
38+
if not script or not script.string:
39+
log.warning("doclounge.se: no __NEXT_DATA__ found")
40+
return
41+
42+
data = json.loads(script.string)
43+
events = data.get("props", {}).get("pageProps", {}).get("events", {}).get("nodes", [])
44+
log.info("doclounge.se: %d events found", len(events))
45+
46+
yielded_venues: set[tuple[str, str]] = set()
47+
48+
for event in events:
49+
content = event.get("gqlEventContent") or {}
50+
51+
# Date & time
52+
date_str = content.get("date", "")
53+
time_str = content.get("time", "")
54+
if not date_str or not time_str:
55+
continue
56+
57+
try:
58+
d = date.fromisoformat(date_str)
59+
except ValueError:
60+
continue
61+
62+
m = re.match(r"(\d{1,2}):(\d{2})", time_str)
63+
if not m:
64+
continue
65+
t = time(int(m.group(1)), int(m.group(2)))
66+
67+
# City — filter to Swedish cities only
68+
city_nodes = event.get("cities", {}).get("nodes", [])
69+
if not city_nodes:
70+
continue
71+
city_slug = city_nodes[0].get("slug", "")
72+
city = _SWEDISH_CITIES.get(city_slug)
73+
if not city:
74+
continue
75+
76+
# Venue & address
77+
address_full = content.get("address", "")
78+
# Address format is typically "Venue Name, Street, City"
79+
# Use the first part as cinema name
80+
parts = [p.strip() for p in address_full.split(",")]
81+
cinema_name = parts[0] if parts else "Doc Lounge"
82+
83+
# Ticket URL
84+
ticket_url = (content.get("goToEvent") or {}).get("url", "")
85+
if not ticket_url:
86+
continue
87+
88+
# Film title — from linked movie or event title
89+
movie = content.get("movie") or {}
90+
title = movie.get("title") or event.get("title", "")
91+
if not title:
92+
continue
93+
94+
tmdb_id = _tmdb(title)
95+
if tmdb_id is None:
96+
continue
97+
98+
# Yield venue if not seen
99+
venue_key = (city, cinema_name)
100+
if venue_key not in yielded_venues:
101+
yielded_venues.add(venue_key)
102+
address = ", ".join(parts[1:]).strip() if len(parts) > 1 else ""
103+
yield Venue(name=cinema_name, city=city, address=address)
104+
105+
yield Screening(
106+
tmdb_id=tmdb_id,
107+
date=d,
108+
time=t,
109+
ticket_url=ticket_url,
110+
cinema_name=cinema_name,
111+
city=city,
112+
)

0 commit comments

Comments
 (0)