4fbb3e813e531ab85bbee1ab2c5b869e099420e0
Force-close each /stream/{channel_id} response instead of allowing
keep-alive reuse. Jellyfin's SharedHttpStream tuner client was reusing
a pooled keep-alive connection mid-stream from an earlier request and
reading raw MPEG-TS bytes as an HTTP status line, failing with
'Received an invalid status line'.
Also broaden the disconnect exception handling to ConnectionAbortedError
alongside ConnectionResetError.
Restreamer
Restreams YouTube livestreams as continuous local streams that Jellyfin (or any player) can watch as Live TV.
How it works
- yt-dlp resolves a YouTube livestream URL to a direct, playable stream URL.
- ffmpeg copies that stream (no re-encoding) into a continuous MPEG-TS byte stream.
- A small async HTTP server fans that byte stream out to any number of connected clients, and publishes an M3U playlist listing every configured channel.
- A worker per channel keeps the stream alive indefinitely: it proactively re-resolves the source before the underlying URL expires, and reactively restarts ffmpeg (with backoff) if it ever exits unexpectedly. This is the "session circulation" that keeps a channel from dying every few hours.
Running
-
Copy
channels.example.yamltochannels.yamland list your channels. -
Copy
.env.exampleto.envand adjust as needed (in particularPUBLIC_BASE_URL, the address Jellyfin will use to reach this service). -
Run with Docker Compose:
docker compose up --build -
In Jellyfin, add a Live TV M3U Tuner pointing at
http://<host>:8000/playlist.m3u.
Local development
poetry install
poetry run python serve.py
Requires ffmpeg on PATH.
Development
poetry run ruff check
poetry run mypy
poetry run pytest
See PROJECT.md for current status and DESIGN_DOCUMENT_PYTHON.md / AGENTS.md for project conventions.
Languages
Python
98.3%
Dockerfile
1.7%