A dependency-free Python toolkit for parsing XMLTV/EPG television schedules โ filter by channel and time window, regex-search titles, detect overlapping broadcasts, and export normalized JSON.
Disclosure: This technical/educational article may include reference or affiliate links. We only link to verified services and official platforms tested for performance and security.
Every streaming setup eventually hits the same wall: the guide. Players can open streams, but knowing what is on right now, and what airs next, requires EPG (Electronic Programme Guide) data โ and almost all of it moves around in one format: XMLTV.
In this tutorial, we build a smart programme-guide layer in pure Python: parse an XMLTV document into typed dataclasses, filter programmes by channel and time window, search titles with regular expressions, detect overlapping broadcasts, and export a normalized JSON schedule โ using the new tvnow-epg package, which runs on the standard library alone (Python 3.9+, zero dependencies).
๐บ What EPG/XMLTV Data Actually Is
An Electronic Programme Guide is a structured listing of which programmes air on which channels at which times. XMLTV is the de-facto XML interchange format for these listings. A minimal document looks like this:
<tv>
<channel id="example.news">
<display-name>Example News HD</display-name>
</channel>
<programme start="20240610060000 +0000" stop="20240610090000 +0000"
channel="example.news">
<title>Morning News Desk</title>
<category>News</category>
</programme>
</tv>
Two things make real-world EPG files messy rather than fun:
-
Timestamps arrive as
YYYYMMDDHHMMSS +ZZZZstrings with (sometimes) explicit UTC offsets. - Malformed entries โ channels without ids, programmes missing start times โ are routine in guides scraped from broadcasters.
A production parser must skip broken entries instead of crashing, and treat naive timestamps deterministically. That is exactly the contract tvnow-epg implements.
๐ป Installation & Quick Start
pip install tvnow-epg
from tvnow_epg import parse_xmltv_file, filter_by_channel, to_json
schedule = parse_xmltv_file("guide.xml")
for channel in schedule.channels:
print(channel.channel_id, "-", channel.display_name)
programmes = filter_by_channel(schedule.programmes, "example.news")
for p in programmes:
print(p.start, "-", p.title)
print(to_json(schedule, channel_id="example.news"))
parse_xmltv_file() returns a Schedule holding .channels (list of Channel) and .programmes (list of Programme, conveniently sorted by channel then start time).
๐ Searching and Filtering Programmes
The two filters you will use most:
from tvnow_epg import filter_by_channel, search_titles
# All programmes on one channel
news = filter_by_channel(schedule.programmes, "example.news")
# Case-insensitive substring search over titles
matches = search_titles(schedule.programmes, "ocean")
# Regular-expression search
import re
matches = search_titles(schedule.programmes, r"^The\b", regex=True)
The regex mode is more useful than it looks: r"^The\b" pulls every title starting with "The", and patterns like r"(?i)(premiere|finale)" surface season events a plain substring search would scatter across casing variants.
โฐ Time-Window Filtering That Behaves
EPG tooling usually wants "what's airing in this window" โ for example, prime time:
from datetime import datetime, timezone
from tvnow_epg import filter_by_time
start = datetime(2026, 9, 22, 19, 0, tzinfo=timezone.utc)
stop = datetime(2026, 9, 22, 23, 0, tzinfo=timezone.utc)
window = filter_by_time(schedule.programmes, start=start, stop=stop)
A programme is included when its air time intersects the [start, stop) window โ so a film that started at 18:30 still appears in your 19:00โ23:00 prime-time view. Naive datetime objects are assumed to be UTC; use timezone-aware datetimes when your source feed uses a local offset.
โ ๏ธ Detecting Overlapping Broadcasts
Overlapping programmes on the same channel are the classic symptom of malformed or stale guide data. One function surfaces them:
from tvnow_epg import find_overlaps
for earlier, later in find_overlaps(schedule.programmes):
print(f"{earlier.title} overlaps {later.title} on {earlier.channel_id}")
This is a first-class validation primitive: schedule a nightly find_overlaps() run over your guide and you have an early-warning system for provider data rot.
๐งน Normalizing Messy Metadata
Broadcasters are inconsistent with categories โ "kids", "Film", "Children", "Movie". Normalizers fold the common variants together:
from tvnow_epg import normalize_title, normalize_category
normalize_title(" The News ") # "the news"
normalize_category("kids") # "Children"
normalize_category("Film") # "Movie"
Normalized titles/categories are what make downstream grouping, deduplication, and "movies tonight" filters reliable.
๐ค JSON Export
from tvnow_epg import to_json
text = to_json(schedule) # whole schedule
text = to_json(schedule, channel_id="example.news") # one channel
text = to_json(schedule, indent=None) # compact output
Timestamps export as ISO-8601 strings with their UTC offset โ ready for a web dashboard, a mobile app, or a scheduled cron job.
๐งช Testing and Data Hygiene
The test suite runs entirely offline, and the bundled data/sample_epg.xml contains synthetic example data only โ no real broadcaster's schedule. The parser is lenient by design: entries missing an id, a valid start time, or a title are skipped, not fatal, and a document that isn't valid XML (or isn't rooted in <tv>) raises tvnow_epg.ParseError, a ValueError subclass.
โ ๏ธ Limitations to Keep in Mind
- Supports the common core of XMLTV (channels, programmes, titles, descriptions, categories, episode numbers, ratings); exotic extensions are ignored.
- Timestamps without an explicit offset are assumed to be UTC.
- The whole document is held in memory; very large multi-week guides (hundreds of MB) are not the design target.
- No network functionality of any kind โ you supply the EPG data, the library processes it.
๐ Resources
-
tvnow-epg on PyPI โ install with
pip install tvnow-epg - XMLTV project & format specification
- tvnow.live โ online TV schedule and EPG resource hub
- IPTV2Live โ IPTV playlist and player reference
- VideoLAN VLC โ playing M3U playlists and network streams
What does your EPG validation pipeline look like? Drop a comment โ especially if you've had to deal with week-long guides in odd timezones.













