XMLTV
I had the idea for this library because we wanted to cancel our paper TV guide subscription. With young children, my wife and I rarely have time to watch live TV — she often catches up on replay and checks the program guide once or twice a week.
XMLTV is an XML-based format for Electronic Program Guide (EPG) data. I wrote this library because there was no Rust crate to read or write XMLTV files at the time. It leverages serde for serialization and deserialization, and flattens empty tags like <new/> into booleans to reduce boilerplate. For big files, programmes can be streamed one by one instead of loaded fully into RAM.
CLI
The xmltv binary is the batteries-included companion. It reads a local file (or - for stdin, e.g. curl … | xmltv - -f json), applies a chain of filters, and writes the result in the format of your choice. Install it with cargo install xmltv-cli or grab a binary from the releases page.
Input & output
| Option | Description |
|---|---|
FILE | Positional. Input XMLTV file, or - to read from stdin. |
-l, --load <FILE> | Load defaults from a JSON config file. CLI flags override. |
-m, --merge <FILE> | Merge another XMLTV file. Repeatable. |
-o, --output <FILE> | Write to a file instead of stdout. |
-f, --format <FMT> | xml (default), json, ical, atom, jsonfeed. |
--pretty | Indent XML and JSON output. |
Filtering
| Option | Description |
|---|---|
-c, --channels <LIST> | Comma-separated channel list to keep (-c tf1,m6,Arte). Use id: prefix for ID-only match. Pass ? to pick channels interactively in the TUI. |
-d, --days <N> | Keep today + the next N days. |
--date <YYYY-MM-DD> | Keep programmes starting on this date. |
-s, --search <QUERY> | Case-insensitive title substring match. |
--category <CAT> | Keep programmes matching this category. Repeatable, OR logic. |
--time-slot <SLOT> | HH:MM-HH:MM, or Mon-Fri 18:00-23:00, or Sat,Sun 00:00-23:59. Repeatable. |
--time-slot-strict | Programme must be fully contained within the slot. |
--exclude-time-slot | Invert time-slot logic (exclude instead of include). |
--new-only | Keep only programmes flagged <new/>. |
--no-reruns | Drop programmes flagged <previously-shown/>. |
--lang <LANG> | Prefer titles/descriptions in this language when several are provided. |
--min-rating <0.0–1.0> | Star rating floor as a fraction (0.6 ≈ 3/5). |
--rating-system <SYS> | Restrict --min-rating to a specific rating system. |
Transformation
| Option | Description |
|---|---|
--cleanse <ITEMS> | Strip heavy fields to shrink output. Accepts credits (or a finer subset: directors, actors, writers, adapters, producers, composers, editors, presenters, commentators, guests), categories, new, countries, keywords, sub-titles, languages, videos, audios, subtitles, last-chances, descriptions, dates, origin-languages, length, premieres, previously-showns, ratings, star-ratings, reviews, images, episode-nums, icons, urls. |
-t, --timezone <TZ> | Output time zone: utc (default), local, or an offset like +0100. |
--sort | Sort programmes by start time within each channel. |
--deduplicate | Drop exact duplicates (same channel, start, title). |
--tag <RULE> | Auto-tag with TAG:FIELD:MATCH:PATTERN (e.g. --tag "News:title:contains:Journal"). FIELD = title|description|channel, MATCH = contains|starts-with|ends-with. Repeatable. |
Inspection & tooling
| Option | Description |
|---|---|
--list-channels | Print channel IDs and display names, then exit. |
--stats | Summary: channel count, programme count, date range, file size. |
--validate | Check file structure and report errors without producing output. |
--epg | Open the embedded TUI EPG (see below). |
--past | Include past days in the EPG view. |
--completions <SHELL> | Emit a completion script for bash, zsh, or fish. |
Examples
# JSON, next 3 days, three channels only
xmltv listings.xml -f json -d 3 -c "TF1.fr,France2.fr,Arte.tv"
# Weekday evenings + full weekends
xmltv listings.xml --time-slot "Mon-Fri 18:00-23:00" --time-slot "Sat,Sun 00:00-23:59"
# Merge two sources, drop heavy metadata
xmltv main.xml -m secondary.xml --cleanse "credits,images,episode-nums"
# Extract football matches as a calendar file
xmltv listings.xml -s "Football" -f ical -o sports.icsEmbedded TUI EPG
xmltv … --epg opens an interactive terminal EPG built with ratatui and crossterm. It renders the same data the CLI would output, but as a browsable grid: channels on the left (with icons when the terminal supports Kitty, Sixel or half-blocks graphics through ratatui-image), a time axis on top, programme blocks in the middle, and a detail panel for the current selection. Channel icons are prefetched in parallel with rayon before entering the alternate screen, so scrolling stays smooth. A channel-picker popup (-c ?) doubles as an interactive filter for the CLI itself.
Static web EPG
For sharing the grid on a browser (desktop, tablet, kiosk, phone), the repo ships a dependency-free static page: one index.html, one styles.css, one app.js, no framework, no bundler. It reads a xmltv.json file produced by the CLI:
xmltv listings.xml -f json -d 3 -o xmltv.json --prettyDrop xmltv.json next to index.html, serve both over any static HTTP server (python3 -m http.server, Nginx, Caddy, GitHub Pages, S3), and the page renders. Features:
- Desktop grid — sticky channel column, absolute-positioned programme blocks, a vertical red line for “now”, live programmes tinted red. Click a block for a detail drawer.
- Mobile — under 768 px the grid collapses to a per-channel vertical list; taps open a full-screen detail sheet.
- Themes — Catppuccin Mocha / Latte with a
System/Dark/Lightselector persisted inlocalStorage. - Navigation — day-by-day arrows, a “Live” button that centres the viewport on the current time, a multi-select channel filter, and a title/description search with match highlighting.
- Display toggles — swap the second time on each block between stop time (
21:45) and duration (1h30). Timestamps are UTC-anchored in the JSON and rendered in the browser’s local time.
An optional build.sh (a ~40-line awk pipeline — no npm, no node) inlines the CSS and JS into a single dist/index.html for USB-stick or email distribution; only xmltv.json stays external.