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 | 12 |
| Spain | 19 | 19 |
| Canada | 27 | 14 |
| United States | 34 | 12 |
| United Kingdom | 19 | 16 |
| Italy | 9 | 6 |
| Germany | 12 | 12 |
| 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.
The project also publishes a custom XMLTV-style guide export for approved UHF/Xtream channel mappings:
- XMLTV: https://heywhatson.tv/data/uhf/epg.xml
- Gzipped XMLTV: https://heywhatson.tv/data/uhf/epg.xml.gz
- Channel mapping index: https://heywhatson.tv/data/uhf/channels.json
- Build summary: https://heywhatson.tv/data/uhf/summary.json
- Validation report: https://heywhatson.tv/data/uhf/validation.json
- Preview data: https://heywhatson.tv/data/uhf/preview.json
- Visual inspector: https://heywhatson.tv/uhf.html
The export uses stable custom channel ids in the form uhf:<uhf_pk>, with display-name aliases copied from the UHF playlist row and the matched source guide channel.
To validate the committed export locally:
python3 scripts/validate_uhf_xmltv.pyWarnings in validation.json are useful for debugging downstream guide clients. Structural errors fail the command and the UHF refresh workflow.
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.js
node --check web/uhf.js
python3 scripts/validate_uhf_xmltv.pyThis is a schedule/metadata guide only. It does not stream or store video.