@ -30,25 +30,8 @@ namespace Jellyfin.LiveTv.TunerHosts
{
public class M3UTunerHost : BaseTunerHost , ITunerHost , IConfigurableTunerHost
{
private static readonly string [ ] _disallowedMimeTypes =
{
"text/plain" ,
"text/html" ,
"video/x-matroska" ,
"video/mp4" ,
"application/vnd.apple.mpegurl" ,
"application/mpegurl" ,
"application/x-mpegurl" ,
"video/vnd.mpeg.dash.mpd"
} ;
private static readonly string [ ] _disallowedSharedStreamExtensions =
{
".mkv" ,
".mp4" ,
".m3u8" ,
".mpd"
} ;
private static readonly string [ ] _mimeTypesCanShareHttpStream = [ "video/MP2T" ] ;
private static readonly string [ ] _extensionsCanShareHttpStream = [ ".ts" , ".tsv" , ".m2t" ] ;
private readonly IHttpClientFactory _httpClientFactory ;
private readonly IServerApplicationHost _appHost ;
@ -113,28 +96,34 @@ namespace Jellyfin.LiveTv.TunerHosts
if ( mediaSource . Protocol = = MediaProtocol . Http & & ! mediaSource . RequiresLooping )
{
using var message = new HttpRequestMessage ( HttpMethod . Head , mediaSource . Path ) ;
using var response = await _httpClientFactory . CreateClient ( NamedClient . Default )
. SendAsync ( message , cancellationToken )
. ConfigureAwait ( false ) ;
var extension = Path . GetExtension ( new UriBuilder ( mediaSource . Path ) . Path ) ;
if ( response . IsSuccessStatusCode )
if ( string . IsNullOrEmpty ( extension ) )
{
if ( ! _disallowedMimeTypes . Con tains( response. Content . Headers . ContentT ype? . MediaType , StringComparison . OrdinalIgnoreCase ) )
try
{
return new SharedHttpStream ( mediaSource , tunerHost , streamId , FileSystem , _httpClientFactory , Logger , Config , _appHost , _streamHelper ) ;
using var message = new HttpRequestMessage ( HttpMethod . Head , mediaSource . Path ) ;
using var response = await _httpClientFactory . CreateClient ( NamedClient . Default )
. SendAsync ( message , cancellationToken )
. ConfigureAwait ( false ) ;
if ( response . IsSuccessStatusCode )
{
if ( _mimeTypesCanShareHttpStream . Contains ( response . Content . Headers . ContentType ? . MediaType , StringComparison . OrdinalIgnoreCase ) )
{
return new SharedHttpStream ( mediaSource , tunerHost , streamId , FileSystem , _httpClientFactory , Logger , Config , _appHost , _streamHelper ) ;
}
}
}
}
else
{
// Fallback to check path extension when the server does not support HEAD method
// Use UriBuilder to remove all query string as GetExtension will include them when used directly
var extension = Path . GetExtension ( new UriBuilder ( mediaSource . Path ) . Path ) ;
if ( ! _disallowedSharedStreamExtensions . Contains ( extension , StringComparison . OrdinalIgnoreCase ) )
catch ( Exception )
{
return new SharedHttpStream ( mediaSource , tunerHost , streamId , FileSystem , _httpClientFactory , Logger , Config , _appHost , _streamHelper ) ;
Logger . LogWarning ( "HEAD request to check MIME type failed, shared stream disabled" ) ;
}
}
else if ( _extensionsCanShareHttpStream . Contains ( extension , StringComparison . OrdinalIgnoreCase ) )
{
return new SharedHttpStream ( mediaSource , tunerHost , streamId , FileSystem , _httpClientFactory , Logger , Config , _appHost , _streamHelper ) ;
}
}
return new LiveStream ( mediaSource , tunerHost , FileSystem , Logger , Config , _streamHelper ) ;