9663734435846fb9d9d3f1c672a0bacee362979d
- 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'.
- Broaden the disconnect exception handling to ConnectionAbortedError
alongside ConnectionResetError.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WDDfSVPh5pgNe7TRpZUgJA
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%