aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Jellyfin.LiveTv/TunerHosts/M3UTunerHost.cs57
-rw-r--r--src/Jellyfin.Networking/Manager/NetworkManager.cs17
2 files changed, 39 insertions, 35 deletions
diff --git a/src/Jellyfin.LiveTv/TunerHosts/M3UTunerHost.cs b/src/Jellyfin.LiveTv/TunerHosts/M3UTunerHost.cs
index c1e1a7bda..365f0188d 100644
--- a/src/Jellyfin.LiveTv/TunerHosts/M3UTunerHost.cs
+++ b/src/Jellyfin.LiveTv/TunerHosts/M3UTunerHost.cs
@@ -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.Contains(response.Content.Headers.ContentType?.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);
diff --git a/src/Jellyfin.Networking/Manager/NetworkManager.cs b/src/Jellyfin.Networking/Manager/NetworkManager.cs
index 70790bb5b..148b33fcb 100644
--- a/src/Jellyfin.Networking/Manager/NetworkManager.cs
+++ b/src/Jellyfin.Networking/Manager/NetworkManager.cs
@@ -903,6 +903,17 @@ public class NetworkManager : INetworkManager, IDisposable
return false;
}
+ /// <summary>
+ /// Get if the IPAddress is Link-local.
+ /// </summary>
+ /// <param name="address">The IP Address.</param>
+ /// <returns>Bool indicates if the address is link-local.</returns>
+ public bool IsLinkLocalAddress(IPAddress address)
+ {
+ ArgumentNullException.ThrowIfNull(address);
+ return NetworkConstants.IPv4RFC3927LinkLocal.Contains(address) || address.IsIPv6LinkLocal;
+ }
+
/// <inheritdoc/>
public bool IsInLocalNetwork(IPAddress address)
{
@@ -1084,7 +1095,11 @@ public class NetworkManager : INetworkManager, IDisposable
private bool MatchesExternalInterface(IPAddress source, out string result)
{
// Get the first external interface address that isn't a loopback.
- var extResult = _interfaces.Where(p => !IsInLocalNetwork(p.Address)).OrderBy(x => x.Index).ToArray();
+ var extResult = _interfaces
+ .Where(p => !IsInLocalNetwork(p.Address))
+ .Where(p => p.Address.AddressFamily.Equals(source.AddressFamily))
+ .Where(p => !IsLinkLocalAddress(p.Address))
+ .OrderBy(x => x.Index).ToArray();
// No external interface found
if (extResult.Length == 0)