Browse Source

fix(iptv): propagate access_token to HLS segment URLs when JWT authentication is enabled

When JWT authentication is enabled (JWT__ISSUERSIGNINGKEY), ErsatzTV requires a valid
token for all IPTV endpoints.

HLS players typically request the playlist with the access_token query parameter,
but segment requests (.ts files) do not include Authorization headers. The generated
playlist previously produced segment URLs without the access_token parameter,
causing segment requests to fail with 401 Unauthorized.

This change propagates the access_token query parameter from the playlist request
to all generated HLS segment URLs.

Before:
  /iptv/hls-direct/1.ts?index=1

After:
  /iptv/hls-direct/1.ts?index=1&access_token=TOKEN

This allows authenticated playback with HLS players that do not support sending
Authorization headers for segment requests.

Impact:
- Fixes 401 errors when streaming with JWT enabled
- Enables JWT-protected IPTV playback in common HLS players
- Maintains backward compatibility when JWT authentication is disabled

Testing:
- Verified playback using IPTV Smarters
- Confirmed HLS segment requests include access_token
- Confirmed successful playback of protected streams
pull/2888/head
jpsantos1 5 months ago
parent
commit
3bc3a6e321
  1. 4
      ErsatzTV.Application/Streaming/Queries/GetHlsPlaylistByChannelNumber.cs
  2. 8
      ErsatzTV.Application/Streaming/Queries/GetHlsPlaylistByChannelNumberHandler.cs
  3. 2
      ErsatzTV/Controllers/IptvController.cs
  4. 4
      ErsatzTV/ErsatzTV.csproj
  5. 5
      ErsatzTV/Properties/launchSettings.json
  6. 90
      README.md

4
ErsatzTV.Application/Streaming/Queries/GetHlsPlaylistByChannelNumber.cs

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
using ErsatzTV.Core;
using ErsatzTV.Core;
namespace ErsatzTV.Application.Streaming;
public record GetHlsPlaylistByChannelNumber(string Scheme, string Host, string ChannelNumber, string Mode)
public record GetHlsPlaylistByChannelNumber(string Scheme, string Host, string ChannelNumber, string Mode, string AccessToken)
: IRequest<Either<BaseError, string>>;

8
ErsatzTV.Application/Streaming/Queries/GetHlsPlaylistByChannelNumberHandler.cs

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
using ErsatzTV.Core;
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories;
using ErsatzTV.FFmpeg.OutputFormat;
@ -71,13 +71,17 @@ public class GetHlsPlaylistByChannelNumberHandler : @@ -71,13 +71,17 @@ public class GetHlsPlaylistByChannelNumberHandler :
long index = GetIndexForChannel(parameters.Channel, parameters.PlayoutItem);
double timeRemaining = Math.Abs((parameters.PlayoutItem.FinishOffset - now).TotalSeconds);
string tokenQuery = string.IsNullOrWhiteSpace(request.AccessToken)
? ""
: $"&access_token={request.AccessToken}";
return $@"#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:{index}
#EXT-X-DISCONTINUITY
#EXTINF:{timeRemaining:F2},
{request.Scheme}://{request.Host}/{endpoint}/{request.ChannelNumber}{extension}?index={index}{mode}
{request.Scheme}://{request.Host}/{endpoint}/{request.ChannelNumber}{extension}?index={index}{mode}{tokenQuery}
";
}

2
ErsatzTV/Controllers/IptvController.cs

@ -262,7 +262,7 @@ public class IptvController : StreamingControllerBase @@ -262,7 +262,7 @@ public class IptvController : StreamingControllerBase
Request.Scheme,
Request.Host.ToString(),
channelNumber,
mode))
mode, Request.Query["access_token"]))
.Map(r => r.Match<IActionResult>(
playlist => Content(playlist, "application/vnd.apple.mpegurl"),
error => BadRequest(error.Value)));

4
ErsatzTV/ErsatzTV.csproj

@ -158,9 +158,7 @@ @@ -158,9 +158,7 @@
</ItemGroup>
<ProjectExtensions>
<VisualStudio>
<UserProperties client-app_4package_1json__JsonSchema="https://json.schemastore.org/phraseapp.json" />
</VisualStudio>
<VisualStudio><UserProperties /></VisualStudio>
</ProjectExtensions>
</Project>

