* ci(release): make test draft releases traceable and self-cleaning Every PR and master build created a draft named "Release v<version>" with tag test-<github.sha>, so 70+ identical drafts piled up and PR drafts were untraceable (for pull_request events github.sha is the ephemeral merge-commit SHA that resolves to nothing in the repo). - Title test drafts as "v<ver> — PR #<n> @ <sha> [test]" / "v<ver> — master @ <sha> [test]"; tag releases keep "Release v<ver>" - Prepend a context header (PR, head commit, workflow run links) to the auto-generated release notes - Use the PR head SHA and pass target_commitish so generated notes actually cover the PR commits - Use stable tags (test-pr-<n>, test-master) so action-gh-release updates one rolling draft in place instead of creating a new one per push - Mark all non-tag drafts as prerelease - Cancel superseded in-progress PR builds via a concurrency group - Delete a PR's rolling draft when the PR closes (new workflow) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci(release): close review-bot race windows in draft release flow - Move concurrency from workflow level to job level: cancelling a whole run could interrupt action-gh-release mid-asset-replacement and leave the rolling draft incomplete. Build slots still cancel superseded PR work (matrix-aware groups); the release job gets its own serializing, never-cancelling group. - Re-check the live PR state in the release job right before touching the draft, so a build that outlives its PR cannot recreate the draft after cleanup deleted it. - In the cleanup workflow, cancel still-running builds of the closed PR (dead work anyway) and wait for them to settle before deleting. - Emit an explicit empty `body=` output for tag builds instead of a blank-line heredoc. Addresses Codex and Greptile review feedback on #1202. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci(release): grant actions:write so PR-close cleanup can cancel builds gh run cancel needs the actions scope; with only contents: write the cancellation 403s silently and the settle-poll burns its full window. Also skip the cleanup job for fork PRs entirely: they never get a draft and their token is read-only regardless of the permissions block. Addresses Greptile P1 / Codex P2 follow-up on #1202. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci(release): re-assert rolling draft title after asset upload action-gh-release@v2 updates name/body/target_commitish on the normal draft-reuse path, but in a rare race (release listing transiently missing the draft) it uploads assets to the canonical oldest draft without refreshing its metadata. PATCH the title and commitish on the release id the action actually used, so the draft title always names the current head SHA; the body is left alone to preserve generated notes. Addresses Codex round-2 feedback on #1202. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci(release): prune stale assets before updating a rolling draft The release action only replaces same-name assets, so a PR that bumps the app version would leave old-version installers beside the new set in its rolling draft. Delete all existing assets of the matched draft before the upload; the action re-uploads the full current set right after. Published releases are never touched. Addresses Codex round-3 feedback on #1202. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci(release): rebuild full draft metadata after asset upload Extend the post-upload metadata step to also rebuild the body (context header + notes from the same generate-notes API the action uses), not just title/commitish. The rolling draft now ends up with correct metadata regardless of which internal action-gh-release path ran, including the rare canonicalize-duplicate fallback. If notes generation fails, the body is left as the action set it. Addresses Codex round-4 feedback on #1202. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci(release): only cancel pull_request runs when cleaning up a closed PR A manually dispatched build on the same head branch is not the PR's work; filter the cancellation list by event so PR-close cleanup cannot abort it. Addresses Codex round-5 feedback on #1202. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci(release): guard PR-close cleanup against close-reopen races Re-check the live PR state at the start of the cleanup job and again right before deleting the draft, so a PR that is reopened while the cleanup is queued or waiting keeps its rolling draft and its fresh reopened-run builds are not cancelled. Addresses Codex round-6 feedback on #1202. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci(release): keep tag_name when patching rolling draft metadata PATCHing a draft release without tag_name makes GitHub drop the pending tag (the draft turns into untagged-<hash>), so the next run cannot find the rolling draft by tag and creates a duplicate — observed live on this PR's own drafts. Include tag_name in both PATCH payloads of the metadata step. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 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) — 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)
Platform
- Cross-platform desktop (Electron) and installable PWA
- Desktop auto-updater and mobile remote control (desktop)
- Docker self-hosting for the PWA + web backend
- 18 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 in the desktop app |
| Global | Ctrl/Cmd+R |
Open recently viewed in the desktop app |
| Global | Enter in workspace search |
Submit the current search |
| Navigation | Ctrl/Cmd+B |
Toggle the live sidebar |
| Navigation | 0-9 |
Select an M3U channel by number |
| Playback | Space / K |
Play or pause embedded MPV playback in the desktop app |
| Playback | F |
Toggle embedded MPV fullscreen in the desktop app |
| Playback | ArrowLeft / ArrowRight |
Seek embedded MPV playback by 5 seconds in the desktop app |
| 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 |
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:
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 with pnpm (via Corepack)
-
Clone this repository and install project dependencies:
$ 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 to debug renderer freezes or GPU/compositor issues in Electron, you can disable hardware acceleration for a run:
$ IPTVNATOR_DISABLE_HARDWARE_ACCELERATION=1 pnpm run serve:backend
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
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.














