From 55c9a796b46ac2685089786e6db06e83716b38b7 Mon Sep 17 00:00:00 2001 From: Trevor Mears Date: Fri, 2 Jan 2026 15:52:19 -0800 Subject: [PATCH] Updated readme --- README.md | 37 +++++++++++++++++++++---------------- public/img/logo-banner.png | Bin 0 -> 19570 bytes 2 files changed, 21 insertions(+), 16 deletions(-) create mode 100644 public/img/logo-banner.png diff --git a/README.md b/README.md index aad0240..d35606c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ -# NodeCast TV +

+ nodecast-tv +

-A modern, web-based IPTV player featuring Live TV, EPG, Movies (VOD), and Series support. Built with performance and user experience in mind. +# What is nodecast-tv? + +nodecast-tv is a modern, web-based IPTV player featuring Live TV, EPG, Movies (VOD), and Series support. Built with performance and user experience in mind. ## Features @@ -15,6 +19,7 @@ A modern, web-based IPTV player featuring Live TV, EPG, Movies (VOD), and Series - Manage hidden content categories. - Playback preferences (volume memory, auto-play). - **🔊 Audio Transcoding**: Optional FFmpeg-based audio transcoding for Dolby/AC3/EAC3 compatibility. +- **📦 Stream Remux**: Lightweight FFmpeg remux for raw MPEG-TS streams from IPTV middleware. - **🐳 Docker Ready**: Easy deployment containerization. ## Screenshots @@ -55,7 +60,7 @@ A modern, web-based IPTV player featuring Live TV, EPG, Movies (VOD), and Series ### Docker Deployment -You can run NodeCast TV easily using Docker. +You can run nodecast-tv easily using Docker. 1. Create a `docker-compose.yml` file (or copy the one from this repo): @@ -91,7 +96,7 @@ The application will be available at `http://localhost:3000`. ## Browser Codec Support -NodeCast TV is a web-based application, which means **video decoding is handled by your browser**, not by the server. The server simply proxies the stream data - it does not transcode or re-encode video. +nodecast-tv is a web-based application, which means **video decoding is handled by your browser**, not by the server. The server simply proxies the stream data - it does not transcode or re-encode video. This means codec support depends entirely on what your browser can decode natively: @@ -128,7 +133,7 @@ For streams with Dolby Digital (AC3/EAC3) audio that browsers can't decode nativ ## Supported Stream Types -NodeCast TV is optimized for **HLS (HTTP Live Streaming)**. +nodecast-tv is optimized for **HLS (HTTP Live Streaming)**. - **✅ HLS (`.m3u8`)**: Fully supported and recommended. Best for adaptive bitrate and network resilience. - **✅ MPEG-TS (`.ts`)**: Supported via Force Remux in settings. @@ -141,7 +146,7 @@ All streaming settings are found in **Settings → Player → Streaming**. | Setting | What It Does | When to Enable | |---------|--------------|----------------| -| **Force Backend Proxy** | Routes streams through the NodeCast TV server, adding proper CORS headers | When streams fail with "Access-Control-Allow-Origin" errors, or when using IPTV middleware | +| **Force Backend Proxy** | Routes streams through the nodecast-tv server, adding proper CORS headers | When streams fail with "Access-Control-Allow-Origin" errors, or when using IPTV middleware | | **Force Audio Transcode** | Transcodes audio to AAC using FFmpeg (video passes through unchanged) | When you have video but no audio (Dolby/AC3/EAC3 streams) | | **Force Remux** | Remuxes MPEG-TS to MP4 container using FFmpeg (no re-encoding, very lightweight) | When using raw `.ts` streams from m3u-editor, dispatcharr, or similar middleware | | **Stream Output Format** | Controls whether Xtream API requests use HLS (.m3u8) or TS format | Try TS if you experience buffering issues with HLS | @@ -172,7 +177,7 @@ All streaming settings are found in **Settings → Player → Streaming**. ### HTTPS / Reverse Proxy Issues -If you're running NodeCast TV behind a reverse proxy (Nginx, Caddy, Traefik) with HTTPS: +If you're running nodecast-tv behind a reverse proxy (Nginx, Caddy, Traefik) with HTTPS: | Symptom | Likely Cause | Solution | |---------|--------------|----------| @@ -204,9 +209,9 @@ location / { ### IPTV Middleware (m3u-editor, dispatcharr, Threadfin, etc.) -If you're using IPTV middleware like **m3u-editor**, **dispatcharr**, **Threadfin**, or **xTeVe** to manage your streams, you may need to adjust NodeCast TV settings for optimal playback. These tools typically use passthrough mode, which preserves original codecs (like HEVC/Dolby) that most browsers cannot decode natively, and may also trigger CORS restrictions. +If you're using IPTV middleware like **m3u-editor**, **dispatcharr**, **Threadfin**, or **xTeVe** to manage your streams, you may need to adjust nodecast-tv settings for optimal playback. These tools typically use passthrough mode, which preserves original codecs (like HEVC/Dolby) that most browsers cannot decode natively, and may also trigger CORS restrictions. -**Recommended Settings in NodeCast TV:** +**Recommended Settings in nodecast-tv:** | Setting | Location | When to Enable | |---------|----------|----------------| @@ -222,7 +227,7 @@ m3u-editor includes an internal proxy that remuxes streams to MPEG-TS. **Setup:** 1. In m3u-editor, configure your playlist and enable the proxy if needed -2. In NodeCast TV, enable **"Force Remux"** in Settings → Streaming (for raw .ts streams) +2. In nodecast-tv, enable **"Force Remux"** in Settings → Streaming (for raw .ts streams) 3. If audio doesn't play, enable **"Force Audio Transcode"** instead **Note:** m3u-editor's proxy preserves original codecs. If your source has HEVC or Dolby, you'll need transcoding or a compatible browser (Safari). @@ -235,11 +240,11 @@ dispatcharr uses FFmpeg stream profiles to process streams. By default it output **Setup:** 1. In dispatcharr, streams are proxied by default via stream profiles -2. In NodeCast TV, enable **"Force Remux"** in Settings → Streaming (for raw .ts streams) +2. In nodecast-tv, enable **"Force Remux"** in Settings → Streaming (for raw .ts streams) 3. If audio doesn't play, enable **"Force Audio Transcode"** instead **Custom dispatcharr profile for browser compatibility:** -If you want dispatcharr to transcode audio for you instead of NodeCast TV: +If you want dispatcharr to transcode audio for you instead of nodecast-tv: ``` -user_agent {userAgent} -i {streamUrl} -c:v copy -c:a aac -f mpegts pipe:1 ``` @@ -252,22 +257,22 @@ This keeps video passthrough but converts audio to AAC. These HDHomeRun emulators work similarly to other middleware. **Setup:** -1. Add your Threadfin/xTeVe M3U URL as an M3U source in NodeCast TV +1. Add your Threadfin/xTeVe M3U URL as an M3U source in nodecast-tv 2. Enable **"Force Remux"** in Settings → Streaming (for raw .ts streams) 3. If needed, enable **"Force Audio Transcode"** instead for Dolby audio ### TVHeadend -If you're using TVHeadend as your source, you may need to configure a few settings for streams to play correctly in NodeCast TV: +If you're using TVHeadend as your source, you may need to configure a few settings for streams to play correctly in nodecast-tv: **Option 1: Enable Force Backend Proxy (Easiest)** -- In NodeCast TV, go to **Settings → Player → Streaming** +- In nodecast-tv, go to **Settings → Player → Streaming** - Enable **"Force Backend Proxy"** - This routes streams through the server, bypassing browser CORS restrictions **Option 2: Configure TVHeadend CORS** - In TVHeadend, go to **Configuration → General → Base → HTTP Server Settings** -- Add your NodeCast TV URL to **"CORS origin"** (e.g., `http://192.168.1.100:3000`) +- Add your nodecast-tv URL to **"CORS origin"** (e.g., `http://192.168.1.100:3000`) - **Note:** You must include the protocol (`http://` or `https://`) **Additional Tips:** diff --git a/public/img/logo-banner.png b/public/img/logo-banner.png new file mode 100644 index 0000000000000000000000000000000000000000..99bd8d9b442a502236a81b8bf3a969afc188feca GIT binary patch literal 19570 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 r?$%YDhWZOyy^#w>DEk0#6ob7fGt21