68 lines
4.7 KiB
Markdown
68 lines
4.7 KiB
Markdown
# Self-hosted IPTV
|
||
|
||
Serves M3U playlists that randomly schedule video files from `/movies` and `/series`, with the same show never appearing twice in a row.
|
||
|
||
## Setup
|
||
|
||
1. Create `/data/channels.json` (see `data/channels.json.example`).
|
||
|
||
**channels.json format:**
|
||
|
||
- `channels`: array of channel objects
|
||
- Each channel:
|
||
- `id`: unique id (used in URLs)
|
||
- `name`: display name
|
||
- `paths`: list of path entries. Each entry can be:
|
||
- **String:** `"movies"` or `"series/Show Name"` — path to videos; EPG title and TMDB search use the folder name.
|
||
- **Object:** `{"path": "series/W817", "display_name": "Phineas en Ferb", "tmdb_search": "Phineas and Ferb"}` — `path` is required; `display_name` is shown in the EPG; `tmdb_search` is used when querying TMDB. You can use `tmdb_id` instead of (or to override) `tmdb_search` for a direct lookup, e.g. `97596` or `"97596-kika-bob"` (the numeric ID is extracted).
|
||
|
||
2. Run the container with volumes:
|
||
|
||
```bash
|
||
docker build -t self-hosted-iptv .
|
||
docker run -d --name iptv -p 8080:8080 \
|
||
-v /path/to/movies:/movies:ro \
|
||
-v /path/to/series:/series:ro \
|
||
-v /path/to/data:/data \
|
||
self-hosted-iptv
|
||
```
|
||
When using TMDB metadata, mount `/data` read-write (as above) so the app can store and reuse metadata in `/data/tmdb_cache/`. Use `-v /path/to/data:/data:ro` only if you do not use TMDB.
|
||
|
||
**EPG timespan:** Set `EPG_TIMESPAN` to control how far ahead the guide is generated (default `14d`). Examples: `2d`, `7d`, `48h`. The randomized schedule is refreshed after (timespan − 3 hours) so a new block of content is ready before the current one ends. Example: `-e EPG_TIMESPAN=2d` gives 2 days of EPG and a new schedule every 45 hours.
|
||
|
||
**Optional – EPG metadata (Dutch) from TMDB:**
|
||
To show series titles, episode names, and descriptions in the TV guide in **Dutch**, set a [TMDB API key](https://www.themoviedb.org/settings/api) (free after sign-up):
|
||
|
||
- `TMDB_API_KEY` – your TMDB API key (v3).
|
||
|
||
Series are matched by folder name (e.g. `series/W817` → search "W817"); episode info is matched from filenames like `S01E01`. All metadata is requested in Dutch (`language=nl`).
|
||
|
||
- **Persistent cache:** Fetched metadata is stored under `/data/tmdb_cache/` (series search, series details, episode, episode credits). When generating the EPG, the app checks this cache first and only calls TMDB for missing entries. Mount `/data` read-write (omit `:ro`) so the cache can be written.
|
||
- **Rate limit:** TMDB allows 40 requests per 10 seconds. The app enforces this limit when calling the API; if the cache is warm, few or no requests are made during EPG generation.
|
||
|
||
**Logging:** Set `LOG_LEVEL=DEBUG` (e.g. in your `docker run` with `-e LOG_LEVEL=DEBUG`) to see where time is spent: schedule building (including ffprobe per video), EPG per-channel programme count and duration, and TMDB cache hits (memory/disk) vs API requests and rate-limit waits.
|
||
|
||
**Stream crashes when switching episodes?** If the live stream freezes or crashes when transitioning between videos (e.g. different episodes with different resolutions), enable normalization to re-encode everything to a uniform format:
|
||
- `NORMALIZE_LIVE_STREAM=1` — enables re-encoding (uses more CPU, produces a stable continuous stream)
|
||
- `NORMALIZE_TARGET_RES=1920x1080` — target resolution (default). Use `1280x720` for lower CPU usage.
|
||
|
||
3. In your IPTV client or **Plex DVR**, add:
|
||
- **Playlist URL:** `http://<host>:8080/playlist.m3u`
|
||
- **EPG (XMLTV) URL:** `http://<host>:8080/epg.xml`
|
||
Plex will show programme titles and times for each channel.
|
||
|
||
Each channel in the M3U points to a **continuous live stream** (`/live/<id>`): FFmpeg concatenates the channel’s randomized videos into one MPEG-TS stream and loops it. The **schedule** (and EPG) is built from video durations (via ffprobe) so start/stop times match what’s actually playing. The schedule is cached for (timespan − 3 h) so the EPG and live stream stay in sync and a new span is ready in time.
|
||
|
||
## Endpoints
|
||
|
||
- `GET /` — simple web index with playlist and EPG links
|
||
- `GET /playlist.m3u` — master M3U for Plex DVR (one live stream URL per channel)
|
||
- `GET /epg.xml` or `GET /xmltv.xml` — XMLTV EPG (duration set by `EPG_TIMESPAN`, UTC)
|
||
- `GET /live/<channel_id>` — continuous MPEG-TS stream for that channel (for tuning/recording)
|
||
- `GET /channel/<id>/playlist.m3u` — single channel M3U (list of individual video URLs)
|
||
- `GET /stream?path=movies/...` or `path=series/...` — stream a single video file (audio track is auto-selected: Dutch → Flemish → default)
|
||
|
||
## Shuffle behaviour
|
||
|
||
Videos are grouped by path (e.g. one group per show or the whole movies folder). Each group is shuffled, then items are interleaved round-robin so the same show never plays back-to-back.
|