I self-host Jellyfin on my homelab and got frustrated opening a laptop every time there was a football match just to deal with popup-infested streaming sites. The actual video underneath is just a standard HLS stream, but getting it into Jellyfin turned out to be harder than expected.
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.
show comments
kstrauser
I love it. I’m willing to pay for streaming sports services, to a point, but all of them are freaking insane. For example, my wife and I like watching baseball. So let’s do the right thing and pay for it, right? LOL, as if that were possible. For $120[0] we can watch the Giants games, or for $220, all games… but subject to blackout. For $120, we can’t actually watch home games. We’d have to pay for a separate streaming service for those.
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!
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:
Very cool. I’ve used a paid service for years now that gets me all sorts of sports and channels very reliably, I would assume they’re doing something similar to make this work. Might try this though with my home server setup.
Think it could be ran from within a docker container so I could add it to an existing docker compose media server setup?
show comments
neko_ranger
Sports should be free to watch, the product itself is covered in ads already
amusingimpala75
Since this works on the raw data streams from the official distributor, this is legal, correct?
kaan_keskin
I totally needed something like this. Looks very good.
dewey
It's a cool hack...but also a bit unethical as the reason they are free is that there's ads. I almost feel it's more ethical to sign up for a paid pirated IPTV service and then use that, with the benefit of that being more stable and probably with higher quality.
I self-host Jellyfin on my homelab and got frustrated opening a laptop every time there was a football match just to deal with popup-infested streaming sites. The actual video underneath is just a standard HLS stream, but getting it into Jellyfin turned out to be harder than expected.
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.
I love it. I’m willing to pay for streaming sports services, to a point, but all of them are freaking insane. For example, my wife and I like watching baseball. So let’s do the right thing and pay for it, right? LOL, as if that were possible. For $120[0] we can watch the Giants games, or for $220, all games… but subject to blackout. For $120, we can’t actually watch home games. We’d have to pay for a separate streaming service for those.
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!
[0] https://www.mlb.com/live-stream-games/subscribe/giants
This works with Jellyfin because it supports m3u:
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
https://github.com/xteve-project/xTeVe
https://github.com/vorghahn/iptv4plex
Very cool. I’ve used a paid service for years now that gets me all sorts of sports and channels very reliably, I would assume they’re doing something similar to make this work. Might try this though with my home server setup.
Think it could be ran from within a docker container so I could add it to an existing docker compose media server setup?
Sports should be free to watch, the product itself is covered in ads already
Since this works on the raw data streams from the official distributor, this is legal, correct?
I totally needed something like this. Looks very good.
It's a cool hack...but also a bit unethical as the reason they are free is that there's ads. I almost feel it's more ethical to sign up for a paid pirated IPTV service and then use that, with the benefit of that being more stable and probably with higher quality.