5
ErsatzTV/Properties/launchSettings.json

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
{
{
"profiles": {
"ErsatzTV": {
"commandName": "Project",
@ -7,7 +7,8 @@ @@ -7,7 +7,8 @@
"ASPNETCORE_ENVIRONMENT": "Development",
"DOTNET_ENVIRONMENT": "Development",
"ETV_STREAMING_PORT": "8409",
"ETV_UI_PORT": "8410"
"ETV_UI_PORT": "8410",
"JWT__ISSUERSIGNINGKEY": "qwertyuiopasdfghjklzxcvbnm123456"
}
}
}

90
README.md

@ -1,6 +1,10 @@ @@ -1,6 +1,10 @@
# ErsatzTV
# ErsatzTV (Community Fork)
ErsatzTV lets you transform your media library into a personalized, live TV experience - complete with EPG, channel scheduling, and seamless streaming to all your devices. Rediscover your content, your way.
ErsatzTV lets you transform your media library into a personalized, live TV experience — complete with EPG, channel scheduling, and seamless streaming to all your devices.
This repository is a **community-maintained fork** of the original ErsatzTV project, which is currently archived.
It includes fixes and improvements to keep the project usable in modern setups.
[![contact](https://img.shields.io/badge/contact_us-510b80?style=for-the-badge)](https://ersatztv.org/contact)
[![features](https://img.shields.io/badge/vote_on_features-darkgreen?style=for-the-badge)](https://features.ersatztv.org/)
@ -8,14 +12,66 @@ ErsatzTV lets you transform your media library into a personalized, live TV expe @@ -8,14 +12,66 @@ ErsatzTV lets you transform your media library into a personalized, live TV expe
![epg-example](https://ersatztv.org/images/home/epg-example.png)
## How It Works
---
# Important Notice
The original ErsatzTV repository has been **archived** and is no longer actively maintained.
This fork exists to:
- maintain compatibility with modern environments
- fix bugs affecting real-world deployments
- continue improving the project for the community
All credit for the original work belongs to the ErsatzTV maintainers and contributors.
---
# Fixes in this Fork
### JWT + HLS Streaming Fix
When JWT authentication is enabled (`JWT__ISSUERSIGNINGKEY`), HLS players request the playlist with an `access_token` query parameter.
However, the original implementation generated segment URLs without propagating this token.
Example before:
/iptv/hls-direct/1.ts?index=1
This caused segment requests to fail with:
401 Unauthorized
Example after:
/iptv/hls-direct/1.ts?index=1&access_token=TOKEN
This change allows authenticated playback with IPTV and HLS players that do not support sending Authorization headers for segment requests.
Tested successfully with:
- IPTV Smarters
- HLS players
- JWT-protected IPTV streams
---
# How It Works
1. **Install ErsatzTV**: Download and set up the server on your system.
2. **Add Your Media**: Connect your media libraries and collections.
3. **Create Channels**: Design and schedule your own live channels.
4. **Stream Anywhere**: Watch on any device with IPTV and EPG support.
## Key Features
---
# Key Features
- **Custom channels**: Create and schedule your own live TV channels.
- **IPTV & EPG**: Stream with IPTV and Electronic Program Guide support.
@ -24,11 +80,27 @@ ErsatzTV lets you transform your media library into a personalized, live TV expe @@ -24,11 +80,27 @@ ErsatzTV lets you transform your media library into a personalized, live TV expe
- **Music & Subtitles**: Mix music videos and enjoy subtitle support.
- **Open Source**: Free, open, and community-driven project.
## Documentation
---
# Documentation
Documentation is available at:
https://ersatztv.org/docs/
---
# Credits
This project is inspired by:
- https://github.com/DEFENDORe/pseudotv-plex
- https://github.com/vexorian/dizquetv
---
Documentation is available at [ersatztv.org](https://ersatztv.org/docs/).
# License
## License
This project is released under the **zlib license**.
This project is inspired by [pseudotv-plex](https://github.com/DEFENDORe/pseudotv) and
the [dizquetv](https://github.com/vexorian/dizquetv) fork and is released under the [zlib license](LICENSE).
See the [LICENSE](LICENSE) file for details.
Loading…
Cancel
Save