* feat(interfaces): add the server storage HTTP contract Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(web-backend): read server storage configuration Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(web-backend): open the server database and track revisions Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(web-backend): playlist and user-state repositories Playlist documents use the shared desktop row codec, so the server writes the same rows. keepPayload upserts take the stored playlist/items/header fields when the client leaves them out. Xtream user data lives in the server-only server_user_state table, one JSON value per namespace and playlist. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(web-backend): storage error mapping and access token guard Storage errors map to JSON status bodies and are logged by name and code only. An optional bearer token guards every route except /, /health, /config.js and /v1/instance, provider proxy routes included. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(web-backend): serve the /v1 server storage API /v1/instance is always public and reports whether server storage is on. With storage, /v1/playlists serves whole documents with ETags and 304s, accepts keepPayload upserts, and deletes playlists with their user state; /v1/user-state and /v1/app-state store the rest. The proxy routes are unchanged unless an access token is configured. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(web-backend): open server storage on start and close on shutdown With IPTVNATOR_STORAGE=server, main.ts opens the database before it listens and exits with a clear message when the data directory is not writable. SIGTERM and SIGINT close the server and the database. The native better-sqlite3 module stays outside the esbuild bundle. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(web-backend): match weakened ETags on playlist reads nginx weakens the ETag of a gzipped response, and clients send the weak form back in If-None-Match. The route now compares tags weakly, as RFC 9110 requires for GET, so it answers 304 before reading the document. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(docker): persist server storage on a volume The image builds better-sqlite3 in a native stage per target platform, enables server storage on the /data volume, and raises the nginx body limit to 64 MB for /api with gzip for JSON. The compose file mounts a named volume and documents the optional access token. The PWA does not use the storage yet; docs say so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(web-backend): type the storage route responses Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(web-backend): harden server storage reads, deletes and app state - Metadata reads return the stored document without its payload fields, so fields that live only in the JSON survive a metadata round-trip. - A conditional GET checks that the playlist exists before answering 304. - Deleting a playlist and its user state commits in one transaction; the ETag moves only after the commit. - Browser app state is stored under a client: prefix and the instance id is repaired when empty, so a client write cannot stop the backend. - User-state buckets have no prototype, so ids like __proto__ round-trip. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(self-hosted): keep the access token unset until the PWA sends it Add the release note for the Docker server storage volume. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(web-backend): bound shutdown and reject padded playlist ids - SIGTERM closes idle connections at once and cuts the rest after five seconds, so a long provider download cannot keep the backend running. - Playlist ids with surrounding whitespace are rejected: the row codec trims ids, so storage and ETags would track different keys. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(web-backend): clear optional columns on whole-document upserts The row codec maps empty values to undefined, which ON CONFLICT DO UPDATE skips, so a cleared User-Agent, Referrer or credential kept its old column value and came back on the next read. Server upserts now write NULL to every replaceable column the document no longer sets. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(self-hosted): linear token parsing, no-store app state, longer start - The access guard reads the Bearer token by string scanning instead of a regular expression that CodeQL flagged as polynomial on long whitespace. - GET /v1/app-state/:key sends Cache-Control: no-store like the other state routes, so devices never reuse a stale value. - The entrypoint waits up to IPTVNATOR_BACKEND_START_TIMEOUT (300 s) for /health, because the backend migrates its database before it listens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(self-hosted): metadata reads skip payloads, redact JSON parse warnings List playlist metadata from a server-only payload-free copy of each document instead of running json_remove over every payload blob, and log only the error name when a stored JSON value fails to parse, since the parser's message quotes the stored text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(web-backend): copy carried-over panel clock into playlist metadata When an upsert keeps the stored serverTimezone, build the metadata copy from the saved payload so metadata and full reads agree. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
IPTVnator - IPTV Player Application
🌐 Website | Telegram channel for discussions | Buy me a coffee | GitHub Sponsors
IPTVnator is a video player application that provides support for IPTV playlist playback (m3u, m3u8). The application allows users to import playlists using remote URLs or by uploading files from the local file system. Additionally, it supports EPG information in XMLTV format which can be provided via URL.
The application is a cross-platform, open-source project built with Electron and Angular.
⚠️ Note: IPTVnator does not provide any playlists or other digital content. The channels and pictures in the screenshots are for demonstration purposes only.
Important
Official sources only. IPTVnator is a free, open-source player — it never sells IPTV subscriptions, channels, or playlists. Websites offering "IPTVnator subscriptions/channels/premium/activated" builds are not affiliated with this project. Get the app only from the official website or GitHub Releases. See Beware of unofficial IPTVnator websites and IPTV services for details.
Features
Playlists & sources
- M3U / M3U8 playlists from local files or remote URLs 📂, with automatic updates on startup
- Xtream Codes (XC) and Stalker / Ministra (STB) portal support
- Custom "User-Agent" header per playlist
Playback
- Built-in HTML5 player (HLS.js or Video.js) with a resizable, resumable inline view
- Optional unified IPTVnator controls for HTML5, Video.js, and ArtPlayer, enabled in Settings → Playback (experimental)
- External players — MPV, VLC, and IINA on macOS (
mpv.app/VLC.appbundle paths supported) (desktop) - Embedded MPV — native mpv rendered inside the app window on macOS, Windows & Linux 🖥️ (experimental · desktop)
- Dedicated radio player for
radio="true"streams 📻
Live TV & EPG
- EPG / XMLTV TV guide with a live timeline ribbon and multi-channel grid (desktop)
- TV archive / catch-up / timeshift (desktop)
- Group-based channel list, channel-number selection, and search 🔍
Movies & series (VOD)
- Redesigned two-state detail pages (browse ↔ watch) with season tabs and resume positions
- Download manager for offline movies & episodes ⬇️ (desktop)
- "Recently added" feeds and category grids with sorting & pagination
Discovery & metadata
- Global search across live TV, movies, and series (desktop)
- TMDB enrichment (opt-in, requires your own TMDB API key) — plots, cast & crew, trailers, ratings, artwork, a "Similar" rail, clickable actor pages, and a trending dashboard rail (trending rail: desktop)
- Dashboard with recently watched & continue-watching
Organization
- Per-playlist and global favorites, aggregated across all playlists ⭐
- Recently viewed / watch history
- Command palette (
Ctrl/Cmd+K) that also finds and opens individual settings - Settings search from the header search box on the Settings page
Platform
- Cross-platform desktop (Electron) and installable PWA
- Desktop auto-updater and mobile remote control (desktop)
- Docker self-hosting for the PWA + web backend
- 19 languages (translation files), light & dark themes, and keyboard shortcuts
Keyboard shortcuts
Press ? or Shift+/ in the workspace to open the in-app shortcuts list.
| Area | Shortcut | Action |
|---|---|---|
| Global | Ctrl/Cmd+K |
Open command palette |
| Global | Ctrl/Cmd+F |
Open global search (desktop); on Settings, search settings |
| Global | Ctrl/Cmd+R |
Open recently viewed in the desktop app |
| Global | Enter in workspace search |
Submit the search; on Settings, open the best match |
| Global | F11 |
Toggle app window fullscreen in the desktop app |
| Navigation | Ctrl/Cmd+B |
Toggle the live sidebar |
| Navigation | 0-9 |
Select an M3U channel by number |
| Playback | Space / K |
Play or pause playback |
| Playback | F |
Toggle player fullscreen |
| Playback | ArrowLeft / ArrowRight |
Seek VOD playback by 5 seconds |
| Playback | ArrowUp / ArrowDown |
Adjust volume by 5% |
| Playback | M |
Mute audio |
| Dialogs and lists | ArrowUp / ArrowDown |
Move command palette selection |
| Dialogs and lists | Enter |
Run the selected command or open a focused item |
| Dialogs and lists | Escape |
Close dialogs and dismiss overlays |
The desktop app can also open at its last size, maximized, or fullscreen on
every launch (Settings → General → "Window on startup"), and iptvnator --fullscreen forces a single fullscreen launch for TV or HTPC autostart
scripts without changing that setting.
Screenshots:
Note: First version of the application which was developed as a PWA is available in an extra git branch.
Self-hosted PWA
The Docker setup builds the Angular PWA and the monorepo web backend into one
image. The backend handles remote M3U parsing plus Xtream and Stalker proxy
requests under /api, so a separate 4gray/iptvnator-backend container is not
required for the default self-hosted flow.
docker compose -f docker/docker-compose.yml up --build -d
The application is available at http://localhost:4333. See
docker/docker-compose.yml for the ready-to-run
compose file and docker/README.md for environment
variables, reverse proxy notes, PWA limitations, and build details.
The self-hosted image runs the browser PWA rather than the Electron desktop app: EPG/XMLTV panels, Embedded MPV, managed MPV/VLC launching, the download manager, and Electron remote-control features are not available there. If browser playback fails, copy the stream URL and open it manually in an external player such as MPV, VLC, or IINA.
Download
Download the latest version of the application for macOS, Windows, and Linux from the release page.
Alternatively, you can install the application using one of the following package managers:
Nightly builds
Every merge into master is published as a prerelease in
4gray/iptvnator-nightly. The
desktop app can follow them: Settings → About → Update channel → Nightly
offers each new build through the built-in updater (macOS, Windows, and the
Linux AppImage). Nightly builds are untested snapshots — they may break, and
their database changes are permanent, so switching back to Stable keeps
the installed nightly until the next stable release is newer. Back up your
playlists first, and mention the version and commit shown in About when
reporting a bug from a nightly.
Homebrew
$ brew install iptvnator
Snap
$ sudo snap install iptvnator
Arch
Also available as an Arch PKG, iptvnator-bin, in the AUR (using your favourite AUR-helper, .e.g. yay)
$ yay -S iptvnator-bin
Gentoo
You can install IPTVnator from the gentoo-zh overlay
sudo eselect repository enable gentoo-zh
sudo emerge --sync gentoo-zh
sudo emerge iptvnator-bin
Linux Embedded MPV Support
Embedded MPV on Linux is experimental and currently supports x64 desktop
sessions where IPTVnator runs under X11 or Xwayland. Native Wayland embedding
is not supported yet. Linux package launchers request X11 with
--ozone-platform=x11, so Wayland desktops still need Xwayland available.
The Linux backend starts a system mpv executable with --wid, so mpv must
be installed and available on PATH. CI validates the Linux native addon and
standard packages on Ubuntu 22.04, with Flatpak packaging built on Ubuntu 24.04.
Expected user targets are Ubuntu/Debian .deb, Arch/Manjaro pacman, RPM
distributions, and AppImage on x64 systems with X11/Xwayland plus mpv
installed. Flatpak and Snap builds remain available, but embedded MPV is not
announced as supported there yet because those sandboxed formats do not expose
the host mpv executable to the embedded backend by default.
Troubleshooting
macOS: "App is damaged and can't be opened"
Older unsigned macOS builds may require removing the quarantine flag from the downloaded application:
xattr -c /Applications/IPTVnator.app
Alternatively, if the app is located in a different directory:
xattr -c ~/Downloads/IPTVnator.app
Linux: chrome-sandbox Issues
If you encounter the following error when launching IPTVnator:
The SUID sandbox helper binary was found, but is not configured correctly.
Rather than run without sandboxing I'm aborting now.
You need to make sure that chrome-sandbox is owned by root and has mode 4755.
Solution 1: Fix chrome-sandbox permissions (Recommended for .deb/.rpm installations)
Navigate to the IPTVnator installation directory and run:
sudo chown root:root chrome-sandbox
sudo chmod 4755 chrome-sandbox
Solution 2: Launch with --no-sandbox flag
Edit the desktop launcher file to add the --no-sandbox flag:
-
Find your desktop file location:
- Ubuntu/Debian:
~/.local/share/applications/iptvnator.desktop - System-wide:
/usr/share/applications/iptvnator.desktop
- Ubuntu/Debian:
-
Edit the file and modify the
Execline:Exec=iptvnator --no-sandbox %U -
Save the file and relaunch the application from your application menu.
Alternatively, you can launch IPTVnator from the terminal with the flag:
iptvnator --no-sandbox
GNU/Linux: Wayland startup failure
If IPTVnator exits on GNU/Linux with errors about failing to connect to Wayland or initialize the Ozone platform, force X11/XWayland instead:
iptvnator --ozone-platform=x11
This workaround is mainly for older or problematic Linux graphics stacks. The Snap package already includes this X11 override by default. For AppImage, direct binaries, and other Linux package formats, pass the flag manually when needed.
How to Build and Develop
Requirements:
- Node.js 22.22.3 or newer within 22.x, or 24.15.0 or newer within 24.x
- pnpm 10.33.0 (via Corepack)
The repository's .nvmrc pins the Node.js version used by CI. With nvm,
run nvm install and nvm use from the repository root to use the same version
for local development.
-
Clone this repository and install project dependencies:
$ nvm install $ nvm use $ corepack enable $ pnpm install -
Start the application:
$ pnpm run serve:backend
This will open the Electron app in a separate window, while the Angular dev server will run at http://localhost:4200.
The equivalent Nx command is:
$ nx serve electron-backend
To start Electron with an empty, isolated data directory instead of your normal
~/.iptvnator folder, set IPTVNATOR_E2E_DATA_DIR for that run:
$ rm -rf .tmp/iptvnator-empty && mkdir -p .tmp/iptvnator-empty
$ IPTVNATOR_E2E_DATA_DIR="$PWD/.tmp/iptvnator-empty" pnpm run serve:backend
This redirects the SQLite database, Electron user data, and local config under the given directory. Delete that directory whenever you want a fresh empty state.
If you need startup diagnostics for a white screen or a frozen route, you can also turn on opt-in Electron tracing. These logs are written to the Electron terminal output so they still help when the renderer DevTools never open:
$ IPTVNATOR_TRACE_STARTUP=1 pnpm run serve:backend
Nx equivalent:
$ IPTVNATOR_TRACE_STARTUP=1 nx serve electron-backend
Useful narrower flags:
IPTVNATOR_TRACE_IPC=1logs rendererwindow.electron.*calls reaching the Electron bridgeIPTVNATOR_TRACE_DB=1logs DB worker requests and request-scoped DB eventsIPTVNATOR_TRACE_SQL=1logs SQLite statements in both the main connection and DB worker connectionIPTVNATOR_TRACE_WINDOW=1logs BrowserWindow load, navigation, and unresponsive eventsIPTVNATOR_TRACE_RENDERER_CONSOLE=1mirrors renderer console messages into the Electron terminal output
Security-sensitive network compatibility flags are opt-in:
IPTVNATOR_ALLOW_PRIVATE_NETWORK_URLS=1permits strict EPG fetches from playlist metadata (x-tvg-url,url-tvg, ortvg-url) to resolve to localhost, LAN, or other private addresses. Directly configured Xtream/Stalker portals and private playlist servers remain supported without this flag. Prefer the in-app source-scoped “Allow source” action for a trusted EPG URL.IPTVNATOR_ALLOW_INSECURE_TLS=1disables certificate validation for remote playlist imports and refreshes for the whole Electron process. Prefer the in-app host-scoped trust action for a trusted provider with a self-signed or otherwise invalid certificate.
If the local Nx daemon gets into a bad state before rerunning Electron, reset it:
$ pnpm nx reset
To run only the Angular app without Electron, use:
$ pnpm run serve:frontend
To see how many bytes the built web app puts on the initial load path (the number the CI ratchet guards), build it and run the measurement:
$ pnpm nx build web
$ pnpm run perf:initial-bytes
The contract behind that number is in docs/architecture/performance-journeys.md.
To benchmark the "launch to usable", "open a source" and "start playback" journeys (fresh Electron processes on a seeded profile against the local Xtream mock, exact renderer counters plus wall-clock), run:
$ pnpm run perf:journeys
The journeys, their counters and the summary written under
dist/performance/journeys/ are described in
docs/architecture/performance-journeys.md.
pnpm run perf:compositing measures the renderer's tile memory and composited
layers on the dashboard, the detail pages, Live TV and settings (the
"compositing budget" section of the same document).
Disclaimer
IPTVnator doesn't provide any playlists or other digital content.
Trademark
The name "IPTVnator" and the IPTVnator logo are unregistered trademarks of the project owner. The MIT license covers the source code only — it does not grant rights to the name or logo. Forks and redistributions (including app-store submissions) must use a different name and their own icon. See TRADEMARK.md for details.
Developer and agent documentation
Start with the task context map to find the authoritative contract and validation for your area. Common agent rules are in AGENTS.md; Claude Code imports that same file. Development and documentation-maintenance conventions live in the agent workflow.














