Three problems: (1) the m3u8 URL is buried behind iframes and obfuscated JS, (2) tokens expire every few hours, and (3) the upstream server checks User-Agent and Referer headers on both the playlist and .ts segments — Jellyfin doesn't send these, so you get 403.
I ended up writing three scripts:
- detect-headers.sh: give it a page URL, it follows the iframe chain, extracts the m3u8, then brute-forces header combinations on both .m3u8 and .ts requests. Tells you exactly what the stream needs.
- hls-proxy.py: single-file Python reverse proxy (stdlib only, zero pip dependencies). Injects the required headers and rewrites the m3u8 so segment requests also go through the proxy.
- refresh-m3u.sh: extracts fresh URLs before tokens expire, outputs a Jellyfin-ready M3U with logos and channel groups. Runs on a systemd timer.
~200 lines of Python, ~100 lines of bash. The proxy is the interesting part technically — it has to handle relative and absolute segment URLs, rewrite URI= in EXT tags (for encryption keys), and add CORS headers since Jellyfin's web client makes cross-origin requests.
Happy to answer questions about the approach or implementation.
How probable is it that this kind of method can be patched or obfuscated further? I assume that since the HLS stream is always at the core, it’s a matter of just finding alternative ways to dig through it.
Any quirks this implementation has wrt. things like quality or additional delay? Thanks! I’d like to try out if your methods could be used to make some sort of snippet that could be sent to VLC that’s running on a TV or streaming device.
Either way, gonna make my World Cup viewing experience this year a lot easier haha
It’s similar for NFL, and I assume NHL and NBA, too. I’d pay to watch the stuff I watch if it were possible, but it’s not!
MLB, I haven't tried for a few years but I could watch any out of market game on mlb.tv, but not any that involved the local team, so it was the opposite. For that there was a special regional sports channel that I'd have to subscribe to. No way to do it directly with the network, I'd have to get satellite or something.
That's the situation for probably 95% of viewers, though. Others might want to watch games from where they grew up, but most people typically follow the local teams. We don't even have a great way to get an antenna feed into our TV, and that also means we have one way to watch everything except local games, and another, worse way to watch them (for example, by not having a way to pause them).
I get why the streaming apps don't show local games from their business POV, but as a potential subscriber, that's a them-problem, not a me-problem. There's no way I'm paying that much money without being able to watch the home games.
If you're cool waiting a day to watch the games, nfl plus has everything with commercials cut.
https://jellyfin.org/docs/general/server/live-tv/setup-guide...
Channels DVR also supports m3u:
https://getchannels.com/docs/channels-dvr-server/how-to/cust...
The README mentions Plex and Emby, but those don't support m3u, so you need to use a proxy which makes an m3u source appear like a local tuner such as:
https://github.com/Threadfin/Threadfin
Above system works pretty well but had trouble with encode/decode speed somewhere. Tried with N100 cpu and still had the same result...probably user error somewhere but none of the options seemed to work. No issues with UHF so kept using that.
Think it could be ran from within a docker container so I could add it to an existing docker compose media server setup?