diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index c28b4ae1c1..bd2bb54bdc 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1201,11 +1201,25 @@ namespace Emby.Server.Implementations } /// - public string GetExternalFacingHttpApiUrl() + public string GetInterfaceHttpApiUrl() { - // Passing an external address cause GetBindInterface to return the externally facing interface on a multi-adapter system. - // LocalNetworkSubnets and LocalNetworkAddresses are used in conjunction with the ip address to help select the best interface. - return GetLocalApiUrl(NetManager.GetBindInterface("0.0.0.0", out var _), Uri.UriSchemeHttp, HttpPort); + // Published server ends with a / + if (!string.IsNullOrEmpty(PublishedServerUrl)) + { + // Published server ends with a '/', so we need to remove it. + return PublishedServerUrl.Trim('/'); + } + + var bind = NetManager.GetInternalBindAddresses().FirstOrDefault() ?? new IPNetAddress(IPAddress.None); + + string smart = NetManager.GetBindInterface(bind, out var 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); } /// diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index c9d9cc49af..d61f74dd23 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -1031,7 +1031,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { var stream = new MediaSourceInfo { - EncoderPath = _appHost.GetLoopbackHttpApiUrl() + "/LiveTv/LiveRecordings/" + info.Id + "/stream", + EncoderPath = _appHost.GetInterfaceHttpApiUrl() + "/LiveTv/LiveRecordings/" + info.Id + "/stream", EncoderProtocol = MediaProtocol.Http, Path = info.Path, Protocol = MediaProtocol.File, diff --git a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs index 804794caae..b5f0b2118c 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs @@ -102,7 +102,7 @@ namespace Emby.Server.Implementations.LiveTv // Dummy this up so that direct play checks can still run if (string.IsNullOrEmpty(source.Path) && source.Protocol == MediaProtocol.Http) { - source.Path = _appHost.GetExternalFacingHttpApiUrl(); + source.Path = _appHost.GetInterfaceHttpApiUrl(); } } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs index b16ccc561a..d6ea320ead 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs @@ -148,7 +148,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun // OpenedMediaSource.Path = tempFile; // OpenedMediaSource.ReadAtNativeFramerate = true; - MediaSource.Path = _appHost.GetLoopbackHttpApiUrl() + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts"; + MediaSource.Path = _appHost.GetInterfaceHttpApiUrl() + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts"; MediaSource.Protocol = MediaProtocol.Http; // OpenedMediaSource.SupportsDirectPlay = false; // OpenedMediaSource.SupportsDirectStream = true; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs index eeb2426f4f..b9a567e40a 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs @@ -97,7 +97,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts // OpenedMediaSource.Path = tempFile; // OpenedMediaSource.ReadAtNativeFramerate = true; - MediaSource.Path = _appHost.GetLoopbackHttpApiUrl() + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts"; + MediaSource.Path = _appHost.GetInterfaceHttpApiUrl() + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts"; MediaSource.Protocol = MediaProtocol.Http; // OpenedMediaSource.Path = TempFilePath; diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs index fd8455dc85..52e2f7964c 100644 --- a/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/Jellyfin.Networking/Manager/NetworkManager.cs @@ -455,10 +455,10 @@ namespace Jellyfin.Networking.Manager } // No bind address, so return all internal interfaces. - return CreateCollection(_internalInterfaces.Where(p => !p.IsLoopback())); + return CreateCollection(_internalInterfaces); } - return new Collection(_bindAddresses); + return new Collection(_bindAddresses.Where(IsInLocalNetwork).ToArray()); } /// @@ -481,7 +481,7 @@ namespace Jellyfin.Networking.Manager } // As private addresses can be redefined by Configuration.LocalNetworkAddresses - return _lanSubnets.ContainsAddress(address) && !_excludedSubnets.ContainsAddress(address); + return address.IsLoopback() || (_lanSubnets.ContainsAddress(address) && !_excludedSubnets.ContainsAddress(address)); } /// @@ -647,16 +647,6 @@ namespace Jellyfin.Networking.Manager _interfaceAddresses.AddItem(address, false); _interfaceNames[parts[2]] = Math.Abs(index); } - - if (IsIP4Enabled) - { - _interfaceAddresses.AddItem(IPNetAddress.IP4Loopback); - } - - if (IsIP6Enabled) - { - _interfaceAddresses.AddItem(IPNetAddress.IP6Loopback); - } } InitialiseLAN(config); @@ -990,7 +980,6 @@ namespace Jellyfin.Networking.Manager // Read and parse bind addresses and exclusions, removing ones that don't exist. _bindAddresses = CreateIPCollection(lanAddresses).ThatAreContainedInNetworks(_interfaceAddresses); _bindExclusions = CreateIPCollection(lanAddresses, true).ThatAreContainedInNetworks(_interfaceAddresses); - _logger.LogInformation("Using bind addresses: {0}", _bindAddresses.AsString()); _logger.LogInformation("Using bind exclusions: {0}", _bindExclusions.AsString()); } @@ -1038,17 +1027,14 @@ namespace Jellyfin.Networking.Manager // Subnets are the same as the calculated internal interface. _lanSubnets = new Collection(); - // We must listen on loopback for LiveTV to function regardless of the settings. if (IsIP6Enabled) { - _lanSubnets.AddItem(IPNetAddress.IP6Loopback); _lanSubnets.AddItem(IPNetAddress.Parse("fc00::/7")); // ULA _lanSubnets.AddItem(IPNetAddress.Parse("fe80::/10")); // Site local } if (IsIP4Enabled) { - _lanSubnets.AddItem(IPNetAddress.IP4Loopback); _lanSubnets.AddItem(IPNetAddress.Parse("10.0.0.0/8")); _lanSubnets.AddItem(IPNetAddress.Parse("172.16.0.0/12")); _lanSubnets.AddItem(IPNetAddress.Parse("192.168.0.0/16")); @@ -1056,17 +1042,6 @@ namespace Jellyfin.Networking.Manager } else { - // We must listen on loopback for LiveTV to function regardless of the settings. - if (IsIP6Enabled) - { - _lanSubnets.AddItem(IPNetAddress.IP6Loopback); - } - - if (IsIP4Enabled) - { - _lanSubnets.AddItem(IPNetAddress.IP4Loopback); - } - // Internal interfaces must be private, not excluded and part of the LocalNetworkSubnet. _internalInterfaces = CreateCollection(_interfaceAddresses.Where(i => IsInLocalNetwork(i))); } diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index 6a65a8e47a..a284dcecab 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -88,11 +88,10 @@ namespace MediaBrowser.Controller 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. - /// over HTTP (not HTTPS). + /// Gets an URL that can be used to access the API over HTTP (not HTTPS). /// /// The API URL. - string GetLoopbackHttpApiUrl(); + string GetInterfaceHttpApiUrl(); /// /// Gets a local (LAN) URL that can be used to access the API. diff --git a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs index f203f9b429..53c17bfb13 100644 --- a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs +++ b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs @@ -35,9 +35,9 @@ namespace Jellyfin.Networking.Tests // eth16 only [InlineData("192.168.1.208/24,-16,eth16|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[192.168.1.208/24]")] // All interfaces excluded. (including loopbacks) - [InlineData("192.168.1.208/24,-16,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[127.0.0.1/8,::1/128]")] + [InlineData("192.168.1.208/24,-16,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[]")] // vEthernet1 and vEthernet212 should be excluded. - [InlineData("192.168.1.200/24,-20,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24;200.200.200.200/24", "[200.200.200.200/24,127.0.0.1/8,::1/128]")] + [InlineData("192.168.1.200/24,-20,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24;200.200.200.200/24", "[200.200.200.200/24]")] // Overlapping interface, [InlineData("192.168.1.110/24,-20,br0|192.168.1.10/24,-16,br0|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[192.168.1.110/24,192.168.1.10/24]")] public void IgnoreVirtualInterfaces(string interfaces, string lan, string value)