A web-based tool for managing, filtering, and serving IPTV playlists and EPG data, with HDHomeRun emulation for seamless Plex, Jellyfin, and Emby Live TV integration.
- Web UI — Add, edit, and manage your IPTV configuration from a browser. Stat cards on the dashboard link directly to their settings pages.
- Dark Mode — Toggle between light and dark themes from the sidebar; preference persists across sessions.
- M3U Playlist Fetching — Download playlists from Xtream Codes or direct M3U URLs.
- Advanced Filtering — Filter channels by language, keywords, channel numbers, and wildcards.
- Full M3U Browser — Browse and preview every channel in the raw (unfiltered) playlist before committing to a filter. Includes provider/category filtering, copy-to-clipboard, and an in-browser stream player for each channel.
- Filtered Channel Viewer — Browse your filtered channel lineup with the same provider/category drill-down and per-row stream playback.
- EPG / Guide Data — Fetches and name-matches guide data automatically; generates a standards-compliant XMLTV file with channel numbers. Unmatched channels receive placeholder "No Guide Data" entries so they remain streamable. EPG rebuilds automatically after each filter save.
- Channel Number Preservation —
tvg-chnovalues from your provider are passed through to the HDHomeRun lineup and EPG so Plex shows the correct channel numbers. - HDHomeRun Emulation — Appears as an HDHomeRun tuner on your network; Plex, Jellyfin, and Emby discover it automatically or via manual IP entry.
- Generic IPTV App Proxy (Xtream Codes) — Acts as a full Xtream Codes API server so TiviMate, IPTV Smarters, VLC, and other IPTV apps can connect directly. Serves live channels, VOD, and series through a single proxy. Each provider gets a unique URL slug; point your app at
http://<host>:<port>/<provider-slug>with the per-provider credentials set in the web UI. See DEPLOYMENT.md for details. - OpenVPN Support — Route all outbound container traffic (to your IPTV provider) through an OpenVPN tunnel. Paste any
.ovpnconfig file into the UI, save credentials, and enable with one click. The dashboard and Active Streams panel both show live VPN connection status. See DEPLOYMENT.md for full setup. - Session Management — Set a maximum number of concurrent streams per config (1–unlimited). New connections over the limit receive HTTP 429. The dashboard shows live
active/maxcounts. Each active stream row has a Kill button; killed clients are blocked from reconnecting for 60 seconds to prevent immediate re-entry. - MQTT Integration — Publishes real-time stream and VPN state to any MQTT broker every 10 seconds on change (heartbeat every 60 s). Supports Home Assistant MQTT auto-discovery — click Publish HA Discovery once and HA creates sensor and binary_sensor entities automatically. Configure in Tools → MQTT Integration. See DEPLOYMENT.md for topic reference and HA card examples.
- In-Browser Channel Player —
/channelslists your filtered lineup with a built-in video player; click any row to watch in the browser via mpegts.js, or open in VLC/IINA. - Stream Tester — Test credentials and channel IDs directly from the browser before committing them to your config. Detects Cloudflare-blocked responses (common with VPN exit IPs) and explains the cause.
- Health Check Endpoint —
GET /api/healthreturns stream reachability as JSON; integrates with Uptime Kuma (JSON Query monitor) and Home Assistant (REST sensor). Skips the test automatically when a live stream is active to avoid interruptions. - Resilient Stream Proxy — Automatically reconnects on upstream drops. Detects token-expiry errors (407) and session-rejection errors (458) and refreshes the channel lineup immediately for a fresh URL before retrying.
- Scheduled M3U Refresh — Set a refresh interval (in hours) per config so playlists stay current without manual re-fetching.
- Docker — Single
docker-compose.ymlworks on macOS and Linux with no changes. VPN support requiresNET_ADMINcapability and/dev/net/tun(already included in the provided compose file).
git clone https://github.com/dreed47/iptv-manager.git
cd iptv-manager
cp sample.env .env
# Edit .env — set HDHR_ADVERTISE_HOST to your machine's LAN IP
docker compose up -dOpen the web UI at http://localhost:5005 (or http://<your-ip>:5005).
See DEPLOYMENT.md for full setup, media server integration, EPG options, OpenVPN, and configuration details.
- Add a config — Enter your IPTV provider credentials (Xtream Codes or M3U URL) in the web UI.
- Fetch playlist — Click Fetch M3U to download the full channel list from your provider.
- Browse (optional) — Use Browse Full M3U to explore the raw playlist and try playing streams before filtering.
- Filter — Build your curated lineup using the Includes list. Add channel names (one per line) to select exactly which channels appear in your lineup. Click Save Changes to apply.
- View filtered lineup — Click Filtered Channel Viewer to confirm the result.
- Connect your media server — Point Plex/Jellyfin/Emby DVR at
http://<your-ip>:5005; it discovers the device as an HDHomeRun tuner. - Add the EPG — In your media server's DVR settings, enter the EPG URL
http://<your-ip>:5005/epg.xmlso the guide populates automatically. See EPG Options below for alternatives. - Connect IPTV apps (optional) — In TiviMate, IPTV Smarters, or any Xtream Codes–compatible app, enter
http://<your-ip>:5005/<provider-slug>as the server with the per-provider credentials from the provider's edit page. See DEPLOYMENT.md for details.
The Includes list is the primary control. Add one channel name per line to select exactly which channels appear in your filtered lineup.
- Includes present — Only channels whose name is an exact match to an entry in the includes list are kept. Excludes are ignored when includes are set.
- Excludes only (no includes) — All channels pass through except those whose name contains an exclude substring.
- Wildcard exclude (
*) — All channels start as excluded; only exact matches in the includes list are kept. Equivalent to "allow-list only" mode.
Saving filter changes resets the filtered playlist and triggers an automatic EPG rebuild, but leaves the original full playlist intact so you can re-filter without re-fetching.
The app generates a custom XMLTV file at http://<your-ip>:5005/epg.xml that can be fed directly to Plex, Jellyfin, or Emby.
How guide matching works:
- Channel names in your filtered playlist are normalized (stripped of provider prefixes, HD/SD suffixes, punctuation).
- They are matched against names in one or more XMLTV source feeds (default: epg.pw US guide).
- Matched channels receive full programme schedules. Unmatched channels get 2-hour "No Guide Data" placeholder blocks so they remain accessible in the guide.
- The output uses each channel's
tvg-idas the XMLTV channel ID, and includes<lcn>elements with channel numbers so media servers can sort the guide correctly.
EPG options — choose one:
| Option | When to use |
|---|---|
Feed /epg.xml to your media server |
Best for most users. Full programme schedules, matched by channel name. Enter http://<host>:5005/epg.xml in your DVR/tuner EPG settings. |
| Use Plex's location-based guide | If you have a Plex Pass and your channels match known US broadcast channels. Plex matches channels by GuideNumber to its own guide data. No EPG URL needed — just skip the "Guide Source" step in Plex DVR setup. |
| Custom XMLTV source | Set EPG_XML_SOURCES in .env to point to any XMLTV feed (e.g., a regional epg.pw feed, Schedules Direct export, or self-hosted guide). |
Loop/replay channels (e.g., 24/7: THE BIG BANG THEORY) are never matched against the EPG source — their broadcast schedule wouldn't align with a 24/7 loop stream — and always receive placeholder entries.
Channel numbers flow through the entire stack so what you see in your IPTV app matches what appears in Plex/Jellyfin/Emby.
- Provider-supplied numbers (
tvg-chnoin the M3U#EXTINFline) are used as-is as theGuideNumberin the HDHomeRun lineup. - Auto-assigned numbers — Channels without a
tvg-chnoget the next available sequential integer, starting from 1 and skipping any numbers already claimed by explicit entries. - Deduplication — When the same channel appears in multiple configs, explicit-numbered entries win. If neither has an explicit number, the first occurrence is kept.
- EPG ordering — The generated
/epg.xmlsorts channels by channel number, and each<channel>element carries both a numbered<display-name>and an<lcn>element so the guide grid appears in channel-number order.
| File/Dir | Purpose |
|---|---|
main.py |
FastAPI app entry point, logging, startup tasks (VPN auto-start, MQTT auto-start, cache warm) |
routes.py |
Web UI, M3U fetch/filter, VPN, MQTT settings/status/test endpoints |
hdhomerun_routes.py |
HDHomeRun emulation, resilient stream proxy, session management, channel browser |
xtream_server_routes.py |
Xtream Codes API proxy for TiviMate, IPTV Smarters, VLC, etc. |
health_routes.py |
Stream tester UI and /api/health endpoint |
vpn_manager.py |
OpenVPN subprocess lifecycle (start, stop, status, IP check) |
mqtt_manager.py |
MQTT singleton manager, polling thread, HA auto-discovery publisher |
models.py |
SQLite database models |
services.py |
Business logic / CRUD |
epg_manager.py |
EPG fetching, name-matching, and XMLTV generation |
config.py |
Centralised environment variable parsing and validation |
templates/dashboard.html |
Dashboard with stat cards, active streams, quick actions |
templates/providers.html |
Provider list page |
templates/provider_edit.html |
Provider add/edit form (credentials, filters, Xtream proxy settings, session limit) |
templates/base.html |
Shared layout, sidebar nav, dark mode toggle |
templates/channels.html |
Filtered channel browser (click to play in browser) |
templates/m3u_browser.html |
Full raw M3U browser UI |
templates/player.html |
In-browser mpegts.js stream player |
templates/stream_test.html |
Stream connection tester and health check config |
m3u_files/ |
Downloaded playlists, filtered playlists, and cached EPG |
data/ |
SQLite database |
docker-compose.yml |
Container definition (includes NET_ADMIN and /dev/net/tun for VPN) |
restart_container.sh |
Rebuild and restart via docker compose |
update.sh |
Git pull + rebuild + restart |
MIT
Content disclaimer: This tool is a playlist manager and stream proxy — it does not provide or host any content. Always use IPTV services that have obtained the appropriate licenses for the content they distribute.

