From 2b742be38e08afea1a1a417c258ac9469dd48a3b Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 26 Oct 2023 10:03:04 -0400 Subject: Use IHostedService for DLNA --- Emby.Server.Implementations/ApplicationHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index c9bf7f085..a1f1cd649 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -866,7 +866,7 @@ namespace Emby.Server.Implementations yield return typeof(MediaBrowser.MediaEncoding.Encoder.MediaEncoder).Assembly; // Dlna - yield return typeof(DlnaEntryPoint).Assembly; + yield return typeof(DlnaHost).Assembly; // Local metadata yield return typeof(BoxSetXmlSaver).Assembly; -- cgit v1.2.3 From 1e1e1560a47439c02931e67736bcd87b606cf35c Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 9 Nov 2023 15:09:51 -0500 Subject: Add IServerApplicationHost parameter to IPluginServiceRegistrator --- Emby.Server.Implementations/Plugins/PluginManager.cs | 11 ++++++----- .../Plugins/IPluginServiceRegistrator.cs | 19 ------------------- .../Plugins/IPluginServiceRegistrator.cs | 19 +++++++++++++++++++ 3 files changed, 25 insertions(+), 24 deletions(-) delete mode 100644 MediaBrowser.Common/Plugins/IPluginServiceRegistrator.cs create mode 100644 MediaBrowser.Controller/Plugins/IPluginServiceRegistrator.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs index 20793ee39..db82a2900 100644 --- a/Emby.Server.Implementations/Plugins/PluginManager.cs +++ b/Emby.Server.Implementations/Plugins/PluginManager.cs @@ -12,10 +12,11 @@ using System.Threading.Tasks; using Emby.Server.Implementations.Library; using Jellyfin.Extensions.Json; using Jellyfin.Extensions.Json.Converters; -using MediaBrowser.Common; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Common.Plugins; +using MediaBrowser.Controller; +using MediaBrowser.Controller.Plugins; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.IO; using MediaBrowser.Model.Plugins; @@ -37,7 +38,7 @@ namespace Emby.Server.Implementations.Plugins private readonly List _assemblyLoadContexts; private readonly JsonSerializerOptions _jsonOptions; private readonly ILogger _logger; - private readonly IApplicationHost _appHost; + private readonly IServerApplicationHost _appHost; private readonly ServerConfiguration _config; private readonly List _plugins; private readonly Version _minimumVersion; @@ -48,13 +49,13 @@ namespace Emby.Server.Implementations.Plugins /// Initializes a new instance of the class. /// /// The . - /// The . + /// The . /// The . /// The plugin path. /// The application version. public PluginManager( ILogger logger, - IApplicationHost appHost, + IServerApplicationHost appHost, ServerConfiguration config, string pluginsPath, Version appVersion) @@ -222,7 +223,7 @@ namespace Emby.Server.Implementations.Plugins try { var instance = (IPluginServiceRegistrator?)Activator.CreateInstance(pluginServiceRegistrator); - instance?.RegisterServices(serviceCollection); + instance?.RegisterServices(serviceCollection, _appHost); } #pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) diff --git a/MediaBrowser.Common/Plugins/IPluginServiceRegistrator.cs b/MediaBrowser.Common/Plugins/IPluginServiceRegistrator.cs deleted file mode 100644 index 3afe874c5..000000000 --- a/MediaBrowser.Common/Plugins/IPluginServiceRegistrator.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace MediaBrowser.Common.Plugins -{ - using Microsoft.Extensions.DependencyInjection; - - /// - /// Defines the . - /// - public interface IPluginServiceRegistrator - { - /// - /// Registers the plugin's services with the service collection. - /// - /// - /// This interface is only used for service registration and requires a parameterless constructor. - /// - /// The service collection. - void RegisterServices(IServiceCollection serviceCollection); - } -} diff --git a/MediaBrowser.Controller/Plugins/IPluginServiceRegistrator.cs b/MediaBrowser.Controller/Plugins/IPluginServiceRegistrator.cs new file mode 100644 index 000000000..8b62f3808 --- /dev/null +++ b/MediaBrowser.Controller/Plugins/IPluginServiceRegistrator.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace MediaBrowser.Controller.Plugins; + +/// +/// Defines the . +/// +/// +/// This interface is only used for service registration and requires a parameterless constructor. +/// +public interface IPluginServiceRegistrator +{ + /// + /// Registers the plugin's services with the service collection. + /// + /// The service collection. + /// The server application host. + void RegisterServices(IServiceCollection serviceCollection, IServerApplicationHost applicationHost); +} -- cgit v1.2.3 From 9595636d6105bbe77292d87c7016c21f9df8d4c7 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 10 Nov 2023 10:59:45 -0500 Subject: Move network utilities to MediaBrowser.Common --- Emby.Dlna/Main/DlnaHost.cs | 3 +- .../EntryPoints/UdpServerEntryPoint.cs | 3 +- .../Extensions/NetworkExtensions.cs | 346 --------------------- Jellyfin.Networking/Manager/NetworkManager.cs | 33 +- .../Extensions/ApiServiceCollectionExtensions.cs | 5 +- MediaBrowser.Common/Net/NetworkUtils.cs | 345 ++++++++++++++++++++ .../NetworkExtensionsTests.cs | 10 +- .../Jellyfin.Networking.Tests/NetworkParseTests.cs | 16 +- 8 files changed, 378 insertions(+), 383 deletions(-) delete mode 100644 Jellyfin.Networking/Extensions/NetworkExtensions.cs create mode 100644 MediaBrowser.Common/Net/NetworkUtils.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Dlna/Main/DlnaHost.cs b/Emby.Dlna/Main/DlnaHost.cs index 3896b74a1..26bf6d5e2 100644 --- a/Emby.Dlna/Main/DlnaHost.cs +++ b/Emby.Dlna/Main/DlnaHost.cs @@ -10,7 +10,6 @@ using System.Threading.Tasks; using Emby.Dlna.PlayTo; using Emby.Dlna.Ssdp; using Jellyfin.Networking.Configuration; -using Jellyfin.Networking.Extensions; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; @@ -280,7 +279,7 @@ public sealed class DlnaHost : IHostedService, IDisposable CacheLifetime = TimeSpan.FromSeconds(1800), // How long SSDP clients can cache this info. Location = uri.Uri, // Must point to the URL that serves your devices UPnP description document. Address = intf.Address, - PrefixLength = NetworkExtensions.MaskToCidr(intf.Subnet.Prefix), + PrefixLength = NetworkUtils.MaskToCidr(intf.Subnet.Prefix), FriendlyName = "Jellyfin", Manufacturer = "Jellyfin", ModelName = "Jellyfin Server", diff --git a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs index 7e4994f1a..662bd88a9 100644 --- a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs +++ b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs @@ -7,7 +7,6 @@ using System.Threading; using System.Threading.Tasks; using Emby.Server.Implementations.Udp; using Jellyfin.Networking.Configuration; -using Jellyfin.Networking.Extensions; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; using MediaBrowser.Controller; @@ -92,7 +91,7 @@ namespace Emby.Server.Implementations.EntryPoints var validInterfaces = _networkManager.GetInternalBindAddresses().Where(i => i.AddressFamily == AddressFamily.InterNetwork); foreach (var intf in validInterfaces) { - var broadcastAddress = NetworkExtensions.GetBroadcastAddress(intf.Subnet); + var broadcastAddress = NetworkUtils.GetBroadcastAddress(intf.Subnet); _logger.LogDebug("Binding UDP server to {Address} on port {PortNumber}", broadcastAddress, PortNumber); server = new UdpServer(_logger, _appHost, _config, broadcastAddress, PortNumber); diff --git a/Jellyfin.Networking/Extensions/NetworkExtensions.cs b/Jellyfin.Networking/Extensions/NetworkExtensions.cs deleted file mode 100644 index eb0cc81f0..000000000 --- a/Jellyfin.Networking/Extensions/NetworkExtensions.cs +++ /dev/null @@ -1,346 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Net; -using System.Net.Sockets; -using System.Text.RegularExpressions; -using Jellyfin.Extensions; -using MediaBrowser.Common.Net; -using Microsoft.AspNetCore.HttpOverrides; - -namespace Jellyfin.Networking.Extensions; - -/// -/// Defines the . -/// -public static partial class NetworkExtensions -{ - // Use regular expression as CheckHostName isn't RFC5892 compliant. - // Modified from gSkinner's expression at https://stackoverflow.com/questions/11809631/fully-qualified-domain-name-validation - [GeneratedRegex(@"(?im)^(?!:\/\/)(?=.{1,255}$)((.{1,63}\.){0,127}(?![0-9]*$)[a-z0-9-]+\.?)(:(\d){1,5}){0,1}$", RegexOptions.IgnoreCase, "en-US")] - private static partial Regex FqdnGeneratedRegex(); - - /// - /// Returns true if the IPAddress contains an IP6 Local link address. - /// - /// IPAddress object to check. - /// True if it is a local link address. - /// - /// See https://stackoverflow.com/questions/6459928/explain-the-instance-properties-of-system-net-ipaddress - /// it appears that the IPAddress.IsIPv6LinkLocal is out of date. - /// - public static bool IsIPv6LinkLocal(IPAddress address) - { - ArgumentNullException.ThrowIfNull(address); - - if (address.IsIPv4MappedToIPv6) - { - address = address.MapToIPv4(); - } - - if (address.AddressFamily != AddressFamily.InterNetworkV6) - { - return false; - } - - // GetAddressBytes - Span octet = stackalloc byte[16]; - address.TryWriteBytes(octet, out _); - uint word = (uint)(octet[0] << 8) + octet[1]; - - return word >= 0xfe80 && word <= 0xfebf; // fe80::/10 :Local link. - } - - /// - /// Convert a subnet mask in CIDR notation to a dotted decimal string value. IPv4 only. - /// - /// Subnet mask in CIDR notation. - /// IPv4 or IPv6 family. - /// String value of the subnet mask in dotted decimal notation. - public static IPAddress CidrToMask(byte cidr, AddressFamily family) - { - uint addr = 0xFFFFFFFF << ((family == AddressFamily.InterNetwork ? NetworkConstants.MinimumIPv4PrefixSize : NetworkConstants.MinimumIPv6PrefixSize) - cidr); - addr = ((addr & 0xff000000) >> 24) - | ((addr & 0x00ff0000) >> 8) - | ((addr & 0x0000ff00) << 8) - | ((addr & 0x000000ff) << 24); - return new IPAddress(addr); - } - - /// - /// Convert a subnet mask in CIDR notation to a dotted decimal string value. IPv4 only. - /// - /// Subnet mask in CIDR notation. - /// IPv4 or IPv6 family. - /// String value of the subnet mask in dotted decimal notation. - public static IPAddress CidrToMask(int cidr, AddressFamily family) - { - uint addr = 0xFFFFFFFF << ((family == AddressFamily.InterNetwork ? NetworkConstants.MinimumIPv4PrefixSize : NetworkConstants.MinimumIPv6PrefixSize) - cidr); - addr = ((addr & 0xff000000) >> 24) - | ((addr & 0x00ff0000) >> 8) - | ((addr & 0x0000ff00) << 8) - | ((addr & 0x000000ff) << 24); - return new IPAddress(addr); - } - - /// - /// Convert a subnet mask to a CIDR. IPv4 only. - /// https://stackoverflow.com/questions/36954345/get-cidr-from-netmask. - /// - /// Subnet mask. - /// Byte CIDR representing the mask. - public static byte MaskToCidr(IPAddress mask) - { - ArgumentNullException.ThrowIfNull(mask); - - byte cidrnet = 0; - if (mask.Equals(IPAddress.Any)) - { - return cidrnet; - } - - // GetAddressBytes - Span bytes = stackalloc byte[mask.AddressFamily == AddressFamily.InterNetwork ? NetworkConstants.IPv4MaskBytes : NetworkConstants.IPv6MaskBytes]; - if (!mask.TryWriteBytes(bytes, out var bytesWritten)) - { - Console.WriteLine("Unable to write address bytes, only ${bytesWritten} bytes written."); - } - - var zeroed = false; - for (var i = 0; i < bytes.Length; i++) - { - for (int v = bytes[i]; (v & 0xFF) != 0; v <<= 1) - { - if (zeroed) - { - // Invalid netmask. - return (byte)~cidrnet; - } - - if ((v & 0x80) == 0) - { - zeroed = true; - } - else - { - cidrnet++; - } - } - } - - return cidrnet; - } - - /// - /// Converts an IPAddress into a string. - /// IPv6 addresses are returned in [ ], with their scope removed. - /// - /// Address to convert. - /// URI safe conversion of the address. - public static string FormatIPString(IPAddress? address) - { - if (address is null) - { - return string.Empty; - } - - var str = address.ToString(); - if (address.AddressFamily == AddressFamily.InterNetworkV6) - { - int i = str.IndexOf('%', StringComparison.Ordinal); - if (i != -1) - { - str = str.Substring(0, i); - } - - return $"[{str}]"; - } - - return str; - } - - /// - /// Try parsing an array of strings into objects, respecting exclusions. - /// Elements without a subnet mask will be represented as with a single IP. - /// - /// Input string array to be parsed. - /// Collection of . - /// Boolean signaling if negated or not negated values should be parsed. - /// True if parsing was successful. - public static bool TryParseToSubnets(string[] values, [NotNullWhen(true)] out IReadOnlyList? result, bool negated = false) - { - if (values is null || values.Length == 0) - { - result = null; - return false; - } - - var tmpResult = new List(); - for (int a = 0; a < values.Length; a++) - { - if (TryParseToSubnet(values[a], out var innerResult, negated)) - { - tmpResult.Add(innerResult); - } - } - - result = tmpResult; - return tmpResult.Count > 0; - } - - /// - /// Try parsing a string into an , respecting exclusions. - /// Inputs without a subnet mask will be represented as with a single IP. - /// - /// Input string to be parsed. - /// An . - /// Boolean signaling if negated or not negated values should be parsed. - /// True if parsing was successful. - public static bool TryParseToSubnet(ReadOnlySpan value, [NotNullWhen(true)] out IPNetwork? result, bool negated = false) - { - var splitString = value.Trim().Split('/'); - if (splitString.MoveNext()) - { - var ipBlock = splitString.Current; - var address = IPAddress.None; - if (negated && ipBlock.StartsWith("!") && IPAddress.TryParse(ipBlock[1..], out var tmpAddress)) - { - address = tmpAddress; - } - else if (!negated && IPAddress.TryParse(ipBlock, out tmpAddress)) - { - address = tmpAddress; - } - - if (address != IPAddress.None) - { - if (splitString.MoveNext()) - { - var subnetBlock = splitString.Current; - if (int.TryParse(subnetBlock, out var netmask)) - { - result = new IPNetwork(address, netmask); - return true; - } - else if (IPAddress.TryParse(subnetBlock, out var netmaskAddress)) - { - result = new IPNetwork(address, NetworkExtensions.MaskToCidr(netmaskAddress)); - return true; - } - } - else if (address.AddressFamily == AddressFamily.InterNetwork) - { - result = address.Equals(IPAddress.Any) ? NetworkConstants.IPv4Any : new IPNetwork(address, NetworkConstants.MinimumIPv4PrefixSize); - return true; - } - else if (address.AddressFamily == AddressFamily.InterNetworkV6) - { - result = address.Equals(IPAddress.IPv6Any) ? NetworkConstants.IPv6Any : new IPNetwork(address, NetworkConstants.MinimumIPv6PrefixSize); - return true; - } - } - } - - result = null; - return false; - } - - /// - /// Attempts to parse a host span. - /// - /// Host name to parse. - /// Object representing the span, if it has successfully been parsed. - /// true if IPv4 is enabled. - /// true if IPv6 is enabled. - /// true if the parsing is successful, false if not. - public static bool TryParseHost(ReadOnlySpan host, [NotNullWhen(true)] out IPAddress[]? addresses, bool isIPv4Enabled = true, bool isIPv6Enabled = false) - { - host = host.Trim(); - if (host.IsEmpty) - { - addresses = null; - return false; - } - - // See if it's an IPv6 with port address e.g. [::1] or [::1]:120. - if (host[0] == '[') - { - int i = host.IndexOf(']'); - if (i != -1) - { - return TryParseHost(host[1..(i - 1)], out addresses); - } - - addresses = Array.Empty(); - return false; - } - - var hosts = new List(); - foreach (var splitSpan in host.Split(':')) - { - hosts.Add(splitSpan.ToString()); - } - - if (hosts.Count <= 2) - { - var firstPart = hosts[0]; - - // Is hostname or hostname:port - if (FqdnGeneratedRegex().IsMatch(firstPart)) - { - try - { - // .NET automatically filters only supported returned addresses based on OS support. - addresses = Dns.GetHostAddresses(firstPart); - return true; - } - catch (SocketException) - { - // Ignore socket errors, as the result value will just be an empty array. - } - } - - // Is an IPv4 or IPv4:port - if (IPAddress.TryParse(firstPart.AsSpan().LeftPart('/'), out var address)) - { - if (((address.AddressFamily == AddressFamily.InterNetwork) && (!isIPv4Enabled && isIPv6Enabled)) - || ((address.AddressFamily == AddressFamily.InterNetworkV6) && (isIPv4Enabled && !isIPv6Enabled))) - { - addresses = Array.Empty(); - return false; - } - - addresses = new[] { address }; - - // Host name is an IPv4 address, so fake resolve. - return true; - } - } - else if (hosts.Count > 0 && hosts.Count <= 9) // 8 octets + port - { - if (IPAddress.TryParse(host.LeftPart('/'), out var address)) - { - addresses = new[] { address }; - return true; - } - } - - addresses = Array.Empty(); - return false; - } - - /// - /// Gets the broadcast address for a . - /// - /// The . - /// The broadcast address. - public static IPAddress GetBroadcastAddress(IPNetwork network) - { - var addressBytes = network.Prefix.GetAddressBytes(); - uint ipAddress = BitConverter.ToUInt32(addressBytes, 0); - uint ipMaskV4 = BitConverter.ToUInt32(CidrToMask(network.PrefixLength, AddressFamily.InterNetwork).GetAddressBytes(), 0); - uint broadCastIPAddress = ipAddress | ~ipMaskV4; - - return new IPAddress(BitConverter.GetBytes(broadCastIPAddress)); - } -} diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs index 1656c3688..d631fa51f 100644 --- a/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/Jellyfin.Networking/Manager/NetworkManager.cs @@ -8,7 +8,6 @@ using System.Net.NetworkInformation; using System.Net.Sockets; using System.Threading; using Jellyfin.Networking.Configuration; -using Jellyfin.Networking.Extensions; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; using MediaBrowser.Model.Net; @@ -318,7 +317,7 @@ namespace Jellyfin.Networking.Manager var subnets = config.LocalNetworkSubnets; // If no LAN addresses are specified, all private subnets and Loopback are deemed to be the LAN - if (!NetworkExtensions.TryParseToSubnets(subnets, out var lanSubnets, false) || lanSubnets.Count == 0) + if (!NetworkUtils.TryParseToSubnets(subnets, out var lanSubnets, false) || lanSubnets.Count == 0) { _logger.LogDebug("Using LAN interface addresses as user provided no LAN details."); @@ -345,7 +344,7 @@ namespace Jellyfin.Networking.Manager _lanSubnets = lanSubnets; } - _excludedSubnets = NetworkExtensions.TryParseToSubnets(subnets, out var excludedSubnets, true) + _excludedSubnets = NetworkUtils.TryParseToSubnets(subnets, out var excludedSubnets, true) ? excludedSubnets : new List(); } @@ -363,7 +362,7 @@ namespace Jellyfin.Networking.Manager var localNetworkAddresses = config.LocalNetworkAddresses; if (localNetworkAddresses.Length > 0 && !string.IsNullOrWhiteSpace(localNetworkAddresses[0])) { - var bindAddresses = localNetworkAddresses.Select(p => NetworkExtensions.TryParseToSubnet(p, out var network) + var bindAddresses = localNetworkAddresses.Select(p => NetworkUtils.TryParseToSubnet(p, out var network) ? network.Prefix : (interfaces.Where(x => x.Name.Equals(p, StringComparison.OrdinalIgnoreCase)) .Select(x => x.Address) @@ -430,7 +429,7 @@ namespace Jellyfin.Networking.Manager // Parse all IPs with netmask to a subnet var remoteAddressFilter = new List(); var remoteFilteredSubnets = remoteIPFilter.Where(x => x.Contains('/', StringComparison.OrdinalIgnoreCase)).ToArray(); - if (NetworkExtensions.TryParseToSubnets(remoteFilteredSubnets, out var remoteAddressFilterResult, false)) + if (NetworkUtils.TryParseToSubnets(remoteFilteredSubnets, out var remoteAddressFilterResult, false)) { remoteAddressFilter = remoteAddressFilterResult.ToList(); } @@ -541,7 +540,7 @@ namespace Jellyfin.Networking.Manager false)); } } - else if (NetworkExtensions.TryParseToSubnet(identifier, out var result) && result is not null) + else if (NetworkUtils.TryParseToSubnet(identifier, out var result) && result is not null) { var data = new IPData(result.Prefix, result); publishedServerUrls.Add( @@ -607,7 +606,7 @@ namespace Jellyfin.Networking.Manager foreach (var details in interfaceList) { var parts = details.Split(','); - if (NetworkExtensions.TryParseToSubnet(parts[0], out var subnet)) + if (NetworkUtils.TryParseToSubnet(parts[0], out var subnet)) { var address = subnet.Prefix; var index = int.Parse(parts[1], CultureInfo.InvariantCulture); @@ -771,7 +770,7 @@ namespace Jellyfin.Networking.Manager /// public string GetBindAddress(string source, out int? port) { - if (!NetworkExtensions.TryParseHost(source, out var addresses, IsIPv4Enabled, IsIPv6Enabled)) + if (!NetworkUtils.TryParseHost(source, out var addresses, IsIPv4Enabled, IsIPv6Enabled)) { addresses = Array.Empty(); } @@ -846,7 +845,7 @@ namespace Jellyfin.Networking.Manager // If no source address is given, use the preferred (first) interface if (source is null) { - result = NetworkExtensions.FormatIPString(availableInterfaces.First().Address); + result = NetworkUtils.FormatIPString(availableInterfaces.First().Address); _logger.LogDebug("{Source}: Using first internal interface as bind address: {Result}", source, result); return result; } @@ -857,14 +856,14 @@ namespace Jellyfin.Networking.Manager { if (intf.Subnet.Contains(source)) { - result = NetworkExtensions.FormatIPString(intf.Address); + result = NetworkUtils.FormatIPString(intf.Address); _logger.LogDebug("{Source}: Found interface with matching subnet, using it as bind address: {Result}", source, result); return result; } } // Fallback to first available interface - result = NetworkExtensions.FormatIPString(availableInterfaces[0].Address); + result = NetworkUtils.FormatIPString(availableInterfaces[0].Address); _logger.LogDebug("{Source}: No matching interfaces found, using preferred interface as bind address: {Result}", source, result); return result; } @@ -881,12 +880,12 @@ namespace Jellyfin.Networking.Manager /// public bool IsInLocalNetwork(string address) { - if (NetworkExtensions.TryParseToSubnet(address, out var subnet)) + if (NetworkUtils.TryParseToSubnet(address, out var subnet)) { return IPAddress.IsLoopback(subnet.Prefix) || (_lanSubnets.Any(x => x.Contains(subnet.Prefix)) && !_excludedSubnets.Any(x => x.Contains(subnet.Prefix))); } - if (NetworkExtensions.TryParseHost(address, out var addresses, IsIPv4Enabled, IsIPv6Enabled)) + if (NetworkUtils.TryParseHost(address, out var addresses, IsIPv4Enabled, IsIPv6Enabled)) { foreach (var ept in addresses) { @@ -1044,7 +1043,7 @@ namespace Jellyfin.Networking.Manager .Select(x => x.Address) .First(); - result = NetworkExtensions.FormatIPString(bindAddress); + result = NetworkUtils.FormatIPString(bindAddress); _logger.LogDebug("{Source}: External request received, matching external bind address found: {Result}", source, result); return true; } @@ -1063,7 +1062,7 @@ namespace Jellyfin.Networking.Manager if (bindAddress is not null) { - result = NetworkExtensions.FormatIPString(bindAddress); + result = NetworkUtils.FormatIPString(bindAddress); _logger.LogDebug("{Source}: Internal request received, matching internal bind address found: {Result}", source, result); return true; } @@ -1097,14 +1096,14 @@ namespace Jellyfin.Networking.Manager { if (intf.Subnet.Contains(source)) { - result = NetworkExtensions.FormatIPString(intf.Address); + result = NetworkUtils.FormatIPString(intf.Address); _logger.LogDebug("{Source}: Found external interface with matching subnet, using it as bind address: {Result}", source, result); return true; } } // Fallback to first external interface. - result = NetworkExtensions.FormatIPString(extResult[0].Address); + result = NetworkUtils.FormatIPString(extResult[0].Address); _logger.LogDebug("{Source}: Using first external interface as bind address: {Result}", source, result); return true; } diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs index 28916e916..a84227495 100644 --- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs +++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs @@ -21,7 +21,6 @@ using Jellyfin.Api.ModelBinders; using Jellyfin.Data.Enums; using Jellyfin.Extensions.Json; using Jellyfin.Networking.Configuration; -using Jellyfin.Networking.Extensions; using Jellyfin.Server.Configuration; using Jellyfin.Server.Filters; using MediaBrowser.Common.Net; @@ -277,14 +276,14 @@ namespace Jellyfin.Server.Extensions { AddIPAddress(config, options, addr, addr.AddressFamily == AddressFamily.InterNetwork ? NetworkConstants.MinimumIPv4PrefixSize : NetworkConstants.MinimumIPv6PrefixSize); } - else if (NetworkExtensions.TryParseToSubnet(allowedProxies[i], out var subnet)) + else if (NetworkUtils.TryParseToSubnet(allowedProxies[i], out var subnet)) { if (subnet is not null) { AddIPAddress(config, options, subnet.Prefix, subnet.PrefixLength); } } - else if (NetworkExtensions.TryParseHost(allowedProxies[i], out var addresses, config.EnableIPv4, config.EnableIPv6)) + else if (NetworkUtils.TryParseHost(allowedProxies[i], out var addresses, config.EnableIPv4, config.EnableIPv6)) { foreach (var address in addresses) { diff --git a/MediaBrowser.Common/Net/NetworkUtils.cs b/MediaBrowser.Common/Net/NetworkUtils.cs new file mode 100644 index 000000000..f3bff7fa9 --- /dev/null +++ b/MediaBrowser.Common/Net/NetworkUtils.cs @@ -0,0 +1,345 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net; +using System.Net.Sockets; +using System.Text.RegularExpressions; +using Jellyfin.Extensions; +using Microsoft.AspNetCore.HttpOverrides; + +namespace MediaBrowser.Common.Net; + +/// +/// Defines the . +/// +public static partial class NetworkUtils +{ + // Use regular expression as CheckHostName isn't RFC5892 compliant. + // Modified from gSkinner's expression at https://stackoverflow.com/questions/11809631/fully-qualified-domain-name-validation + [GeneratedRegex(@"(?im)^(?!:\/\/)(?=.{1,255}$)((.{1,63}\.){0,127}(?![0-9]*$)[a-z0-9-]+\.?)(:(\d){1,5}){0,1}$", RegexOptions.IgnoreCase, "en-US")] + private static partial Regex FqdnGeneratedRegex(); + + /// + /// Returns true if the IPAddress contains an IP6 Local link address. + /// + /// IPAddress object to check. + /// True if it is a local link address. + /// + /// See https://stackoverflow.com/questions/6459928/explain-the-instance-properties-of-system-net-ipaddress + /// it appears that the IPAddress.IsIPv6LinkLocal is out of date. + /// + public static bool IsIPv6LinkLocal(IPAddress address) + { + ArgumentNullException.ThrowIfNull(address); + + if (address.IsIPv4MappedToIPv6) + { + address = address.MapToIPv4(); + } + + if (address.AddressFamily != AddressFamily.InterNetworkV6) + { + return false; + } + + // GetAddressBytes + Span octet = stackalloc byte[16]; + address.TryWriteBytes(octet, out _); + uint word = (uint)(octet[0] << 8) + octet[1]; + + return word >= 0xfe80 && word <= 0xfebf; // fe80::/10 :Local link. + } + + /// + /// Convert a subnet mask in CIDR notation to a dotted decimal string value. IPv4 only. + /// + /// Subnet mask in CIDR notation. + /// IPv4 or IPv6 family. + /// String value of the subnet mask in dotted decimal notation. + public static IPAddress CidrToMask(byte cidr, AddressFamily family) + { + uint addr = 0xFFFFFFFF << ((family == AddressFamily.InterNetwork ? NetworkConstants.MinimumIPv4PrefixSize : NetworkConstants.MinimumIPv6PrefixSize) - cidr); + addr = ((addr & 0xff000000) >> 24) + | ((addr & 0x00ff0000) >> 8) + | ((addr & 0x0000ff00) << 8) + | ((addr & 0x000000ff) << 24); + return new IPAddress(addr); + } + + /// + /// Convert a subnet mask in CIDR notation to a dotted decimal string value. IPv4 only. + /// + /// Subnet mask in CIDR notation. + /// IPv4 or IPv6 family. + /// String value of the subnet mask in dotted decimal notation. + public static IPAddress CidrToMask(int cidr, AddressFamily family) + { + uint addr = 0xFFFFFFFF << ((family == AddressFamily.InterNetwork ? NetworkConstants.MinimumIPv4PrefixSize : NetworkConstants.MinimumIPv6PrefixSize) - cidr); + addr = ((addr & 0xff000000) >> 24) + | ((addr & 0x00ff0000) >> 8) + | ((addr & 0x0000ff00) << 8) + | ((addr & 0x000000ff) << 24); + return new IPAddress(addr); + } + + /// + /// Convert a subnet mask to a CIDR. IPv4 only. + /// https://stackoverflow.com/questions/36954345/get-cidr-from-netmask. + /// + /// Subnet mask. + /// Byte CIDR representing the mask. + public static byte MaskToCidr(IPAddress mask) + { + ArgumentNullException.ThrowIfNull(mask); + + byte cidrnet = 0; + if (mask.Equals(IPAddress.Any)) + { + return cidrnet; + } + + // GetAddressBytes + Span bytes = stackalloc byte[mask.AddressFamily == AddressFamily.InterNetwork ? NetworkConstants.IPv4MaskBytes : NetworkConstants.IPv6MaskBytes]; + if (!mask.TryWriteBytes(bytes, out var bytesWritten)) + { + Console.WriteLine("Unable to write address bytes, only ${bytesWritten} bytes written."); + } + + var zeroed = false; + for (var i = 0; i < bytes.Length; i++) + { + for (int v = bytes[i]; (v & 0xFF) != 0; v <<= 1) + { + if (zeroed) + { + // Invalid netmask. + return (byte)~cidrnet; + } + + if ((v & 0x80) == 0) + { + zeroed = true; + } + else + { + cidrnet++; + } + } + } + + return cidrnet; + } + + /// + /// Converts an IPAddress into a string. + /// IPv6 addresses are returned in [ ], with their scope removed. + /// + /// Address to convert. + /// URI safe conversion of the address. + public static string FormatIPString(IPAddress? address) + { + if (address is null) + { + return string.Empty; + } + + var str = address.ToString(); + if (address.AddressFamily == AddressFamily.InterNetworkV6) + { + int i = str.IndexOf('%', StringComparison.Ordinal); + if (i != -1) + { + str = str.Substring(0, i); + } + + return $"[{str}]"; + } + + return str; + } + + /// + /// Try parsing an array of strings into objects, respecting exclusions. + /// Elements without a subnet mask will be represented as with a single IP. + /// + /// Input string array to be parsed. + /// Collection of . + /// Boolean signaling if negated or not negated values should be parsed. + /// True if parsing was successful. + public static bool TryParseToSubnets(string[] values, [NotNullWhen(true)] out IReadOnlyList? result, bool negated = false) + { + if (values is null || values.Length == 0) + { + result = null; + return false; + } + + var tmpResult = new List(); + for (int a = 0; a < values.Length; a++) + { + if (TryParseToSubnet(values[a], out var innerResult, negated)) + { + tmpResult.Add(innerResult); + } + } + + result = tmpResult; + return tmpResult.Count > 0; + } + + /// + /// Try parsing a string into an , respecting exclusions. + /// Inputs without a subnet mask will be represented as with a single IP. + /// + /// Input string to be parsed. + /// An . + /// Boolean signaling if negated or not negated values should be parsed. + /// True if parsing was successful. + public static bool TryParseToSubnet(ReadOnlySpan value, [NotNullWhen(true)] out IPNetwork? result, bool negated = false) + { + var splitString = value.Trim().Split('/'); + if (splitString.MoveNext()) + { + var ipBlock = splitString.Current; + var address = IPAddress.None; + if (negated && ipBlock.StartsWith("!") && IPAddress.TryParse(ipBlock[1..], out var tmpAddress)) + { + address = tmpAddress; + } + else if (!negated && IPAddress.TryParse(ipBlock, out tmpAddress)) + { + address = tmpAddress; + } + + if (address != IPAddress.None) + { + if (splitString.MoveNext()) + { + var subnetBlock = splitString.Current; + if (int.TryParse(subnetBlock, out var netmask)) + { + result = new IPNetwork(address, netmask); + return true; + } + else if (IPAddress.TryParse(subnetBlock, out var netmaskAddress)) + { + result = new IPNetwork(address, NetworkUtils.MaskToCidr(netmaskAddress)); + return true; + } + } + else if (address.AddressFamily == AddressFamily.InterNetwork) + { + result = address.Equals(IPAddress.Any) ? NetworkConstants.IPv4Any : new IPNetwork(address, NetworkConstants.MinimumIPv4PrefixSize); + return true; + } + else if (address.AddressFamily == AddressFamily.InterNetworkV6) + { + result = address.Equals(IPAddress.IPv6Any) ? NetworkConstants.IPv6Any : new IPNetwork(address, NetworkConstants.MinimumIPv6PrefixSize); + return true; + } + } + } + + result = null; + return false; + } + + /// + /// Attempts to parse a host span. + /// + /// Host name to parse. + /// Object representing the span, if it has successfully been parsed. + /// true if IPv4 is enabled. + /// true if IPv6 is enabled. + /// true if the parsing is successful, false if not. + public static bool TryParseHost(ReadOnlySpan host, [NotNullWhen(true)] out IPAddress[]? addresses, bool isIPv4Enabled = true, bool isIPv6Enabled = false) + { + host = host.Trim(); + if (host.IsEmpty) + { + addresses = null; + return false; + } + + // See if it's an IPv6 with port address e.g. [::1] or [::1]:120. + if (host[0] == '[') + { + int i = host.IndexOf(']'); + if (i != -1) + { + return TryParseHost(host[1..(i - 1)], out addresses); + } + + addresses = Array.Empty(); + return false; + } + + var hosts = new List(); + foreach (var splitSpan in host.Split(':')) + { + hosts.Add(splitSpan.ToString()); + } + + if (hosts.Count <= 2) + { + var firstPart = hosts[0]; + + // Is hostname or hostname:port + if (FqdnGeneratedRegex().IsMatch(firstPart)) + { + try + { + // .NET automatically filters only supported returned addresses based on OS support. + addresses = Dns.GetHostAddresses(firstPart); + return true; + } + catch (SocketException) + { + // Ignore socket errors, as the result value will just be an empty array. + } + } + + // Is an IPv4 or IPv4:port + if (IPAddress.TryParse(firstPart.AsSpan().LeftPart('/'), out var address)) + { + if (((address.AddressFamily == AddressFamily.InterNetwork) && (!isIPv4Enabled && isIPv6Enabled)) + || ((address.AddressFamily == AddressFamily.InterNetworkV6) && (isIPv4Enabled && !isIPv6Enabled))) + { + addresses = Array.Empty(); + return false; + } + + addresses = new[] { address }; + + // Host name is an IPv4 address, so fake resolve. + return true; + } + } + else if (hosts.Count > 0 && hosts.Count <= 9) // 8 octets + port + { + if (IPAddress.TryParse(host.LeftPart('/'), out var address)) + { + addresses = new[] { address }; + return true; + } + } + + addresses = Array.Empty(); + return false; + } + + /// + /// Gets the broadcast address for a . + /// + /// The . + /// The broadcast address. + public static IPAddress GetBroadcastAddress(IPNetwork network) + { + var addressBytes = network.Prefix.GetAddressBytes(); + uint ipAddress = BitConverter.ToUInt32(addressBytes, 0); + uint ipMaskV4 = BitConverter.ToUInt32(CidrToMask(network.PrefixLength, AddressFamily.InterNetwork).GetAddressBytes(), 0); + uint broadCastIPAddress = ipAddress | ~ipMaskV4; + + return new IPAddress(BitConverter.GetBytes(broadCastIPAddress)); + } +} diff --git a/tests/Jellyfin.Networking.Tests/NetworkExtensionsTests.cs b/tests/Jellyfin.Networking.Tests/NetworkExtensionsTests.cs index 072e0a8c5..01546aa2b 100644 --- a/tests/Jellyfin.Networking.Tests/NetworkExtensionsTests.cs +++ b/tests/Jellyfin.Networking.Tests/NetworkExtensionsTests.cs @@ -1,6 +1,6 @@ using FsCheck; using FsCheck.Xunit; -using Jellyfin.Networking.Extensions; +using MediaBrowser.Common.Net; using Xunit; namespace Jellyfin.Networking.Tests @@ -26,15 +26,15 @@ namespace Jellyfin.Networking.Tests [InlineData("192.168.1.2/255.255.255.0")] [InlineData("192.168.1.2/24")] public static void TryParse_ValidHostStrings_True(string address) - => Assert.True(NetworkExtensions.TryParseHost(address, out _, true, true)); + => Assert.True(NetworkUtils.TryParseHost(address, out _, true, true)); [Property] public static Property TryParse_IPv4Address_True(IPv4Address address) - => NetworkExtensions.TryParseHost(address.Item.ToString(), out _, true, true).ToProperty(); + => NetworkUtils.TryParseHost(address.Item.ToString(), out _, true, true).ToProperty(); [Property] public static Property TryParse_IPv6Address_True(IPv6Address address) - => NetworkExtensions.TryParseHost(address.Item.ToString(), out _, true, true).ToProperty(); + => NetworkUtils.TryParseHost(address.Item.ToString(), out _, true, true).ToProperty(); /// /// All should be invalid address strings. @@ -47,6 +47,6 @@ namespace Jellyfin.Networking.Tests [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517:1231")] [InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517:1231]")] public static void TryParse_InvalidAddressString_False(string address) - => Assert.False(NetworkExtensions.TryParseHost(address, out _, true, true)); + => Assert.False(NetworkUtils.TryParseHost(address, out _, true, true)); } } diff --git a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs index 022b8a3d0..97beb6940 100644 --- a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs +++ b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs @@ -3,9 +3,9 @@ using System.Collections.Generic; using System.Linq; using System.Net; using Jellyfin.Networking.Configuration; -using Jellyfin.Networking.Extensions; using Jellyfin.Networking.Manager; using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Net; using MediaBrowser.Model.Net; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging.Abstractions; @@ -80,7 +80,7 @@ namespace Jellyfin.Networking.Tests [InlineData("[fe80::7add:12ff:febb:c67b%16]")] [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517/56")] public static void TryParseValidIPStringsTrue(string address) - => Assert.True(NetworkExtensions.TryParseToSubnet(address, out _)); + => Assert.True(NetworkUtils.TryParseToSubnet(address, out _)); /// /// Checks invalid IP address formats. @@ -93,7 +93,7 @@ namespace Jellyfin.Networking.Tests [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517:1231")] [InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517:1231]")] public static void TryParseInvalidIPStringsFalse(string address) - => Assert.False(NetworkExtensions.TryParseToSubnet(address, out _)); + => Assert.False(NetworkUtils.TryParseToSubnet(address, out _)); /// /// Checks if IPv4 address is within a defined subnet. @@ -113,7 +113,7 @@ namespace Jellyfin.Networking.Tests public void IPv4SubnetMaskMatchesValidIPAddress(string netMask, string ipAddress) { var ipa = IPAddress.Parse(ipAddress); - Assert.True(NetworkExtensions.TryParseToSubnet(netMask, out var subnet) && subnet.Contains(IPAddress.Parse(ipAddress))); + Assert.True(NetworkUtils.TryParseToSubnet(netMask, out var subnet) && subnet.Contains(IPAddress.Parse(ipAddress))); } /// @@ -133,7 +133,7 @@ namespace Jellyfin.Networking.Tests public void IPv4SubnetMaskDoesNotMatchInvalidIPAddress(string netMask, string ipAddress) { var ipa = IPAddress.Parse(ipAddress); - Assert.False(NetworkExtensions.TryParseToSubnet(netMask, out var subnet) && subnet.Contains(IPAddress.Parse(ipAddress))); + Assert.False(NetworkUtils.TryParseToSubnet(netMask, out var subnet) && subnet.Contains(IPAddress.Parse(ipAddress))); } /// @@ -149,7 +149,7 @@ namespace Jellyfin.Networking.Tests [InlineData("2001:db8:abcd:0012::0/128", "2001:0DB8:ABCD:0012:0000:0000:0000:0000")] public void IPv6SubnetMaskMatchesValidIPAddress(string netMask, string ipAddress) { - Assert.True(NetworkExtensions.TryParseToSubnet(netMask, out var subnet) && subnet.Contains(IPAddress.Parse(ipAddress))); + Assert.True(NetworkUtils.TryParseToSubnet(netMask, out var subnet) && subnet.Contains(IPAddress.Parse(ipAddress))); } [Theory] @@ -160,7 +160,7 @@ namespace Jellyfin.Networking.Tests [InlineData("2001:db8:abcd:0012::0/128", "2001:0DB8:ABCD:0012:0000:0000:0000:0001")] public void IPv6SubnetMaskDoesNotMatchInvalidIPAddress(string netMask, string ipAddress) { - Assert.False(NetworkExtensions.TryParseToSubnet(netMask, out var subnet) && subnet.Contains(IPAddress.Parse(ipAddress))); + Assert.False(NetworkUtils.TryParseToSubnet(netMask, out var subnet) && subnet.Contains(IPAddress.Parse(ipAddress))); } [Theory] @@ -207,7 +207,7 @@ namespace Jellyfin.Networking.Tests NetworkManager.MockNetworkSettings = string.Empty; // Check to see if DNS resolution is working. If not, skip test. - if (!NetworkExtensions.TryParseHost(source, out var host)) + if (!NetworkUtils.TryParseHost(source, out var host)) { return; } -- cgit v1.2.3 From e463dbda47cc51d9e774e867140921f001a3a52a Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 10 Nov 2023 11:10:51 -0500 Subject: Move network configuration to MediaBrowser.Common --- Emby.Dlna/Main/DlnaHost.cs | 1 - Emby.Server.Implementations/ApplicationHost.cs | 1 - .../EntryPoints/ExternalPortForwarding.cs | 2 +- .../EntryPoints/UdpServerEntryPoint.cs | 1 - Jellyfin.Api/Controllers/StartupController.cs | 2 +- .../Middleware/BaseUrlRedirectionMiddleware.cs | 2 +- Jellyfin.Api/Middleware/LanFilteringMiddleware.cs | 1 - .../Configuration/NetworkConfiguration.cs | 176 --------------------- .../NetworkConfigurationExtensions.cs | 20 --- .../Configuration/NetworkConfigurationFactory.cs | 23 --- .../Configuration/NetworkConfigurationStore.cs | 24 --- Jellyfin.Networking/Manager/NetworkManager.cs | 1 - .../Extensions/ApiApplicationBuilderExtensions.cs | 2 +- .../Extensions/ApiServiceCollectionExtensions.cs | 1 - .../MigrateNetworkConfiguration.cs | 2 +- Jellyfin.Server/Startup.cs | 1 - MediaBrowser.Common/Net/NetworkConfiguration.cs | 175 ++++++++++++++++++++ .../Net/NetworkConfigurationExtensions.cs | 19 +++ .../Net/NetworkConfigurationFactory.cs | 22 +++ .../Net/NetworkConfigurationStore.cs | 23 +++ .../Configuration/NetworkConfigurationTests.cs | 2 +- .../NetworkManagerTests.cs | 2 +- .../Jellyfin.Networking.Tests/NetworkParseTests.cs | 1 - tests/Jellyfin.Server.Tests/ParseNetworkTests.cs | 2 +- 24 files changed, 247 insertions(+), 259 deletions(-) delete mode 100644 Jellyfin.Networking/Configuration/NetworkConfiguration.cs delete mode 100644 Jellyfin.Networking/Configuration/NetworkConfigurationExtensions.cs delete mode 100644 Jellyfin.Networking/Configuration/NetworkConfigurationFactory.cs delete mode 100644 Jellyfin.Networking/Configuration/NetworkConfigurationStore.cs create mode 100644 MediaBrowser.Common/Net/NetworkConfiguration.cs create mode 100644 MediaBrowser.Common/Net/NetworkConfigurationExtensions.cs create mode 100644 MediaBrowser.Common/Net/NetworkConfigurationFactory.cs create mode 100644 MediaBrowser.Common/Net/NetworkConfigurationStore.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Dlna/Main/DlnaHost.cs b/Emby.Dlna/Main/DlnaHost.cs index 26bf6d5e2..58db7c26f 100644 --- a/Emby.Dlna/Main/DlnaHost.cs +++ b/Emby.Dlna/Main/DlnaHost.cs @@ -9,7 +9,6 @@ using System.Threading; using System.Threading.Tasks; using Emby.Dlna.PlayTo; using Emby.Dlna.Ssdp; -using Jellyfin.Networking.Configuration; 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 a1f1cd649..40aee063e 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -41,7 +41,6 @@ using Emby.Server.Implementations.Updates; using Jellyfin.Api.Helpers; using Jellyfin.Drawing; using Jellyfin.MediaEncoding.Hls.Playlist; -using Jellyfin.Networking.Configuration; using Jellyfin.Networking.Manager; using Jellyfin.Server.Implementations; using MediaBrowser.Common; diff --git a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs index d6da597b8..c4cd935c3 100644 --- a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs +++ b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs @@ -9,7 +9,7 @@ using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Networking.Configuration; +using MediaBrowser.Common.Net; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Plugins; diff --git a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs index 662bd88a9..18e60b210 100644 --- a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs +++ b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs @@ -6,7 +6,6 @@ using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; using Emby.Server.Implementations.Udp; -using Jellyfin.Networking.Configuration; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; using MediaBrowser.Controller; diff --git a/Jellyfin.Api/Controllers/StartupController.cs b/Jellyfin.Api/Controllers/StartupController.cs index 1098733b2..fe99cee77 100644 --- a/Jellyfin.Api/Controllers/StartupController.cs +++ b/Jellyfin.Api/Controllers/StartupController.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Threading.Tasks; using Jellyfin.Api.Constants; using Jellyfin.Api.Models.StartupDtos; -using Jellyfin.Networking.Configuration; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; using Microsoft.AspNetCore.Authorization; diff --git a/Jellyfin.Api/Middleware/BaseUrlRedirectionMiddleware.cs b/Jellyfin.Api/Middleware/BaseUrlRedirectionMiddleware.cs index 2241c68e7..cbd948db0 100644 --- a/Jellyfin.Api/Middleware/BaseUrlRedirectionMiddleware.cs +++ b/Jellyfin.Api/Middleware/BaseUrlRedirectionMiddleware.cs @@ -1,6 +1,6 @@ using System; using System.Threading.Tasks; -using Jellyfin.Networking.Configuration; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; diff --git a/Jellyfin.Api/Middleware/LanFilteringMiddleware.cs b/Jellyfin.Api/Middleware/LanFilteringMiddleware.cs index 94de30d1b..d8c95ddff 100644 --- a/Jellyfin.Api/Middleware/LanFilteringMiddleware.cs +++ b/Jellyfin.Api/Middleware/LanFilteringMiddleware.cs @@ -1,5 +1,4 @@ using System.Threading.Tasks; -using Jellyfin.Networking.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; diff --git a/Jellyfin.Networking/Configuration/NetworkConfiguration.cs b/Jellyfin.Networking/Configuration/NetworkConfiguration.cs deleted file mode 100644 index 90ebcd390..000000000 --- a/Jellyfin.Networking/Configuration/NetworkConfiguration.cs +++ /dev/null @@ -1,176 +0,0 @@ -#pragma warning disable CA1819 // Properties should not return arrays - -using System; - -namespace Jellyfin.Networking.Configuration -{ - /// - /// Defines the . - /// - public class NetworkConfiguration - { - /// - /// The default value for . - /// - public const int DefaultHttpPort = 8096; - - /// - /// The default value for and . - /// - public const int DefaultHttpsPort = 8920; - - private string _baseUrl = string.Empty; - - /// - /// Gets or sets a value used to specify the URL prefix that your Jellyfin instance can be accessed at. - /// - public string BaseUrl - { - get => _baseUrl; - set - { - // Normalize the start of the string - if (string.IsNullOrWhiteSpace(value)) - { - // If baseUrl is empty, set an empty prefix string - _baseUrl = string.Empty; - return; - } - - if (value[0] != '/') - { - // If baseUrl was not configured with a leading slash, append one for consistency - value = "/" + value; - } - - // Normalize the end of the string - if (value[^1] == '/') - { - // If baseUrl was configured with a trailing slash, remove it for consistency - value = value.Remove(value.Length - 1); - } - - _baseUrl = value; - } - } - - /// - /// Gets or sets a value indicating whether to use HTTPS. - /// - /// - /// In order for HTTPS to be used, in addition to setting this to true, valid values must also be - /// provided for and . - /// - public bool EnableHttps { get; set; } - - /// - /// Gets or sets a value indicating whether the server should force connections over HTTPS. - /// - public bool RequireHttps { get; set; } - - /// - /// Gets or sets the filesystem path of an X.509 certificate to use for SSL. - /// - public string CertificatePath { get; set; } = string.Empty; - - /// - /// Gets or sets the password required to access the X.509 certificate data in the file specified by . - /// - public string CertificatePassword { get; set; } = string.Empty; - - /// - /// Gets or sets the internal HTTP server port. - /// - /// The HTTP server port. - public int InternalHttpPort { get; set; } = DefaultHttpPort; - - /// - /// Gets or sets the internal HTTPS server port. - /// - /// The HTTPS server port. - public int InternalHttpsPort { get; set; } = DefaultHttpsPort; - - /// - /// Gets or sets the public HTTP port. - /// - /// The public HTTP port. - public int PublicHttpPort { get; set; } = DefaultHttpPort; - - /// - /// Gets or sets the public HTTPS port. - /// - /// The public HTTPS port. - public int PublicHttpsPort { get; set; } = DefaultHttpsPort; - - /// - /// Gets or sets a value indicating whether Autodiscovery is enabled. - /// - public bool AutoDiscovery { get; set; } = true; - - /// - /// Gets or sets a value indicating whether to enable automatic port forwarding. - /// - public bool EnableUPnP { get; set; } - - /// - /// Gets or sets a value indicating whether IPv6 is enabled. - /// - public bool EnableIPv4 { get; set; } = true; - - /// - /// Gets or sets a value indicating whether IPv6 is enabled. - /// - public bool EnableIPv6 { get; set; } - - /// - /// Gets or sets a value indicating whether access from outside of the LAN is permitted. - /// - public bool EnableRemoteAccess { get; set; } = true; - - /// - /// Gets or sets the subnets that are deemed to make up the LAN. - /// - public string[] LocalNetworkSubnets { get; set; } = Array.Empty(); - - /// - /// Gets or sets the interface addresses which Jellyfin will bind to. If empty, all interfaces will be used. - /// - public string[] LocalNetworkAddresses { get; set; } = Array.Empty(); - - /// - /// Gets or sets the known proxies. - /// - public string[] KnownProxies { get; set; } = Array.Empty(); - - /// - /// Gets or sets a value indicating whether address names that match should be ignored for the purposes of binding. - /// - public bool IgnoreVirtualInterfaces { get; set; } = true; - - /// - /// Gets or sets a value indicating the interface name prefixes that should be ignored. The list can be comma separated and values are case-insensitive. . - /// - public string[] VirtualInterfaceNames { get; set; } = new string[] { "veth" }; - - /// - /// Gets or sets a value indicating whether the published server uri is based on information in HTTP requests. - /// - public bool EnablePublishedServerUriByRequest { get; set; } = false; - - /// - /// Gets or sets the PublishedServerUriBySubnet - /// Gets or sets PublishedServerUri to advertise for specific subnets. - /// - public string[] PublishedServerUriBySubnet { get; set; } = Array.Empty(); - - /// - /// Gets or sets the filter for remote IP connectivity. Used in conjunction with . - /// - public string[] RemoteIPFilter { get; set; } = Array.Empty(); - - /// - /// Gets or sets a value indicating whether contains a blacklist or a whitelist. Default is a whitelist. - /// - public bool IsRemoteIPFilterBlacklist { get; set; } - } -} diff --git a/Jellyfin.Networking/Configuration/NetworkConfigurationExtensions.cs b/Jellyfin.Networking/Configuration/NetworkConfigurationExtensions.cs deleted file mode 100644 index 3ba6bb8fc..000000000 --- a/Jellyfin.Networking/Configuration/NetworkConfigurationExtensions.cs +++ /dev/null @@ -1,20 +0,0 @@ -using MediaBrowser.Common.Configuration; - -namespace Jellyfin.Networking.Configuration -{ - /// - /// Defines the . - /// - public static class NetworkConfigurationExtensions - { - /// - /// Retrieves the network configuration. - /// - /// The . - /// The . - public static NetworkConfiguration GetNetworkConfiguration(this IConfigurationManager config) - { - return config.GetConfiguration(NetworkConfigurationStore.StoreKey); - } - } -} diff --git a/Jellyfin.Networking/Configuration/NetworkConfigurationFactory.cs b/Jellyfin.Networking/Configuration/NetworkConfigurationFactory.cs deleted file mode 100644 index 14726565a..000000000 --- a/Jellyfin.Networking/Configuration/NetworkConfigurationFactory.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Collections.Generic; -using MediaBrowser.Common.Configuration; - -namespace Jellyfin.Networking.Configuration -{ - /// - /// Defines the . - /// - public class NetworkConfigurationFactory : IConfigurationFactory - { - /// - /// The GetConfigurations. - /// - /// The . - public IEnumerable GetConfigurations() - { - return new[] - { - new NetworkConfigurationStore() - }; - } - } -} diff --git a/Jellyfin.Networking/Configuration/NetworkConfigurationStore.cs b/Jellyfin.Networking/Configuration/NetworkConfigurationStore.cs deleted file mode 100644 index a268ebb68..000000000 --- a/Jellyfin.Networking/Configuration/NetworkConfigurationStore.cs +++ /dev/null @@ -1,24 +0,0 @@ -using MediaBrowser.Common.Configuration; - -namespace Jellyfin.Networking.Configuration -{ - /// - /// A configuration that stores network related settings. - /// - public class NetworkConfigurationStore : ConfigurationStore - { - /// - /// The name of the configuration in the storage. - /// - public const string StoreKey = "network"; - - /// - /// Initializes a new instance of the class. - /// - public NetworkConfigurationStore() - { - ConfigurationType = typeof(NetworkConfiguration); - Key = StoreKey; - } - } -} diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs index d631fa51f..b0fe4aba6 100644 --- a/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/Jellyfin.Networking/Manager/NetworkManager.cs @@ -7,7 +7,6 @@ using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; using System.Threading; -using Jellyfin.Networking.Configuration; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; using MediaBrowser.Model.Net; diff --git a/Jellyfin.Server/Extensions/ApiApplicationBuilderExtensions.cs b/Jellyfin.Server/Extensions/ApiApplicationBuilderExtensions.cs index b6af9baec..6066893de 100644 --- a/Jellyfin.Server/Extensions/ApiApplicationBuilderExtensions.cs +++ b/Jellyfin.Server/Extensions/ApiApplicationBuilderExtensions.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using Jellyfin.Api.Middleware; -using Jellyfin.Networking.Configuration; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using Microsoft.AspNetCore.Builder; using Microsoft.OpenApi.Models; diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs index a84227495..93df7d315 100644 --- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs +++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs @@ -20,7 +20,6 @@ using Jellyfin.Api.Formatters; using Jellyfin.Api.ModelBinders; using Jellyfin.Data.Enums; using Jellyfin.Extensions.Json; -using Jellyfin.Networking.Configuration; using Jellyfin.Server.Configuration; using Jellyfin.Server.Filters; using MediaBrowser.Common.Net; diff --git a/Jellyfin.Server/Migrations/PreStartupRoutines/MigrateNetworkConfiguration.cs b/Jellyfin.Server/Migrations/PreStartupRoutines/MigrateNetworkConfiguration.cs index c6d86b8cd..d92c00991 100644 --- a/Jellyfin.Server/Migrations/PreStartupRoutines/MigrateNetworkConfiguration.cs +++ b/Jellyfin.Server/Migrations/PreStartupRoutines/MigrateNetworkConfiguration.cs @@ -3,7 +3,7 @@ using System.IO; using System.Xml; using System.Xml.Serialization; using Emby.Server.Implementations; -using Jellyfin.Networking.Configuration; +using MediaBrowser.Common.Net; using Microsoft.Extensions.Logging; namespace Jellyfin.Server.Migrations.PreStartupRoutines; diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs index 2acddb243..18d13c056 100644 --- a/Jellyfin.Server/Startup.cs +++ b/Jellyfin.Server/Startup.cs @@ -7,7 +7,6 @@ using System.Text; using Emby.Dlna.Extensions; using Jellyfin.Api.Middleware; using Jellyfin.MediaEncoding.Hls.Extensions; -using Jellyfin.Networking.Configuration; using Jellyfin.Networking.HappyEyeballs; using Jellyfin.Server.Extensions; using Jellyfin.Server.HealthChecks; diff --git a/MediaBrowser.Common/Net/NetworkConfiguration.cs b/MediaBrowser.Common/Net/NetworkConfiguration.cs new file mode 100644 index 000000000..61a51c99e --- /dev/null +++ b/MediaBrowser.Common/Net/NetworkConfiguration.cs @@ -0,0 +1,175 @@ +#pragma warning disable CA1819 // Properties should not return arrays + +using System; + +namespace MediaBrowser.Common.Net; + +/// +/// Defines the . +/// +public class NetworkConfiguration +{ + /// + /// The default value for . + /// + public const int DefaultHttpPort = 8096; + + /// + /// The default value for and . + /// + public const int DefaultHttpsPort = 8920; + + private string _baseUrl = string.Empty; + + /// + /// Gets or sets a value used to specify the URL prefix that your Jellyfin instance can be accessed at. + /// + public string BaseUrl + { + get => _baseUrl; + set + { + // Normalize the start of the string + if (string.IsNullOrWhiteSpace(value)) + { + // If baseUrl is empty, set an empty prefix string + _baseUrl = string.Empty; + return; + } + + if (value[0] != '/') + { + // If baseUrl was not configured with a leading slash, append one for consistency + value = "/" + value; + } + + // Normalize the end of the string + if (value[^1] == '/') + { + // If baseUrl was configured with a trailing slash, remove it for consistency + value = value.Remove(value.Length - 1); + } + + _baseUrl = value; + } + } + + /// + /// Gets or sets a value indicating whether to use HTTPS. + /// + /// + /// In order for HTTPS to be used, in addition to setting this to true, valid values must also be + /// provided for and . + /// + public bool EnableHttps { get; set; } + + /// + /// Gets or sets a value indicating whether the server should force connections over HTTPS. + /// + public bool RequireHttps { get; set; } + + /// + /// Gets or sets the filesystem path of an X.509 certificate to use for SSL. + /// + public string CertificatePath { get; set; } = string.Empty; + + /// + /// Gets or sets the password required to access the X.509 certificate data in the file specified by . + /// + public string CertificatePassword { get; set; } = string.Empty; + + /// + /// Gets or sets the internal HTTP server port. + /// + /// The HTTP server port. + public int InternalHttpPort { get; set; } = DefaultHttpPort; + + /// + /// Gets or sets the internal HTTPS server port. + /// + /// The HTTPS server port. + public int InternalHttpsPort { get; set; } = DefaultHttpsPort; + + /// + /// Gets or sets the public HTTP port. + /// + /// The public HTTP port. + public int PublicHttpPort { get; set; } = DefaultHttpPort; + + /// + /// Gets or sets the public HTTPS port. + /// + /// The public HTTPS port. + public int PublicHttpsPort { get; set; } = DefaultHttpsPort; + + /// + /// Gets or sets a value indicating whether Autodiscovery is enabled. + /// + public bool AutoDiscovery { get; set; } = true; + + /// + /// Gets or sets a value indicating whether to enable automatic port forwarding. + /// + public bool EnableUPnP { get; set; } + + /// + /// Gets or sets a value indicating whether IPv6 is enabled. + /// + public bool EnableIPv4 { get; set; } = true; + + /// + /// Gets or sets a value indicating whether IPv6 is enabled. + /// + public bool EnableIPv6 { get; set; } + + /// + /// Gets or sets a value indicating whether access from outside of the LAN is permitted. + /// + public bool EnableRemoteAccess { get; set; } = true; + + /// + /// Gets or sets the subnets that are deemed to make up the LAN. + /// + public string[] LocalNetworkSubnets { get; set; } = Array.Empty(); + + /// + /// Gets or sets the interface addresses which Jellyfin will bind to. If empty, all interfaces will be used. + /// + public string[] LocalNetworkAddresses { get; set; } = Array.Empty(); + + /// + /// Gets or sets the known proxies. + /// + public string[] KnownProxies { get; set; } = Array.Empty(); + + /// + /// Gets or sets a value indicating whether address names that match should be ignored for the purposes of binding. + /// + public bool IgnoreVirtualInterfaces { get; set; } = true; + + /// + /// Gets or sets a value indicating the interface name prefixes that should be ignored. The list can be comma separated and values are case-insensitive. . + /// + public string[] VirtualInterfaceNames { get; set; } = new string[] { "veth" }; + + /// + /// Gets or sets a value indicating whether the published server uri is based on information in HTTP requests. + /// + public bool EnablePublishedServerUriByRequest { get; set; } = false; + + /// + /// Gets or sets the PublishedServerUriBySubnet + /// Gets or sets PublishedServerUri to advertise for specific subnets. + /// + public string[] PublishedServerUriBySubnet { get; set; } = Array.Empty(); + + /// + /// Gets or sets the filter for remote IP connectivity. Used in conjunction with . + /// + public string[] RemoteIPFilter { get; set; } = Array.Empty(); + + /// + /// Gets or sets a value indicating whether contains a blacklist or a whitelist. Default is a whitelist. + /// + public bool IsRemoteIPFilterBlacklist { get; set; } +} diff --git a/MediaBrowser.Common/Net/NetworkConfigurationExtensions.cs b/MediaBrowser.Common/Net/NetworkConfigurationExtensions.cs new file mode 100644 index 000000000..9288964d2 --- /dev/null +++ b/MediaBrowser.Common/Net/NetworkConfigurationExtensions.cs @@ -0,0 +1,19 @@ +using MediaBrowser.Common.Configuration; + +namespace MediaBrowser.Common.Net; + +/// +/// Defines the . +/// +public static class NetworkConfigurationExtensions +{ + /// + /// Retrieves the network configuration. + /// + /// The . + /// The . + public static NetworkConfiguration GetNetworkConfiguration(this IConfigurationManager config) + { + return config.GetConfiguration(NetworkConfigurationStore.StoreKey); + } +} diff --git a/MediaBrowser.Common/Net/NetworkConfigurationFactory.cs b/MediaBrowser.Common/Net/NetworkConfigurationFactory.cs new file mode 100644 index 000000000..9309834f4 --- /dev/null +++ b/MediaBrowser.Common/Net/NetworkConfigurationFactory.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using MediaBrowser.Common.Configuration; + +namespace MediaBrowser.Common.Net; + +/// +/// Defines the . +/// +public class NetworkConfigurationFactory : IConfigurationFactory +{ + /// + /// The GetConfigurations. + /// + /// The . + public IEnumerable GetConfigurations() + { + return new[] + { + new NetworkConfigurationStore() + }; + } +} diff --git a/MediaBrowser.Common/Net/NetworkConfigurationStore.cs b/MediaBrowser.Common/Net/NetworkConfigurationStore.cs new file mode 100644 index 000000000..d2f518707 --- /dev/null +++ b/MediaBrowser.Common/Net/NetworkConfigurationStore.cs @@ -0,0 +1,23 @@ +using MediaBrowser.Common.Configuration; + +namespace MediaBrowser.Common.Net; + +/// +/// A configuration that stores network related settings. +/// +public class NetworkConfigurationStore : ConfigurationStore +{ + /// + /// The name of the configuration in the storage. + /// + public const string StoreKey = "network"; + + /// + /// Initializes a new instance of the class. + /// + public NetworkConfigurationStore() + { + ConfigurationType = typeof(NetworkConfiguration); + Key = StoreKey; + } +} diff --git a/tests/Jellyfin.Networking.Tests/Configuration/NetworkConfigurationTests.cs b/tests/Jellyfin.Networking.Tests/Configuration/NetworkConfigurationTests.cs index a78b872df..30726f1d3 100644 --- a/tests/Jellyfin.Networking.Tests/Configuration/NetworkConfigurationTests.cs +++ b/tests/Jellyfin.Networking.Tests/Configuration/NetworkConfigurationTests.cs @@ -1,4 +1,4 @@ -using Jellyfin.Networking.Configuration; +using MediaBrowser.Common.Net; using Xunit; namespace Jellyfin.Networking.Tests.Configuration; diff --git a/tests/Jellyfin.Networking.Tests/NetworkManagerTests.cs b/tests/Jellyfin.Networking.Tests/NetworkManagerTests.cs index 2302f90b8..0333d98e6 100644 --- a/tests/Jellyfin.Networking.Tests/NetworkManagerTests.cs +++ b/tests/Jellyfin.Networking.Tests/NetworkManagerTests.cs @@ -1,6 +1,6 @@ using System.Net; -using Jellyfin.Networking.Configuration; using Jellyfin.Networking.Manager; +using MediaBrowser.Common.Net; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging.Abstractions; using Moq; diff --git a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs index 97beb6940..93514a501 100644 --- a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs +++ b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Linq; using System.Net; -using Jellyfin.Networking.Configuration; using Jellyfin.Networking.Manager; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; diff --git a/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs b/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs index 288102037..e0b65cbc4 100644 --- a/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs +++ b/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs @@ -1,10 +1,10 @@ using System; using System.Linq; using System.Net; -using Jellyfin.Networking.Configuration; using Jellyfin.Networking.Manager; using Jellyfin.Server.Extensions; using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Net; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.Extensions.Configuration; -- cgit v1.2.3 From a76a9056a2804fe80dfb7dcfb30c4b38d07b5bc7 Mon Sep 17 00:00:00 2001 From: Simas Šimas Date: Sun, 12 Nov 2023 15:50:05 +0000 Subject: Translated using Weblate (Lithuanian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/lt/ --- Emby.Server.Implementations/Localization/Core/lt-LT.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/lt-LT.json b/Emby.Server.Implementations/Localization/Core/lt-LT.json index ce8d8fc32..e7279994b 100644 --- a/Emby.Server.Implementations/Localization/Core/lt-LT.json +++ b/Emby.Server.Implementations/Localization/Core/lt-LT.json @@ -124,5 +124,7 @@ "TaskKeyframeExtractor": "Pagrindinių kadrų ištraukėjas", "TaskOptimizeDatabaseDescription": "Suspaudžia duomenų bazę ir atlaisvina vietą. Paleidžiant šią užduotį, po bibliotekos skenavimo arba kitų veiksmų kurie galimai modifikuoja duomenų bazė, gali pagerinti greitaveiką.", "External": "Išorinis", - "HearingImpaired": "Su klausos sutrikimais" + "HearingImpaired": "Su klausos sutrikimais", + "TaskRefreshTrickplayImages": "Generuoti Trickplay atvaizdus", + "TaskRefreshTrickplayImagesDescription": "Sukuria trickplay peržiūras vaizdo įrašams įgalintose bibliotekose." } -- cgit v1.2.3 From 532f1e844b96ce819138e6091d2a9ad6c5aad3ab Mon Sep 17 00:00:00 2001 From: Christo Date: Sun, 12 Nov 2023 14:05:48 +0000 Subject: Translated using Weblate (Afrikaans) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/af/ --- Emby.Server.Implementations/Localization/Core/af.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/af.json b/Emby.Server.Implementations/Localization/Core/af.json index 9fbf364ef..ecea8df6a 100644 --- a/Emby.Server.Implementations/Localization/Core/af.json +++ b/Emby.Server.Implementations/Localization/Core/af.json @@ -123,5 +123,7 @@ "TaskKeyframeExtractorDescription": "Haal keyframes vanuit video lêers om meer presiese HLS afspeellyste te maak. Dit kan lank duur.", "TaskKeyframeExtractor": "Keyframe Ekstraktor", "External": "Ekstern", - "HearingImpaired": "gehoorgestremd" + "HearingImpaired": "gehoorgestremd", + "TaskRefreshTrickplayImages": "Genereer Fopspeel Beelde", + "TaskRefreshTrickplayImagesDescription": "Skep fopspeel voorskou vir videos in aangeskakelde media versameling." } -- cgit v1.2.3 From ea546230586a00a75db5c379db904e47cbbf270b Mon Sep 17 00:00:00 2001 From: Radu Terec Date: Sun, 12 Nov 2023 13:42:37 +0000 Subject: Translated using Weblate (Romanian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ro/ --- Emby.Server.Implementations/Localization/Core/ro.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ro.json b/Emby.Server.Implementations/Localization/Core/ro.json index 2c10bb477..537a6d3f2 100644 --- a/Emby.Server.Implementations/Localization/Core/ro.json +++ b/Emby.Server.Implementations/Localization/Core/ro.json @@ -123,5 +123,7 @@ "TaskKeyframeExtractorDescription": "Extrage cadrele cheie din fișierele video pentru a crea liste de redare HLS mai precise. Această sarcină poate rula o perioadă lungă de timp.", "External": "Extern", "TaskKeyframeExtractor": "Extractor de cadre cheie", - "HearingImpaired": "Ascultare Impară" + "HearingImpaired": "Ascultare Impară", + "TaskRefreshTrickplayImages": "Generează imagini Trickplay", + "TaskRefreshTrickplayImagesDescription": "Generează previzualizările trickplay pentru videourile din librăriile selectate." } -- cgit v1.2.3 From eb022c49ccb310ee46d8d7b7f46678312b13abc6 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 23 Oct 2023 23:36:56 +0200 Subject: Update to .NET 8 --- .ci/azure-pipelines-test.yml | 2 +- .config/dotnet-tools.json | 2 +- .github/workflows/ci-openapi.yml | 4 +- .vscode/launch.json | 4 +- Directory.Packages.props | 52 +++++++++++----------- Emby.Dlna/Emby.Dlna.csproj | 2 +- Emby.Naming/Emby.Naming.csproj | 2 +- Emby.Photos/Emby.Photos.csproj | 2 +- .../Emby.Server.Implementations.csproj | 2 +- Jellyfin.Api/Jellyfin.Api.csproj | 2 +- Jellyfin.Data/Jellyfin.Data.csproj | 2 +- Jellyfin.Networking/Jellyfin.Networking.csproj | 2 +- .../Jellyfin.Server.Implementations.csproj | 2 +- Jellyfin.Server/Jellyfin.Server.csproj | 2 +- MediaBrowser.Common/MediaBrowser.Common.csproj | 2 +- .../MediaBrowser.Controller.csproj | 2 +- .../MediaBrowser.LocalMetadata.csproj | 2 +- .../MediaBrowser.MediaEncoding.csproj | 2 +- MediaBrowser.Model/MediaBrowser.Model.csproj | 2 +- .../MediaBrowser.Providers.csproj | 2 +- .../MediaBrowser.XbmcMetadata.csproj | 2 +- README.md | 2 +- RSSDP/RSSDP.csproj | 2 +- fedora/jellyfin.spec | 2 +- .../Emby.Server.Implementations.Fuzz.csproj | 2 +- fuzz/Emby.Server.Implementations.Fuzz/fuzz.sh | 2 +- fuzz/Jellyfin.Api.Fuzz/Jellyfin.Api.Fuzz.csproj | 2 +- fuzz/Jellyfin.Api.Fuzz/fuzz.sh | 2 +- global.json | 2 +- .../Jellyfin.Drawing.Skia.csproj | 2 +- src/Jellyfin.Drawing/Jellyfin.Drawing.csproj | 2 +- src/Jellyfin.Extensions/Jellyfin.Extensions.csproj | 2 +- .../Jellyfin.MediaEncoding.Hls.csproj | 2 +- .../Jellyfin.MediaEncoding.Keyframes.csproj | 2 +- tests/Directory.Build.props | 2 +- 35 files changed, 62 insertions(+), 62 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/.ci/azure-pipelines-test.yml b/.ci/azure-pipelines-test.yml index 81362aab2..f15d6a6cd 100644 --- a/.ci/azure-pipelines-test.yml +++ b/.ci/azure-pipelines-test.yml @@ -94,5 +94,5 @@ jobs: displayName: 'Publish OpenAPI Artifact' condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) inputs: - targetPath: "tests/Jellyfin.Server.Integration.Tests/bin/Release/net7.0/openapi.json" + targetPath: "tests/Jellyfin.Server.Integration.Tests/bin/Release/net8.0/openapi.json" artifactName: 'OpenAPI Spec' diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index dbe78984a..37aa7721e 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -9,4 +9,4 @@ ] } } -} \ No newline at end of file +} diff --git a/.github/workflows/ci-openapi.yml b/.github/workflows/ci-openapi.yml index 8c463a8fc..96b790c0c 100644 --- a/.github/workflows/ci-openapi.yml +++ b/.github/workflows/ci-openapi.yml @@ -30,7 +30,7 @@ jobs: name: openapi-head retention-days: 14 if-no-files-found: error - path: tests/Jellyfin.Server.Integration.Tests/bin/Release/net7.0/openapi.json + path: tests/Jellyfin.Server.Integration.Tests/bin/Release/net8.0/openapi.json openapi-base: name: OpenAPI - BASE @@ -64,7 +64,7 @@ jobs: name: openapi-base retention-days: 14 if-no-files-found: error - path: tests/Jellyfin.Server.Integration.Tests/bin/Release/net7.0/openapi.json + path: tests/Jellyfin.Server.Integration.Tests/bin/Release/net8.0/openapi.json openapi-diff: permissions: diff --git a/.vscode/launch.json b/.vscode/launch.json index 55e6508a9..be55764fd 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,7 +6,7 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "build", - "program": "${workspaceFolder}/Jellyfin.Server/bin/Debug/net7.0/jellyfin.dll", + "program": "${workspaceFolder}/Jellyfin.Server/bin/Debug/net8.0/jellyfin.dll", "args": [], "cwd": "${workspaceFolder}/Jellyfin.Server", "console": "internalConsole", @@ -22,7 +22,7 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "build", - "program": "${workspaceFolder}/Jellyfin.Server/bin/Debug/net7.0/jellyfin.dll", + "program": "${workspaceFolder}/Jellyfin.Server/bin/Debug/net8.0/jellyfin.dll", "args": ["--nowebclient"], "cwd": "${workspaceFolder}/Jellyfin.Server", "console": "internalConsole", diff --git a/Directory.Packages.props b/Directory.Packages.props index 9109a5a18..0d7dbf12c 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -23,30 +23,30 @@ - + - + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + @@ -77,9 +77,9 @@ - - - + + + @@ -88,4 +88,4 @@ - \ No newline at end of file + diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj index efbef0564..7336482e5 100644 --- a/Emby.Dlna/Emby.Dlna.csproj +++ b/Emby.Dlna/Emby.Dlna.csproj @@ -17,7 +17,7 @@ - net7.0 + net8.0 false true diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj index bc7548189..47f260550 100644 --- a/Emby.Naming/Emby.Naming.csproj +++ b/Emby.Naming/Emby.Naming.csproj @@ -6,7 +6,7 @@ - net7.0 + net8.0 false true true diff --git a/Emby.Photos/Emby.Photos.csproj b/Emby.Photos/Emby.Photos.csproj index 5a04bbe49..55dbe393c 100644 --- a/Emby.Photos/Emby.Photos.csproj +++ b/Emby.Photos/Emby.Photos.csproj @@ -19,7 +19,7 @@ - net7.0 + net8.0 false true diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index b48e389ac..905f36e43 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -40,7 +40,7 @@ - net7.0 + net8.0 false true diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index 03dd97367..2473fb288 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -6,7 +6,7 @@ - net7.0 + net8.0 true diff --git a/Jellyfin.Data/Jellyfin.Data.csproj b/Jellyfin.Data/Jellyfin.Data.csproj index 847853ca0..c26e6cf23 100644 --- a/Jellyfin.Data/Jellyfin.Data.csproj +++ b/Jellyfin.Data/Jellyfin.Data.csproj @@ -1,7 +1,7 @@  - net7.0 + net8.0 false true true diff --git a/Jellyfin.Networking/Jellyfin.Networking.csproj b/Jellyfin.Networking/Jellyfin.Networking.csproj index 43d08c37a..30f41aeb2 100644 --- a/Jellyfin.Networking/Jellyfin.Networking.csproj +++ b/Jellyfin.Networking/Jellyfin.Networking.csproj @@ -1,6 +1,6 @@ - net7.0 + net8.0 false true diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index df1d5a3e1..0ed1578c7 100644 --- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 false true diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 5479d2296..1d4d97551 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -8,7 +8,7 @@ jellyfin Exe - net7.0 + net8.0 false false true diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 7d0d7a173..9b3ea4368 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -29,7 +29,7 @@ - net7.0 + net8.0 false true true diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index f9468f6cd..83faac337 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -35,7 +35,7 @@ - net7.0 + net8.0 false true true diff --git a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj index a39bc238a..05177ac39 100644 --- a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj +++ b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj @@ -11,7 +11,7 @@ - net7.0 + net8.0 false true diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index 1f39e88cd..a4e8194c1 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -6,7 +6,7 @@ - net7.0 + net8.0 false true diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 75c5bc6f0..89ec156a9 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -14,7 +14,7 @@ - net7.0 + net8.0 false true true diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 8471f6fa1..7a50c6cf4 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -27,7 +27,7 @@ - net7.0 + net8.0 false true ../jellyfin.ruleset diff --git a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj index d7e34fd22..c20073eea 100644 --- a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj +++ b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj @@ -15,7 +15,7 @@ - net7.0 + net8.0 false true diff --git a/README.md b/README.md index 2362741b4..911d9a094 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,7 @@ A second option is to build the project and then run the resulting executable fi ```bash dotnet build # Build the project -cd Jellyfin.Server/bin/Debug/net7.0 # Change into the build output directory +cd Jellyfin.Server/bin/Debug/net8.0 # Change into the build output directory ``` 2. Execute the build output. On Linux, Mac, etc. use `./jellyfin` and on Windows use `jellyfin.exe`. diff --git a/RSSDP/RSSDP.csproj b/RSSDP/RSSDP.csproj index df5d982f6..3f24de4e6 100644 --- a/RSSDP/RSSDP.csproj +++ b/RSSDP/RSSDP.csproj @@ -11,7 +11,7 @@ - net7.0 + net8.0 false AllDisabledByDefault disable diff --git a/fedora/jellyfin.spec b/fedora/jellyfin.spec index e78368906..c56a189ce 100644 --- a/fedora/jellyfin.spec +++ b/fedora/jellyfin.spec @@ -73,7 +73,7 @@ dotnet publish --configuration Release --self-contained --runtime %{dotnet_runti %install # Jellyfin files %{__mkdir} -p %{buildroot}%{_libdir}/jellyfin %{buildroot}%{_bindir} -%{__cp} -r Jellyfin.Server/bin/Release/net7.0/%{dotnet_runtime}/publish/* %{buildroot}%{_libdir}/jellyfin +%{__cp} -r Jellyfin.Server/bin/Release/net8.0/%{dotnet_runtime}/publish/* %{buildroot}%{_libdir}/jellyfin %{__install} -D %{SOURCE10} %{buildroot}%{_bindir}/jellyfin sed -i -e 's|/usr/lib64|%{_libdir}|g' %{buildroot}%{_bindir}/jellyfin diff --git a/fuzz/Emby.Server.Implementations.Fuzz/Emby.Server.Implementations.Fuzz.csproj b/fuzz/Emby.Server.Implementations.Fuzz/Emby.Server.Implementations.Fuzz.csproj index 1e3f8a048..73aae3f3d 100644 --- a/fuzz/Emby.Server.Implementations.Fuzz/Emby.Server.Implementations.Fuzz.csproj +++ b/fuzz/Emby.Server.Implementations.Fuzz/Emby.Server.Implementations.Fuzz.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net8.0 diff --git a/fuzz/Emby.Server.Implementations.Fuzz/fuzz.sh b/fuzz/Emby.Server.Implementations.Fuzz/fuzz.sh index aa2a34cdd..80a5cd7c1 100755 --- a/fuzz/Emby.Server.Implementations.Fuzz/fuzz.sh +++ b/fuzz/Emby.Server.Implementations.Fuzz/fuzz.sh @@ -8,4 +8,4 @@ cp bin/Emby.Server.Implementations.dll . dotnet build mkdir -p Findings -AFL_SKIP_BIN_CHECK=1 afl-fuzz -i "Testcases/$1" -o "Findings/$1" -t 5000 ./bin/Debug/net7.0/Emby.Server.Implementations.Fuzz "$1" +AFL_SKIP_BIN_CHECK=1 afl-fuzz -i "Testcases/$1" -o "Findings/$1" -t 5000 ./bin/Debug/net8.0/Emby.Server.Implementations.Fuzz "$1" diff --git a/fuzz/Jellyfin.Api.Fuzz/Jellyfin.Api.Fuzz.csproj b/fuzz/Jellyfin.Api.Fuzz/Jellyfin.Api.Fuzz.csproj index da46e63a5..faac7d976 100644 --- a/fuzz/Jellyfin.Api.Fuzz/Jellyfin.Api.Fuzz.csproj +++ b/fuzz/Jellyfin.Api.Fuzz/Jellyfin.Api.Fuzz.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net8.0 diff --git a/fuzz/Jellyfin.Api.Fuzz/fuzz.sh b/fuzz/Jellyfin.Api.Fuzz/fuzz.sh index edf965562..96b0192cf 100755 --- a/fuzz/Jellyfin.Api.Fuzz/fuzz.sh +++ b/fuzz/Jellyfin.Api.Fuzz/fuzz.sh @@ -8,4 +8,4 @@ cp bin/Jellyfin.Api.dll . dotnet build mkdir -p Findings -AFL_SKIP_BIN_CHECK=1 afl-fuzz -i "Testcases/$1" -o "Findings/$1" -t 5000 ./bin/Debug/net7.0/Jellyfin.Api.Fuzz "$1" +AFL_SKIP_BIN_CHECK=1 afl-fuzz -i "Testcases/$1" -o "Findings/$1" -t 5000 ./bin/Debug/net8.0/Jellyfin.Api.Fuzz "$1" diff --git a/global.json b/global.json index 24335d7a0..9db4b532c 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.0", + "version": "8.0.0", "rollForward": "latestMinor" } } diff --git a/src/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/src/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj index 3c417f8ff..f0f8e7afc 100644 --- a/src/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj +++ b/src/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj @@ -6,7 +6,7 @@ - net7.0 + net8.0 false true diff --git a/src/Jellyfin.Drawing/Jellyfin.Drawing.csproj b/src/Jellyfin.Drawing/Jellyfin.Drawing.csproj index d7ef6f8e7..23c4c0a9a 100644 --- a/src/Jellyfin.Drawing/Jellyfin.Drawing.csproj +++ b/src/Jellyfin.Drawing/Jellyfin.Drawing.csproj @@ -6,7 +6,7 @@ - net7.0 + net8.0 false true diff --git a/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj b/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj index 997df6dbe..c91f5d008 100644 --- a/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj +++ b/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 false true true diff --git a/src/Jellyfin.MediaEncoding.Hls/Jellyfin.MediaEncoding.Hls.csproj b/src/Jellyfin.MediaEncoding.Hls/Jellyfin.MediaEncoding.Hls.csproj index 76dde1cf6..ee79802a1 100644 --- a/src/Jellyfin.MediaEncoding.Hls/Jellyfin.MediaEncoding.Hls.csproj +++ b/src/Jellyfin.MediaEncoding.Hls/Jellyfin.MediaEncoding.Hls.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 true diff --git a/src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj b/src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj index 0d91a447b..c79dcee3c 100644 --- a/src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj +++ b/src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 true diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index de8fc1bb8..bec3481cb 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -4,7 +4,7 @@ - net7.0 + net8.0 false $(MSBuildThisFileDirectory)/jellyfin-tests.ruleset -- cgit v1.2.3 From b62b0ec2b581369de42c69305773f0edb9d701b4 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 24 Oct 2023 00:10:31 +0200 Subject: Fix warnings --- Emby.Server.Implementations/ApplicationHost.cs | 1 + .../EntryPoints/UdpServerEntryPoint.cs | 1 + .../LiveTv/TunerHosts/LiveStream.cs | 6 ++---- Jellyfin.Api/Auth/CustomAuthenticationHandler.cs | 5 ++--- Jellyfin.Api/Controllers/ImageController.cs | 16 ++++++++-------- Jellyfin.Api/Helpers/DynamicHlsHelper.cs | 4 ++-- Jellyfin.Api/Helpers/StreamingHelpers.cs | 14 +++++++------- Jellyfin.Api/Helpers/TranscodingJobHelper.cs | 2 +- Jellyfin.Networking/HappyEyeballs/HttpClientExtension.cs | 6 +++--- Jellyfin.Networking/Manager/NetworkManager.cs | 11 ++++++----- .../Security/AuthorizationContext.cs | 2 +- Jellyfin.Server.Implementations/Users/UserManager.cs | 2 +- .../Extensions/ApiServiceCollectionExtensions.cs | 8 +++++--- .../Migrations/Routines/MigrateRatingLevels.cs | 6 +----- MediaBrowser.Common/Net/NetworkUtils.cs | 2 +- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 1 + .../Probing/ProbeResultNormalizer.cs | 4 ++-- MediaBrowser.Model/Dlna/StreamBuilder.cs | 11 +++-------- MediaBrowser.Providers/Lyric/LrcLyricParser.cs | 2 +- MediaBrowser.Providers/Manager/ItemImageProvider.cs | 4 ++-- MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs | 4 ++-- MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs | 2 +- MediaBrowser.Providers/Music/AlbumMetadataService.cs | 2 +- .../Plugins/AudioDb/AudioDbAlbumImageProvider.cs | 2 +- .../Plugins/AudioDb/AudioDbArtistImageProvider.cs | 2 +- MediaBrowser.Providers/TV/SeriesMetadataService.cs | 2 +- MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs | 4 ++-- jellyfin.ruleset | 3 +++ .../FfProbe/FfProbeKeyframeExtractor.cs | 7 ++++--- 29 files changed, 67 insertions(+), 69 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 40aee063e..9affe235d 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -99,6 +99,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Prometheus.DotNetRuntime; using static MediaBrowser.Controller.Extensions.ConfigurationExtensions; +using IConfigurationManager = MediaBrowser.Common.Configuration.IConfigurationManager; using WebSocketManager = Emby.Server.Implementations.HttpServer.WebSocketManager; namespace Emby.Server.Implementations diff --git a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs index 18e60b210..56ccb21ee 100644 --- a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs +++ b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs @@ -12,6 +12,7 @@ using MediaBrowser.Controller; using MediaBrowser.Controller.Plugins; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; +using IConfigurationManager = MediaBrowser.Common.Configuration.IConfigurationManager; namespace Emby.Server.Implementations.EntryPoints { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs index 3ae9e256b..767b94136 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs @@ -84,15 +84,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts return Task.CompletedTask; } - public Task Close() + public async Task Close() { EnableStreamSharing = false; Logger.LogInformation("Closing {Type}", GetType().Name); - LiveStreamCancellationTokenSource.Cancel(); - - return Task.CompletedTask; + await LiveStreamCancellationTokenSource.CancelAsync().ConfigureAwait(false); } public Stream GetStream() diff --git a/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs b/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs index bd3e7d9e3..2853e69b0 100644 --- a/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs +++ b/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs @@ -27,13 +27,12 @@ namespace Jellyfin.Api.Auth /// Options monitor. /// The logger. /// The url encoder. - /// The system clock. public CustomAuthenticationHandler( IAuthService authService, IOptionsMonitor options, ILoggerFactory logger, - UrlEncoder encoder, - ISystemClock clock) : base(options, logger, encoder, clock) + UrlEncoder encoder) + : base(options, logger, encoder) { _authService = authService; _logger = logger.CreateLogger(); diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index 1e6580ed1..7d1a54ece 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -2080,30 +2080,30 @@ public class ImageController : BaseJellyfinApiController foreach (var (key, value) in headers) { - Response.Headers.Add(key, value); + Response.Headers.Append(key, value); } Response.ContentType = imageContentType ?? MediaTypeNames.Text.Plain; - Response.Headers.Add(HeaderNames.Age, Convert.ToInt64((DateTime.UtcNow - dateImageModified).TotalSeconds).ToString(CultureInfo.InvariantCulture)); - Response.Headers.Add(HeaderNames.Vary, HeaderNames.Accept); + Response.Headers.Append(HeaderNames.Age, Convert.ToInt64((DateTime.UtcNow - dateImageModified).TotalSeconds).ToString(CultureInfo.InvariantCulture)); + Response.Headers.Append(HeaderNames.Vary, HeaderNames.Accept); if (disableCaching) { - Response.Headers.Add(HeaderNames.CacheControl, "no-cache, no-store, must-revalidate"); - Response.Headers.Add(HeaderNames.Pragma, "no-cache, no-store, must-revalidate"); + Response.Headers.Append(HeaderNames.CacheControl, "no-cache, no-store, must-revalidate"); + Response.Headers.Append(HeaderNames.Pragma, "no-cache, no-store, must-revalidate"); } else { if (cacheDuration.HasValue) { - Response.Headers.Add(HeaderNames.CacheControl, "public, max-age=" + cacheDuration.Value.TotalSeconds); + Response.Headers.Append(HeaderNames.CacheControl, "public, max-age=" + cacheDuration.Value.TotalSeconds); } else { - Response.Headers.Add(HeaderNames.CacheControl, "public"); + Response.Headers.Append(HeaderNames.CacheControl, "public"); } - Response.Headers.Add(HeaderNames.LastModified, dateImageModified.ToUniversalTime().ToString("ddd, dd MMM yyyy HH:mm:ss \"GMT\"", CultureInfo.InvariantCulture)); + Response.Headers.Append(HeaderNames.LastModified, dateImageModified.ToUniversalTime().ToString("ddd, dd MMM yyyy HH:mm:ss \"GMT\"", CultureInfo.InvariantCulture)); // if the image was not modified since "ifModifiedSinceHeader"-header, return a HTTP status code 304 not modified if (!(dateImageModified > ifModifiedSinceHeader) && cacheDuration.HasValue) diff --git a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs index 24082fcff..a8df628f0 100644 --- a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs +++ b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs @@ -147,7 +147,7 @@ public class DynamicHlsHelper cancellationTokenSource.Token) .ConfigureAwait(false); - _httpContextAccessor.HttpContext.Response.Headers.Add(HeaderNames.Expires, "0"); + _httpContextAccessor.HttpContext.Response.Headers.Append(HeaderNames.Expires, "0"); if (isHeadRequest) { return new FileContentResult(Array.Empty(), MimeTypes.GetMimeType("playlist.m3u8")); @@ -568,7 +568,7 @@ public class DynamicHlsHelper && state.VideoStream is not null && state.VideoStream.Level.HasValue) { - levelString = state.VideoStream.Level.ToString() ?? string.Empty; + levelString = state.VideoStream.Level.Value.ToString(CultureInfo.InvariantCulture) ?? string.Empty; } else { diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index 6fbbceeab..7d9a38931 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -279,15 +279,15 @@ public static class StreamingHelpers var profile = state.DeviceProfile; StringValues transferMode = request.Headers["transferMode.dlna.org"]; - responseHeaders.Add("transferMode.dlna.org", string.IsNullOrEmpty(transferMode) ? "Streaming" : transferMode.ToString()); - responseHeaders.Add("realTimeInfo.dlna.org", "DLNA.ORG_TLAG=*"); + responseHeaders.Append("transferMode.dlna.org", string.IsNullOrEmpty(transferMode) ? "Streaming" : transferMode.ToString()); + responseHeaders.Append("realTimeInfo.dlna.org", "DLNA.ORG_TLAG=*"); if (state.RunTimeTicks.HasValue) { if (string.Equals(request.Headers["getMediaInfo.sec"], "1", StringComparison.OrdinalIgnoreCase)) { var ms = TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalMilliseconds; - responseHeaders.Add("MediaInfo.sec", string.Format( + responseHeaders.Append("MediaInfo.sec", string.Format( CultureInfo.InvariantCulture, "SEC_Duration={0};", Convert.ToInt32(ms))); @@ -305,7 +305,7 @@ public static class StreamingHelpers if (!state.IsVideoRequest) { - responseHeaders.Add("contentFeatures.dlna.org", ContentFeatureBuilder.BuildAudioHeader( + responseHeaders.Append("contentFeatures.dlna.org", ContentFeatureBuilder.BuildAudioHeader( profile, state.OutputContainer, audioCodec, @@ -321,7 +321,7 @@ public static class StreamingHelpers { var videoCodec = state.ActualOutputVideoCodec; - responseHeaders.Add( + responseHeaders.Append( "contentFeatures.dlna.org", ContentFeatureBuilder.BuildVideoHeader(profile, state.OutputContainer, videoCodec, audioCodec, state.OutputWidth, state.OutputHeight, state.TargetVideoBitDepth, state.OutputVideoBitrate, state.TargetTimestamp, isStaticallyStreamed, state.RunTimeTicks, state.TargetVideoProfile, state.TargetVideoRangeType, state.TargetVideoLevel, state.TargetFramerate, state.TargetPacketLength, state.TranscodeSeekInfo, state.IsTargetAnamorphic, state.IsTargetInterlaced, state.TargetRefFrames, state.TargetVideoStreamCount, state.TargetAudioStreamCount, state.TargetVideoCodecTag, state.IsTargetAVC).FirstOrDefault() ?? string.Empty); } @@ -404,12 +404,12 @@ public static class StreamingHelpers var runtimeSeconds = TimeSpan.FromTicks(state.RunTimeTicks!.Value).TotalSeconds.ToString(CultureInfo.InvariantCulture); var startSeconds = TimeSpan.FromTicks(startTimeTicks ?? 0).TotalSeconds.ToString(CultureInfo.InvariantCulture); - responseHeaders.Add("TimeSeekRange.dlna.org", string.Format( + responseHeaders.Append("TimeSeekRange.dlna.org", string.Format( CultureInfo.InvariantCulture, "npt={0}-{1}/{1}", startSeconds, runtimeSeconds)); - responseHeaders.Add("X-AvailableSeekRange", string.Format( + responseHeaders.Append("X-AvailableSeekRange", string.Format( CultureInfo.InvariantCulture, "1 npt={0}-{1}", startSeconds, diff --git a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs index c16a586d6..5b407e4a9 100644 --- a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs +++ b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs @@ -280,6 +280,7 @@ public class TranscodingJobHelper : IDisposable if (job.CancellationTokenSource?.IsCancellationRequested == false) { +#pragma warning disable CA1849 // Can't await in lock block job.CancellationTokenSource.Cancel(); } } @@ -291,7 +292,6 @@ public class TranscodingJobHelper : IDisposable lock (job.ProcessLock!) { -#pragma warning disable CA1849 // Can't await in lock block job.TranscodingThrottler?.Stop().GetAwaiter().GetResult(); var process = job.Process; diff --git a/Jellyfin.Networking/HappyEyeballs/HttpClientExtension.cs b/Jellyfin.Networking/HappyEyeballs/HttpClientExtension.cs index 59e6956c7..d59e4e5e3 100644 --- a/Jellyfin.Networking/HappyEyeballs/HttpClientExtension.cs +++ b/Jellyfin.Networking/HappyEyeballs/HttpClientExtension.cs @@ -65,7 +65,7 @@ namespace Jellyfin.Networking.HappyEyeballs // See https://github.com/dotnet/corefx/pull/29792/files#r189415885 for more details. if (await Task.WhenAny(tryConnectAsyncIPv6, Task.Delay(200, cancelIPv6.Token)).ConfigureAwait(false) == tryConnectAsyncIPv6 && tryConnectAsyncIPv6.IsCompletedSuccessfully) { - cancelIPv6.Cancel(); + await cancelIPv6.CancelAsync().ConfigureAwait(false); return tryConnectAsyncIPv6.GetAwaiter().GetResult(); } @@ -76,7 +76,7 @@ namespace Jellyfin.Networking.HappyEyeballs { if (tryConnectAsyncIPv6.IsCompletedSuccessfully) { - cancelIPv4.Cancel(); + await cancelIPv4.CancelAsync().ConfigureAwait(false); return tryConnectAsyncIPv6.GetAwaiter().GetResult(); } @@ -86,7 +86,7 @@ namespace Jellyfin.Networking.HappyEyeballs { if (tryConnectAsyncIPv4.IsCompletedSuccessfully) { - cancelIPv6.Cancel(); + await cancelIPv6.CancelAsync().ConfigureAwait(false); return tryConnectAsyncIPv4.GetAwaiter().GetResult(); } diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs index 3c03d137b..dbf7127a6 100644 --- a/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/Jellyfin.Networking/Manager/NetworkManager.cs @@ -14,6 +14,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using static MediaBrowser.Controller.Extensions.ConfigurationExtensions; +using IConfigurationManager = MediaBrowser.Common.Configuration.IConfigurationManager; namespace Jellyfin.Networking.Manager { @@ -422,7 +423,7 @@ namespace Jellyfin.Networking.Manager { // Parse config values into filter collection var remoteIPFilter = config.RemoteIPFilter; - if (remoteIPFilter.Any() && !string.IsNullOrWhiteSpace(remoteIPFilter.First())) + if (remoteIPFilter.Length != 0 && !string.IsNullOrWhiteSpace(remoteIPFilter[0])) { // Parse all IPs with netmask to a subnet var remoteAddressFilter = new List(); @@ -540,7 +541,7 @@ namespace Jellyfin.Networking.Manager } else if (NetworkUtils.TryParseToSubnet(identifier, out var result) && result is not null) { - var data = new IPData(result.BaseAddress, result); + var data = new IPData(result.Value.BaseAddress, result); publishedServerUrls.Add( new PublishedServerUriOverride( data, @@ -606,11 +607,11 @@ namespace Jellyfin.Networking.Manager var parts = details.Split(','); if (NetworkUtils.TryParseToSubnet(parts[0], out var subnet)) { - var address = subnet.BaseAddress; + var address = subnet.Value.BaseAddress; var index = int.Parse(parts[1], CultureInfo.InvariantCulture); if (address.AddressFamily == AddressFamily.InterNetwork || address.AddressFamily == AddressFamily.InterNetworkV6) { - var data = new IPData(address, subnet, parts[2]) + var data = new IPData(address, subnet.Value, parts[2]) { Index = index }; @@ -880,7 +881,7 @@ namespace Jellyfin.Networking.Manager { if (NetworkUtils.TryParseToSubnet(address, out var subnet)) { - return IPAddress.IsLoopback(subnet.BaseAddress) || (_lanSubnets.Any(x => x.Contains(subnet.BaseAddress)) && !_excludedSubnets.Any(x => x.Contains(subnet.BaseAddress))); + return IPAddress.IsLoopback(subnet.Value.BaseAddress) || (_lanSubnets.Any(x => x.Contains(subnet.Value.BaseAddress)) && !_excludedSubnets.Any(x => x.Contains(subnet.Value.BaseAddress))); } if (NetworkUtils.TryParseHost(address, out var addresses, IsIPv4Enabled, IsIPv6Enabled)) diff --git a/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs b/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs index 77f8f7071..6bda12c5b 100644 --- a/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs +++ b/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs @@ -60,7 +60,7 @@ namespace Jellyfin.Server.Implementations.Security } private async Task GetAuthorizationInfoFromDictionary( - IReadOnlyDictionary? auth, + Dictionary? auth, IHeaderDictionary headers, IQueryCollection queryString) { diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index edae4cfc5..075f4cb3a 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -775,7 +775,7 @@ namespace Jellyfin.Server.Implementations.Users return providers; } - private IList GetPasswordResetProviders(User user) + private IPasswordResetProvider[] GetPasswordResetProviders(User user) { var passwordResetProviderId = user.PasswordResetProviderId; var providers = _passwordResetProviders.Where(i => i.IsEnabled).ToArray(); diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs index 753029f2c..92c483c0f 100644 --- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs +++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs @@ -30,12 +30,14 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Cors.Infrastructure; +using Microsoft.AspNetCore.HttpOverrides; using Microsoft.Extensions.DependencyInjection; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; using AuthenticationSchemes = Jellyfin.Api.Constants.AuthenticationSchemes; +using IPNetwork = System.Net.IPNetwork; namespace Jellyfin.Server.Extensions { @@ -277,9 +279,9 @@ namespace Jellyfin.Server.Extensions } else if (NetworkUtils.TryParseToSubnet(allowedProxies[i], out var subnet)) { - if (subnet is not null) + if (subnet.HasValue) { - AddIPAddress(config, options, subnet.BaseAddress, subnet.PrefixLength); + AddIPAddress(config, options, subnet.Value.BaseAddress, subnet.Value.PrefixLength); } } else if (NetworkUtils.TryParseHost(allowedProxies[i], out var addresses, config.EnableIPv4, config.EnableIPv6)) @@ -310,7 +312,7 @@ namespace Jellyfin.Server.Extensions } else { - options.KnownNetworks.Add(new IPNetwork(addr, prefixLength)); + options.KnownNetworks.Add(new Microsoft.AspNetCore.HttpOverrides.IPNetwork(addr, prefixLength)); } } diff --git a/Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs b/Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs index ac5047401..247e1d845 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs @@ -78,11 +78,7 @@ namespace Jellyfin.Server.Migrations.Routines } else { - var ratingValue = _localizationManager.GetRatingLevel(ratingString).ToString(); - if (string.IsNullOrEmpty(ratingValue)) - { - ratingValue = "NULL"; - } + var ratingValue = _localizationManager.GetRatingLevel(ratingString)?.ToString(CultureInfo.InvariantCulture) ?? "NULL"; using var statement = connection.PrepareStatement("UPDATE TypedBaseItems SET InheritedParentalRatingValue = @Value WHERE OfficialRating = @Rating;"); statement.TryBind("@Value", ratingValue); diff --git a/MediaBrowser.Common/Net/NetworkUtils.cs b/MediaBrowser.Common/Net/NetworkUtils.cs index 452cb694e..7ae99336f 100644 --- a/MediaBrowser.Common/Net/NetworkUtils.cs +++ b/MediaBrowser.Common/Net/NetworkUtils.cs @@ -180,7 +180,7 @@ public static partial class NetworkUtils { if (TryParseToSubnet(values[a], out var innerResult, negated)) { - tmpResult.Add(innerResult); + tmpResult.Add(innerResult.Value); } } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 6621ae284..46fd1ae47 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -20,6 +20,7 @@ using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.MediaInfo; using Microsoft.Extensions.Configuration; +using IConfigurationManager = MediaBrowser.Common.Configuration.IConfigurationManager; namespace MediaBrowser.Controller.MediaEncoding { diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index be1e8a172..020e69ece 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -516,7 +516,7 @@ namespace MediaBrowser.MediaEncoding.Probing private void ProcessPairs(string key, List pairs, MediaInfo info) { - IList peoples = new List(); + List peoples = new List(); if (string.Equals(key, "studio", StringComparison.OrdinalIgnoreCase)) { info.Studios = pairs.Select(p => p.Value) @@ -1182,7 +1182,7 @@ namespace MediaBrowser.MediaEncoding.Probing info.Size = string.IsNullOrEmpty(data.Format.Size) ? null : long.Parse(data.Format.Size, CultureInfo.InvariantCulture); } - private void SetAudioInfoFromTags(MediaInfo audio, IReadOnlyDictionary tags) + private void SetAudioInfoFromTags(MediaInfo audio, Dictionary tags) { var people = new List(); if (tags.TryGetValue("composer", out var composer) && !string.IsNullOrWhiteSpace(composer)) diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 666e78795..252ac1303 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -835,11 +835,6 @@ namespace MediaBrowser.Model.Dlna playlistItem.SetOption(qualifier, "profile", videoStream.Profile.ToLowerInvariant()); } - if (videoStream is not null && videoStream.Level != 0) - { - playlistItem.SetOption(qualifier, "level", videoStream.Level.ToString() ?? string.Empty); - } - // Prefer matching audio codecs, could do better here var audioCodecs = ContainerProfile.SplitValue(audioCodec); @@ -866,16 +861,16 @@ namespace MediaBrowser.Model.Dlna // Copy matching audio codec options playlistItem.AudioSampleRate = audioStream.SampleRate; - playlistItem.SetOption(qualifier, "audiochannels", audioStream.Channels.ToString() ?? string.Empty); + playlistItem.SetOption(qualifier, "audiochannels", audioStream.Channels?.ToString(CultureInfo.InvariantCulture) ?? string.Empty); if (!string.IsNullOrEmpty(audioStream.Profile)) { playlistItem.SetOption(audioStream.Codec, "profile", audioStream.Profile.ToLowerInvariant()); } - if (audioStream.Level != 0) + if (audioStream.Level.HasValue) { - playlistItem.SetOption(audioStream.Codec, "level", audioStream.Level.ToString() ?? string.Empty); + playlistItem.SetOption(audioStream.Codec, "level", audioStream.Level.Value.ToString(CultureInfo.InvariantCulture)); } } diff --git a/MediaBrowser.Providers/Lyric/LrcLyricParser.cs b/MediaBrowser.Providers/Lyric/LrcLyricParser.cs index 7f1ecd743..a10ff198b 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricParser.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricParser.cs @@ -125,7 +125,7 @@ public class LrcLyricParser : ILyricParser /// /// The metadata from the LRC file. /// A lyricMetadata object with mapped property data. - private static LyricMetadata MapMetadataValues(IDictionary metaData) + private static LyricMetadata MapMetadataValues(Dictionary metaData) { LyricMetadata lyricMetadata = new(); diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs index dab36625e..1a5dbd7a5 100644 --- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs +++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs @@ -175,7 +175,7 @@ namespace MediaBrowser.Providers.Manager IDynamicImageProvider provider, ImageRefreshOptions refreshOptions, TypeOptions savedOptions, - ICollection downloadedImages, + List downloadedImages, RefreshResult result, CancellationToken cancellationToken) { @@ -263,7 +263,7 @@ namespace MediaBrowser.Providers.Manager ImageRefreshOptions refreshOptions, TypeOptions savedOptions, int backdropLimit, - ICollection downloadedImages, + List downloadedImages, RefreshResult result, CancellationToken cancellationToken) { diff --git a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs index b4b1895f5..d1c0ddb37 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs @@ -82,8 +82,8 @@ namespace MediaBrowser.Providers.MediaInfo { Directory.CreateDirectory(Path.GetDirectoryName(path)); - var imageStream = imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).IndexOf("front", StringComparison.OrdinalIgnoreCase) != -1) ?? - imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).IndexOf("cover", StringComparison.OrdinalIgnoreCase) != -1) ?? + var imageStream = imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).Contains("front", StringComparison.OrdinalIgnoreCase)) ?? + imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).Contains("cover", StringComparison.OrdinalIgnoreCase)) ?? imageStreams.FirstOrDefault(); var imageStreamIndex = imageStream?.Index; diff --git a/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs b/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs index 909cbb9b9..f846aa5de 100644 --- a/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs @@ -183,7 +183,7 @@ namespace MediaBrowser.Providers.MediaInfo files.AddRange(directoryService.GetFilePaths(internalMetadataPath, clearCache, true)); } - if (!files.Any()) + if (files.Count == 0) { return Array.Empty(); } diff --git a/MediaBrowser.Providers/Music/AlbumMetadataService.cs b/MediaBrowser.Providers/Music/AlbumMetadataService.cs index 0ddb2ad67..e4f34776b 100644 --- a/MediaBrowser.Providers/Music/AlbumMetadataService.cs +++ b/MediaBrowser.Providers/Music/AlbumMetadataService.cs @@ -148,7 +148,7 @@ namespace MediaBrowser.Providers.Music .ToArray(); var id = item.GetProviderId(provider); - if (ids.Any()) + if (ids.Length != 0) { var firstId = ids[0]; if (!string.IsNullOrEmpty(firstId) diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumImageProvider.cs index 7f73afc53..8a516e1ce 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumImageProvider.cs @@ -73,7 +73,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb return Enumerable.Empty(); } - private IEnumerable GetImages(AudioDbAlbumProvider.Album item) + private List GetImages(AudioDbAlbumProvider.Album item) { var list = new List(); diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistImageProvider.cs index 2232dfa0d..4e7757cd2 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistImageProvider.cs @@ -78,7 +78,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb return Enumerable.Empty(); } - private IEnumerable GetImages(AudioDbArtistProvider.Artist item) + private List GetImages(AudioDbArtistProvider.Artist item) { var list = new List(); diff --git a/MediaBrowser.Providers/TV/SeriesMetadataService.cs b/MediaBrowser.Providers/TV/SeriesMetadataService.cs index e01c0f483..01c07d633 100644 --- a/MediaBrowser.Providers/TV/SeriesMetadataService.cs +++ b/MediaBrowser.Providers/TV/SeriesMetadataService.cs @@ -121,7 +121,7 @@ namespace MediaBrowser.Providers.TV var seasonNumber = virtualSeason.IndexNumber; // If there's a physical season with the same number or no episodes in the season, delete it if ((seasonNumber.HasValue && physicalSeasonNumbers.Contains(seasonNumber.Value)) - || !virtualSeason.GetEpisodes().Any()) + || virtualSeason.GetEpisodes().Count == 0) { Logger.LogInformation("Removing virtual season {SeasonNumber} in series {SeriesName}", virtualSeason.IndexNumber, series.Name); diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs index bf66a3145..1399ac307 100644 --- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs @@ -314,11 +314,11 @@ namespace MediaBrowser.XbmcMetadata.Savers { var codec = stream.Codec; - if ((stream.CodecTag ?? string.Empty).IndexOf("xvid", StringComparison.OrdinalIgnoreCase) != -1) + if ((stream.CodecTag ?? string.Empty).Contains("xvid", StringComparison.OrdinalIgnoreCase)) { codec = "xvid"; } - else if ((stream.CodecTag ?? string.Empty).IndexOf("divx", StringComparison.OrdinalIgnoreCase) != -1) + else if ((stream.CodecTag ?? string.Empty).Contains("divx", StringComparison.OrdinalIgnoreCase)) { codec = "divx"; } diff --git a/jellyfin.ruleset b/jellyfin.ruleset index 870cf253f..10225e3af 100644 --- a/jellyfin.ruleset +++ b/jellyfin.ruleset @@ -140,6 +140,9 @@ + + + diff --git a/src/Jellyfin.MediaEncoding.Keyframes/FfProbe/FfProbeKeyframeExtractor.cs b/src/Jellyfin.MediaEncoding.Keyframes/FfProbe/FfProbeKeyframeExtractor.cs index 479e6ffdc..720d987f1 100644 --- a/src/Jellyfin.MediaEncoding.Keyframes/FfProbe/FfProbeKeyframeExtractor.cs +++ b/src/Jellyfin.MediaEncoding.Keyframes/FfProbe/FfProbeKeyframeExtractor.cs @@ -11,8 +11,6 @@ namespace Jellyfin.MediaEncoding.Keyframes.FfProbe; /// public static class FfProbeKeyframeExtractor { - private const string DefaultArguments = "-fflags +genpts -v error -skip_frame nokey -show_entries format=duration -show_entries stream=duration -show_entries packet=pts_time,flags -select_streams v -of csv \"{0}\""; - /// /// Extracts the keyframes using the ffprobe executable at the specified path. /// @@ -26,7 +24,10 @@ public static class FfProbeKeyframeExtractor StartInfo = new ProcessStartInfo { FileName = ffProbePath, - Arguments = string.Format(CultureInfo.InvariantCulture, DefaultArguments, filePath), + Arguments = string.Format( + CultureInfo.InvariantCulture, + "-fflags +genpts -v error -skip_frame nokey -show_entries format=duration -show_entries stream=duration -show_entries packet=pts_time,flags -select_streams v -of csv \"{0}\"", + filePath), CreateNoWindow = true, UseShellExecute = false, -- cgit v1.2.3 From 8ee15258944deb83b880cb5263fae2843a7af248 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 14 Nov 2023 22:01:10 +0100 Subject: Fix runtime errors --- Emby.Server.Implementations/ApplicationHost.cs | 4 +++- src/Jellyfin.Extensions/Json/JsonDefaults.cs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 9affe235d..4540ab205 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -310,7 +310,9 @@ namespace Emby.Server.Implementations { _creatingInstances.Add(type); Logger.LogDebug("Creating instance of {Type}", type); - return ActivatorUtilities.CreateInstance(ServiceProvider, type); + return ServiceProvider is null + ? Activator.CreateInstance(type) + : ActivatorUtilities.CreateInstance(ServiceProvider, type); } catch (Exception ex) { diff --git a/src/Jellyfin.Extensions/Json/JsonDefaults.cs b/src/Jellyfin.Extensions/Json/JsonDefaults.cs index 4d56ca615..9e6d4c3f8 100644 --- a/src/Jellyfin.Extensions/Json/JsonDefaults.cs +++ b/src/Jellyfin.Extensions/Json/JsonDefaults.cs @@ -1,5 +1,6 @@ using System.Text.Json; using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; using Jellyfin.Extensions.Json.Converters; namespace Jellyfin.Extensions.Json @@ -41,7 +42,8 @@ namespace Jellyfin.Extensions.Json new JsonNullableStructConverterFactory(), new JsonDateTimeConverter(), new JsonStringConverter() - } + }, + TypeInfoResolver = new DefaultJsonTypeInfoResolver() }; private static readonly JsonSerializerOptions _pascalCaseJsonSerializerOptions = new(_jsonSerializerOptions) -- cgit v1.2.3 From 464de13acf3be8e466e12c8087bef8623e9edba9 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 16 Nov 2023 00:49:23 +0100 Subject: Use new static ZipFile functions --- Emby.Server.Implementations/Updates/InstallationManager.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index c717744b1..b31b4116d 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -551,8 +551,7 @@ namespace Emby.Server.Implementations.Updates } stream.Position = 0; - using var reader = new ZipArchive(stream); - reader.ExtractToDirectory(targetDir, true); + ZipFile.ExtractToDirectory(stream, targetDir, true); // Ensure we create one or populate existing ones with missing data. await _pluginManager.PopulateManifest(package.PackageInfo, package.Version, targetDir, status).ConfigureAwait(false); -- cgit v1.2.3 From 8eb2fa53b5ca0e0bc03b70029e3669e36d54d10c Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 15 Nov 2023 16:23:51 -0500 Subject: Use pattern matching for EnableRefreshMessage --- .../EntryPoints/LibraryChangedNotifier.cs | 30 ++-------------------- 1 file changed, 2 insertions(+), 28 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index be36bbd2c..6654f4867 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -147,34 +147,8 @@ namespace Emby.Server.Implementations.EntryPoints } private static bool EnableRefreshMessage(BaseItem item) - { - if (item is not Folder folder) - { - return false; - } - - if (folder.IsRoot) - { - return false; - } - - if (folder is AggregateFolder || folder is UserRootFolder) - { - return false; - } - - if (folder is UserView || folder is Channel) - { - return false; - } - - if (!folder.IsTopParent) - { - return false; - } - - return true; - } + => item is Folder { IsRoot: false, IsTopParent: true } + and not (AggregateFolder or UserRootFolder or UserView or Channel); /// /// Handles the ItemAdded event of the libraryManager control. -- cgit v1.2.3 From 98f8cb2ad04d23edb9a6dfdf5d0b6e577d7ebb85 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 15 Nov 2023 16:28:35 -0500 Subject: Use target-typed new for fields --- .../EntryPoints/LibraryChangedNotifier.cs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index 6654f4867..7a8686ec8 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -34,17 +34,13 @@ namespace Emby.Server.Implementations.EntryPoints private readonly IUserManager _userManager; private readonly ILogger _logger; - /// - /// The library changed sync lock. - /// - private readonly object _libraryChangedSyncLock = new object(); - - private readonly List _foldersAddedTo = new List(); - private readonly List _foldersRemovedFrom = new List(); - private readonly List _itemsAdded = new List(); - private readonly List _itemsRemoved = new List(); - private readonly List _itemsUpdated = new List(); - private readonly ConcurrentDictionary _lastProgressMessageTimes = new ConcurrentDictionary(); + private readonly object _libraryChangedSyncLock = new(); + private readonly List _foldersAddedTo = new(); + private readonly List _foldersRemovedFrom = new(); + private readonly List _itemsAdded = new(); + private readonly List _itemsRemoved = new(); + private readonly List _itemsUpdated = new(); + private readonly ConcurrentDictionary _lastProgressMessageTimes = new(); public LibraryChangedNotifier( ILibraryManager libraryManager, -- cgit v1.2.3 From eb4d8e13dfa10123c84b1676e9f20983b6abcfee Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 15 Nov 2023 16:35:01 -0500 Subject: Break up long lines --- .../EntryPoints/LibraryChangedNotifier.cs | 58 ++++++++++++++++------ 1 file changed, 44 insertions(+), 14 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index 7a8686ec8..5a2f8a6dd 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -296,7 +296,13 @@ namespace Emby.Server.Implementations.EntryPoints /// The folders added to. /// The folders removed from. /// The cancellation token. - private async Task SendChangeNotifications(List itemsAdded, List itemsUpdated, List itemsRemoved, List foldersAddedTo, List foldersRemovedFrom, CancellationToken cancellationToken) + private async Task SendChangeNotifications( + List itemsAdded, + List itemsUpdated, + List itemsRemoved, + List foldersAddedTo, + List foldersRemovedFrom, + CancellationToken cancellationToken) { var userIds = _sessionManager.Sessions .Select(i => i.UserId) @@ -325,7 +331,12 @@ namespace Emby.Server.Implementations.EntryPoints try { - await _sessionManager.SendMessageToUserSessions(new List { userId }, SessionMessageType.LibraryChanged, info, cancellationToken).ConfigureAwait(false); + await _sessionManager.SendMessageToUserSessions( + new List { userId }, + SessionMessageType.LibraryChanged, + info, + cancellationToken) + .ConfigureAwait(false); } catch (Exception ex) { @@ -344,7 +355,13 @@ namespace Emby.Server.Implementations.EntryPoints /// The folders removed from. /// The user id. /// LibraryUpdateInfo. - private LibraryUpdateInfo GetLibraryUpdateInfo(List itemsAdded, List itemsUpdated, List itemsRemoved, List foldersAddedTo, List foldersRemovedFrom, Guid userId) + private LibraryUpdateInfo GetLibraryUpdateInfo( + List itemsAdded, + List itemsUpdated, + List itemsRemoved, + List foldersAddedTo, + List foldersRemovedFrom, + Guid userId) { var user = _userManager.GetUserById(userId); @@ -352,20 +369,33 @@ namespace Emby.Server.Implementations.EntryPoints newAndRemoved.AddRange(foldersAddedTo); newAndRemoved.AddRange(foldersRemovedFrom); - var allUserRootChildren = _libraryManager.GetUserRootFolder().GetChildren(user, true).OfType().ToList(); + var allUserRootChildren = _libraryManager.GetUserRootFolder() + .GetChildren(user, true) + .OfType() + .ToList(); return new LibraryUpdateInfo { - ItemsAdded = itemsAdded.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)).Distinct().ToArray(), - - ItemsUpdated = itemsUpdated.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)).Distinct().ToArray(), - - ItemsRemoved = itemsRemoved.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user, true)).Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)).Distinct().ToArray(), - - FoldersAddedTo = foldersAddedTo.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)).Distinct().ToArray(), - - FoldersRemovedFrom = foldersRemovedFrom.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)).Distinct().ToArray(), - + ItemsAdded = itemsAdded.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)) + .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) + .Distinct() + .ToArray(), + ItemsUpdated = itemsUpdated.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)) + .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) + .Distinct() + .ToArray(), + ItemsRemoved = itemsRemoved.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user, true)) + .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) + .Distinct() + .ToArray(), + FoldersAddedTo = foldersAddedTo.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)) + .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) + .Distinct() + .ToArray(), + FoldersRemovedFrom = foldersRemovedFrom.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)) + .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) + .Distinct() + .ToArray(), CollectionFolders = GetTopParentIds(newAndRemoved, allUserRootChildren).ToArray() }; } -- cgit v1.2.3 From 8f5f0a0310ca7dd55b64623782c05bbb810f946f Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 15 Nov 2023 16:49:25 -0500 Subject: Combine library item event handlers --- .../EntryPoints/LibraryChangedNotifier.cs | 83 ++++------------------ 1 file changed, 13 insertions(+), 70 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index 5a2f8a6dd..4d5065b92 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -146,98 +146,41 @@ namespace Emby.Server.Implementations.EntryPoints => item is Folder { IsRoot: false, IsTopParent: true } and not (AggregateFolder or UserRootFolder or UserView or Channel); - /// - /// Handles the ItemAdded event of the libraryManager control. - /// - /// The source of the event. - /// The instance containing the event data. private void OnLibraryItemAdded(object sender, ItemChangeEventArgs e) - { - if (!FilterItem(e.Item)) - { - return; - } - - lock (_libraryChangedSyncLock) - { - if (LibraryUpdateTimer is null) - { - LibraryUpdateTimer = new Timer( - LibraryUpdateTimerCallback, - null, - TimeSpan.FromSeconds(_configurationManager.Configuration.LibraryUpdateDuration), - Timeout.InfiniteTimeSpan); - } - else - { - LibraryUpdateTimer.Change(TimeSpan.FromSeconds(_configurationManager.Configuration.LibraryUpdateDuration), Timeout.InfiniteTimeSpan); - } + => OnLibraryChange(e.Item, e.Parent, _itemsAdded, _foldersAddedTo); - if (e.Item.GetParent() is Folder parent) - { - _foldersAddedTo.Add(parent); - } - - _itemsAdded.Add(e.Item); - } - } - - /// - /// Handles the ItemUpdated event of the libraryManager control. - /// - /// The source of the event. - /// The instance containing the event data. private void OnLibraryItemUpdated(object sender, ItemChangeEventArgs e) - { - if (!FilterItem(e.Item)) - { - return; - } + => OnLibraryChange(e.Item, e.Parent, _itemsUpdated, null); - lock (_libraryChangedSyncLock) - { - if (LibraryUpdateTimer is null) - { - LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, TimeSpan.FromSeconds(_configurationManager.Configuration.LibraryUpdateDuration), Timeout.InfiniteTimeSpan); - } - else - { - LibraryUpdateTimer.Change(TimeSpan.FromSeconds(_configurationManager.Configuration.LibraryUpdateDuration), Timeout.InfiniteTimeSpan); - } - - _itemsUpdated.Add(e.Item); - } - } - - /// - /// Handles the ItemRemoved event of the libraryManager control. - /// - /// The source of the event. - /// The instance containing the event data. private void OnLibraryItemRemoved(object sender, ItemChangeEventArgs e) + => OnLibraryChange(e.Item, e.Parent, _itemsRemoved, _foldersRemovedFrom); + + private void OnLibraryChange(BaseItem item, BaseItem parent, List itemsList, List foldersList) { - if (!FilterItem(e.Item)) + if (!FilterItem(item)) { return; } lock (_libraryChangedSyncLock) { + var updateDuration = TimeSpan.FromSeconds(_configurationManager.Configuration.LibraryUpdateDuration); + if (LibraryUpdateTimer is null) { - LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, TimeSpan.FromSeconds(_configurationManager.Configuration.LibraryUpdateDuration), Timeout.InfiniteTimeSpan); + LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, updateDuration, Timeout.InfiniteTimeSpan); } else { - LibraryUpdateTimer.Change(TimeSpan.FromSeconds(_configurationManager.Configuration.LibraryUpdateDuration), Timeout.InfiniteTimeSpan); + LibraryUpdateTimer.Change(updateDuration, Timeout.InfiniteTimeSpan); } - if (e.Parent is Folder parent) + if (foldersList is not null && parent is Folder folder) { - _foldersRemovedFrom.Add(parent); + foldersList.Add(folder); } - _itemsRemoved.Add(e.Item); + itemsList.Add(item); } } -- cgit v1.2.3 From 7e645dcfc0809ed2bfa9d31235839bf649bc76d2 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 15 Nov 2023 19:48:50 -0500 Subject: Make ILibraryChangedNotifier sealed --- .../EntryPoints/LibraryChangedNotifier.cs | 39 +++++++--------------- 1 file changed, 12 insertions(+), 27 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index 4d5065b92..0df8c2a5a 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -25,7 +25,7 @@ using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.EntryPoints { - public class LibraryChangedNotifier : IServerEntryPoint + public sealed class LibraryChangedNotifier : IServerEntryPoint { private readonly ILibraryManager _libraryManager; private readonly IServerConfigurationManager _configurationManager; @@ -405,36 +405,21 @@ namespace Emby.Server.Implementations.EntryPoints return Array.Empty(); } - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// + /// public void Dispose() { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool dispose) - { - if (dispose) - { - if (LibraryUpdateTimer is not null) - { - LibraryUpdateTimer.Dispose(); - LibraryUpdateTimer = null; - } + _libraryManager.ItemAdded -= OnLibraryItemAdded; + _libraryManager.ItemUpdated -= OnLibraryItemUpdated; + _libraryManager.ItemRemoved -= OnLibraryItemRemoved; - _libraryManager.ItemAdded -= OnLibraryItemAdded; - _libraryManager.ItemUpdated -= OnLibraryItemUpdated; - _libraryManager.ItemRemoved -= OnLibraryItemRemoved; + _providerManager.RefreshCompleted -= OnProviderRefreshCompleted; + _providerManager.RefreshStarted -= OnProviderRefreshStarted; + _providerManager.RefreshProgress -= OnProviderRefreshProgress; - _providerManager.RefreshCompleted -= OnProviderRefreshCompleted; - _providerManager.RefreshStarted -= OnProviderRefreshStarted; - _providerManager.RefreshProgress -= OnProviderRefreshProgress; + if (LibraryUpdateTimer is not null) + { + LibraryUpdateTimer.Dispose(); + LibraryUpdateTimer = null; } } } -- cgit v1.2.3 From c38bfd281c9616a626eceb22ad5f5e2a4a120b86 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 15 Nov 2023 19:49:15 -0500 Subject: Use file-scoped namespace --- .../EntryPoints/LibraryChangedNotifier.cs | 635 ++++++++++----------- 1 file changed, 317 insertions(+), 318 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index 0df8c2a5a..40dc00e1a 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -23,404 +23,403 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Session; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.EntryPoints +namespace Emby.Server.Implementations.EntryPoints; + +public sealed class LibraryChangedNotifier : IServerEntryPoint { - public sealed class LibraryChangedNotifier : IServerEntryPoint + private readonly ILibraryManager _libraryManager; + private readonly IServerConfigurationManager _configurationManager; + private readonly IProviderManager _providerManager; + private readonly ISessionManager _sessionManager; + private readonly IUserManager _userManager; + private readonly ILogger _logger; + + private readonly object _libraryChangedSyncLock = new(); + private readonly List _foldersAddedTo = new(); + private readonly List _foldersRemovedFrom = new(); + private readonly List _itemsAdded = new(); + private readonly List _itemsRemoved = new(); + private readonly List _itemsUpdated = new(); + private readonly ConcurrentDictionary _lastProgressMessageTimes = new(); + + public LibraryChangedNotifier( + ILibraryManager libraryManager, + IServerConfigurationManager configurationManager, + ISessionManager sessionManager, + IUserManager userManager, + ILogger logger, + IProviderManager providerManager) { - private readonly ILibraryManager _libraryManager; - private readonly IServerConfigurationManager _configurationManager; - private readonly IProviderManager _providerManager; - private readonly ISessionManager _sessionManager; - private readonly IUserManager _userManager; - private readonly ILogger _logger; - - private readonly object _libraryChangedSyncLock = new(); - private readonly List _foldersAddedTo = new(); - private readonly List _foldersRemovedFrom = new(); - private readonly List _itemsAdded = new(); - private readonly List _itemsRemoved = new(); - private readonly List _itemsUpdated = new(); - private readonly ConcurrentDictionary _lastProgressMessageTimes = new(); - - public LibraryChangedNotifier( - ILibraryManager libraryManager, - IServerConfigurationManager configurationManager, - ISessionManager sessionManager, - IUserManager userManager, - ILogger logger, - IProviderManager providerManager) - { - _libraryManager = libraryManager; - _configurationManager = configurationManager; - _sessionManager = sessionManager; - _userManager = userManager; - _logger = logger; - _providerManager = providerManager; - } + _libraryManager = libraryManager; + _configurationManager = configurationManager; + _sessionManager = sessionManager; + _userManager = userManager; + _logger = logger; + _providerManager = providerManager; + } - /// - /// Gets or sets the library update timer. - /// - /// The library update timer. - private Timer LibraryUpdateTimer { get; set; } + /// + /// Gets or sets the library update timer. + /// + /// The library update timer. + private Timer LibraryUpdateTimer { get; set; } - public Task RunAsync() - { - _libraryManager.ItemAdded += OnLibraryItemAdded; - _libraryManager.ItemUpdated += OnLibraryItemUpdated; - _libraryManager.ItemRemoved += OnLibraryItemRemoved; + public Task RunAsync() + { + _libraryManager.ItemAdded += OnLibraryItemAdded; + _libraryManager.ItemUpdated += OnLibraryItemUpdated; + _libraryManager.ItemRemoved += OnLibraryItemRemoved; - _providerManager.RefreshCompleted += OnProviderRefreshCompleted; - _providerManager.RefreshStarted += OnProviderRefreshStarted; - _providerManager.RefreshProgress += OnProviderRefreshProgress; + _providerManager.RefreshCompleted += OnProviderRefreshCompleted; + _providerManager.RefreshStarted += OnProviderRefreshStarted; + _providerManager.RefreshProgress += OnProviderRefreshProgress; - return Task.CompletedTask; - } + return Task.CompletedTask; + } - private void OnProviderRefreshProgress(object sender, GenericEventArgs> e) + private void OnProviderRefreshProgress(object sender, GenericEventArgs> e) + { + var item = e.Argument.Item1; + + if (!EnableRefreshMessage(item)) { - var item = e.Argument.Item1; + return; + } + + var progress = e.Argument.Item2; - if (!EnableRefreshMessage(item)) + if (_lastProgressMessageTimes.TryGetValue(item.Id, out var lastMessageSendTime)) + { + if (progress > 0 && progress < 100 && (DateTime.UtcNow - lastMessageSendTime).TotalMilliseconds < 1000) { return; } + } - var progress = e.Argument.Item2; + _lastProgressMessageTimes.AddOrUpdate(item.Id, _ => DateTime.UtcNow, (_, _) => DateTime.UtcNow); - if (_lastProgressMessageTimes.TryGetValue(item.Id, out var lastMessageSendTime)) - { - if (progress > 0 && progress < 100 && (DateTime.UtcNow - lastMessageSendTime).TotalMilliseconds < 1000) - { - return; - } - } + var dict = new Dictionary(); + dict["ItemId"] = item.Id.ToString("N", CultureInfo.InvariantCulture); + dict["Progress"] = progress.ToString(CultureInfo.InvariantCulture); - _lastProgressMessageTimes.AddOrUpdate(item.Id, _ => DateTime.UtcNow, (_, _) => DateTime.UtcNow); + try + { + _sessionManager.SendMessageToAdminSessions(SessionMessageType.RefreshProgress, dict, CancellationToken.None); + } + catch + { + } + + var collectionFolders = _libraryManager.GetCollectionFolders(item); - var dict = new Dictionary(); - dict["ItemId"] = item.Id.ToString("N", CultureInfo.InvariantCulture); - dict["Progress"] = progress.ToString(CultureInfo.InvariantCulture); + foreach (var collectionFolder in collectionFolders) + { + var collectionFolderDict = new Dictionary + { + ["ItemId"] = collectionFolder.Id.ToString("N", CultureInfo.InvariantCulture), + ["Progress"] = (collectionFolder.GetRefreshProgress() ?? 0).ToString(CultureInfo.InvariantCulture) + }; try { - _sessionManager.SendMessageToAdminSessions(SessionMessageType.RefreshProgress, dict, CancellationToken.None); + _sessionManager.SendMessageToAdminSessions(SessionMessageType.RefreshProgress, collectionFolderDict, CancellationToken.None); } catch { } - - var collectionFolders = _libraryManager.GetCollectionFolders(item); - - foreach (var collectionFolder in collectionFolders) - { - var collectionFolderDict = new Dictionary - { - ["ItemId"] = collectionFolder.Id.ToString("N", CultureInfo.InvariantCulture), - ["Progress"] = (collectionFolder.GetRefreshProgress() ?? 0).ToString(CultureInfo.InvariantCulture) - }; - - try - { - _sessionManager.SendMessageToAdminSessions(SessionMessageType.RefreshProgress, collectionFolderDict, CancellationToken.None); - } - catch - { - } - } } + } - private void OnProviderRefreshStarted(object sender, GenericEventArgs e) - { - OnProviderRefreshProgress(sender, new GenericEventArgs>(new Tuple(e.Argument, 0))); - } + private void OnProviderRefreshStarted(object sender, GenericEventArgs e) + { + OnProviderRefreshProgress(sender, new GenericEventArgs>(new Tuple(e.Argument, 0))); + } - private void OnProviderRefreshCompleted(object sender, GenericEventArgs e) - { - OnProviderRefreshProgress(sender, new GenericEventArgs>(new Tuple(e.Argument, 100))); + private void OnProviderRefreshCompleted(object sender, GenericEventArgs e) + { + OnProviderRefreshProgress(sender, new GenericEventArgs>(new Tuple(e.Argument, 100))); - _lastProgressMessageTimes.TryRemove(e.Argument.Id, out _); - } + _lastProgressMessageTimes.TryRemove(e.Argument.Id, out _); + } - private static bool EnableRefreshMessage(BaseItem item) - => item is Folder { IsRoot: false, IsTopParent: true } - and not (AggregateFolder or UserRootFolder or UserView or Channel); + private static bool EnableRefreshMessage(BaseItem item) + => item is Folder { IsRoot: false, IsTopParent: true } + and not (AggregateFolder or UserRootFolder or UserView or Channel); - private void OnLibraryItemAdded(object sender, ItemChangeEventArgs e) - => OnLibraryChange(e.Item, e.Parent, _itemsAdded, _foldersAddedTo); + private void OnLibraryItemAdded(object sender, ItemChangeEventArgs e) + => OnLibraryChange(e.Item, e.Parent, _itemsAdded, _foldersAddedTo); - private void OnLibraryItemUpdated(object sender, ItemChangeEventArgs e) - => OnLibraryChange(e.Item, e.Parent, _itemsUpdated, null); + private void OnLibraryItemUpdated(object sender, ItemChangeEventArgs e) + => OnLibraryChange(e.Item, e.Parent, _itemsUpdated, null); - private void OnLibraryItemRemoved(object sender, ItemChangeEventArgs e) - => OnLibraryChange(e.Item, e.Parent, _itemsRemoved, _foldersRemovedFrom); + private void OnLibraryItemRemoved(object sender, ItemChangeEventArgs e) + => OnLibraryChange(e.Item, e.Parent, _itemsRemoved, _foldersRemovedFrom); - private void OnLibraryChange(BaseItem item, BaseItem parent, List itemsList, List foldersList) + private void OnLibraryChange(BaseItem item, BaseItem parent, List itemsList, List foldersList) + { + if (!FilterItem(item)) { - if (!FilterItem(item)) + return; + } + + lock (_libraryChangedSyncLock) + { + var updateDuration = TimeSpan.FromSeconds(_configurationManager.Configuration.LibraryUpdateDuration); + + if (LibraryUpdateTimer is null) { - return; + LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, updateDuration, Timeout.InfiniteTimeSpan); } - - lock (_libraryChangedSyncLock) + else { - var updateDuration = TimeSpan.FromSeconds(_configurationManager.Configuration.LibraryUpdateDuration); - - if (LibraryUpdateTimer is null) - { - LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, updateDuration, Timeout.InfiniteTimeSpan); - } - else - { - LibraryUpdateTimer.Change(updateDuration, Timeout.InfiniteTimeSpan); - } - - if (foldersList is not null && parent is Folder folder) - { - foldersList.Add(folder); - } - - itemsList.Add(item); + LibraryUpdateTimer.Change(updateDuration, Timeout.InfiniteTimeSpan); } - } - /// - /// Libraries the update timer callback. - /// - /// The state. - private async void LibraryUpdateTimerCallback(object state) - { - List foldersAddedTo; - List foldersRemovedFrom; - List itemsUpdated; - List itemsAdded; - List itemsRemoved; - lock (_libraryChangedSyncLock) + if (foldersList is not null && parent is Folder folder) { - // Remove dupes in case some were saved multiple times - foldersAddedTo = _foldersAddedTo - .DistinctBy(x => x.Id) - .ToList(); - - foldersRemovedFrom = _foldersRemovedFrom - .DistinctBy(x => x.Id) - .ToList(); - - itemsUpdated = _itemsUpdated - .Where(i => !_itemsAdded.Contains(i)) - .DistinctBy(x => x.Id) - .ToList(); - - itemsAdded = _itemsAdded.ToList(); - itemsRemoved = _itemsRemoved.ToList(); - - if (LibraryUpdateTimer is not null) - { - LibraryUpdateTimer.Dispose(); - LibraryUpdateTimer = null; - } - - _itemsAdded.Clear(); - _itemsRemoved.Clear(); - _itemsUpdated.Clear(); - _foldersAddedTo.Clear(); - _foldersRemovedFrom.Clear(); + foldersList.Add(folder); } - await SendChangeNotifications(itemsAdded, itemsUpdated, itemsRemoved, foldersAddedTo, foldersRemovedFrom, CancellationToken.None).ConfigureAwait(false); + itemsList.Add(item); } + } - /// - /// Sends the change notifications. - /// - /// The items added. - /// The items updated. - /// The items removed. - /// The folders added to. - /// The folders removed from. - /// The cancellation token. - private async Task SendChangeNotifications( - List itemsAdded, - List itemsUpdated, - List itemsRemoved, - List foldersAddedTo, - List foldersRemovedFrom, - CancellationToken cancellationToken) + /// + /// Libraries the update timer callback. + /// + /// The state. + private async void LibraryUpdateTimerCallback(object state) + { + List foldersAddedTo; + List foldersRemovedFrom; + List itemsUpdated; + List itemsAdded; + List itemsRemoved; + lock (_libraryChangedSyncLock) { - var userIds = _sessionManager.Sessions - .Select(i => i.UserId) - .Where(i => !i.Equals(default)) - .Distinct() - .ToArray(); + // Remove dupes in case some were saved multiple times + foldersAddedTo = _foldersAddedTo + .DistinctBy(x => x.Id) + .ToList(); - foreach (var userId in userIds) + foldersRemovedFrom = _foldersRemovedFrom + .DistinctBy(x => x.Id) + .ToList(); + + itemsUpdated = _itemsUpdated + .Where(i => !_itemsAdded.Contains(i)) + .DistinctBy(x => x.Id) + .ToList(); + + itemsAdded = _itemsAdded.ToList(); + itemsRemoved = _itemsRemoved.ToList(); + + if (LibraryUpdateTimer is not null) { - LibraryUpdateInfo info; - - try - { - info = GetLibraryUpdateInfo(itemsAdded, itemsUpdated, itemsRemoved, foldersAddedTo, foldersRemovedFrom, userId); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error in GetLibraryUpdateInfo"); - return; - } - - if (info.IsEmpty) - { - continue; - } - - try - { - await _sessionManager.SendMessageToUserSessions( - new List { userId }, - SessionMessageType.LibraryChanged, - info, - cancellationToken) - .ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error sending LibraryChanged message"); - } + LibraryUpdateTimer.Dispose(); + LibraryUpdateTimer = null; } + + _itemsAdded.Clear(); + _itemsRemoved.Clear(); + _itemsUpdated.Clear(); + _foldersAddedTo.Clear(); + _foldersRemovedFrom.Clear(); } - /// - /// Gets the library update info. - /// - /// The items added. - /// The items updated. - /// The items removed. - /// The folders added to. - /// The folders removed from. - /// The user id. - /// LibraryUpdateInfo. - private LibraryUpdateInfo GetLibraryUpdateInfo( - List itemsAdded, - List itemsUpdated, - List itemsRemoved, - List foldersAddedTo, - List foldersRemovedFrom, - Guid userId) - { - var user = _userManager.GetUserById(userId); + await SendChangeNotifications(itemsAdded, itemsUpdated, itemsRemoved, foldersAddedTo, foldersRemovedFrom, CancellationToken.None).ConfigureAwait(false); + } - var newAndRemoved = new List(); - newAndRemoved.AddRange(foldersAddedTo); - newAndRemoved.AddRange(foldersRemovedFrom); + /// + /// Sends the change notifications. + /// + /// The items added. + /// The items updated. + /// The items removed. + /// The folders added to. + /// The folders removed from. + /// The cancellation token. + private async Task SendChangeNotifications( + List itemsAdded, + List itemsUpdated, + List itemsRemoved, + List foldersAddedTo, + List foldersRemovedFrom, + CancellationToken cancellationToken) + { + var userIds = _sessionManager.Sessions + .Select(i => i.UserId) + .Where(i => !i.Equals(default)) + .Distinct() + .ToArray(); - var allUserRootChildren = _libraryManager.GetUserRootFolder() - .GetChildren(user, true) - .OfType() - .ToList(); + foreach (var userId in userIds) + { + LibraryUpdateInfo info; - return new LibraryUpdateInfo + try { - ItemsAdded = itemsAdded.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)) - .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) - .Distinct() - .ToArray(), - ItemsUpdated = itemsUpdated.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)) - .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) - .Distinct() - .ToArray(), - ItemsRemoved = itemsRemoved.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user, true)) - .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) - .Distinct() - .ToArray(), - FoldersAddedTo = foldersAddedTo.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)) - .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) - .Distinct() - .ToArray(), - FoldersRemovedFrom = foldersRemovedFrom.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)) - .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) - .Distinct() - .ToArray(), - CollectionFolders = GetTopParentIds(newAndRemoved, allUserRootChildren).ToArray() - }; - } - - private static bool FilterItem(BaseItem item) - { - if (!item.IsFolder && !item.HasPathProtocol) + info = GetLibraryUpdateInfo(itemsAdded, itemsUpdated, itemsRemoved, foldersAddedTo, foldersRemovedFrom, userId); + } + catch (Exception ex) { - return false; + _logger.LogError(ex, "Error in GetLibraryUpdateInfo"); + return; } - if (item is IItemByName && item is not MusicArtist) + if (info.IsEmpty) { - return false; + continue; } - return item.SourceType == SourceType.Library; + try + { + await _sessionManager.SendMessageToUserSessions( + new List { userId }, + SessionMessageType.LibraryChanged, + info, + cancellationToken) + .ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error sending LibraryChanged message"); + } } + } + + /// + /// Gets the library update info. + /// + /// The items added. + /// The items updated. + /// The items removed. + /// The folders added to. + /// The folders removed from. + /// The user id. + /// LibraryUpdateInfo. + private LibraryUpdateInfo GetLibraryUpdateInfo( + List itemsAdded, + List itemsUpdated, + List itemsRemoved, + List foldersAddedTo, + List foldersRemovedFrom, + Guid userId) + { + var user = _userManager.GetUserById(userId); + + var newAndRemoved = new List(); + newAndRemoved.AddRange(foldersAddedTo); + newAndRemoved.AddRange(foldersRemovedFrom); - private IEnumerable GetTopParentIds(List items, List allUserRootChildren) + var allUserRootChildren = _libraryManager.GetUserRootFolder() + .GetChildren(user, true) + .OfType() + .ToList(); + + return new LibraryUpdateInfo { - var list = new List(); + ItemsAdded = itemsAdded.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)) + .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) + .Distinct() + .ToArray(), + ItemsUpdated = itemsUpdated.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)) + .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) + .Distinct() + .ToArray(), + ItemsRemoved = itemsRemoved.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user, true)) + .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) + .Distinct() + .ToArray(), + FoldersAddedTo = foldersAddedTo.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)) + .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) + .Distinct() + .ToArray(), + FoldersRemovedFrom = foldersRemovedFrom.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)) + .Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)) + .Distinct() + .ToArray(), + CollectionFolders = GetTopParentIds(newAndRemoved, allUserRootChildren).ToArray() + }; + } - foreach (var item in items) - { - // If the physical root changed, return the user root - if (item is AggregateFolder) - { - continue; - } - - foreach (var folder in allUserRootChildren) - { - list.Add(folder.Id.ToString("N", CultureInfo.InvariantCulture)); - } - } + private static bool FilterItem(BaseItem item) + { + if (!item.IsFolder && !item.HasPathProtocol) + { + return false; + } - return list.Distinct(StringComparer.Ordinal); + if (item is IItemByName && item is not MusicArtist) + { + return false; } - /// - /// Translates the physical item to user library. - /// - /// The type of item. - /// The item. - /// The user. - /// if set to true [include if not found]. - /// IEnumerable{``0}. - private IEnumerable TranslatePhysicalItemToUserLibrary(T item, User user, bool includeIfNotFound = false) - where T : BaseItem + return item.SourceType == SourceType.Library; + } + + private IEnumerable GetTopParentIds(List items, List allUserRootChildren) + { + var list = new List(); + + foreach (var item in items) { // If the physical root changed, return the user root if (item is AggregateFolder) { - return new[] { _libraryManager.GetUserRootFolder() as T }; + continue; } - // Return it only if it's in the user's library - if (includeIfNotFound || item.IsVisibleStandalone(user)) + foreach (var folder in allUserRootChildren) { - return new[] { item }; + list.Add(folder.Id.ToString("N", CultureInfo.InvariantCulture)); } + } + + return list.Distinct(StringComparer.Ordinal); + } - return Array.Empty(); + /// + /// Translates the physical item to user library. + /// + /// The type of item. + /// The item. + /// The user. + /// if set to true [include if not found]. + /// IEnumerable{``0}. + private IEnumerable TranslatePhysicalItemToUserLibrary(T item, User user, bool includeIfNotFound = false) + where T : BaseItem + { + // If the physical root changed, return the user root + if (item is AggregateFolder) + { + return new[] { _libraryManager.GetUserRootFolder() as T }; } - /// - public void Dispose() + // Return it only if it's in the user's library + if (includeIfNotFound || item.IsVisibleStandalone(user)) { - _libraryManager.ItemAdded -= OnLibraryItemAdded; - _libraryManager.ItemUpdated -= OnLibraryItemUpdated; - _libraryManager.ItemRemoved -= OnLibraryItemRemoved; + return new[] { item }; + } - _providerManager.RefreshCompleted -= OnProviderRefreshCompleted; - _providerManager.RefreshStarted -= OnProviderRefreshStarted; - _providerManager.RefreshProgress -= OnProviderRefreshProgress; + return Array.Empty(); + } - if (LibraryUpdateTimer is not null) - { - LibraryUpdateTimer.Dispose(); - LibraryUpdateTimer = null; - } + /// + public void Dispose() + { + _libraryManager.ItemAdded -= OnLibraryItemAdded; + _libraryManager.ItemUpdated -= OnLibraryItemUpdated; + _libraryManager.ItemRemoved -= OnLibraryItemRemoved; + + _providerManager.RefreshCompleted -= OnProviderRefreshCompleted; + _providerManager.RefreshStarted -= OnProviderRefreshStarted; + _providerManager.RefreshProgress -= OnProviderRefreshProgress; + + if (LibraryUpdateTimer is not null) + { + LibraryUpdateTimer.Dispose(); + LibraryUpdateTimer = null; } } } -- cgit v1.2.3 From 0ea9f713f48e433f66a95bad7bf765cdd2589fa3 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 15 Nov 2023 20:07:07 -0500 Subject: Document LibraryChangedNotifier --- .../EntryPoints/LibraryChangedNotifier.cs | 50 ++++++---------------- 1 file changed, 13 insertions(+), 37 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index 40dc00e1a..8e0f37d89 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -1,7 +1,5 @@ #nullable disable -#pragma warning disable CS1591 - using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -25,6 +23,9 @@ using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.EntryPoints; +/// +/// A that notifies users when libraries are updated. +/// public sealed class LibraryChangedNotifier : IServerEntryPoint { private readonly ILibraryManager _libraryManager; @@ -42,6 +43,15 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint private readonly List _itemsUpdated = new(); private readonly ConcurrentDictionary _lastProgressMessageTimes = new(); + /// + /// Initializes a new instance of the class. + /// + /// The . + /// The . + /// The . + /// The . + /// The . + /// The . public LibraryChangedNotifier( ILibraryManager libraryManager, IServerConfigurationManager configurationManager, @@ -58,12 +68,9 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint _providerManager = providerManager; } - /// - /// Gets or sets the library update timer. - /// - /// The library update timer. private Timer LibraryUpdateTimer { get; set; } + /// public Task RunAsync() { _libraryManager.ItemAdded += OnLibraryItemAdded; @@ -184,10 +191,6 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint } } - /// - /// Libraries the update timer callback. - /// - /// The state. private async void LibraryUpdateTimerCallback(object state) { List foldersAddedTo; @@ -230,15 +233,6 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint await SendChangeNotifications(itemsAdded, itemsUpdated, itemsRemoved, foldersAddedTo, foldersRemovedFrom, CancellationToken.None).ConfigureAwait(false); } - /// - /// Sends the change notifications. - /// - /// The items added. - /// The items updated. - /// The items removed. - /// The folders added to. - /// The folders removed from. - /// The cancellation token. private async Task SendChangeNotifications( List itemsAdded, List itemsUpdated, @@ -288,16 +282,6 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint } } - /// - /// Gets the library update info. - /// - /// The items added. - /// The items updated. - /// The items removed. - /// The folders added to. - /// The folders removed from. - /// The user id. - /// LibraryUpdateInfo. private LibraryUpdateInfo GetLibraryUpdateInfo( List itemsAdded, List itemsUpdated, @@ -379,14 +363,6 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint return list.Distinct(StringComparer.Ordinal); } - /// - /// Translates the physical item to user library. - /// - /// The type of item. - /// The item. - /// The user. - /// if set to true [include if not found]. - /// IEnumerable{``0}. private IEnumerable TranslatePhysicalItemToUserLibrary(T item, User user, bool includeIfNotFound = false) where T : BaseItem { -- cgit v1.2.3 From 4e61c2b4ec7ff1a25ac7b8e71bcf6a2833f78e75 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 15 Nov 2023 20:31:07 -0500 Subject: Enable nullable in LibraryChangedNotifier --- .../EntryPoints/LibraryChangedNotifier.cs | 43 +++++++++++----------- 1 file changed, 21 insertions(+), 22 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index 8e0f37d89..a83d7a410 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -43,6 +41,8 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint private readonly List _itemsUpdated = new(); private readonly ConcurrentDictionary _lastProgressMessageTimes = new(); + private Timer? _libraryUpdateTimer; + /// /// Initializes a new instance of the class. /// @@ -68,8 +68,6 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint _providerManager = providerManager; } - private Timer LibraryUpdateTimer { get; set; } - /// public Task RunAsync() { @@ -84,7 +82,7 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint return Task.CompletedTask; } - private void OnProviderRefreshProgress(object sender, GenericEventArgs> e) + private void OnProviderRefreshProgress(object? sender, GenericEventArgs> e) { var item = e.Argument.Item1; @@ -137,12 +135,12 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint } } - private void OnProviderRefreshStarted(object sender, GenericEventArgs e) + private void OnProviderRefreshStarted(object? sender, GenericEventArgs e) { OnProviderRefreshProgress(sender, new GenericEventArgs>(new Tuple(e.Argument, 0))); } - private void OnProviderRefreshCompleted(object sender, GenericEventArgs e) + private void OnProviderRefreshCompleted(object? sender, GenericEventArgs e) { OnProviderRefreshProgress(sender, new GenericEventArgs>(new Tuple(e.Argument, 100))); @@ -153,16 +151,16 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint => item is Folder { IsRoot: false, IsTopParent: true } and not (AggregateFolder or UserRootFolder or UserView or Channel); - private void OnLibraryItemAdded(object sender, ItemChangeEventArgs e) + private void OnLibraryItemAdded(object? sender, ItemChangeEventArgs e) => OnLibraryChange(e.Item, e.Parent, _itemsAdded, _foldersAddedTo); - private void OnLibraryItemUpdated(object sender, ItemChangeEventArgs e) + private void OnLibraryItemUpdated(object? sender, ItemChangeEventArgs e) => OnLibraryChange(e.Item, e.Parent, _itemsUpdated, null); - private void OnLibraryItemRemoved(object sender, ItemChangeEventArgs e) + private void OnLibraryItemRemoved(object? sender, ItemChangeEventArgs e) => OnLibraryChange(e.Item, e.Parent, _itemsRemoved, _foldersRemovedFrom); - private void OnLibraryChange(BaseItem item, BaseItem parent, List itemsList, List foldersList) + private void OnLibraryChange(BaseItem item, BaseItem parent, List itemsList, List? foldersList) { if (!FilterItem(item)) { @@ -173,13 +171,13 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint { var updateDuration = TimeSpan.FromSeconds(_configurationManager.Configuration.LibraryUpdateDuration); - if (LibraryUpdateTimer is null) + if (_libraryUpdateTimer is null) { - LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, updateDuration, Timeout.InfiniteTimeSpan); + _libraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, updateDuration, Timeout.InfiniteTimeSpan); } else { - LibraryUpdateTimer.Change(updateDuration, Timeout.InfiniteTimeSpan); + _libraryUpdateTimer.Change(updateDuration, Timeout.InfiniteTimeSpan); } if (foldersList is not null && parent is Folder folder) @@ -191,7 +189,7 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint } } - private async void LibraryUpdateTimerCallback(object state) + private async void LibraryUpdateTimerCallback(object? state) { List foldersAddedTo; List foldersRemovedFrom; @@ -217,10 +215,10 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint itemsAdded = _itemsAdded.ToList(); itemsRemoved = _itemsRemoved.ToList(); - if (LibraryUpdateTimer is not null) + if (_libraryUpdateTimer is not null) { - LibraryUpdateTimer.Dispose(); - LibraryUpdateTimer = null; + _libraryUpdateTimer.Dispose(); + _libraryUpdateTimer = null; } _itemsAdded.Clear(); @@ -291,6 +289,7 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint Guid userId) { var user = _userManager.GetUserById(userId); + ArgumentNullException.ThrowIfNull(user); var newAndRemoved = new List(); newAndRemoved.AddRange(foldersAddedTo); @@ -369,7 +368,7 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint // If the physical root changed, return the user root if (item is AggregateFolder) { - return new[] { _libraryManager.GetUserRootFolder() as T }; + return _libraryManager.GetUserRootFolder() is T t ? new[] { t } : Array.Empty(); } // Return it only if it's in the user's library @@ -392,10 +391,10 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint _providerManager.RefreshStarted -= OnProviderRefreshStarted; _providerManager.RefreshProgress -= OnProviderRefreshProgress; - if (LibraryUpdateTimer is not null) + if (_libraryUpdateTimer is not null) { - LibraryUpdateTimer.Dispose(); - LibraryUpdateTimer = null; + _libraryUpdateTimer.Dispose(); + _libraryUpdateTimer = null; } } } -- cgit v1.2.3 From 380c367e4985899f24277282a74f01bf5b922d87 Mon Sep 17 00:00:00 2001 From: Lucas Fogolin Date: Fri, 17 Nov 2023 20:08:52 +0000 Subject: Translated using Weblate (Portuguese (Brazil)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pt_BR/ --- Emby.Server.Implementations/Localization/Core/pt-BR.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/pt-BR.json b/Emby.Server.Implementations/Localization/Core/pt-BR.json index b9b93b7b6..2c8c46050 100644 --- a/Emby.Server.Implementations/Localization/Core/pt-BR.json +++ b/Emby.Server.Implementations/Localization/Core/pt-BR.json @@ -124,5 +124,7 @@ "TaskKeyframeExtractor": "Extrator de quadro-chave", "TaskKeyframeExtractorDescription": "Extrai quadros-chave de arquivos de vídeo para criar listas de reprodução HLS mais precisas. Esta tarefa pode ser executada por um longo tempo.", "External": "Externo", - "HearingImpaired": "Deficiência Auditiva" + "HearingImpaired": "Deficiência Auditiva", + "TaskRefreshTrickplayImages": "Gerar imagens Trickplay", + "TaskRefreshTrickplayImagesDescription": "Cria prévias Trickplay para vídeos em bibliotecas em que o recurso está habilitado." } -- cgit v1.2.3 From 2f87158e682fc4f7257053d4792bf390c0fe4a0f Mon Sep 17 00:00:00 2001 From: Atharva Vaidya Date: Mon, 20 Nov 2023 17:16:30 +0000 Subject: Translated using Weblate (Marathi) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/mr/ --- Emby.Server.Implementations/Localization/Core/mr.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/mr.json b/Emby.Server.Implementations/Localization/Core/mr.json index a8fb26b91..13c58e0ab 100644 --- a/Emby.Server.Implementations/Localization/Core/mr.json +++ b/Emby.Server.Implementations/Localization/Core/mr.json @@ -123,5 +123,7 @@ "DeviceOnlineWithName": "{0} कनेक्ट झाले", "DeviceOfflineWithName": "{0} डिस्कनेक्ट झाला आहे", "AuthenticationSucceededWithUserName": "{0} यशस्वीरित्या प्रमाणीकृत", - "HearingImpaired": "कर्णबधीर" + "HearingImpaired": "कर्णबधीर", + "TaskRefreshTrickplayImages": "ट्रिकप्ले प्रतिमा तयार करा", + "TaskRefreshTrickplayImagesDescription": "सक्षम लायब्ररीमधील व्हिडिओंसाठी ट्रिकप्ले पूर्वावलोकन तयार करते." } -- cgit v1.2.3 From fd1dc860c9981055634595e2064d13b21227927b Mon Sep 17 00:00:00 2001 From: Ahmad Mujahid Date: Wed, 22 Nov 2023 09:55:04 +0000 Subject: Translated using Weblate (Arabic) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ar/ --- Emby.Server.Implementations/Localization/Core/ar.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ar.json b/Emby.Server.Implementations/Localization/Core/ar.json index 93d50e6e3..ecdc01a3d 100644 --- a/Emby.Server.Implementations/Localization/Core/ar.json +++ b/Emby.Server.Implementations/Localization/Core/ar.json @@ -124,5 +124,6 @@ "TaskKeyframeExtractorDescription": "يستخرج الإطارات الرئيسية من ملفات الفيديو لكي ينشئ قوائم تشغيل بث HTTP المباشر. قد تستمر هذه العملية لوقت طويل.", "TaskKeyframeExtractor": "مستخرج الإطار الرئيسي", "External": "خارجي", - "HearingImpaired": "ضعاف السمع" + "HearingImpaired": "ضعاف السمع", + "TaskRefreshTrickplayImages": "توليد صور Trickplay" } -- cgit v1.2.3