diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs
index 98f50c09af..a5da2fc5c8 100644
--- a/Emby.Dlna/Main/DlnaEntryPoint.cs
+++ b/Emby.Dlna/Main/DlnaEntryPoint.cs
@@ -9,7 +9,7 @@ using System.Threading;
using System.Threading.Tasks;
using Emby.Dlna.PlayTo;
using Emby.Dlna.Ssdp;
-using Jellyfin.Networking.Manager;
+
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index cc04cb03f5..67a352fc93 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -160,6 +160,11 @@ namespace Emby.Server.Implementations
}
}
+ ///
+ /// Gets the singleton instance.
+ ///
+ public INetworkManager NetManager { get; internal set; }
+
///
/// Occurs when [has pending restart changed].
///
@@ -189,11 +194,6 @@ namespace Emby.Server.Implementations
/// The plugins.
public IReadOnlyList Plugins => _plugins;
- ///
- /// Gets the NetworkManager object.
- ///
- private readonly INetworkManager _networkManager;
-
///
/// Gets the logger factory.
///
@@ -267,7 +267,7 @@ namespace Emby.Server.Implementations
ConfigurationManager = new ServerConfigurationManager(ApplicationPaths, LoggerFactory, _xmlSerializer, _fileSystemManager);
- _networkManager = new NetworkManager((IServerConfigurationManager)ConfigurationManager, LoggerFactory.CreateLogger());
+ NetManager = new NetworkManager((IServerConfigurationManager)ConfigurationManager, LoggerFactory.CreateLogger());
Logger = LoggerFactory.CreateLogger();
@@ -524,7 +524,7 @@ namespace Emby.Server.Implementations
ServiceCollection.AddSingleton(_fileSystemManager);
ServiceCollection.AddSingleton();
- ServiceCollection.AddSingleton(_networkManager);
+ ServiceCollection.AddSingleton(NetManager);
ServiceCollection.AddSingleton();
@@ -1116,7 +1116,7 @@ namespace Emby.Server.Implementations
}
public IEnumerable GetWakeOnLanInfo()
- => _networkManager.GetMacAddresses()
+ => NetManager.GetMacAddresses()
.Select(i => new WakeOnLanInfo(i))
.ToList();
@@ -1138,7 +1138,47 @@ namespace Emby.Server.Implementations
public bool ListenWithHttps => Certificate != null && ServerConfigurationManager.Configuration.EnableHttps;
///
- public string GetSmartApiUrl(object source)
+ public string GetSmartApiUrl(IPAddress ipAddress, int? port = null)
+ {
+ // Published server ends with a /
+ if (_startupOptions.PublishedServerUrl != null)
+ {
+ // Published server ends with a '/', so we need to remove it.
+ return _startupOptions.PublishedServerUrl.ToString().Trim('/');
+ }
+
+ string smart = NetManager.GetBindInterface(ipAddress, out port);
+ // If the smartAPI doesn't start with http then treat it as a host or ip.
+ if (smart.StartsWith("http", StringComparison.OrdinalIgnoreCase))
+ {
+ return smart.Trim('/');
+ }
+
+ return GetLocalApiUrl(smart.Trim('/'), null, port);
+ }
+
+ ///
+ public string GetSmartApiUrl(HttpRequest request, int? port = null)
+ {
+ // Published server ends with a /
+ if (_startupOptions.PublishedServerUrl != null)
+ {
+ // Published server ends with a '/', so we need to remove it.
+ return _startupOptions.PublishedServerUrl.ToString().Trim('/');
+ }
+
+ string smart = NetManager.GetBindInterface(request, out port);
+ // If the smartAPI doesn't start with http then treat it as a host or ip.
+ if (smart.StartsWith("http", StringComparison.OrdinalIgnoreCase))
+ {
+ return smart.Trim('/');
+ }
+
+ return GetLocalApiUrl(smart.Trim('/'), request.Scheme, port);
+ }
+
+ ///
+ public string GetSmartApiUrl(string hostname, int? port = null)
{
// Published server ends with a /
if (_startupOptions.PublishedServerUrl != null)
@@ -1147,7 +1187,7 @@ namespace Emby.Server.Implementations
return _startupOptions.PublishedServerUrl.ToString().Trim('/');
}
- string smart = _networkManager.GetBindInterface(source, out int? port);
+ string smart = NetManager.GetBindInterface(hostname, out port);
// If the smartAPI doesn't start with http then treat it as a host or ip.
if (smart.StartsWith("http", StringComparison.OrdinalIgnoreCase))
@@ -1155,7 +1195,7 @@ namespace Emby.Server.Implementations
return smart.Trim('/');
}
- return GetLocalApiUrl(smart.Trim('/'), source is HttpRequest request ? request.Scheme : null, port);
+ return GetLocalApiUrl(smart.Trim('/'), null, port);
}
///
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
index 1cf129ad2d..27937d2f84 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
@@ -10,7 +10,7 @@ using System.Net.Http;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
-using Jellyfin.Networking.Manager;
+
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
index 02ee302d0d..efb6d162ac 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
@@ -7,7 +7,7 @@ using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
-using Jellyfin.Networking.Manager;
+
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
index f297ecd5df..531a785a07 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
@@ -8,7 +8,7 @@ using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
-using Jellyfin.Networking.Manager;
+
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
diff --git a/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs b/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs
index 08746b346b..bd76b93bf4 100644
--- a/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs
+++ b/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs
@@ -1,7 +1,7 @@
using System.Security.Claims;
using Jellyfin.Api.Helpers;
using Jellyfin.Data.Enums;
-using Jellyfin.Networking.Manager;
+
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Library;
diff --git a/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs b/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs
index 69e6a8fb2d..dfa366796f 100644
--- a/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs
+++ b/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs
@@ -1,5 +1,5 @@
using System.Threading.Tasks;
-using Jellyfin.Networking.Manager;
+
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Library;
using Microsoft.AspNetCore.Authorization;
diff --git a/Jellyfin.Api/Auth/DownloadPolicy/DownloadHandler.cs b/Jellyfin.Api/Auth/DownloadPolicy/DownloadHandler.cs
index d1297119cd..3183d13186 100644
--- a/Jellyfin.Api/Auth/DownloadPolicy/DownloadHandler.cs
+++ b/Jellyfin.Api/Auth/DownloadPolicy/DownloadHandler.cs
@@ -1,5 +1,5 @@
using System.Threading.Tasks;
-using Jellyfin.Networking.Manager;
+
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Library;
using Microsoft.AspNetCore.Authorization;
diff --git a/Jellyfin.Api/Auth/FirstTimeOrIgnoreParentalControlSetupPolicy/FirstTimeOrIgnoreParentalControlSetupHandler.cs b/Jellyfin.Api/Auth/FirstTimeOrIgnoreParentalControlSetupPolicy/FirstTimeOrIgnoreParentalControlSetupHandler.cs
index 53b5d47787..1cee962e9f 100644
--- a/Jellyfin.Api/Auth/FirstTimeOrIgnoreParentalControlSetupPolicy/FirstTimeOrIgnoreParentalControlSetupHandler.cs
+++ b/Jellyfin.Api/Auth/FirstTimeOrIgnoreParentalControlSetupPolicy/FirstTimeOrIgnoreParentalControlSetupHandler.cs
@@ -1,5 +1,5 @@
using System.Threading.Tasks;
-using Jellyfin.Networking.Manager;
+
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Library;
diff --git a/Jellyfin.Api/Auth/FirstTimeSetupOrDefaultPolicy/FirstTimeSetupOrDefaultHandler.cs b/Jellyfin.Api/Auth/FirstTimeSetupOrDefaultPolicy/FirstTimeSetupOrDefaultHandler.cs
index abdf2858d0..214198e001 100644
--- a/Jellyfin.Api/Auth/FirstTimeSetupOrDefaultPolicy/FirstTimeSetupOrDefaultHandler.cs
+++ b/Jellyfin.Api/Auth/FirstTimeSetupOrDefaultPolicy/FirstTimeSetupOrDefaultHandler.cs
@@ -1,5 +1,5 @@
using System.Threading.Tasks;
-using Jellyfin.Networking.Manager;
+
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Library;
diff --git a/Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandler.cs b/Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandler.cs
index ada8a0d4ec..9867ea4ca6 100644
--- a/Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandler.cs
+++ b/Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandler.cs
@@ -1,6 +1,6 @@
using System.Threading.Tasks;
using Jellyfin.Api.Constants;
-using Jellyfin.Networking.Manager;
+
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Library;
diff --git a/Jellyfin.Api/Auth/IgnoreParentalControlPolicy/IgnoreParentalControlHandler.cs b/Jellyfin.Api/Auth/IgnoreParentalControlPolicy/IgnoreParentalControlHandler.cs
index 475e3cdac4..affd955515 100644
--- a/Jellyfin.Api/Auth/IgnoreParentalControlPolicy/IgnoreParentalControlHandler.cs
+++ b/Jellyfin.Api/Auth/IgnoreParentalControlPolicy/IgnoreParentalControlHandler.cs
@@ -1,5 +1,5 @@
using System.Threading.Tasks;
-using Jellyfin.Networking.Manager;
+
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Library;
using Microsoft.AspNetCore.Authorization;
diff --git a/Jellyfin.Api/Auth/LocalAccessOrRequiresElevationPolicy/LocalAccessOrRequiresElevationHandler.cs b/Jellyfin.Api/Auth/LocalAccessOrRequiresElevationPolicy/LocalAccessOrRequiresElevationHandler.cs
index d022c9067f..fab464b503 100644
--- a/Jellyfin.Api/Auth/LocalAccessOrRequiresElevationPolicy/LocalAccessOrRequiresElevationHandler.cs
+++ b/Jellyfin.Api/Auth/LocalAccessOrRequiresElevationPolicy/LocalAccessOrRequiresElevationHandler.cs
@@ -1,6 +1,6 @@
using System.Threading.Tasks;
using Jellyfin.Api.Constants;
-using Jellyfin.Networking.Manager;
+
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Library;
using Microsoft.AspNetCore.Authorization;
diff --git a/Jellyfin.Api/Auth/LocalAccessPolicy/LocalAccessHandler.cs b/Jellyfin.Api/Auth/LocalAccessPolicy/LocalAccessHandler.cs
index 418d63de68..801ee2f4c6 100644
--- a/Jellyfin.Api/Auth/LocalAccessPolicy/LocalAccessHandler.cs
+++ b/Jellyfin.Api/Auth/LocalAccessPolicy/LocalAccessHandler.cs
@@ -1,5 +1,5 @@
using System.Threading.Tasks;
-using Jellyfin.Networking.Manager;
+
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Library;
using Microsoft.AspNetCore.Authorization;
diff --git a/Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationHandler.cs b/Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationHandler.cs
index a1cddbca3e..7adf72c3d3 100644
--- a/Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationHandler.cs
+++ b/Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationHandler.cs
@@ -1,6 +1,6 @@
using System.Threading.Tasks;
using Jellyfin.Api.Constants;
-using Jellyfin.Networking.Manager;
+
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Library;
using Microsoft.AspNetCore.Authorization;
diff --git a/Jellyfin.Api/Controllers/SystemController.cs b/Jellyfin.Api/Controllers/SystemController.cs
index 6876b47b44..b87e275d0f 100644
--- a/Jellyfin.Api/Controllers/SystemController.cs
+++ b/Jellyfin.Api/Controllers/SystemController.cs
@@ -8,7 +8,7 @@ using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Api.Attributes;
using Jellyfin.Api.Constants;
-using Jellyfin.Networking.Manager;
+
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs
index 152e650bc0..c60497e304 100644
--- a/Jellyfin.Api/Controllers/UserController.cs
+++ b/Jellyfin.Api/Controllers/UserController.cs
@@ -7,7 +7,7 @@ using Jellyfin.Api.Constants;
using Jellyfin.Api.Helpers;
using Jellyfin.Api.Models.UserDtos;
using Jellyfin.Data.Enums;
-using Jellyfin.Networking.Manager;
+
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Authentication;
diff --git a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs
index 3be8734b94..f81fb88fb1 100644
--- a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs
+++ b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs
@@ -8,7 +8,7 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Api.Models.StreamingDtos;
-using Jellyfin.Networking.Manager;
+
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
diff --git a/Jellyfin.Api/Helpers/MediaInfoHelper.cs b/Jellyfin.Api/Helpers/MediaInfoHelper.cs
index d63e3ab118..9e4def774a 100644
--- a/Jellyfin.Api/Helpers/MediaInfoHelper.cs
+++ b/Jellyfin.Api/Helpers/MediaInfoHelper.cs
@@ -6,7 +6,7 @@ using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
-using Jellyfin.Networking.Manager;
+
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs
index 36a0a94a09..760938c40f 100644
--- a/Jellyfin.Networking/Manager/NetworkManager.cs
+++ b/Jellyfin.Networking/Manager/NetworkManager.cs
@@ -7,6 +7,7 @@ using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Net;
using MediaBrowser.Model.Configuration;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
@@ -19,8 +20,6 @@ namespace Jellyfin.Networking.Manager
///
public class NetworkManager : INetworkManager, IDisposable
{
- private static NetworkManager? _instance;
-
///
/// Contains the description of the interface along with its index.
///
@@ -48,7 +47,7 @@ namespace Jellyfin.Networking.Manager
///
/// Holds the bind address overrides.
///
- private readonly Dictionary _overrideUrls;
+ private readonly Dictionary _publishedServerUrls;
///
/// Used to stop "event-racing conditions".
@@ -105,7 +104,7 @@ namespace Jellyfin.Networking.Manager
_interfaceAddresses = new NetCollection(unique: false);
_macAddresses = new List();
_interfaceNames = new SortedList();
- _overrideUrls = new Dictionary();
+ _publishedServerUrls = new Dictionary();
UpdateSettings((ServerConfiguration)_configurationManager.CommonConfiguration);
if (!IsIP6Enabled && !IsIP4Enabled)
@@ -117,8 +116,6 @@ namespace Jellyfin.Networking.Manager
NetworkChange.NetworkAvailabilityChanged += OnNetworkAvailabilityChanged;
_configurationManager.ConfigurationUpdated += ConfigurationUpdated;
-
- Instance = this;
}
#pragma warning restore CS8618 // Non-nullable field is uninitialized.
@@ -127,19 +124,6 @@ namespace Jellyfin.Networking.Manager
///
public event EventHandler? NetworkChanged;
- ///
- /// Gets the singleton of this object.
- ///
- public static NetworkManager Instance
- {
- get => GetInstance();
-
- internal set
- {
- _instance = value;
- }
- }
-
///
/// Gets the unique network location signature, which is updated on every network change.
///
@@ -176,7 +160,7 @@ namespace Jellyfin.Networking.Manager
///
/// Gets the Published server override list.
///
- public Dictionary PublishedServerOverrides => _overrideUrls;
+ public Dictionary PublishedServerUrls => _publishedServerUrls;
///
public void Dispose()
@@ -198,9 +182,12 @@ namespace Jellyfin.Networking.Manager
///
public bool IsGatewayInterface(object? addressObj)
{
- var address = (addressObj is IPAddress addressIP) ?
- addressIP : (addressObj is IPObject addressIPObj) ?
- addressIPObj.Address : IPAddress.None;
+ var address = addressObj switch
+ {
+ IPAddress addressIp => addressIp,
+ IPObject addressIpObj => addressIpObj.Address,
+ _ => IPAddress.None
+ };
lock (_intLock)
{
@@ -320,243 +307,127 @@ namespace Jellyfin.Networking.Manager
}
///
- public string GetBindInterface(object? source, out int? port)
+ public string GetBindInterface(string source, out int? port)
{
- bool chromeCast = false;
- port = null;
- // Parse the source object in an attempt to discover where the request originated.
- IPObject sourceAddr;
- if (source is HttpRequest sourceReq)
+ if (!string.IsNullOrEmpty(source))
{
- port = sourceReq.Host.Port;
- if (IPHost.TryParse(sourceReq.Host.Host, out IPHost host))
+ if (string.Equals(source, "chromecast", StringComparison.OrdinalIgnoreCase))
{
- sourceAddr = host;
- }
- else
- {
- // Assume it's external, as we cannot resolve the host.
- sourceAddr = IPHost.None;
- }
- }
- else if (source is string sourceStr && !string.IsNullOrEmpty(sourceStr))
- {
- if (string.Equals(sourceStr, "chromecast", StringComparison.OrdinalIgnoreCase))
- {
- chromeCast = true;
// Just assign a variable so has source = true;
- sourceAddr = IPNetAddress.IP4Loopback;
+ return GetBindInterface(IPNetAddress.IP4Loopback, out port);
}
- if (IPHost.TryParse(sourceStr, out IPHost host))
+ if (IPHost.TryParse(source, out IPHost host))
{
- sourceAddr = host;
- }
- else
- {
- // Assume it's external, as we cannot resolve the host.
- sourceAddr = IPHost.None;
+ return GetBindInterface(host, out port);
}
}
- else if (source is IPAddress sourceIP)
+
+ return GetBindInterface(IPHost.None, out port);
+ }
+
+ ///
+ public string GetBindInterface(IPAddress source, out int? port)
+ {
+ return GetBindInterface(new IPNetAddress(source), out port);
+ }
+
+ ///
+ public string GetBindInterface(HttpRequest source, out int? port)
+ {
+ string result;
+
+ if (source != null && IPHost.TryParse(source.Host.Host, out IPHost host))
{
- sourceAddr = new IPNetAddress(sourceIP);
+ result = GetBindInterface(host, out port);
+ port ??= source.Host.Port;
}
else
{
- // If we have no idea, then assume it came from an external address.
- sourceAddr = IPHost.None;
+ result = GetBindInterface(IPNetAddress.None, out port);
+ port ??= source?.Host.Port;
}
+ return result;
+ }
+
+ ///
+ public string GetBindInterface(IPObject source, out int? port)
+ {
+ port = null;
+ bool isChromeCast = source == IPNetAddress.IP4Loopback;
// Do we have a source?
- bool haveSource = !sourceAddr.Address.Equals(IPAddress.None);
+ bool haveSource = !source.Address.Equals(IPAddress.None);
+ bool isExternal = false;
if (haveSource)
{
- if (!IsIP6Enabled && sourceAddr.AddressFamily == AddressFamily.InterNetworkV6)
+ if (!IsIP6Enabled && source.AddressFamily == AddressFamily.InterNetworkV6)
{
_logger.LogWarning("IPv6 is disabled in JellyFin, but enabled in the OS. This may affect how the interface is selected.");
}
- if (!IsIP4Enabled && sourceAddr.AddressFamily == AddressFamily.InterNetwork)
+ if (!IsIP4Enabled && source.AddressFamily == AddressFamily.InterNetwork)
{
_logger.LogWarning("IPv4 is disabled in JellyFin, but enabled in the OS. This may affect how the interface is selected.");
}
- }
- bool isExternal = haveSource && !IsInLocalNetwork(sourceAddr);
+ isExternal = !IsInLocalNetwork(source);
- string bindPreference = string.Empty;
- if (haveSource)
- {
- // Check for user override.
- foreach (var addr in _overrideUrls)
+ if (MatchesPublishedServerUrl(source, isExternal, isChromeCast, out string result, out port))
{
- // Remaining. Match anything.
- if (addr.Key.Equals(IPAddress.Broadcast))
- {
- bindPreference = addr.Value;
- break;
- }
- else if ((addr.Key.Equals(IPAddress.Any) || addr.Key.Equals(IPAddress.IPv6Any)) && (isExternal || chromeCast))
- {
- // External.
- bindPreference = addr.Value;
- break;
- }
- else if (addr.Key.Contains(sourceAddr))
- {
- // Match ip address.
- bindPreference = addr.Value;
- break;
- }
+ _logger.LogInformation("{0}: Using BindAddress {1}:{2}", source, result, port);
+ return result;
}
}
_logger.LogDebug("GetBindInterface: Souce: {0}, External: {1}:", haveSource, isExternal);
- if (!string.IsNullOrEmpty(bindPreference))
- {
- // Has it got a port defined?
- var parts = bindPreference.Split(':');
- if (parts.Length > 1)
- {
- if (int.TryParse(parts[1], out int p))
- {
- bindPreference = parts[0];
- port = p;
- }
- }
-
- _logger.LogInformation("{0}: Using BindAddress {1}:{2}", sourceAddr, bindPreference, port);
- return bindPreference;
- }
-
- string ipresult;
-
// No preference given, so move on to bind addresses.
lock (_intLock)
{
- var nc = _bindAddresses.Exclude(_bindExclusions).Where(p => !p.IsLoopback());
-
- int count = nc.Count();
- if (count == 1 && (_bindAddresses[0].Equals(IPAddress.Any) || _bindAddresses.Equals(IPAddress.IPv6Any)))
- {
- // Ignore IPAny addresses.
- count = 0;
- }
-
- if (count != 0)
+ if (MatchesBindInterface(source, isExternal, out string result))
{
- // Check to see if any of the bind interfaces are in the same subnet.
-
- IEnumerable bindResult;
- IPAddress? defaultGateway = null;
-
- if (isExternal)
- {
- // Find all external bind addresses. Store the default gateway, but check to see if there is a better match first.
- bindResult = nc.Where(p => !IsInLocalNetwork(p)).OrderBy(p => p.Tag);
- defaultGateway = bindResult.FirstOrDefault()?.Address;
- bindResult = bindResult.Where(p => p.Contains(sourceAddr)).OrderBy(p => p.Tag);
- }
- else
- {
- // Look for the best internal address.
- bindResult = nc.Where(p => IsInLocalNetwork(p) && p.Contains(sourceAddr)).OrderBy(p => p.Tag);
- }
-
- if (bindResult.Any())
- {
- ipresult = FormatIP6String(bindResult.First().Address);
- _logger.LogDebug("{0}: GetBindInterface: Has source, found a match bind interface subnets. {1}", sourceAddr, ipresult);
- return ipresult;
- }
-
- if (isExternal && defaultGateway != null)
- {
- ipresult = FormatIP6String(defaultGateway);
- _logger.LogDebug("{0}: GetBindInterface: Using first user defined external interface. {1}", sourceAddr, ipresult);
- return ipresult;
- }
-
- ipresult = FormatIP6String(nc.First().Address);
- _logger.LogDebug("{0}: GetBindInterface: Selected first user defined interface. {1}", sourceAddr, ipresult);
-
- if (isExternal)
- {
- // TODO: remove this after testing.
- _logger.LogWarning("{0}: External request received, however, only an internal interface bind found.", sourceAddr);
- }
-
- return ipresult;
+ return result;
}
- if (isExternal)
+ if (isExternal && MatchesExternalInterface(source, out result))
{
- // Get the first WAN interface address that isn't a loopback.
- var extResult = _interfaceAddresses
- .Exclude(_bindExclusions)
- .Where(p => !IsInLocalNetwork(p))
- .OrderBy(p => p.Tag);
-
- if (extResult.Any())
- {
- // Does the request originate in one of the interface subnets?
- // (For systems with multiple internal network cards, and multiple subnets)
- foreach (var intf in extResult)
- {
- if (!IsInLocalNetwork(intf) && intf.Contains(sourceAddr))
- {
- ipresult = FormatIP6String(intf.Address);
- _logger.LogDebug("{0}: GetBindInterface: Selected best external on interface on range. {1}", sourceAddr, ipresult);
- return ipresult;
- }
- }
-
- ipresult = FormatIP6String(extResult.First().Address);
- _logger.LogDebug("{0}: GetBindInterface: Selected first external interface. {0}", sourceAddr, ipresult);
- return ipresult;
- }
-
- // Have to return something, so return an internal address
-
- // TODO: remove this after testing.
- _logger.LogWarning("{0}: External request received, however, no WAN interface found.", sourceAddr);
+ return result;
}
// Get the first LAN interface address that isn't a loopback.
- var result = _interfaceAddresses
+ var interfaces = new NetCollection(_interfaceAddresses
.Exclude(_bindExclusions)
.Where(p => IsInLocalNetwork(p))
- .OrderBy(p => p.Tag);
+ .OrderBy(p => p.Tag));
- if (result.Any())
+ if (interfaces.Count > 0)
{
if (haveSource)
{
// Does the request originate in one of the interface subnets?
// (For systems with multiple internal network cards, and multiple subnets)
- foreach (var intf in result)
+ foreach (var intf in interfaces)
{
- if (intf.Contains(sourceAddr))
+ if (intf.Contains(source))
{
- ipresult = FormatIP6String(intf.Address);
- _logger.LogDebug("{0}: GetBindInterface: Has source, matched best internal interface on range. {1}", sourceAddr, ipresult);
- return ipresult;
+ result = FormatIP6String(intf.Address);
+ _logger.LogDebug("{0}: GetBindInterface: Has source, matched best internal interface on range. {1}", source, result);
+ return result;
}
}
}
- ipresult = FormatIP6String(result.First().Address);
- _logger.LogDebug("{0}: GetBindInterface: Matched first internal interface. {1}", sourceAddr, ipresult);
- return ipresult;
+ result = FormatIP6String(interfaces.First().Address);
+ _logger.LogDebug("{0}: GetBindInterface: Matched first internal interface. {1}", source, result);
+ return result;
}
// There isn't any others, so we'll use the loopback.
- ipresult = IsIP6Enabled ? "::" : "127.0.0.1";
- _logger.LogWarning("{0}: GetBindInterface: Loopback return.", sourceAddr, ipresult);
- return ipresult;
+ result = IsIP6Enabled ? "::" : "127.0.0.1";
+ _logger.LogWarning("{0}: GetBindInterface: Loopback return.", source, result);
+ return result;
}
}
@@ -771,16 +642,6 @@ namespace Jellyfin.Networking.Manager
}
}
- private static NetworkManager GetInstance()
- {
- if (_instance == null)
- {
- throw new ApplicationException("NetworkManager is not initialised.");
- }
-
- return _instance;
- }
-
private void ConfigurationUpdated(object? sender, EventArgs args)
{
UpdateSettings((ServerConfiguration)_configurationManager.CommonConfiguration);
@@ -944,7 +805,7 @@ namespace Jellyfin.Networking.Manager
{
lock (_intLock)
{
- _overrideUrls.Clear();
+ _publishedServerUrls.Clear();
}
return;
@@ -952,7 +813,7 @@ namespace Jellyfin.Networking.Manager
lock (_intLock)
{
- _overrideUrls.Clear();
+ _publishedServerUrls.Clear();
foreach (var entry in overrides)
{
@@ -966,15 +827,15 @@ namespace Jellyfin.Networking.Manager
var replacement = parts[1].Trim();
if (string.Equals(parts[0], "remaining", StringComparison.OrdinalIgnoreCase))
{
- _overrideUrls[new IPNetAddress(IPAddress.Broadcast)] = replacement;
+ _publishedServerUrls[new IPNetAddress(IPAddress.Broadcast)] = replacement;
}
else if (string.Equals(parts[0], "external", StringComparison.OrdinalIgnoreCase))
{
- _overrideUrls[new IPNetAddress(IPAddress.Any)] = replacement;
+ _publishedServerUrls[new IPNetAddress(IPAddress.Any)] = replacement;
}
else if (TryParseInterface(parts[0], out IPNetAddress address))
{
- _overrideUrls[address] = replacement;
+ _publishedServerUrls[address] = replacement;
}
else
{
@@ -1199,5 +1060,179 @@ namespace Jellyfin.Networking.Manager
}
}
}
+
+ ///
+ /// Attempts to match the source against a user defined bind interface.
+ ///
+ /// IP source address to use.
+ /// True if the source is in the external subnet.
+ /// True if the request is for a chromecast device.
+ /// The published server url that matches the source address.
+ /// The resultant port, if one exists.
+ /// True if a match is found.
+ private bool MatchesPublishedServerUrl(IPObject source, bool isExternal, bool isChromeCast, out string bindPreference, out int? port)
+ {
+ bindPreference = string.Empty;
+ port = null;
+
+ // Check for user override.
+ foreach (var addr in _publishedServerUrls)
+ {
+ // Remaining. Match anything.
+ if (addr.Key.Equals(IPAddress.Broadcast))
+ {
+ bindPreference = addr.Value;
+ break;
+ }
+ else if ((addr.Key.Equals(IPAddress.Any) || addr.Key.Equals(IPAddress.IPv6Any)) && (isExternal || isChromeCast))
+ {
+ // External.
+ bindPreference = addr.Value;
+ break;
+ }
+ else if (addr.Key.Contains(source))
+ {
+ // Match ip address.
+ bindPreference = addr.Value;
+ break;
+ }
+ }
+
+ if (!string.IsNullOrEmpty(bindPreference))
+ {
+ // Has it got a port defined?
+ var parts = bindPreference.Split(':');
+ if (parts.Length > 1)
+ {
+ if (int.TryParse(parts[1], out int p))
+ {
+ bindPreference = parts[0];
+ port = p;
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Attempts to match the source against a user defined bind interface.
+ ///
+ /// IP source address to use.
+ /// True if the source is in the external subnet.
+ /// The result, if a match is found.
+ /// True if a match is found.
+ private bool MatchesBindInterface(IPObject source, bool isExternal, out string result)
+ {
+ result = string.Empty;
+ var nc = new NetCollection(_bindAddresses.Exclude(_bindExclusions).Where(p => !p.IsLoopback()));
+
+ int count = nc.Count;
+ if (count == 1 && (_bindAddresses[0].Equals(IPAddress.Any) || _bindAddresses[0].Equals(IPAddress.IPv6Any)))
+ {
+ // Ignore IPAny addresses.
+ count = 0;
+ }
+
+ if (count != 0)
+ {
+ // Check to see if any of the bind interfaces are in the same subnet.
+
+ NetCollection bindResult;
+ IPAddress? defaultGateway = null;
+ IPAddress? bindAddress;
+
+ if (isExternal)
+ {
+ // Find all external bind addresses. Store the default gateway, but check to see if there is a better match first.
+ bindResult = new NetCollection(nc
+ .Where(p => !IsInLocalNetwork(p))
+ .OrderBy(p => p.Tag));
+ defaultGateway = bindResult.FirstOrDefault()?.Address;
+ bindAddress = bindResult
+ .Where(p => p.Contains(source))
+ .OrderBy(p => p.Tag)
+ .FirstOrDefault()?.Address;
+ }
+ else
+ {
+ // Look for the best internal address.
+ bindAddress = nc
+ .Where(p => IsInLocalNetwork(p) && (p.Contains(source) || p.Equals(IPAddress.None)))
+ .OrderBy(p => p.Tag)
+ .FirstOrDefault()?.Address;
+ }
+
+ if (bindAddress != null)
+ {
+ result = FormatIP6String(bindAddress);
+ _logger.LogDebug("{0}: GetBindInterface: Has source, found a match bind interface subnets. {1}", source, result);
+ return true;
+ }
+
+ if (isExternal && defaultGateway != null)
+ {
+ result = FormatIP6String(defaultGateway);
+ _logger.LogDebug("{0}: GetBindInterface: Using first user defined external interface. {1}", source, result);
+ return true;
+ }
+
+ result = FormatIP6String(nc.First().Address);
+ _logger.LogDebug("{0}: GetBindInterface: Selected first user defined interface. {1}", source, result);
+
+ if (isExternal)
+ {
+ // TODO: remove this after testing.
+ _logger.LogWarning("{0}: External request received, however, only an internal interface bind found.", source);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Attempts to match the source against am external interface.
+ ///
+ /// IP source address to use.
+ /// The result, if a match is found.
+ /// True if a match is found.
+ private bool MatchesExternalInterface(IPObject source, out string result)
+ {
+ result = string.Empty;
+ // Get the first WAN interface address that isn't a loopback.
+ var extResult = new NetCollection(_interfaceAddresses
+ .Exclude(_bindExclusions)
+ .Where(p => !IsInLocalNetwork(p))
+ .OrderBy(p => p.Tag));
+
+ if (extResult.Count > 0)
+ {
+ // Does the request originate in one of the interface subnets?
+ // (For systems with multiple internal network cards, and multiple subnets)
+ foreach (var intf in extResult)
+ {
+ if (!IsInLocalNetwork(intf) && intf.Contains(source))
+ {
+ result = FormatIP6String(intf.Address);
+ _logger.LogDebug("{0}: GetBindInterface: Selected best external on interface on range. {1}", source, result);
+ return true;
+ }
+ }
+
+ result = FormatIP6String(extResult.First().Address);
+ _logger.LogDebug("{0}: GetBindInterface: Selected first external interface. {0}", source, result);
+ return true;
+ }
+
+ // Have to return something, so return an internal address
+
+ // TODO: remove this after testing.
+ _logger.LogWarning("{0}: External request received, however, no WAN interface found.", source);
+ return false;
+ }
}
}
diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs
index 8c19665cc1..f73c917a0b 100644
--- a/Jellyfin.Server.Implementations/Users/UserManager.cs
+++ b/Jellyfin.Server.Implementations/Users/UserManager.cs
@@ -12,10 +12,11 @@ using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using Jellyfin.Data.Events;
using Jellyfin.Data.Events.Users;
-using Jellyfin.Networking.Manager;
+
using MediaBrowser.Common;
using MediaBrowser.Common.Cryptography;
using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Events;
diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs
index 566ba0ad85..e2d7305af4 100644
--- a/Jellyfin.Server/CoreAppHost.cs
+++ b/Jellyfin.Server/CoreAppHost.cs
@@ -5,7 +5,7 @@ using System.Reflection;
using Emby.Drawing;
using Emby.Server.Implementations;
using Jellyfin.Drawing.Skia;
-using Jellyfin.Networking.Manager;
+
using Jellyfin.Server.Implementations;
using Jellyfin.Server.Implementations.Activity;
using Jellyfin.Server.Implementations.Events;
diff --git a/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs b/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs
index ff82fe6cc6..e927a147aa 100644
--- a/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs
+++ b/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs
@@ -1,6 +1,6 @@
using System.Linq;
using System.Threading.Tasks;
-using Jellyfin.Networking.Manager;
+
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
diff --git a/Jellyfin.Server/Middleware/LanFilteringMiddleware.cs b/Jellyfin.Server/Middleware/LanFilteringMiddleware.cs
index 87c82bf583..fa34a167b3 100644
--- a/Jellyfin.Server/Middleware/LanFilteringMiddleware.cs
+++ b/Jellyfin.Server/Middleware/LanFilteringMiddleware.cs
@@ -2,7 +2,7 @@ using System;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
-using Jellyfin.Networking.Manager;
+
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs
index 8549e39dbc..939f61656f 100644
--- a/Jellyfin.Server/Program.cs
+++ b/Jellyfin.Server/Program.cs
@@ -13,7 +13,7 @@ using CommandLine;
using Emby.Server.Implementations;
using Emby.Server.Implementations.IO;
using Jellyfin.Api.Controllers;
-using Jellyfin.Networking.Manager;
+
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Extensions;
using Microsoft.AspNetCore.Hosting;
@@ -272,7 +272,7 @@ namespace Jellyfin.Server
return builder
.UseKestrel((builderContext, options) =>
{
- NetCollection addresses = NetworkManager.Instance.GetAllBindInterfaces();
+ NetCollection addresses = appHost.NetManager.GetAllBindInterfaces();
bool flagged = false;
foreach (IPObject netAdd in addresses)
diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj
index 70dcc2397c..0fda47788b 100644
--- a/MediaBrowser.Common/MediaBrowser.Common.csproj
+++ b/MediaBrowser.Common/MediaBrowser.Common.csproj
@@ -20,8 +20,9 @@
-
+
+
diff --git a/Jellyfin.Networking/Manager/INetworkManager.cs b/MediaBrowser.Common/Net/INetworkManager.cs
similarity index 79%
rename from Jellyfin.Networking/Manager/INetworkManager.cs
rename to MediaBrowser.Common/Net/INetworkManager.cs
index ba571750b9..32c017aee6 100644
--- a/Jellyfin.Networking/Manager/INetworkManager.cs
+++ b/MediaBrowser.Common/Net/INetworkManager.cs
@@ -1,11 +1,13 @@
+#nullable enable
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.NetworkInformation;
using MediaBrowser.Model.Configuration;
+using Microsoft.AspNetCore.Http;
using NetworkCollection;
-namespace Jellyfin.Networking.Manager
+namespace MediaBrowser.Common.Net
{
///
/// Interface for the NetworkManager class.
@@ -18,9 +20,9 @@ namespace Jellyfin.Networking.Manager
event EventHandler NetworkChanged;
///
- /// Gets the Published server override list.
+ /// Gets the published server urls list.
///
- Dictionary PublishedServerOverrides { get; }
+ Dictionary PublishedServerUrls { get; }
///
/// Gets a value indicating whether is all IPv6 interfaces are trusted as internal.
@@ -28,7 +30,7 @@ namespace Jellyfin.Networking.Manager
public bool TrustAllIP6Interfaces { get; }
///
- /// Gets returns the remote address filter.
+ /// Gets the remote address filter.
///
NetCollection RemoteAddressFilter { get; }
@@ -75,7 +77,37 @@ namespace Jellyfin.Networking.Manager
/// Source of the request.
/// Optional port returned, if it's part of an override.
/// IP Address to use, or loopback address if all else fails.
- string GetBindInterface(object? source, out int? port);
+ string GetBindInterface(IPObject source, out int? port);
+
+ ///
+ /// Retrieves the bind address to use in system url's. (Server Discovery, PlayTo, LiveTV, SystemInfo)
+ /// If no bind addresses are specified, an internal interface address is selected.
+ /// (See above).
+ ///
+ /// Source of the request.
+ /// Optional port returned, if it's part of an override.
+ /// IP Address to use, or loopback address if all else fails.
+ string GetBindInterface(HttpRequest source, out int? port);
+
+ ///
+ /// Retrieves the bind address to use in system url's. (Server Discovery, PlayTo, LiveTV, SystemInfo)
+ /// If no bind addresses are specified, an internal interface address is selected.
+ /// (See above).
+ ///
+ /// IP address of the request.
+ /// Optional port returned, if it's part of an override.
+ /// IP Address to use, or loopback address if all else fails.
+ string GetBindInterface(IPAddress source, out int? port);
+
+ ///
+ /// Retrieves the bind address to use in system url's. (Server Discovery, PlayTo, LiveTV, SystemInfo)
+ /// If no bind addresses are specified, an internal interface address is selected.
+ /// (See above).
+ ///
+ /// Source of the request.
+ /// Optional port returned, if it's part of an override.
+ /// IP Address to use, or loopback address if all else fails.
+ string GetBindInterface(string source, out int? port);
///
/// Checks to see if the ip address is specifically excluded in LocalNetworkAddresses.
diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs
index f147e6a86c..7bc1006fc0 100644
--- a/MediaBrowser.Controller/IServerApplicationHost.cs
+++ b/MediaBrowser.Controller/IServerApplicationHost.cs
@@ -63,12 +63,28 @@ namespace MediaBrowser.Controller
PublicSystemInfo GetPublicSystemInfo(IPAddress address);
///
- /// Gets a local (LAN) URL that can be used to access the API. The hostname used is the first valid configured
- /// HTTPS will be preferred when available.
+ /// Gets a URL specific for the request.
///
- /// The source of the request.
- /// The server URL.
- string GetSmartApiUrl(object source);
+ /// The instance.
+ /// Optional port number.
+ /// An accessible URL.
+ string GetSmartApiUrl(HttpRequest request, int? port = null);
+
+ ///
+ /// Gets a URL specific for the request.
+ ///
+ /// The remote of the connection.
+ /// Optional port number.
+ /// An accessible URL.
+ string GetSmartApiUrl(IPAddress remoteAddr, int? port = null);
+
+ ///
+ /// Gets a URL specific for the request.
+ ///
+ /// The hostname used in the connection.
+ /// Optional port number.
+ /// An accessible URL.
+ string GetSmartApiUrl(string hostname, int? port = null);
///
/// Gets a localhost URL that can be used to access the API using the loop-back IP address.
diff --git a/RSSDP/SsdpCommunicationsServer.cs b/RSSDP/SsdpCommunicationsServer.cs
index e28a2f48d4..932f6bbb40 100644
--- a/RSSDP/SsdpCommunicationsServer.cs
+++ b/RSSDP/SsdpCommunicationsServer.cs
@@ -7,7 +7,7 @@ using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using Jellyfin.Networking.Manager;
+
using MediaBrowser.Common.Net;
using MediaBrowser.Model.Net;
using Microsoft.Extensions.Logging;
diff --git a/RSSDP/SsdpDevicePublisher.cs b/RSSDP/SsdpDevicePublisher.cs
index 43fccdad42..84456f7dcd 100644
--- a/RSSDP/SsdpDevicePublisher.cs
+++ b/RSSDP/SsdpDevicePublisher.cs
@@ -5,7 +5,7 @@ using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
-using Jellyfin.Networking.Manager;
+
using MediaBrowser.Common.Net;
using NetworkCollection;
diff --git a/tests/Jellyfin.Api.Tests/Auth/LocalAccessPolicy/LocalAccessHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/LocalAccessPolicy/LocalAccessHandlerTests.cs
index 2c7f0c4f9b..05dd8f325e 100644
--- a/tests/Jellyfin.Api.Tests/Auth/LocalAccessPolicy/LocalAccessHandlerTests.cs
+++ b/tests/Jellyfin.Api.Tests/Auth/LocalAccessPolicy/LocalAccessHandlerTests.cs
@@ -4,7 +4,7 @@ using AutoFixture;
using AutoFixture.AutoMoq;
using Jellyfin.Api.Auth.LocalAccessPolicy;
using Jellyfin.Api.Constants;
-using Jellyfin.Networking.Manager;
+
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Library;