This is a static TV guide for quickly seeing what is on now and next across a curated set of international channels.
Live app: https://heywhatson.tv
- Shows a live, time-aligned guide grid for selected channels.
- Groups channels by country.
- Includes curated general channels plus premium/pay-TV sports channels.
- Searches across channels and programme titles/events.
- Opens programme details in a modal when metadata is available.
- Saves selected channels in browser
localStorage.
Countries currently included:
| Country | General channels | Premium sports/pay-TV channels |
|---|---|---|
| France | 12 | 25 |
| Spain | 18 | 21 |
| Canada | 20 | 14 |
| United States | 14 | 13 |
| United Kingdom | 19 | 16 |
| Italy | 10 | 8 |
| Germany | 12 | 13 |
| Turkiye | 12 | 6 |
| Portugal | 21 | 17 |
| Mexico | 10 | 10 |
| Brazil | 12 | 9 |
The app is intentionally simple and static:
web/contains the browser app:index.html,app.js, CSS themes, and generated JSON data.data/sources/iptv-org/contains curated XMLTV channel files and iptv-org metadata snapshots.data/normalized/contains XMLTV guide snapshots fetched from validated public guide sources.scripts/refresh_epg.pyrefreshes all curated XMLTV snapshots and rebuilds the static JSON payloads.scripts/build_web_data.pyconverts XMLTV snapshots into browser-friendly files underweb/data/.tests/covers the data-building and normalization logic.
The browser loads web/data/countries.json, then country payloads such as web/data/FR.json and web/data/premium-FR.json. Normal and premium payloads are merged client-side, de-duplicated by channel name, and rendered as schedule columns.
Times are stored in UTC in the generated JSON. The browser renders the current guide window from the user's local time.
When deployed to Cloudflare Pages, the live app is served from the committed web/ directory at https://heywhatson.tv.
A GitHub Actions workflow runs every 6 hours (0 */6 * * *) and updates the data. It runs:
python3 scripts/refresh_epg.pyThat script:
- Runs the iptv-org EPG grabber for each curated channel file.
- Sets
CURR_DATEto yesterday and grabs 3 days of guide data. - Writes refreshed XMLTV snapshots to
data/normalized/. - Rebuilds
web/data/*.jsonwith a 24-hour browser payload window: now - 4h through now + 20h. - Runs the unit tests and
node --check web/app.js.
From the repository root:
python3 -m http.server 8000 --directory webThen open:
http://localhost:8000
The committed web/data/ files are enough to run the app locally without refreshing guide data.
python3 scripts/build_web_data.pyThis reads data/normalized/*.xml and rewrites web/data/*.json.
Refreshing source guide data requires the upstream iptv-org EPG grabber checkout and Node dependencies:
git clone --depth 1 https://github.com/iptv-org/epg.git .cache/epg
npm install --prefix .cache/epg
python3 scripts/refresh_epg.pyUse a dry run to inspect the grab commands without fetching data:
python3 scripts/refresh_epg.py --dry-runSome upstream guide sources can fail, block, or return partial data. The refresh script keeps going and uses previous snapshots for failed sources.
python3 -m unittest discover -s tests -v
node --check web/app.jsThis is a schedule/metadata guide only. It does not stream or store video.