About this project
M3U Playlist Editor is a self-hosted IPTV playlist manager built with Vue 3, Express and PostgreSQL (Node.js 22+, PostgreSQL 13+, PostgreSQL 16 in Docker and CI), released under the MIT licence. It imports, edits, organises, exports and shares M3U and Xtream playlists through a responsive web interface with multi-user isolation and a production Docker stack.
Core capabilities: Xtream Codes imports for Live TV, VOD and Series; local or remote M3U import and escaped M3U export; XMLTV guide sources with source-scoped channel matching and timezone-aware guide views; category and channel sorting, bulk operations, logo uploads, search and metadata editing; a three-pane channel editor (stream-type sidebar, category list, channels table, per-channel edit panel); a multi-day EPG editor grid with auto-matching by tvg-id; drag-and-drop sorting whose order persists immediately; Turkish and English interface, light/dark/system themes, keyboard navigation and mobile layouts.
Accounts and security: short-lived access tokens with rotating HttpOnly refresh sessions. Registration can be turned off with ALLOW_REGISTRATION=false, but the first user can still register while the users table is empty so a fresh install cannot lock itself out. npm scripts grant or revoke admin rights for a registered email, and reset a user's password (revoking all active sessions). JWT_SECRET and CREDENTIAL_ENCRYPTION_KEY must be two independently generated values of at least 32 characters; production startup rejects the shipped placeholders, short or low-entropy values, and reusing one value for both.
Deployment: the Compose stack serves plain HTTP only - it has no TLS listener, certificate or HTTP-to-HTTPS redirect - so TLS must be terminated in front of it (Caddy, Traefik, nginx or a cloud load balancer) with the published port kept private or bound to loopback; refresh cookies are marked Secure in production. The API container applies pending migrations before startup, PostgreSQL is not exposed to the host, uploaded logos and database files use named volumes, the API runs as a non-root user with a read-only root filesystem, and the frontend is served by nginx with security and cache headers. Private-network remote URLs are blocked by default as SSRF protection, with ALLOW_PRIVATE_NETWORK_URLS as the documented opt-in for trusted local services. Xtream import defaults (byte cap, category and type concurrency, Node heap size) are sized for playlists of roughly 150,000 Live/VOD/Series entries.
Xtream Codes output: a playlist can be exposed through POST /api/playlists/:id/xtream-output, returning a server address plus a random username and password; the password is stored AES-256-GCM encrypted, always shown in plain text to its owner, regenerable, and removable to switch player access off. Supported root paths are /player_api.php, /xmltv.php, /get.php and /m3u.php. A shorter /m3u.php?id=...&secret=... address carries its own credential pair so it can be handed out without exposing the Xtream username and password, and rotates when the output is regenerated. The server is never the stream address: get.php publishes the provider's own channel URLs and player_api.php fills direct_source, so playback never touches the application, and the earlier proxy mode has been removed along with its column. The README warns that those published URLs may embed the upstream provider's credentials, so sharing output credentials effectively shares the provider account; use clients that honour direct_source.
Logo library: the channel panel has a Find logo button that searches an in-memory index of 10,700+ logos from tv-logo/tv-logos across 48 countries and 16 other groups. The index is pulled from GitHub's tree endpoint once a day and cached, images are served from jsDelivr, and matching strips quality and country tags and folds Turkish characters (TRT 1 FHD finds trt-1-tr.png). The assistant can call the same functionality and apply logos to channels that lack one.
AI assistant: an optional chat launcher on every authenticated page operates the application through 175 server-side tools covering playlists, categories, channels, EPG, imports and sync, export and sharing, attachments and output files, scheduled tasks, and account/system information. It works with any OpenAI-compatible provider (base URL, encrypted API key, model list fetching), streams answers over Server-Sent Events with a fallback to a plain endpoint, supports an approval gate before destructive actions, accepts file attachments, and can convert a spoken instruction into a recurring server-side task (minimum 15-minute interval) whose runs are logged, backed up before execution, and reversible with one click.
Web search ships without user configuration: an internal SearXNG instance (reachable only as searxng:8080, not exposed publicly) backs web_search and open_web_page, with JSON output enabled in the bundled settings; both tools are not registered at all when SEARXNG_URL is empty.
Attachments: .m3u, .m3u8, .txt, .xml, .xmltv, .json and .csv up to 20 MB each and 200 MB per account are detected by content rather than extension, stored on disk, and paged or searched by the assistant instead of being passed whole to the model. Messages beyond 12,000 characters are automatically converted into attachments.
Dead-channel repair: repair_dead_channels matches broken channels by tolerant name reduction against an Xtream catalogue and rewrites only the stream URL, testing each candidate before writing, excluding channels that were never checked, and supporting a dry run; paired with a scheduled task it can run unattended.
Repository: https://github.com/fyildirim-debug/M3uEditor
Comments
0 Rating appears after 10 ratings
Sign in to join the discussion.