From ee40f210494985286f5afd37aca4a05dba6f4763 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Thu, 1 Oct 2020 18:59:46 +0100 Subject: [PATCH 01/35] Update ApplicationHost.cs --- .../ApplicationHost.cs | 371 ++++++------------ 1 file changed, 116 insertions(+), 255 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 7a46fdf2e..91fb68ed1 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1,7 +1,6 @@ #pragma warning disable CS1591 using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; @@ -9,7 +8,6 @@ using System.IO; using System.Linq; using System.Net; using System.Net.Http; -using System.Net.Sockets; using System.Reflection; using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; @@ -17,8 +15,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using Emby.Dlna; -using Emby.Dlna.Main; -using Emby.Dlna.Ssdp; +using Emby.Dlna.Common; using Emby.Drawing; using Emby.Notifications; using Emby.Photos; @@ -30,13 +27,11 @@ using Emby.Server.Implementations.Cryptography; using Emby.Server.Implementations.Data; using Emby.Server.Implementations.Devices; using Emby.Server.Implementations.Dto; -using Emby.Server.Implementations.HttpServer; using Emby.Server.Implementations.HttpServer.Security; using Emby.Server.Implementations.IO; using Emby.Server.Implementations.Library; using Emby.Server.Implementations.LiveTv; using Emby.Server.Implementations.Localization; -using Emby.Server.Implementations.Net; using Emby.Server.Implementations.Playlists; using Emby.Server.Implementations.Plugins; using Emby.Server.Implementations.QuickConnect; @@ -48,10 +43,13 @@ using Emby.Server.Implementations.SyncPlay; using Emby.Server.Implementations.TV; using Emby.Server.Implementations.Updates; using Jellyfin.Api.Helpers; +using Jellyfin.Networking.Advertising; +using Jellyfin.Networking.Gateway; +using Jellyfin.Networking.Manager; +using Jellyfin.Networking.UPnP; using MediaBrowser.Common; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Events; -using MediaBrowser.Common.Json; using MediaBrowser.Common.Net; using MediaBrowser.Common.Plugins; using MediaBrowser.Common.Updates; @@ -61,7 +59,6 @@ using MediaBrowser.Controller.Chapters; using MediaBrowser.Controller.Collections; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Devices; -using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; @@ -86,11 +83,9 @@ using MediaBrowser.LocalMetadata.Savers; using MediaBrowser.MediaEncoding.BdInfo; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Cryptography; -using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; -using MediaBrowser.Model.Net; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.System; using MediaBrowser.Model.Tasks; @@ -99,6 +94,7 @@ using MediaBrowser.Providers.Manager; using MediaBrowser.Providers.Plugins.TheTvdb; using MediaBrowser.Providers.Subtitles; using MediaBrowser.XbmcMetadata.Providers; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -119,7 +115,6 @@ namespace Emby.Server.Implementations private static readonly string[] _relevantEnvVarPrefixes = { "JELLYFIN_", "DOTNET_", "ASPNETCORE_" }; private readonly IFileSystem _fileSystemManager; - private readonly INetworkManager _networkManager; private readonly IXmlSerializer _xmlSerializer; private readonly IJsonSerializer _jsonSerializer; private readonly IStartupOptions _startupOptions; @@ -214,7 +209,7 @@ namespace Emby.Server.Implementations private readonly List _disposableParts = new List(); /// - /// Gets the configuration manager. + /// Gets or sets the configuration manager. /// /// The configuration manager. protected IConfigurationManager ConfigurationManager { get; set; } @@ -247,23 +242,18 @@ namespace Emby.Server.Implementations /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. public ApplicationHost( IServerApplicationPaths applicationPaths, ILoggerFactory loggerFactory, IStartupOptions options, IFileSystem fileSystem, - INetworkManager networkManager, IServiceCollection serviceCollection) { _xmlSerializer = new MyXmlSerializer(); - _jsonSerializer = new JsonSerializer(); - - ServiceCollection = serviceCollection; + _jsonSerializer = new JsonSerializer(); - _networkManager = networkManager; - networkManager.LocalSubnetsFn = GetConfiguredLocalSubnets; + ServiceCollection = serviceCollection; ApplicationPaths = applicationPaths; LoggerFactory = loggerFactory; @@ -271,6 +261,8 @@ namespace Emby.Server.Implementations ConfigurationManager = new ServerConfigurationManager(ApplicationPaths, LoggerFactory, _xmlSerializer, _fileSystemManager); + NetManager = new NetworkManager((IServerConfigurationManager)ConfigurationManager, LoggerFactory.CreateLogger()); + Logger = LoggerFactory.CreateLogger(); _startupOptions = options; @@ -283,20 +275,19 @@ namespace Emby.Server.Implementations fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem)); - _networkManager.NetworkChanged += OnNetworkChanged; - CertificateInfo = new CertificateInfo { Path = ServerConfigurationManager.Configuration.CertificatePath, Password = ServerConfigurationManager.Configuration.CertificatePassword }; Certificate = GetCertificate(CertificateInfo); - - ApplicationVersion = typeof(ApplicationHost).Assembly.GetName().Version; - ApplicationVersionString = ApplicationVersion.ToString(3); - ApplicationUserAgent = Name.Replace(' ', '-') + "/" + ApplicationVersionString; } + /// + /// Gets the NetworkManager instance. + /// + public INetworkManager NetManager { get; internal set; } + public string ExpandVirtualPath(string path) { var appPaths = ApplicationPaths; @@ -313,27 +304,17 @@ namespace Emby.Server.Implementations .Replace(appPaths.InternalMetadataPath, appPaths.VirtualInternalMetadataPath, StringComparison.OrdinalIgnoreCase); } - private string[] GetConfiguredLocalSubnets() - { - return ServerConfigurationManager.Configuration.LocalNetworkSubnets; - } - - private void OnNetworkChanged(object sender, EventArgs e) - { - _validAddressResults.Clear(); - } + /// + public Version ApplicationVersion { get; } = typeof(ApplicationHost).Assembly.GetName().Version; /// - public Version ApplicationVersion { get; } - - /// - public string ApplicationVersionString { get; } + public string ApplicationVersionString { get; } = typeof(ApplicationHost).Assembly.GetName().Version.ToString(3); /// /// Gets the current application user agent. /// /// The application user agent. - public string ApplicationUserAgent { get; } + public string ApplicationUserAgent => Name.Replace(' ', '-') + "/" + ApplicationVersionString; /// /// Gets the email address for use within a comment section of a user agent field. @@ -403,7 +384,7 @@ namespace Emby.Server.Implementations /// /// Resolves this instance. /// - /// The type + /// The type. /// ``0. public T Resolve() => ServiceProvider.GetService(); @@ -499,21 +480,6 @@ namespace Emby.Server.Implementations HttpsPort = ServerConfiguration.DefaultHttpsPort; } - if (Plugins != null) - { - var pluginBuilder = new StringBuilder(); - - foreach (var plugin in Plugins) - { - pluginBuilder.Append(plugin.Name) - .Append(' ') - .Append(plugin.Version) - .AppendLine(); - } - - Logger.LogInformation("Plugins: {Plugins}", pluginBuilder.ToString()); - } - DiscoverTypes(); RegisterServices(); @@ -538,7 +504,10 @@ namespace Emby.Server.Implementations ServiceCollection.AddSingleton(_fileSystemManager); ServiceCollection.AddSingleton(); - ServiceCollection.AddSingleton(_networkManager); + ServiceCollection.AddSingleton(NetManager); + ServiceCollection.AddSingleton(); + ServiceCollection.AddSingleton(); + ServiceCollection.AddSingleton(); ServiceCollection.AddSingleton(); @@ -550,8 +519,6 @@ namespace Emby.Server.Implementations ServiceCollection.AddSingleton(); - ServiceCollection.AddSingleton(); - ServiceCollection.AddSingleton(); ServiceCollection.AddSingleton(); @@ -628,8 +595,6 @@ namespace Emby.Server.Implementations ServiceCollection.AddSingleton(); - ServiceCollection.AddSingleton(); - ServiceCollection.AddSingleton(); ServiceCollection.AddSingleton(); @@ -785,6 +750,21 @@ namespace Emby.Server.Implementations .Where(i => i != null) .ToArray(); + if (Plugins != null) + { + var pluginBuilder = new StringBuilder(); + + foreach (var plugin in Plugins) + { + pluginBuilder.Append(plugin.Name) + .Append(' ') + .Append(plugin.Version) + .AppendLine(); + } + + Logger.LogInformation("Plugins: {Plugins}", pluginBuilder.ToString()); + } + _urlPrefixes = GetUrlPrefixes().ToArray(); _webSocketManager.Init(GetExports()); @@ -819,38 +799,6 @@ namespace Emby.Server.Implementations { try { - if (plugin is IPluginAssembly assemblyPlugin) - { - var assembly = plugin.GetType().Assembly; - var assemblyName = assembly.GetName(); - var assemblyFilePath = assembly.Location; - - var dataFolderPath = Path.Combine(ApplicationPaths.PluginsPath, Path.GetFileNameWithoutExtension(assemblyFilePath)); - - assemblyPlugin.SetAttributes(assemblyFilePath, dataFolderPath, assemblyName.Version); - - try - { - var idAttributes = assembly.GetCustomAttributes(typeof(GuidAttribute), true); - if (idAttributes.Length > 0) - { - var attribute = (GuidAttribute)idAttributes[0]; - var assemblyId = new Guid(attribute.Value); - - assemblyPlugin.SetId(assemblyId); - } - } - catch (Exception ex) - { - Logger.LogError(ex, "Error getting plugin Id from {PluginName}.", plugin.GetType().FullName); - } - } - - if (plugin is IHasPluginConfiguration hasPluginConfiguration) - { - hasPluginConfiguration.SetStartupInfo(s => Directory.CreateDirectory(s)); - } - plugin.RegisterServices(ServiceCollection); } catch (Exception ex) @@ -880,6 +828,21 @@ namespace Emby.Server.Implementations try { exportedTypes = ass.GetExportedTypes(); + + try + { + Type reg = (Type)exportedTypes.Where(p => string.Equals(p.Name, "PluginRegistration", StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); + if (reg != null) + { + var pluginRegistration = Activator.CreateInstance(reg); + reg.InvokeMember("RegisterServices", BindingFlags.InvokeMethod, null, pluginRegistration, new object[] { ServiceCollection }, CultureInfo.InvariantCulture); + } + } + catch (Exception ex) + { + Logger.LogError(ex, "Error registering {Assembly} with D.I.", ass.FullName); + continue; + } } catch (FileNotFoundException ex) { @@ -1088,7 +1051,7 @@ namespace Emby.Server.Implementations { // No metafile, so lets see if the folder is versioned. metafile = dir.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries)[^1]; - + int versionIndex = dir.LastIndexOf('_'); if (versionIndex != -1 && Version.TryParse(dir.Substring(versionIndex + 1), out Version ver)) { @@ -1097,9 +1060,9 @@ namespace Emby.Server.Implementations } else { - // Un-versioned folder - Add it under the path name and version 0.0.0.1. + // Un-versioned folder - Add it under the path name and version 0.0.0.1. versions.Add((new Version(0, 0, 0, 1), metafile, dir)); - } + } } } catch @@ -1186,9 +1149,6 @@ namespace Emby.Server.Implementations // MediaEncoding yield return typeof(MediaBrowser.MediaEncoding.Encoder.MediaEncoder).Assembly; - // Dlna - yield return typeof(DlnaEntryPoint).Assembly; - // Local metadata yield return typeof(BoxSetXmlSaver).Assembly; @@ -1209,13 +1169,10 @@ namespace Emby.Server.Implementations /// /// Gets the system status. /// - /// The cancellation token. + /// Where this request originated. /// SystemInfo. - public async Task GetSystemInfo(CancellationToken cancellationToken) + public SystemInfo GetSystemInfo(IPAddress source) { - var localAddress = await GetLocalApiUrl(cancellationToken).ConfigureAwait(false); - var transcodingTempPath = ConfigurationManager.GetTranscodePath(); - return new SystemInfo { HasPendingRestart = HasPendingRestart, @@ -1235,9 +1192,9 @@ namespace Emby.Server.Implementations CanSelfRestart = CanSelfRestart, CanLaunchWebBrowser = CanLaunchWebBrowser, HasUpdateAvailable = HasUpdateAvailable, - TranscodingTempPath = transcodingTempPath, + TranscodingTempPath = ConfigurationManager.GetTranscodePath(), ServerName = FriendlyName, - LocalAddress = localAddress, + LocalAddress = GetSmartApiUrl(source), SupportsLibraryMonitor = true, EncoderLocation = _mediaEncoder.EncoderLocation, SystemArchitecture = RuntimeInformation.OSArchitecture, @@ -1246,14 +1203,12 @@ namespace Emby.Server.Implementations } public IEnumerable GetWakeOnLanInfo() - => _networkManager.GetMacAddresses() + => NetManager.GetMacAddresses() .Select(i => new WakeOnLanInfo(i)) .ToList(); - public async Task GetPublicSystemInfo(CancellationToken cancellationToken) + public PublicSystemInfo GetPublicSystemInfo(IPAddress source) { - var localAddress = await GetLocalApiUrl(cancellationToken).ConfigureAwait(false); - return new PublicSystemInfo { Version = ApplicationVersionString, @@ -1261,7 +1216,7 @@ namespace Emby.Server.Implementations Id = SystemId, OperatingSystem = OperatingSystem.Id.ToString(), ServerName = FriendlyName, - LocalAddress = localAddress, + LocalAddress = GetSmartApiUrl(source), StartupWizardCompleted = ConfigurationManager.CommonConfiguration.IsStartupWizardCompleted }; } @@ -1270,186 +1225,92 @@ namespace Emby.Server.Implementations public bool ListenWithHttps => Certificate != null && ServerConfigurationManager.Configuration.EnableHttps; /// - public async Task GetLocalApiUrl(CancellationToken cancellationToken) + public string GetSmartApiUrl(IPAddress ipAddress, int? port = null) { - try + // Published server ends with a / + if (_startupOptions.PublishedServerUrl != null) { - // Return the first matched address, if found, or the first known local address - var addresses = await GetLocalIpAddressesInternal(false, 1, cancellationToken).ConfigureAwait(false); - if (addresses.Count == 0) - { - return null; - } - - return GetLocalApiUrl(addresses[0]); - } - catch (Exception ex) - { - Logger.LogError(ex, "Error getting local Ip address information"); + // Published server ends with a '/', so we need to remove it. + return _startupOptions.PublishedServerUrl.ToString().Trim('/'); } - return null; + string smart = NetManager.GetBindInterface(ipAddress, out port); + // If the smartAPI doesn't start with http then treat it as a host or ip. + if (smart.StartsWith("http", StringComparison.OrdinalIgnoreCase)) + { + return smart.Trim('/'); + } + + return GetLocalApiUrl(smart.Trim('/'), null, port); } - /// - /// Removes the scope id from IPv6 addresses. - /// - /// The IPv6 address. - /// The IPv6 address without the scope id. - private ReadOnlySpan RemoveScopeId(ReadOnlySpan address) + public string GetSmartApiUrl(HttpRequest request, int? port = null) { - var index = address.IndexOf('%'); - if (index == -1) + // Published server ends with a / + if (_startupOptions.PublishedServerUrl != null) { - return address; + // Published server ends with a '/', so we need to remove it. + return _startupOptions.PublishedServerUrl.ToString().Trim('/'); } - return address.Slice(0, index); + string smart = NetManager.GetBindInterface(request, out port); + // If the smartAPI doesn't start with http then treat it as a host or ip. + if (smart.StartsWith("http", StringComparison.OrdinalIgnoreCase)) + { + return smart.Trim('/'); + } + + return GetLocalApiUrl(smart.Trim('/'), request.Scheme, port); } - /// - public string GetLocalApiUrl(IPAddress ipAddress) + public string GetSmartApiUrl(string hostname, int? port = null) { - if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6) + // Published server ends with a / + if (_startupOptions.PublishedServerUrl != null) { - var str = RemoveScopeId(ipAddress.ToString()); - Span span = new char[str.Length + 2]; - span[0] = '['; - str.CopyTo(span.Slice(1)); - span[^1] = ']'; - - return GetLocalApiUrl(span); + // Published server ends with a '/', so we need to remove it. + return _startupOptions.PublishedServerUrl.ToString().Trim('/'); } - return GetLocalApiUrl(ipAddress.ToString()); + string smart = NetManager.GetBindInterface(hostname, out port); + + // If the smartAPI doesn't start with http then treat it as a host or ip. + if (smart.StartsWith("http", StringComparison.OrdinalIgnoreCase)) + { + return smart.Trim('/'); + } + + return GetLocalApiUrl(smart.Trim('/'), null, port); } /// public string GetLoopbackHttpApiUrl() { + if (NetManager.IsIP6Enabled) + { + return GetLocalApiUrl("::1", Uri.UriSchemeHttp, HttpPort); + } + return GetLocalApiUrl("127.0.0.1", Uri.UriSchemeHttp, HttpPort); } /// - public string GetLocalApiUrl(ReadOnlySpan host, string scheme = null, int? port = null) + public string GetLocalApiUrl(string host, string scheme = null, int? port = null) { // NOTE: If no BaseUrl is set then UriBuilder appends a trailing slash, but if there is no BaseUrl it does // not. For consistency, always trim the trailing slash. return new UriBuilder { Scheme = scheme ?? (ListenWithHttps ? Uri.UriSchemeHttps : Uri.UriSchemeHttp), - Host = host.ToString(), + Host = host, Port = port ?? (ListenWithHttps ? HttpsPort : HttpPort), Path = ServerConfigurationManager.Configuration.BaseUrl }.ToString().TrimEnd('/'); } - public Task> GetLocalIpAddresses(CancellationToken cancellationToken) - { - return GetLocalIpAddressesInternal(true, 0, cancellationToken); - } - - private async Task> GetLocalIpAddressesInternal(bool allowLoopback, int limit, CancellationToken cancellationToken) - { - var addresses = ServerConfigurationManager - .Configuration - .LocalNetworkAddresses - .Select(x => NormalizeConfiguredLocalAddress(x)) - .Where(i => i != null) - .ToList(); - - if (addresses.Count == 0) - { - addresses.AddRange(_networkManager.GetLocalIpAddresses()); - } - - var resultList = new List(); - - foreach (var address in addresses) - { - if (!allowLoopback) - { - if (address.Equals(IPAddress.Loopback) || address.Equals(IPAddress.IPv6Loopback)) - { - continue; - } - } - - if (await IsLocalIpAddressValidAsync(address, cancellationToken).ConfigureAwait(false)) - { - resultList.Add(address); - - if (limit > 0 && resultList.Count >= limit) - { - return resultList; - } - } - } - - return resultList; - } - - public IPAddress NormalizeConfiguredLocalAddress(ReadOnlySpan address) - { - var index = address.Trim('/').IndexOf('/'); - if (index != -1) - { - address = address.Slice(index + 1); - } - - if (IPAddress.TryParse(address.Trim('/'), out IPAddress result)) - { - return result; - } - - return null; - } - - private readonly ConcurrentDictionary _validAddressResults = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); - - private async Task IsLocalIpAddressValidAsync(IPAddress address, CancellationToken cancellationToken) - { - if (address.Equals(IPAddress.Loopback) - || address.Equals(IPAddress.IPv6Loopback)) - { - return true; - } - - var apiUrl = GetLocalApiUrl(address) + "/system/ping"; - - if (_validAddressResults.TryGetValue(apiUrl, out var cachedResult)) - { - return cachedResult; - } - - try - { - using var request = new HttpRequestMessage(HttpMethod.Post, apiUrl); - using var response = await _httpClientFactory.CreateClient(NamedClient.Default) - .SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - - await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); - var result = await System.Text.Json.JsonSerializer.DeserializeAsync(stream, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); - var valid = string.Equals(Name, result, StringComparison.OrdinalIgnoreCase); - - _validAddressResults.AddOrUpdate(apiUrl, valid, (k, v) => valid); - Logger.LogDebug("Ping test result to {0}. Success: {1}", apiUrl, valid); - return valid; - } - catch (OperationCanceledException) - { - Logger.LogDebug("Ping test result to {0}. Success: {1}", apiUrl, "Cancelled"); - throw; - } - catch (Exception ex) - { - Logger.LogDebug(ex, "Ping test result to {0}. Success: {1}", apiUrl, false); - - _validAddressResults.AddOrUpdate(apiUrl, false, (k, v) => false); - return false; - } - } - + /// + /// Gets the servers friendly name. + /// public string FriendlyName => string.IsNullOrEmpty(ServerConfigurationManager.Configuration.ServerName) ? Environment.MachineName @@ -1521,7 +1382,7 @@ namespace Emby.Server.Implementations foreach (var assembly in assemblies) { - Logger.LogDebug("Found API endpoints in plugin {Name}", assembly.FullName); + Logger.LogDebug("Found API endpoints in plugin {name}", assembly.FullName); yield return assembly; } } From 0738a2dc4b64c3656cc8a613d1dc5b2c09ab0240 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Thu, 1 Oct 2020 19:22:58 +0100 Subject: [PATCH 02/35] Update ApplicationHost.cs --- .../ApplicationHost.cs | 358 +++++++++++++----- 1 file changed, 256 insertions(+), 102 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 91fb68ed1..f36bc0eef 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1,6 +1,7 @@ #pragma warning disable CS1591 using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; @@ -8,6 +9,7 @@ using System.IO; using System.Linq; using System.Net; using System.Net.Http; +using System.Net.Sockets; using System.Reflection; using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; @@ -15,7 +17,8 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using Emby.Dlna; -using Emby.Dlna.Common; +using Emby.Dlna.Main; +using Emby.Dlna.Ssdp; using Emby.Drawing; using Emby.Notifications; using Emby.Photos; @@ -27,11 +30,13 @@ using Emby.Server.Implementations.Cryptography; using Emby.Server.Implementations.Data; using Emby.Server.Implementations.Devices; using Emby.Server.Implementations.Dto; +using Emby.Server.Implementations.HttpServer; using Emby.Server.Implementations.HttpServer.Security; using Emby.Server.Implementations.IO; using Emby.Server.Implementations.Library; using Emby.Server.Implementations.LiveTv; using Emby.Server.Implementations.Localization; +using Emby.Server.Implementations.Net; using Emby.Server.Implementations.Playlists; using Emby.Server.Implementations.Plugins; using Emby.Server.Implementations.QuickConnect; @@ -43,13 +48,10 @@ using Emby.Server.Implementations.SyncPlay; using Emby.Server.Implementations.TV; using Emby.Server.Implementations.Updates; using Jellyfin.Api.Helpers; -using Jellyfin.Networking.Advertising; -using Jellyfin.Networking.Gateway; -using Jellyfin.Networking.Manager; -using Jellyfin.Networking.UPnP; using MediaBrowser.Common; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Events; +using MediaBrowser.Common.Json; using MediaBrowser.Common.Net; using MediaBrowser.Common.Plugins; using MediaBrowser.Common.Updates; @@ -59,6 +61,7 @@ using MediaBrowser.Controller.Chapters; using MediaBrowser.Controller.Collections; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Devices; +using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; @@ -83,9 +86,11 @@ using MediaBrowser.LocalMetadata.Savers; using MediaBrowser.MediaEncoding.BdInfo; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Cryptography; +using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; +using MediaBrowser.Model.Net; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.System; using MediaBrowser.Model.Tasks; @@ -94,7 +99,6 @@ using MediaBrowser.Providers.Manager; using MediaBrowser.Providers.Plugins.TheTvdb; using MediaBrowser.Providers.Subtitles; using MediaBrowser.XbmcMetadata.Providers; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -115,6 +119,7 @@ namespace Emby.Server.Implementations private static readonly string[] _relevantEnvVarPrefixes = { "JELLYFIN_", "DOTNET_", "ASPNETCORE_" }; private readonly IFileSystem _fileSystemManager; + private readonly INetworkManager _networkManager; private readonly IXmlSerializer _xmlSerializer; private readonly IJsonSerializer _jsonSerializer; private readonly IStartupOptions _startupOptions; @@ -209,7 +214,7 @@ namespace Emby.Server.Implementations private readonly List _disposableParts = new List(); /// - /// Gets or sets the configuration manager. + /// Gets the configuration manager. /// /// The configuration manager. protected IConfigurationManager ConfigurationManager { get; set; } @@ -242,27 +247,30 @@ namespace Emby.Server.Implementations /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. + /// Instance of the interface. /// Instance of the interface. public ApplicationHost( IServerApplicationPaths applicationPaths, ILoggerFactory loggerFactory, IStartupOptions options, IFileSystem fileSystem, + INetworkManager networkManager, IServiceCollection serviceCollection) { _xmlSerializer = new MyXmlSerializer(); - _jsonSerializer = new JsonSerializer(); - + _jsonSerializer = new JsonSerializer(); + ServiceCollection = serviceCollection; + _networkManager = networkManager; + networkManager.LocalSubnetsFn = GetConfiguredLocalSubnets; + ApplicationPaths = applicationPaths; LoggerFactory = loggerFactory; _fileSystemManager = fileSystem; ConfigurationManager = new ServerConfigurationManager(ApplicationPaths, LoggerFactory, _xmlSerializer, _fileSystemManager); - NetManager = new NetworkManager((IServerConfigurationManager)ConfigurationManager, LoggerFactory.CreateLogger()); - Logger = LoggerFactory.CreateLogger(); _startupOptions = options; @@ -275,18 +283,19 @@ namespace Emby.Server.Implementations fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem)); + _networkManager.NetworkChanged += OnNetworkChanged; + CertificateInfo = new CertificateInfo { Path = ServerConfigurationManager.Configuration.CertificatePath, Password = ServerConfigurationManager.Configuration.CertificatePassword }; Certificate = GetCertificate(CertificateInfo); - } - /// - /// Gets the NetworkManager instance. - /// - public INetworkManager NetManager { get; internal set; } + ApplicationVersion = typeof(ApplicationHost).Assembly.GetName().Version; + ApplicationVersionString = ApplicationVersion.ToString(3); + ApplicationUserAgent = Name.Replace(' ', '-') + "/" + ApplicationVersionString; + } public string ExpandVirtualPath(string path) { @@ -304,17 +313,27 @@ namespace Emby.Server.Implementations .Replace(appPaths.InternalMetadataPath, appPaths.VirtualInternalMetadataPath, StringComparison.OrdinalIgnoreCase); } - /// - public Version ApplicationVersion { get; } = typeof(ApplicationHost).Assembly.GetName().Version; + private string[] GetConfiguredLocalSubnets() + { + return ServerConfigurationManager.Configuration.LocalNetworkSubnets; + } + + private void OnNetworkChanged(object sender, EventArgs e) + { + _validAddressResults.Clear(); + } /// - public string ApplicationVersionString { get; } = typeof(ApplicationHost).Assembly.GetName().Version.ToString(3); + public Version ApplicationVersion { get; } + + /// + public string ApplicationVersionString { get; } /// /// Gets the current application user agent. /// /// The application user agent. - public string ApplicationUserAgent => Name.Replace(' ', '-') + "/" + ApplicationVersionString; + public string ApplicationUserAgent { get; } /// /// Gets the email address for use within a comment section of a user agent field. @@ -384,7 +403,7 @@ namespace Emby.Server.Implementations /// /// Resolves this instance. /// - /// The type. + /// The type /// ``0. public T Resolve() => ServiceProvider.GetService(); @@ -480,6 +499,21 @@ namespace Emby.Server.Implementations HttpsPort = ServerConfiguration.DefaultHttpsPort; } + if (Plugins != null) + { + var pluginBuilder = new StringBuilder(); + + foreach (var plugin in Plugins) + { + pluginBuilder.Append(plugin.Name) + .Append(' ') + .Append(plugin.Version) + .AppendLine(); + } + + Logger.LogInformation("Plugins: {Plugins}", pluginBuilder.ToString()); + } + DiscoverTypes(); RegisterServices(); @@ -504,10 +538,7 @@ namespace Emby.Server.Implementations ServiceCollection.AddSingleton(_fileSystemManager); ServiceCollection.AddSingleton(); - ServiceCollection.AddSingleton(NetManager); - ServiceCollection.AddSingleton(); - ServiceCollection.AddSingleton(); - ServiceCollection.AddSingleton(); + ServiceCollection.AddSingleton(_networkManager); ServiceCollection.AddSingleton(); @@ -519,6 +550,8 @@ namespace Emby.Server.Implementations ServiceCollection.AddSingleton(); + ServiceCollection.AddSingleton(); + ServiceCollection.AddSingleton(); ServiceCollection.AddSingleton(); @@ -595,6 +628,8 @@ namespace Emby.Server.Implementations ServiceCollection.AddSingleton(); + ServiceCollection.AddSingleton(); + ServiceCollection.AddSingleton(); ServiceCollection.AddSingleton(); @@ -750,21 +785,6 @@ namespace Emby.Server.Implementations .Where(i => i != null) .ToArray(); - if (Plugins != null) - { - var pluginBuilder = new StringBuilder(); - - foreach (var plugin in Plugins) - { - pluginBuilder.Append(plugin.Name) - .Append(' ') - .Append(plugin.Version) - .AppendLine(); - } - - Logger.LogInformation("Plugins: {Plugins}", pluginBuilder.ToString()); - } - _urlPrefixes = GetUrlPrefixes().ToArray(); _webSocketManager.Init(GetExports()); @@ -799,6 +819,38 @@ namespace Emby.Server.Implementations { try { + if (plugin is IPluginAssembly assemblyPlugin) + { + var assembly = plugin.GetType().Assembly; + var assemblyName = assembly.GetName(); + var assemblyFilePath = assembly.Location; + + var dataFolderPath = Path.Combine(ApplicationPaths.PluginsPath, Path.GetFileNameWithoutExtension(assemblyFilePath)); + + assemblyPlugin.SetAttributes(assemblyFilePath, dataFolderPath, assemblyName.Version); + + try + { + var idAttributes = assembly.GetCustomAttributes(typeof(GuidAttribute), true); + if (idAttributes.Length > 0) + { + var attribute = (GuidAttribute)idAttributes[0]; + var assemblyId = new Guid(attribute.Value); + + assemblyPlugin.SetId(assemblyId); + } + } + catch (Exception ex) + { + Logger.LogError(ex, "Error getting plugin Id from {PluginName}.", plugin.GetType().FullName); + } + } + + if (plugin is IHasPluginConfiguration hasPluginConfiguration) + { + hasPluginConfiguration.SetStartupInfo(s => Directory.CreateDirectory(s)); + } + plugin.RegisterServices(ServiceCollection); } catch (Exception ex) @@ -828,7 +880,7 @@ namespace Emby.Server.Implementations try { exportedTypes = ass.GetExportedTypes(); - + try { Type reg = (Type)exportedTypes.Where(p => string.Equals(p.Name, "PluginRegistration", StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); @@ -1051,7 +1103,7 @@ namespace Emby.Server.Implementations { // No metafile, so lets see if the folder is versioned. metafile = dir.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries)[^1]; - + int versionIndex = dir.LastIndexOf('_'); if (versionIndex != -1 && Version.TryParse(dir.Substring(versionIndex + 1), out Version ver)) { @@ -1060,9 +1112,9 @@ namespace Emby.Server.Implementations } else { - // Un-versioned folder - Add it under the path name and version 0.0.0.1. + // Un-versioned folder - Add it under the path name and version 0.0.0.1. versions.Add((new Version(0, 0, 0, 1), metafile, dir)); - } + } } } catch @@ -1149,6 +1201,9 @@ namespace Emby.Server.Implementations // MediaEncoding yield return typeof(MediaBrowser.MediaEncoding.Encoder.MediaEncoder).Assembly; + // Dlna + yield return typeof(DlnaEntryPoint).Assembly; + // Local metadata yield return typeof(BoxSetXmlSaver).Assembly; @@ -1169,10 +1224,13 @@ namespace Emby.Server.Implementations /// /// Gets the system status. /// - /// Where this request originated. + /// The cancellation token. /// SystemInfo. - public SystemInfo GetSystemInfo(IPAddress source) + public async Task GetSystemInfo(CancellationToken cancellationToken) { + var localAddress = await GetLocalApiUrl(cancellationToken).ConfigureAwait(false); + var transcodingTempPath = ConfigurationManager.GetTranscodePath(); + return new SystemInfo { HasPendingRestart = HasPendingRestart, @@ -1192,9 +1250,9 @@ namespace Emby.Server.Implementations CanSelfRestart = CanSelfRestart, CanLaunchWebBrowser = CanLaunchWebBrowser, HasUpdateAvailable = HasUpdateAvailable, - TranscodingTempPath = ConfigurationManager.GetTranscodePath(), + TranscodingTempPath = transcodingTempPath, ServerName = FriendlyName, - LocalAddress = GetSmartApiUrl(source), + LocalAddress = localAddress, SupportsLibraryMonitor = true, EncoderLocation = _mediaEncoder.EncoderLocation, SystemArchitecture = RuntimeInformation.OSArchitecture, @@ -1203,12 +1261,14 @@ namespace Emby.Server.Implementations } public IEnumerable GetWakeOnLanInfo() - => NetManager.GetMacAddresses() + => _networkManager.GetMacAddresses() .Select(i => new WakeOnLanInfo(i)) .ToList(); - public PublicSystemInfo GetPublicSystemInfo(IPAddress source) + public async Task GetPublicSystemInfo(CancellationToken cancellationToken) { + var localAddress = await GetLocalApiUrl(cancellationToken).ConfigureAwait(false); + return new PublicSystemInfo { Version = ApplicationVersionString, @@ -1216,7 +1276,7 @@ namespace Emby.Server.Implementations Id = SystemId, OperatingSystem = OperatingSystem.Id.ToString(), ServerName = FriendlyName, - LocalAddress = GetSmartApiUrl(source), + LocalAddress = localAddress, StartupWizardCompleted = ConfigurationManager.CommonConfiguration.IsStartupWizardCompleted }; } @@ -1225,92 +1285,186 @@ namespace Emby.Server.Implementations public bool ListenWithHttps => Certificate != null && ServerConfigurationManager.Configuration.EnableHttps; /// - public string GetSmartApiUrl(IPAddress ipAddress, int? port = null) + public async Task GetLocalApiUrl(CancellationToken cancellationToken) { - // Published server ends with a / - if (_startupOptions.PublishedServerUrl != null) + try { - // Published server ends with a '/', so we need to remove it. - return _startupOptions.PublishedServerUrl.ToString().Trim('/'); + // Return the first matched address, if found, or the first known local address + var addresses = await GetLocalIpAddressesInternal(false, 1, cancellationToken).ConfigureAwait(false); + if (addresses.Count == 0) + { + return null; + } + + return GetLocalApiUrl(addresses[0]); + } + catch (Exception ex) + { + Logger.LogError(ex, "Error getting local Ip address information"); } - string smart = NetManager.GetBindInterface(ipAddress, out port); - // If the smartAPI doesn't start with http then treat it as a host or ip. - if (smart.StartsWith("http", StringComparison.OrdinalIgnoreCase)) - { - return smart.Trim('/'); - } - - return GetLocalApiUrl(smart.Trim('/'), null, port); + return null; } - public string GetSmartApiUrl(HttpRequest request, int? port = null) + /// + /// Removes the scope id from IPv6 addresses. + /// + /// The IPv6 address. + /// The IPv6 address without the scope id. + private ReadOnlySpan RemoveScopeId(ReadOnlySpan address) { - // Published server ends with a / - if (_startupOptions.PublishedServerUrl != null) + var index = address.IndexOf('%'); + if (index == -1) { - // Published server ends with a '/', so we need to remove it. - return _startupOptions.PublishedServerUrl.ToString().Trim('/'); + return address; } - string smart = NetManager.GetBindInterface(request, out port); - // If the smartAPI doesn't start with http then treat it as a host or ip. - if (smart.StartsWith("http", StringComparison.OrdinalIgnoreCase)) - { - return smart.Trim('/'); - } - - return GetLocalApiUrl(smart.Trim('/'), request.Scheme, port); + return address.Slice(0, index); } - public string GetSmartApiUrl(string hostname, int? port = null) + /// + public string GetLocalApiUrl(IPAddress ipAddress) { - // Published server ends with a / - if (_startupOptions.PublishedServerUrl != null) + if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6) { - // Published server ends with a '/', so we need to remove it. - return _startupOptions.PublishedServerUrl.ToString().Trim('/'); + var str = RemoveScopeId(ipAddress.ToString()); + Span span = new char[str.Length + 2]; + span[0] = '['; + str.CopyTo(span.Slice(1)); + span[^1] = ']'; + + return GetLocalApiUrl(span); } - string smart = NetManager.GetBindInterface(hostname, out port); - - // If the smartAPI doesn't start with http then treat it as a host or ip. - if (smart.StartsWith("http", StringComparison.OrdinalIgnoreCase)) - { - return smart.Trim('/'); - } - - return GetLocalApiUrl(smart.Trim('/'), null, port); + return GetLocalApiUrl(ipAddress.ToString()); } /// public string GetLoopbackHttpApiUrl() { - if (NetManager.IsIP6Enabled) - { - return GetLocalApiUrl("::1", Uri.UriSchemeHttp, HttpPort); - } - return GetLocalApiUrl("127.0.0.1", Uri.UriSchemeHttp, HttpPort); } /// - public string GetLocalApiUrl(string host, string scheme = null, int? port = null) + public string GetLocalApiUrl(ReadOnlySpan host, string scheme = null, int? port = null) { // NOTE: If no BaseUrl is set then UriBuilder appends a trailing slash, but if there is no BaseUrl it does // not. For consistency, always trim the trailing slash. return new UriBuilder { Scheme = scheme ?? (ListenWithHttps ? Uri.UriSchemeHttps : Uri.UriSchemeHttp), - Host = host, + Host = host.ToString(), Port = port ?? (ListenWithHttps ? HttpsPort : HttpPort), Path = ServerConfigurationManager.Configuration.BaseUrl }.ToString().TrimEnd('/'); } - /// - /// Gets the servers friendly name. - /// + public Task> GetLocalIpAddresses(CancellationToken cancellationToken) + { + return GetLocalIpAddressesInternal(true, 0, cancellationToken); + } + + private async Task> GetLocalIpAddressesInternal(bool allowLoopback, int limit, CancellationToken cancellationToken) + { + var addresses = ServerConfigurationManager + .Configuration + .LocalNetworkAddresses + .Select(x => NormalizeConfiguredLocalAddress(x)) + .Where(i => i != null) + .ToList(); + + if (addresses.Count == 0) + { + addresses.AddRange(_networkManager.GetLocalIpAddresses()); + } + + var resultList = new List(); + + foreach (var address in addresses) + { + if (!allowLoopback) + { + if (address.Equals(IPAddress.Loopback) || address.Equals(IPAddress.IPv6Loopback)) + { + continue; + } + } + + if (await IsLocalIpAddressValidAsync(address, cancellationToken).ConfigureAwait(false)) + { + resultList.Add(address); + + if (limit > 0 && resultList.Count >= limit) + { + return resultList; + } + } + } + + return resultList; + } + + public IPAddress NormalizeConfiguredLocalAddress(ReadOnlySpan address) + { + var index = address.Trim('/').IndexOf('/'); + if (index != -1) + { + address = address.Slice(index + 1); + } + + if (IPAddress.TryParse(address.Trim('/'), out IPAddress result)) + { + return result; + } + + return null; + } + + private readonly ConcurrentDictionary _validAddressResults = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + + private async Task IsLocalIpAddressValidAsync(IPAddress address, CancellationToken cancellationToken) + { + if (address.Equals(IPAddress.Loopback) + || address.Equals(IPAddress.IPv6Loopback)) + { + return true; + } + + var apiUrl = GetLocalApiUrl(address) + "/system/ping"; + + if (_validAddressResults.TryGetValue(apiUrl, out var cachedResult)) + { + return cachedResult; + } + + try + { + using var request = new HttpRequestMessage(HttpMethod.Post, apiUrl); + using var response = await _httpClientFactory.CreateClient(NamedClient.Default) + .SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + var result = await System.Text.Json.JsonSerializer.DeserializeAsync(stream, JsonDefaults.GetOptions(), cancellationToken).ConfigureAwait(false); + var valid = string.Equals(Name, result, StringComparison.OrdinalIgnoreCase); + + _validAddressResults.AddOrUpdate(apiUrl, valid, (k, v) => valid); + Logger.LogDebug("Ping test result to {0}. Success: {1}", apiUrl, valid); + return valid; + } + catch (OperationCanceledException) + { + Logger.LogDebug("Ping test result to {0}. Success: {1}", apiUrl, "Cancelled"); + throw; + } + catch (Exception ex) + { + Logger.LogDebug(ex, "Ping test result to {0}. Success: {1}", apiUrl, false); + + _validAddressResults.AddOrUpdate(apiUrl, false, (k, v) => false); + return false; + } + } + public string FriendlyName => string.IsNullOrEmpty(ServerConfigurationManager.Configuration.ServerName) ? Environment.MachineName @@ -1382,7 +1536,7 @@ namespace Emby.Server.Implementations foreach (var assembly in assemblies) { - Logger.LogDebug("Found API endpoints in plugin {name}", assembly.FullName); + Logger.LogDebug("Found API endpoints in plugin {Name}", assembly.FullName); yield return assembly; } } From ba685d8092aae223e3b5a48c4bc331e2266318ef Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sat, 3 Oct 2020 09:08:28 +0100 Subject: [PATCH 03/35] Update ApplicationHost.cs --- .../ApplicationHost.cs | 82 +++++++++++-------- 1 file changed, 46 insertions(+), 36 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index f36bc0eef..a3e9693b3 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -128,7 +128,7 @@ namespace Emby.Server.Implementations private ISessionManager _sessionManager; private IHttpClientFactory _httpClientFactory; private IWebSocketManager _webSocketManager; - + private Dictionary _pluginRegistrations; private string[] _urlPrefixes; /// @@ -258,10 +258,12 @@ namespace Emby.Server.Implementations IServiceCollection serviceCollection) { _xmlSerializer = new MyXmlSerializer(); - _jsonSerializer = new JsonSerializer(); - + _jsonSerializer = new JsonSerializer(); + ServiceCollection = serviceCollection; + _pluginRegistrations = new Dictionary(); + _networkManager = networkManager; networkManager.LocalSubnetsFn = GetConfiguredLocalSubnets; @@ -499,24 +501,11 @@ namespace Emby.Server.Implementations HttpsPort = ServerConfiguration.DefaultHttpsPort; } - if (Plugins != null) - { - var pluginBuilder = new StringBuilder(); - - foreach (var plugin in Plugins) - { - pluginBuilder.Append(plugin.Name) - .Append(' ') - .Append(plugin.Version) - .AppendLine(); - } - - Logger.LogInformation("Plugins: {Plugins}", pluginBuilder.ToString()); - } - DiscoverTypes(); RegisterServices(); + + RegisterPlugIns(); } /// @@ -781,10 +770,24 @@ namespace Emby.Server.Implementations ConfigurationManager.AddParts(GetExports()); _plugins = GetExports() - .Select(LoadPlugin) .Where(i => i != null) .ToArray(); + if (Plugins != null) + { + var pluginBuilder = new StringBuilder(); + + foreach (var plugin in Plugins) + { + pluginBuilder.Append(plugin.Name) + .Append(' ') + .Append(plugin.Version) + .AppendLine(); + } + + Logger.LogInformation("Plugins: {Plugins}", pluginBuilder.ToString()); + } + _urlPrefixes = GetUrlPrefixes().ToArray(); _webSocketManager.Init(GetExports()); @@ -850,8 +853,6 @@ namespace Emby.Server.Implementations { hasPluginConfiguration.SetStartupInfo(s => Directory.CreateDirectory(s)); } - - plugin.RegisterServices(ServiceCollection); } catch (Exception ex) { @@ -872,6 +873,24 @@ namespace Emby.Server.Implementations _allConcreteTypes = GetTypes(GetComposablePartAssemblies()).ToArray(); } + private void RegisterPlugIns() + { + foreach ((var pluginType, var assembly) in _pluginRegistrations) + { + try + { + var pluginRegistration = Activator.CreateInstance(pluginType); + pluginType.InvokeMember("RegisterServices", BindingFlags.InvokeMethod, null, pluginRegistration, new object[] { ServiceCollection }, CultureInfo.InvariantCulture); + } + catch (Exception ex) + { + Logger.LogError(ex, "Error registering {Assembly} with D.I.", assembly); + } + } + + _pluginRegistrations.Clear(); + } + private IEnumerable GetTypes(IEnumerable assemblies) { foreach (var ass in assemblies) @@ -880,20 +899,11 @@ namespace Emby.Server.Implementations try { exportedTypes = ass.GetExportedTypes(); - - try + + Type reg = (Type)exportedTypes.Where(p => string.Equals(p.Name, "PluginRegistration", StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); + if (reg != null) { - Type reg = (Type)exportedTypes.Where(p => string.Equals(p.Name, "PluginRegistration", StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); - if (reg != null) - { - var pluginRegistration = Activator.CreateInstance(reg); - reg.InvokeMember("RegisterServices", BindingFlags.InvokeMethod, null, pluginRegistration, new object[] { ServiceCollection }, CultureInfo.InvariantCulture); - } - } - catch (Exception ex) - { - Logger.LogError(ex, "Error registering {Assembly} with D.I.", ass.FullName); - continue; + _pluginRegistrations.Add(ass, reg); } } catch (FileNotFoundException ex) @@ -1103,7 +1113,7 @@ namespace Emby.Server.Implementations { // No metafile, so lets see if the folder is versioned. metafile = dir.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries)[^1]; - + int versionIndex = dir.LastIndexOf('_'); if (versionIndex != -1 && Version.TryParse(dir.Substring(versionIndex + 1), out Version ver)) { @@ -1114,7 +1124,7 @@ namespace Emby.Server.Implementations { // Un-versioned folder - Add it under the path name and version 0.0.0.1. versions.Add((new Version(0, 0, 0, 1), metafile, dir)); - } + } } } catch From 298a322ac1b8aece032511c39c3e3397dd09a0c4 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sat, 3 Oct 2020 09:13:04 +0100 Subject: [PATCH 04/35] Update ApplicationHost.cs --- Emby.Server.Implementations/ApplicationHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index a3e9693b3..2bd364984 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -903,7 +903,7 @@ namespace Emby.Server.Implementations Type reg = (Type)exportedTypes.Where(p => string.Equals(p.Name, "PluginRegistration", StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); if (reg != null) { - _pluginRegistrations.Add(ass, reg); + _pluginRegistrations.Add(reg, ass); } } catch (FileNotFoundException ex) From 2929ce6e0dd8f3308bd1249e8b5eb1cf8edba008 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sat, 3 Oct 2020 09:18:00 +0100 Subject: [PATCH 05/35] Update ApplicationHost.cs --- Emby.Server.Implementations/ApplicationHost.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 2bd364984..3419675c3 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1540,6 +1540,7 @@ namespace Emby.Server.Implementations public IEnumerable GetApiPluginAssemblies() { var assemblies = _allConcreteTypes + .Select(LoadPlugin) .Where(i => typeof(ControllerBase).IsAssignableFrom(i)) .Select(i => i.Assembly) .Distinct(); From 7459baac8bf9bac4a29469eeead1204b1c0114b2 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sat, 3 Oct 2020 09:23:12 +0100 Subject: [PATCH 06/35] Update ApplicationHost.cs --- Emby.Server.Implementations/ApplicationHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 3419675c3..d14e503b0 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -770,6 +770,7 @@ namespace Emby.Server.Implementations ConfigurationManager.AddParts(GetExports()); _plugins = GetExports() + .Select(LoadPlugin) .Where(i => i != null) .ToArray(); @@ -1540,7 +1541,6 @@ namespace Emby.Server.Implementations public IEnumerable GetApiPluginAssemblies() { var assemblies = _allConcreteTypes - .Select(LoadPlugin) .Where(i => typeof(ControllerBase).IsAssignableFrom(i)) .Select(i => i.Assembly) .Distinct(); From 0b73a1d90f80456ab6ea8e53134eae193aa9d5da Mon Sep 17 00:00:00 2001 From: Greenback Date: Sun, 11 Oct 2020 13:19:14 +0100 Subject: [PATCH 07/35] Added extra functionality to support registrar. --- .../AppBase/BaseConfigurationManager.cs | 29 ++++++++ .../ApplicationHost.cs | 69 ++----------------- .../Configuration/IConfigurationManager.cs | 6 ++ MediaBrowser.Common/Plugins/BasePlugin.cs | 35 +++++++--- MediaBrowser.Common/Plugins/IPlugin.cs | 12 ---- .../Plugins/IPluginRegistrar.cs | 17 +++++ 6 files changed, 83 insertions(+), 85 deletions(-) create mode 100644 MediaBrowser.Common/Plugins/IPluginRegistrar.cs diff --git a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs index 4ab0a2a3f..ea4c1ad08 100644 --- a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs +++ b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs @@ -133,6 +133,35 @@ namespace Emby.Server.Implementations.AppBase } } + /// + /// Manually pre-loads a factory so that it is available pre system initialisation. + /// + /// Class to register. + public virtual void RegisterConfiguration() + { + if (!typeof(IConfigurationFactory).IsAssignableFrom(typeof(T))) + { + throw new ArgumentException("Parameter does not implement IConfigurationFactory"); + } + + IConfigurationFactory factory = (IConfigurationFactory)Activator.CreateInstance(typeof(T)); + + if (_configurationFactories == null) + { + _configurationFactories = new IConfigurationFactory[] { factory }; + } + else + { + var list = _configurationFactories.ToList(); + list.Add(factory); + _configurationFactories = list.ToArray(); + } + + _configurationStores = _configurationFactories + .SelectMany(i => i.GetConfigurations()) + .ToArray(); + } + /// /// Adds parts. /// diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index d14e503b0..3f2307d80 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -128,7 +128,6 @@ namespace Emby.Server.Implementations private ISessionManager _sessionManager; private IHttpClientFactory _httpClientFactory; private IWebSocketManager _webSocketManager; - private Dictionary _pluginRegistrations; private string[] _urlPrefixes; /// @@ -262,8 +261,6 @@ namespace Emby.Server.Implementations ServiceCollection = serviceCollection; - _pluginRegistrations = new Dictionary(); - _networkManager = networkManager; networkManager.LocalSubnetsFn = GetConfiguredLocalSubnets; @@ -505,7 +502,7 @@ namespace Emby.Server.Implementations RegisterServices(); - RegisterPlugIns(); + RegisterPlugInServices(); } /// @@ -770,7 +767,6 @@ namespace Emby.Server.Implementations ConfigurationManager.AddParts(GetExports()); _plugins = GetExports() - .Select(LoadPlugin) .Where(i => i != null) .ToArray(); @@ -819,51 +815,6 @@ namespace Emby.Server.Implementations Resolve().AddParts(GetExports()); } - private IPlugin LoadPlugin(IPlugin plugin) - { - try - { - if (plugin is IPluginAssembly assemblyPlugin) - { - var assembly = plugin.GetType().Assembly; - var assemblyName = assembly.GetName(); - var assemblyFilePath = assembly.Location; - - var dataFolderPath = Path.Combine(ApplicationPaths.PluginsPath, Path.GetFileNameWithoutExtension(assemblyFilePath)); - - assemblyPlugin.SetAttributes(assemblyFilePath, dataFolderPath, assemblyName.Version); - - try - { - var idAttributes = assembly.GetCustomAttributes(typeof(GuidAttribute), true); - if (idAttributes.Length > 0) - { - var attribute = (GuidAttribute)idAttributes[0]; - var assemblyId = new Guid(attribute.Value); - - assemblyPlugin.SetId(assemblyId); - } - } - catch (Exception ex) - { - Logger.LogError(ex, "Error getting plugin Id from {PluginName}.", plugin.GetType().FullName); - } - } - - if (plugin is IHasPluginConfiguration hasPluginConfiguration) - { - hasPluginConfiguration.SetStartupInfo(s => Directory.CreateDirectory(s)); - } - } - catch (Exception ex) - { - Logger.LogError(ex, "Error loading plugin {PluginName}", plugin.GetType().FullName); - return null; - } - - return plugin; - } - /// /// Discovers the types. /// @@ -874,22 +825,20 @@ namespace Emby.Server.Implementations _allConcreteTypes = GetTypes(GetComposablePartAssemblies()).ToArray(); } - private void RegisterPlugIns() + private void RegisterPlugInServices() { - foreach ((var pluginType, var assembly) in _pluginRegistrations) + foreach (var pluginServiceRegistrar in GetExportTypes()) { try { - var pluginRegistration = Activator.CreateInstance(pluginType); - pluginType.InvokeMember("RegisterServices", BindingFlags.InvokeMethod, null, pluginRegistration, new object[] { ServiceCollection }, CultureInfo.InvariantCulture); + var instance = (IPluginRegistrar)Activator.CreateInstance(pluginServiceRegistrar); + instance.RegisterServices(ServiceCollection); } catch (Exception ex) { - Logger.LogError(ex, "Error registering {Assembly} with D.I.", assembly); + Logger.LogError(ex, "Error registering {Assembly} with D.I.", pluginServiceRegistrar.Assembly); } } - - _pluginRegistrations.Clear(); } private IEnumerable GetTypes(IEnumerable assemblies) @@ -900,12 +849,6 @@ namespace Emby.Server.Implementations try { exportedTypes = ass.GetExportedTypes(); - - Type reg = (Type)exportedTypes.Where(p => string.Equals(p.Name, "PluginRegistration", StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); - if (reg != null) - { - _pluginRegistrations.Add(reg, ass); - } } catch (FileNotFoundException ex) { diff --git a/MediaBrowser.Common/Configuration/IConfigurationManager.cs b/MediaBrowser.Common/Configuration/IConfigurationManager.cs index fe726090d..8cbeaea86 100644 --- a/MediaBrowser.Common/Configuration/IConfigurationManager.cs +++ b/MediaBrowser.Common/Configuration/IConfigurationManager.cs @@ -46,6 +46,12 @@ namespace MediaBrowser.Common.Configuration /// The new configuration. void ReplaceConfiguration(BaseApplicationConfiguration newConfiguration); + /// + /// Manually pre-loads a factory so that it is available pre system initialisation. + /// + /// Class to register. + void RegisterConfiguration(); + /// /// Gets the configuration. /// diff --git a/MediaBrowser.Common/Plugins/BasePlugin.cs b/MediaBrowser.Common/Plugins/BasePlugin.cs index 4b2918d08..b89bc7eba 100644 --- a/MediaBrowser.Common/Plugins/BasePlugin.cs +++ b/MediaBrowser.Common/Plugins/BasePlugin.cs @@ -3,6 +3,7 @@ using System; using System.IO; using System.Reflection; +using System.Runtime.InteropServices; using MediaBrowser.Common.Configuration; using MediaBrowser.Model.Plugins; using MediaBrowser.Model.Serialization; @@ -82,16 +83,6 @@ namespace MediaBrowser.Common.Plugins { } - /// - public virtual void RegisterServices(IServiceCollection serviceCollection) - { - } - - /// - public virtual void UnregisterServices(IServiceCollection serviceCollection) - { - } - /// public void SetAttributes(string assemblyFilePath, string dataFolderPath, Version assemblyVersion) { @@ -140,6 +131,30 @@ namespace MediaBrowser.Common.Plugins { ApplicationPaths = applicationPaths; XmlSerializer = xmlSerializer; + if (this is IPluginAssembly assemblyPlugin) + { + var assembly = GetType().Assembly; + var assemblyName = assembly.GetName(); + var assemblyFilePath = assembly.Location; + + var dataFolderPath = Path.Combine(ApplicationPaths.PluginsPath, Path.GetFileNameWithoutExtension(assemblyFilePath)); + + assemblyPlugin.SetAttributes(assemblyFilePath, dataFolderPath, assemblyName.Version); + + var idAttributes = assembly.GetCustomAttributes(typeof(GuidAttribute), true); + if (idAttributes.Length > 0) + { + var attribute = (GuidAttribute)idAttributes[0]; + var assemblyId = new Guid(attribute.Value); + + assemblyPlugin.SetId(assemblyId); + } + } + + if (this is IHasPluginConfiguration hasPluginConfiguration) + { + hasPluginConfiguration.SetStartupInfo(s => Directory.CreateDirectory(s)); + } } /// diff --git a/MediaBrowser.Common/Plugins/IPlugin.cs b/MediaBrowser.Common/Plugins/IPlugin.cs index 1844eb124..d583a5887 100644 --- a/MediaBrowser.Common/Plugins/IPlugin.cs +++ b/MediaBrowser.Common/Plugins/IPlugin.cs @@ -62,18 +62,6 @@ namespace MediaBrowser.Common.Plugins /// Called when just before the plugin is uninstalled from the server. /// void OnUninstalling(); - - /// - /// Registers the plugin's services to the service collection. - /// - /// The service collection. - void RegisterServices(IServiceCollection serviceCollection); - - /// - /// Unregisters the plugin's services from the service collection. - /// - /// The service collection. - void UnregisterServices(IServiceCollection serviceCollection); } public interface IHasPluginConfiguration diff --git a/MediaBrowser.Common/Plugins/IPluginRegistrar.cs b/MediaBrowser.Common/Plugins/IPluginRegistrar.cs new file mode 100644 index 000000000..79901c368 --- /dev/null +++ b/MediaBrowser.Common/Plugins/IPluginRegistrar.cs @@ -0,0 +1,17 @@ +namespace MediaBrowser.Common.Plugins +{ + using Microsoft.Extensions.DependencyInjection; + + /// + /// Defines the . + /// + public interface IPluginRegistrar + { + /// + /// Registers the plugin's services with the service collection. + /// This object is created prior to the plugin creation, so access to other classes is limited. + /// + /// The service collection. + void RegisterServices(IServiceCollection serviceCollection); + } +} From 8c0748b677eaac0eafefda774aef8abb5870aaf1 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sun, 11 Oct 2020 16:41:30 +0100 Subject: [PATCH 08/35] Update BasePlugin.cs Added ConfigurationChanged event. --- MediaBrowser.Common/Plugins/BasePlugin.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MediaBrowser.Common/Plugins/BasePlugin.cs b/MediaBrowser.Common/Plugins/BasePlugin.cs index b89bc7eba..55443e518 100644 --- a/MediaBrowser.Common/Plugins/BasePlugin.cs +++ b/MediaBrowser.Common/Plugins/BasePlugin.cs @@ -175,6 +175,11 @@ namespace MediaBrowser.Common.Plugins /// The type of the configuration. public Type ConfigurationType => typeof(TConfigurationType); + /// + /// Gets or sets the event handler that is triggered when this configuration changes. + /// + public EventHandler ConfigurationChanged { get; set; } + /// /// Gets the name the assembly file. /// @@ -270,6 +275,8 @@ namespace MediaBrowser.Common.Plugins Configuration = (TConfigurationType)configuration; SaveConfiguration(); + + ConfigurationChanged.Invoke(this, configuration); } /// From 85d36a55a12c85af58fbfd2d3c087d21f6f3c387 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sun, 11 Oct 2020 18:26:59 +0100 Subject: [PATCH 09/35] Update BasePlugin.cs Removed trailing spaces --- MediaBrowser.Common/Plugins/BasePlugin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Common/Plugins/BasePlugin.cs b/MediaBrowser.Common/Plugins/BasePlugin.cs index 55443e518..e21d8c7d1 100644 --- a/MediaBrowser.Common/Plugins/BasePlugin.cs +++ b/MediaBrowser.Common/Plugins/BasePlugin.cs @@ -275,7 +275,7 @@ namespace MediaBrowser.Common.Plugins Configuration = (TConfigurationType)configuration; SaveConfiguration(); - + ConfigurationChanged.Invoke(this, configuration); } From 7e939fa2cb16298d5c24db29a9115584e4a0679b Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sun, 11 Oct 2020 19:56:05 +0100 Subject: [PATCH 10/35] Update BasePlugin.cs Plugins supporting di cannot be unloaded. --- MediaBrowser.Common/Plugins/BasePlugin.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Common/Plugins/BasePlugin.cs b/MediaBrowser.Common/Plugins/BasePlugin.cs index e21d8c7d1..cfd9bbf1d 100644 --- a/MediaBrowser.Common/Plugins/BasePlugin.cs +++ b/MediaBrowser.Common/Plugins/BasePlugin.cs @@ -56,7 +56,8 @@ namespace MediaBrowser.Common.Plugins /// Gets a value indicating whether the plugin can be uninstalled. /// public bool CanUninstall => !Path.GetDirectoryName(AssemblyFilePath) - .Equals(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), StringComparison.InvariantCulture); + .Equals(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), StringComparison.InvariantCulture) && + !typeof(IPluginRegistrar).IsAssignableFrom(typeof(this)); /// /// Gets the plugin info. From d31178ab4b60e2a71d57f64c6d96d3f0effc0bc7 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sun, 11 Oct 2020 20:00:46 +0100 Subject: [PATCH 11/35] Update BasePlugin.cs --- MediaBrowser.Common/Plugins/BasePlugin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Common/Plugins/BasePlugin.cs b/MediaBrowser.Common/Plugins/BasePlugin.cs index cfd9bbf1d..93e286710 100644 --- a/MediaBrowser.Common/Plugins/BasePlugin.cs +++ b/MediaBrowser.Common/Plugins/BasePlugin.cs @@ -57,7 +57,7 @@ namespace MediaBrowser.Common.Plugins /// public bool CanUninstall => !Path.GetDirectoryName(AssemblyFilePath) .Equals(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), StringComparison.InvariantCulture) && - !typeof(IPluginRegistrar).IsAssignableFrom(typeof(this)); + !typeof(IPluginRegistrar).IsAssignableFrom(this.GetType()); /// /// Gets the plugin info. From ae8a3bc02cffd01f4c64ec86fdcc9c807ec5b1a8 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sun, 11 Oct 2020 23:49:34 +0100 Subject: [PATCH 12/35] Update MediaBrowser.Common/Plugins/IPluginRegistrar.cs Co-authored-by: Claus Vium --- MediaBrowser.Common/Plugins/IPluginRegistrar.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Common/Plugins/IPluginRegistrar.cs b/MediaBrowser.Common/Plugins/IPluginRegistrar.cs index 79901c368..4e6265fe2 100644 --- a/MediaBrowser.Common/Plugins/IPluginRegistrar.cs +++ b/MediaBrowser.Common/Plugins/IPluginRegistrar.cs @@ -5,7 +5,7 @@ namespace MediaBrowser.Common.Plugins /// /// Defines the . /// - public interface IPluginRegistrar + public interface IPluginServiceRegistrator { /// /// Registers the plugin's services with the service collection. From ed05ae683e49bd9771f5817eb81a10fe40e49d94 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sun, 11 Oct 2020 23:49:51 +0100 Subject: [PATCH 13/35] Update Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs Co-authored-by: Claus Vium --- Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs index ea4c1ad08..dd4c44e1a 100644 --- a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs +++ b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs @@ -137,7 +137,7 @@ namespace Emby.Server.Implementations.AppBase /// Manually pre-loads a factory so that it is available pre system initialisation. /// /// Class to register. - public virtual void RegisterConfiguration() + public virtual void RegisterConfiguration() where T : IConfigurationFactory { if (!typeof(IConfigurationFactory).IsAssignableFrom(typeof(T))) { From 387fdb0f1376f65b342c10e5222d7c0b56cdd8e5 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sun, 11 Oct 2020 23:50:36 +0100 Subject: [PATCH 14/35] Update Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs Co-authored-by: Claus Vium --- .../AppBase/BaseConfigurationManager.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs index dd4c44e1a..ca52b80cc 100644 --- a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs +++ b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs @@ -139,11 +139,6 @@ namespace Emby.Server.Implementations.AppBase /// Class to register. public virtual void RegisterConfiguration() where T : IConfigurationFactory { - if (!typeof(IConfigurationFactory).IsAssignableFrom(typeof(T))) - { - throw new ArgumentException("Parameter does not implement IConfigurationFactory"); - } - IConfigurationFactory factory = (IConfigurationFactory)Activator.CreateInstance(typeof(T)); if (_configurationFactories == null) From d49ba961230c438b25332a77ac345e22e3cb0ba6 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sun, 11 Oct 2020 23:50:48 +0100 Subject: [PATCH 15/35] Update Emby.Server.Implementations/ApplicationHost.cs Co-authored-by: Claus Vium --- Emby.Server.Implementations/ApplicationHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 3f2307d80..20cc9d76f 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -825,7 +825,7 @@ namespace Emby.Server.Implementations _allConcreteTypes = GetTypes(GetComposablePartAssemblies()).ToArray(); } - private void RegisterPlugInServices() + private void RegisterPluginServices() { foreach (var pluginServiceRegistrar in GetExportTypes()) { From 5c8015128f4e29cfa8fd15977d0984bb587b4f13 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sun, 11 Oct 2020 23:51:03 +0100 Subject: [PATCH 16/35] Update Emby.Server.Implementations/ApplicationHost.cs Co-authored-by: Claus Vium --- Emby.Server.Implementations/ApplicationHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 20cc9d76f..c1e6038ac 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -836,7 +836,7 @@ namespace Emby.Server.Implementations } catch (Exception ex) { - Logger.LogError(ex, "Error registering {Assembly} with D.I.", pluginServiceRegistrar.Assembly); + Logger.LogError(ex, "Error registering plugin services from {Assembly}.", pluginServiceRegistrar.Assembly); } } } From 16a0357617f576c1fae9a996f36f92b903810087 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sun, 11 Oct 2020 23:51:56 +0100 Subject: [PATCH 17/35] Update Emby.Server.Implementations/ApplicationHost.cs Co-authored-by: Claus Vium --- Emby.Server.Implementations/ApplicationHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index c1e6038ac..11143128a 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -502,7 +502,7 @@ namespace Emby.Server.Implementations RegisterServices(); - RegisterPlugInServices(); + RegisterPluginServices(); } /// From f7cc2f785c134d16c9ad7a5ed5d9a3e12455cbab Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 12 Oct 2020 07:48:53 +0100 Subject: [PATCH 18/35] Rename IPluginRegistrar.cs to IPluginServiceRegistrator.cs --- .../Plugins/{IPluginRegistrar.cs => IPluginServiceRegistrator.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename MediaBrowser.Common/Plugins/{IPluginRegistrar.cs => IPluginServiceRegistrator.cs} (100%) diff --git a/MediaBrowser.Common/Plugins/IPluginRegistrar.cs b/MediaBrowser.Common/Plugins/IPluginServiceRegistrator.cs similarity index 100% rename from MediaBrowser.Common/Plugins/IPluginRegistrar.cs rename to MediaBrowser.Common/Plugins/IPluginServiceRegistrator.cs From 53bea919d0fcc7812b30ac5c47668152f5c66689 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 12 Oct 2020 07:51:09 +0100 Subject: [PATCH 19/35] Update IPluginServiceRegistrator.cs --- MediaBrowser.Common/Plugins/IPluginServiceRegistrator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Common/Plugins/IPluginServiceRegistrator.cs b/MediaBrowser.Common/Plugins/IPluginServiceRegistrator.cs index 4e6265fe2..cb6c27f86 100644 --- a/MediaBrowser.Common/Plugins/IPluginServiceRegistrator.cs +++ b/MediaBrowser.Common/Plugins/IPluginServiceRegistrator.cs @@ -3,7 +3,7 @@ namespace MediaBrowser.Common.Plugins using Microsoft.Extensions.DependencyInjection; /// - /// Defines the . + /// Defines the . /// public interface IPluginServiceRegistrator { From 257acbc2c94b4764d7654f29a63c8af299442b89 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 12 Oct 2020 20:33:08 +0100 Subject: [PATCH 20/35] Update BasePlugin.cs --- MediaBrowser.Common/Plugins/BasePlugin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Common/Plugins/BasePlugin.cs b/MediaBrowser.Common/Plugins/BasePlugin.cs index 93e286710..11445d79d 100644 --- a/MediaBrowser.Common/Plugins/BasePlugin.cs +++ b/MediaBrowser.Common/Plugins/BasePlugin.cs @@ -57,7 +57,7 @@ namespace MediaBrowser.Common.Plugins /// public bool CanUninstall => !Path.GetDirectoryName(AssemblyFilePath) .Equals(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), StringComparison.InvariantCulture) && - !typeof(IPluginRegistrar).IsAssignableFrom(this.GetType()); + !typeof(IPluginServiceRegistrator).IsAssignableFrom(this.GetType()); /// /// Gets the plugin info. From 63e514e6c438a07db5f20c7a8a79545c4029f915 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 12 Oct 2020 20:34:18 +0100 Subject: [PATCH 21/35] Update ApplicationHost.cs --- Emby.Server.Implementations/ApplicationHost.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 11143128a..62c70235d 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -827,11 +827,11 @@ namespace Emby.Server.Implementations private void RegisterPluginServices() { - foreach (var pluginServiceRegistrar in GetExportTypes()) + foreach (var pluginServiceRegistrar in GetExportTypes()) { try { - var instance = (IPluginRegistrar)Activator.CreateInstance(pluginServiceRegistrar); + var instance = (IPluginServiceRegistrator)Activator.CreateInstance(pluginServiceRegistrar); instance.RegisterServices(ServiceCollection); } catch (Exception ex) From c7364be743004720a4ce1a45b07b3fa74f5fb8e0 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 12 Oct 2020 20:53:10 +0100 Subject: [PATCH 22/35] Update IConfigurationManager.cs --- MediaBrowser.Common/Configuration/IConfigurationManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Common/Configuration/IConfigurationManager.cs b/MediaBrowser.Common/Configuration/IConfigurationManager.cs index 8cbeaea86..60faaa617 100644 --- a/MediaBrowser.Common/Configuration/IConfigurationManager.cs +++ b/MediaBrowser.Common/Configuration/IConfigurationManager.cs @@ -50,7 +50,7 @@ namespace MediaBrowser.Common.Configuration /// Manually pre-loads a factory so that it is available pre system initialisation. /// /// Class to register. - void RegisterConfiguration(); + void RegisterConfiguration() where T : IConfigurationFactory; /// /// Gets the configuration. From 5e5f1cc9c57a2bca162f63b4c5ce559ae785344a Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 12 Oct 2020 20:56:37 +0100 Subject: [PATCH 23/35] Update IConfigurationManager.cs --- MediaBrowser.Common/Configuration/IConfigurationManager.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Common/Configuration/IConfigurationManager.cs b/MediaBrowser.Common/Configuration/IConfigurationManager.cs index 60faaa617..47c009ea9 100644 --- a/MediaBrowser.Common/Configuration/IConfigurationManager.cs +++ b/MediaBrowser.Common/Configuration/IConfigurationManager.cs @@ -50,7 +50,8 @@ namespace MediaBrowser.Common.Configuration /// Manually pre-loads a factory so that it is available pre system initialisation. /// /// Class to register. - void RegisterConfiguration() where T : IConfigurationFactory; + void RegisterConfiguration() + where T : IConfigurationFactory; /// /// Gets the configuration. From 00b2539a7013abea12596333dbcce146bbd72745 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 12 Oct 2020 21:00:54 +0100 Subject: [PATCH 24/35] Update IConfigurationManager.cs --- MediaBrowser.Common/Configuration/IConfigurationManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Common/Configuration/IConfigurationManager.cs b/MediaBrowser.Common/Configuration/IConfigurationManager.cs index 47c009ea9..fc63d9350 100644 --- a/MediaBrowser.Common/Configuration/IConfigurationManager.cs +++ b/MediaBrowser.Common/Configuration/IConfigurationManager.cs @@ -50,7 +50,7 @@ namespace MediaBrowser.Common.Configuration /// Manually pre-loads a factory so that it is available pre system initialisation. /// /// Class to register. - void RegisterConfiguration() + void RegisterConfiguration() where T : IConfigurationFactory; /// From 0e872ca65ccae174a0e4090f6a3197d6337356d6 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Wed, 14 Oct 2020 19:03:54 +0100 Subject: [PATCH 25/35] Update Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs Co-authored-by: Cody Robibero --- .../AppBase/BaseConfigurationManager.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs index ca52b80cc..bb22c31fe 100644 --- a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs +++ b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs @@ -137,7 +137,8 @@ namespace Emby.Server.Implementations.AppBase /// Manually pre-loads a factory so that it is available pre system initialisation. /// /// Class to register. - public virtual void RegisterConfiguration() where T : IConfigurationFactory + public virtual void RegisterConfiguration() + where T : IConfigurationFactory { IConfigurationFactory factory = (IConfigurationFactory)Activator.CreateInstance(typeof(T)); From 002190f0a392a5f248f508cbd9ffd7a9fd4f3982 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Wed, 14 Oct 2020 19:04:09 +0100 Subject: [PATCH 26/35] Update Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs Co-authored-by: Cody Robibero --- Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs index bb22c31fe..9e667a497 100644 --- a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs +++ b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs @@ -144,7 +144,7 @@ namespace Emby.Server.Implementations.AppBase if (_configurationFactories == null) { - _configurationFactories = new IConfigurationFactory[] { factory }; + _configurationFactories = new[] { factory }; } else { From a8cee0bd36278035135bf871f25c126a12fde9ef Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Wed, 14 Oct 2020 19:04:17 +0100 Subject: [PATCH 27/35] Update Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs Co-authored-by: Cody Robibero --- Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs index 9e667a497..a5a361d24 100644 --- a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs +++ b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs @@ -148,7 +148,7 @@ namespace Emby.Server.Implementations.AppBase } else { - var list = _configurationFactories.ToList(); + var list = _configurationFactories.ToList(); list.Add(factory); _configurationFactories = list.ToArray(); } From f63a706a86aeedd6bdea96903bd8dd03c6703b5e Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 9 Nov 2020 11:23:52 +0000 Subject: [PATCH 28/35] Update Emby.Server.Implementations/ApplicationHost.cs Co-authored-by: Claus Vium --- Emby.Server.Implementations/ApplicationHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 69f8521d0..97d46a0c0 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -826,7 +826,7 @@ namespace Emby.Server.Implementations private void RegisterPluginServices() { - foreach (var pluginServiceRegistrar in GetExportTypes()) + foreach (var pluginServiceRegistrator in GetExportTypes()) { try { From 69790ef6b8632e46cbdad0ea8415c35a107dcb1e Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 9 Nov 2020 11:24:53 +0000 Subject: [PATCH 29/35] Update Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs Co-authored-by: Claus Vium --- .../AppBase/BaseConfigurationManager.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs index a5a361d24..15bf92db1 100644 --- a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs +++ b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs @@ -148,9 +148,11 @@ namespace Emby.Server.Implementations.AppBase } else { - var list = _configurationFactories.ToList(); - list.Add(factory); - _configurationFactories = list.ToArray(); + var oldLen = _configurationFactories.Length; + var arr = new IConfigurationFactory[oldLen + 1]; + _configurationFactories.CopyTo(arr, 0); + arr[oldLen] = factory; + _configurationFactories = arr; } _configurationStores = _configurationFactories From e340e755f27f0022806e8c119ef3a12e11b88911 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 9 Nov 2020 11:25:05 +0000 Subject: [PATCH 30/35] Update Emby.Server.Implementations/ApplicationHost.cs Co-authored-by: Claus Vium --- Emby.Server.Implementations/ApplicationHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 97d46a0c0..04f0b5518 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -830,7 +830,7 @@ namespace Emby.Server.Implementations { try { - var instance = (IPluginServiceRegistrator)Activator.CreateInstance(pluginServiceRegistrar); + var instance = (IPluginServiceRegistrator)Activator.CreateInstance(pluginServiceRegistrator); instance.RegisterServices(ServiceCollection); } catch (Exception ex) From 11a5353810badafb0b0d4256007ed057c0b94e27 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 9 Nov 2020 11:25:16 +0000 Subject: [PATCH 31/35] Update Emby.Server.Implementations/ApplicationHost.cs Co-authored-by: Claus Vium --- Emby.Server.Implementations/ApplicationHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 04f0b5518..b180df5e7 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -835,7 +835,7 @@ namespace Emby.Server.Implementations } catch (Exception ex) { - Logger.LogError(ex, "Error registering plugin services from {Assembly}.", pluginServiceRegistrar.Assembly); + Logger.LogError(ex, "Error registering plugin services from {Assembly}.", pluginServiceRegistrator.Assembly); } } } From 429de724430671a9019ab38af55b8f11889a62cd Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 9 Nov 2020 11:25:42 +0000 Subject: [PATCH 32/35] Update MediaBrowser.Common/Plugins/BasePlugin.cs Co-authored-by: Claus Vium --- MediaBrowser.Common/Plugins/BasePlugin.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Common/Plugins/BasePlugin.cs b/MediaBrowser.Common/Plugins/BasePlugin.cs index 11445d79d..ae9218bcd 100644 --- a/MediaBrowser.Common/Plugins/BasePlugin.cs +++ b/MediaBrowser.Common/Plugins/BasePlugin.cs @@ -56,8 +56,8 @@ namespace MediaBrowser.Common.Plugins /// Gets a value indicating whether the plugin can be uninstalled. /// public bool CanUninstall => !Path.GetDirectoryName(AssemblyFilePath) - .Equals(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), StringComparison.InvariantCulture) && - !typeof(IPluginServiceRegistrator).IsAssignableFrom(this.GetType()); + .Equals(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), StringComparison.InvariantCulture) + && !typeof(IPluginServiceRegistrator).IsAssignableFrom(this.GetType()); /// /// Gets the plugin info. From 957b5df0f0048f5e4930cdce97e4306c98a96b40 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 9 Nov 2020 11:26:06 +0000 Subject: [PATCH 33/35] Update MediaBrowser.Common/Plugins/IPluginServiceRegistrator.cs Co-authored-by: Claus Vium --- MediaBrowser.Common/Plugins/IPluginServiceRegistrator.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Common/Plugins/IPluginServiceRegistrator.cs b/MediaBrowser.Common/Plugins/IPluginServiceRegistrator.cs index cb6c27f86..3afe874c5 100644 --- a/MediaBrowser.Common/Plugins/IPluginServiceRegistrator.cs +++ b/MediaBrowser.Common/Plugins/IPluginServiceRegistrator.cs @@ -9,8 +9,10 @@ namespace MediaBrowser.Common.Plugins { /// /// Registers the plugin's services with the service collection. - /// This object is created prior to the plugin creation, so access to other classes is limited. /// + /// + /// This interface is only used for service registration and requires a parameterless constructor. + /// /// The service collection. void RegisterServices(IServiceCollection serviceCollection); } From caea2bb8623c37ef1162c6132dcfc36439bb2f1a Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Wed, 11 Nov 2020 17:00:19 +0000 Subject: [PATCH 34/35] Update Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs Co-authored-by: Bond-009 --- Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs index 15bf92db1..4f72c8ce1 100644 --- a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs +++ b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs @@ -140,7 +140,7 @@ namespace Emby.Server.Implementations.AppBase public virtual void RegisterConfiguration() where T : IConfigurationFactory { - IConfigurationFactory factory = (IConfigurationFactory)Activator.CreateInstance(typeof(T)); + IConfigurationFactory factory = Activator.CreateInstance(); if (_configurationFactories == null) { From 51996cd34dface6c9d6a6fb969bd412ac8cb56f7 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Wed, 11 Nov 2020 19:04:22 +0000 Subject: [PATCH 35/35] Update BasePlugin.cs --- MediaBrowser.Common/Plugins/BasePlugin.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MediaBrowser.Common/Plugins/BasePlugin.cs b/MediaBrowser.Common/Plugins/BasePlugin.cs index ae9218bcd..e21d8c7d1 100644 --- a/MediaBrowser.Common/Plugins/BasePlugin.cs +++ b/MediaBrowser.Common/Plugins/BasePlugin.cs @@ -56,8 +56,7 @@ namespace MediaBrowser.Common.Plugins /// Gets a value indicating whether the plugin can be uninstalled. /// public bool CanUninstall => !Path.GetDirectoryName(AssemblyFilePath) - .Equals(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), StringComparison.InvariantCulture) - && !typeof(IPluginServiceRegistrator).IsAssignableFrom(this.GetType()); + .Equals(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), StringComparison.InvariantCulture); /// /// Gets the plugin info.