diff options
| author | BaronGreenback <jimcartlidge@yahoo.co.uk> | 2020-10-17 09:26:14 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-10-17 09:26:14 +0100 |
| commit | d42bb515ce3692abd9295008872c7f9d62b47652 (patch) | |
| tree | 53743242f3b34aeeb24334572e50d67dc9f92727 /Emby.Server.Implementations/ApplicationHost.cs | |
| parent | 75efb8f52d4234bfdefa2a0ac48f7261aa9ef58b (diff) | |
| parent | 86090ab1f65c66958c897cacd04221c537053eb3 (diff) | |
Merge branch 'master' into video-resolver
Diffstat (limited to 'Emby.Server.Implementations/ApplicationHost.cs')
| -rw-r--r-- | Emby.Server.Implementations/ApplicationHost.cs | 469 |
1 files changed, 277 insertions, 192 deletions
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index be4e05a64..31ca73829 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -38,23 +38,24 @@ 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; using Emby.Server.Implementations.ScheduledTasks; using Emby.Server.Implementations.Security; using Emby.Server.Implementations.Serialization; -using Emby.Server.Implementations.Services; using Emby.Server.Implementations.Session; +using Emby.Server.Implementations.SyncPlay; using Emby.Server.Implementations.TV; using Emby.Server.Implementations.Updates; -using Emby.Server.Implementations.SyncPlay; -using MediaBrowser.Api; +using Jellyfin.Api.Helpers; 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; using MediaBrowser.Controller; -using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Chapters; using MediaBrowser.Controller.Collections; @@ -73,13 +74,14 @@ using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Playlists; using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Providers; +using MediaBrowser.Controller.QuickConnect; using MediaBrowser.Controller.Resolvers; using MediaBrowser.Controller.Security; using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Sorting; using MediaBrowser.Controller.Subtitles; -using MediaBrowser.Controller.TV; using MediaBrowser.Controller.SyncPlay; +using MediaBrowser.Controller.TV; using MediaBrowser.LocalMetadata.Savers; using MediaBrowser.MediaEncoding.BdInfo; using MediaBrowser.Model.Configuration; @@ -90,20 +92,20 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Net; using MediaBrowser.Model.Serialization; -using MediaBrowser.Model.Services; using MediaBrowser.Model.System; using MediaBrowser.Model.Tasks; using MediaBrowser.Providers.Chapters; using MediaBrowser.Providers.Manager; using MediaBrowser.Providers.Plugins.TheTvdb; +using MediaBrowser.Providers.Plugins.Tmdb; using MediaBrowser.Providers.Subtitles; -using MediaBrowser.WebDashboard.Api; using MediaBrowser.XbmcMetadata.Providers; -using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Prometheus.DotNetRuntime; using OperatingSystem = MediaBrowser.Common.System.OperatingSystem; +using WebSocketManager = Emby.Server.Implementations.HttpServer.WebSocketManager; namespace Emby.Server.Implementations { @@ -120,18 +122,22 @@ namespace Emby.Server.Implementations private readonly IFileSystem _fileSystemManager; private readonly INetworkManager _networkManager; private readonly IXmlSerializer _xmlSerializer; + private readonly IJsonSerializer _jsonSerializer; private readonly IStartupOptions _startupOptions; private IMediaEncoder _mediaEncoder; private ISessionManager _sessionManager; - private IHttpServer _httpServer; - private IHttpClient _httpClient; + private IHttpClientFactory _httpClientFactory; + + private string[] _urlPrefixes; /// <summary> /// Gets a value indicating whether this instance can self restart. /// </summary> public bool CanSelfRestart => _startupOptions.RestartPath != null; + public bool CoreStartupHasCompleted { get; private set; } + public virtual bool CanLaunchWebBrowser { get @@ -173,7 +179,9 @@ namespace Emby.Server.Implementations /// <summary> /// Gets the logger. /// </summary> - protected ILogger Logger { get; } + protected ILogger<ApplicationHost> Logger { get; } + + protected IServiceCollection ServiceCollection { get; } private IPlugin[] _plugins; @@ -192,7 +200,7 @@ namespace Emby.Server.Implementations /// Gets or sets the application paths. /// </summary> /// <value>The application paths.</value> - protected ServerApplicationPaths ApplicationPaths { get; set; } + protected IServerApplicationPaths ApplicationPaths { get; set; } /// <summary> /// Gets or sets all concrete types. @@ -233,16 +241,26 @@ namespace Emby.Server.Implementations public IServerConfigurationManager ServerConfigurationManager => (IServerConfigurationManager)ConfigurationManager; /// <summary> - /// Initializes a new instance of the <see cref="ApplicationHost" /> class. + /// Initializes a new instance of the <see cref="ApplicationHost"/> class. /// </summary> + /// <param name="applicationPaths">Instance of the <see cref="IServerApplicationPaths"/> interface.</param> + /// <param name="loggerFactory">Instance of the <see cref="ILoggerFactory"/> interface.</param> + /// <param name="options">Instance of the <see cref="IStartupOptions"/> interface.</param> + /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param> + /// <param name="networkManager">Instance of the <see cref="INetworkManager"/> interface.</param> + /// <param name="serviceCollection">Instance of the <see cref="IServiceCollection"/> interface.</param> public ApplicationHost( - ServerApplicationPaths applicationPaths, + IServerApplicationPaths applicationPaths, ILoggerFactory loggerFactory, IStartupOptions options, IFileSystem fileSystem, - INetworkManager networkManager) + INetworkManager networkManager, + IServiceCollection serviceCollection) { _xmlSerializer = new MyXmlSerializer(); + _jsonSerializer = new JsonSerializer(); + + ServiceCollection = serviceCollection; _networkManager = networkManager; networkManager.LocalSubnetsFn = GetConfiguredLocalSubnets; @@ -273,6 +291,10 @@ namespace Emby.Server.Implementations Password = ServerConfigurationManager.Configuration.CertificatePassword }; Certificate = GetCertificate(CertificateInfo); + + ApplicationVersion = typeof(ApplicationHost).Assembly.GetName().Version; + ApplicationVersionString = ApplicationVersion.ToString(3); + ApplicationUserAgent = Name.Replace(' ', '-') + "/" + ApplicationVersionString; } public string ExpandVirtualPath(string path) @@ -302,16 +324,16 @@ namespace Emby.Server.Implementations } /// <inheritdoc /> - public Version ApplicationVersion { get; } = typeof(ApplicationHost).Assembly.GetName().Version; + public Version ApplicationVersion { get; } /// <inheritdoc /> - public string ApplicationVersionString { get; } = typeof(ApplicationHost).Assembly.GetName().Version.ToString(3); + public string ApplicationVersionString { get; } /// <summary> /// Gets the current application user agent. /// </summary> /// <value>The application user agent.</value> - public string ApplicationUserAgent => Name.Replace(' ', '-') + "/" + ApplicationVersionString; + public string ApplicationUserAgent { get; } /// <summary> /// Gets the email address for use within a comment section of a user agent field. @@ -442,8 +464,7 @@ namespace Emby.Server.Implementations Logger.LogInformation("Executed all pre-startup entry points in {Elapsed:g}", stopWatch.Elapsed); Logger.LogInformation("Core startup complete"); - _httpServer.GlobalResponse = null; - + CoreStartupHasCompleted = true; stopWatch.Restart(); await Task.WhenAll(StartEntryPoints(entryPoints, false)).ConfigureAwait(false); Logger.LogInformation("Executed all post-startup entry points in {Elapsed:g}", stopWatch.Elapsed); @@ -466,7 +487,7 @@ namespace Emby.Server.Implementations } /// <inheritdoc/> - public void Init(IServiceCollection serviceCollection) + public void Init() { HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber; HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber; @@ -484,12 +505,10 @@ namespace Emby.Server.Implementations foreach (var plugin in Plugins) { - pluginBuilder.AppendLine( - string.Format( - CultureInfo.InvariantCulture, - "{0} {1}", - plugin.Name, - plugin.Version)); + pluginBuilder.Append(plugin.Name) + .Append(' ') + .Append(plugin.Version) + .AppendLine(); } Logger.LogInformation("Plugins: {Plugins}", pluginBuilder.ToString()); @@ -497,150 +516,142 @@ namespace Emby.Server.Implementations DiscoverTypes(); - RegisterServices(serviceCollection); + RegisterServices(); } - public Task ExecuteHttpHandlerAsync(HttpContext context, Func<Task> next) - => _httpServer.RequestHandler(context); - /// <summary> /// Registers services/resources with the service collection that will be available via DI. /// </summary> - protected virtual void RegisterServices(IServiceCollection serviceCollection) + protected virtual void RegisterServices() { - serviceCollection.AddSingleton(_startupOptions); - - serviceCollection.AddMemoryCache(); - - serviceCollection.AddSingleton(ConfigurationManager); - serviceCollection.AddSingleton<IApplicationHost>(this); - - serviceCollection.AddSingleton<IApplicationPaths>(ApplicationPaths); - - serviceCollection.AddSingleton<IJsonSerializer, JsonSerializer>(); + ServiceCollection.AddSingleton(_startupOptions); - serviceCollection.AddSingleton(_fileSystemManager); - serviceCollection.AddSingleton<TvdbClientManager>(); + ServiceCollection.AddMemoryCache(); - serviceCollection.AddSingleton<IHttpClient, HttpClientManager.HttpClientManager>(); + ServiceCollection.AddSingleton(ConfigurationManager); + ServiceCollection.AddSingleton<IApplicationHost>(this); - serviceCollection.AddSingleton(_networkManager); + ServiceCollection.AddSingleton<IApplicationPaths>(ApplicationPaths); - serviceCollection.AddSingleton<IIsoManager, IsoManager>(); + ServiceCollection.AddSingleton<IJsonSerializer, JsonSerializer>(); - serviceCollection.AddSingleton<ITaskManager, TaskManager>(); + ServiceCollection.AddSingleton(_fileSystemManager); + ServiceCollection.AddSingleton<TvdbClientManager>(); + ServiceCollection.AddSingleton<TmdbClientManager>(); - serviceCollection.AddSingleton(_xmlSerializer); + ServiceCollection.AddSingleton(_networkManager); - serviceCollection.AddSingleton<IStreamHelper, StreamHelper>(); + ServiceCollection.AddSingleton<IIsoManager, IsoManager>(); - serviceCollection.AddSingleton<ICryptoProvider, CryptographyProvider>(); + ServiceCollection.AddSingleton<ITaskManager, TaskManager>(); - serviceCollection.AddSingleton<ISocketFactory, SocketFactory>(); + ServiceCollection.AddSingleton(_xmlSerializer); - serviceCollection.AddSingleton<IInstallationManager, InstallationManager>(); + ServiceCollection.AddSingleton<IStreamHelper, StreamHelper>(); - serviceCollection.AddSingleton<IZipClient, ZipClient>(); + ServiceCollection.AddSingleton<ICryptoProvider, CryptographyProvider>(); - serviceCollection.AddSingleton<IHttpResultFactory, HttpResultFactory>(); + ServiceCollection.AddSingleton<ISocketFactory, SocketFactory>(); - serviceCollection.AddSingleton<IServerApplicationHost>(this); - serviceCollection.AddSingleton<IServerApplicationPaths>(ApplicationPaths); + ServiceCollection.AddSingleton<IInstallationManager, InstallationManager>(); - serviceCollection.AddSingleton(ServerConfigurationManager); + ServiceCollection.AddSingleton<IZipClient, ZipClient>(); - serviceCollection.AddSingleton<ILocalizationManager, LocalizationManager>(); + ServiceCollection.AddSingleton<IServerApplicationHost>(this); + ServiceCollection.AddSingleton<IServerApplicationPaths>(ApplicationPaths); - serviceCollection.AddSingleton<IBlurayExaminer, BdInfoExaminer>(); + ServiceCollection.AddSingleton(ServerConfigurationManager); - serviceCollection.AddSingleton<IUserDataRepository, SqliteUserDataRepository>(); - serviceCollection.AddSingleton<IUserDataManager, UserDataManager>(); + ServiceCollection.AddSingleton<ILocalizationManager, LocalizationManager>(); - serviceCollection.AddSingleton<IDisplayPreferencesRepository, SqliteDisplayPreferencesRepository>(); + ServiceCollection.AddSingleton<IBlurayExaminer, BdInfoExaminer>(); - serviceCollection.AddSingleton<IItemRepository, SqliteItemRepository>(); + ServiceCollection.AddSingleton<IUserDataRepository, SqliteUserDataRepository>(); + ServiceCollection.AddSingleton<IUserDataManager, UserDataManager>(); - serviceCollection.AddSingleton<IAuthenticationRepository, AuthenticationRepository>(); + ServiceCollection.AddSingleton<IItemRepository, SqliteItemRepository>(); - serviceCollection.AddSingleton<IUserRepository, SqliteUserRepository>(); + ServiceCollection.AddSingleton<IAuthenticationRepository, AuthenticationRepository>(); // TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required - serviceCollection.AddTransient(provider => new Lazy<IDtoService>(provider.GetRequiredService<IDtoService>)); - serviceCollection.AddSingleton<IUserManager, UserManager>(); + ServiceCollection.AddTransient(provider => new Lazy<IDtoService>(provider.GetRequiredService<IDtoService>)); // TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required - // TODO: Add StartupOptions.FFmpegPath to IConfiguration and remove this custom activation - serviceCollection.AddTransient(provider => new Lazy<EncodingHelper>(provider.GetRequiredService<EncodingHelper>)); - serviceCollection.AddSingleton<IMediaEncoder>(provider => - ActivatorUtilities.CreateInstance<MediaBrowser.MediaEncoding.Encoder.MediaEncoder>(provider, _startupOptions.FFmpegPath ?? string.Empty)); + ServiceCollection.AddTransient(provider => new Lazy<EncodingHelper>(provider.GetRequiredService<EncodingHelper>)); + ServiceCollection.AddSingleton<IMediaEncoder, MediaBrowser.MediaEncoding.Encoder.MediaEncoder>(); // TODO: Refactor to eliminate the circular dependencies here so that Lazy<T> isn't required - serviceCollection.AddTransient(provider => new Lazy<ILibraryMonitor>(provider.GetRequiredService<ILibraryMonitor>)); - serviceCollection.AddTransient(provider => new Lazy<IProviderManager>(provider.GetRequiredService<IProviderManager>)); - serviceCollection.AddTransient(provider => new Lazy<IUserViewManager>(provider.GetRequiredService<IUserViewManager>)); - serviceCollection.AddSingleton<ILibraryManager, LibraryManager>(); + ServiceCollection.AddTransient(provider => new Lazy<ILibraryMonitor>(provider.GetRequiredService<ILibraryMonitor>)); + ServiceCollection.AddTransient(provider => new Lazy<IProviderManager>(provider.GetRequiredService<IProviderManager>)); + ServiceCollection.AddTransient(provider => new Lazy<IUserViewManager>(provider.GetRequiredService<IUserViewManager>)); + ServiceCollection.AddSingleton<ILibraryManager, LibraryManager>(); - serviceCollection.AddSingleton<IMusicManager, MusicManager>(); + ServiceCollection.AddSingleton<IMusicManager, MusicManager>(); - serviceCollection.AddSingleton<ILibraryMonitor, LibraryMonitor>(); + ServiceCollection.AddSingleton<ILibraryMonitor, LibraryMonitor>(); - serviceCollection.AddSingleton<ISearchEngine, SearchEngine>(); + ServiceCollection.AddSingleton<ISearchEngine, SearchEngine>(); - serviceCollection.AddSingleton<ServiceController>(); - serviceCollection.AddSingleton<IHttpServer, HttpListenerHost>(); + ServiceCollection.AddSingleton<IWebSocketManager, WebSocketManager>(); - serviceCollection.AddSingleton<IImageProcessor, ImageProcessor>(); + ServiceCollection.AddSingleton<IImageProcessor, ImageProcessor>(); - serviceCollection.AddSingleton<ITVSeriesManager, TVSeriesManager>(); + ServiceCollection.AddSingleton<ITVSeriesManager, TVSeriesManager>(); - serviceCollection.AddSingleton<IDeviceManager, DeviceManager>(); + ServiceCollection.AddSingleton<IDeviceManager, DeviceManager>(); - serviceCollection.AddSingleton<IMediaSourceManager, MediaSourceManager>(); + ServiceCollection.AddSingleton<IMediaSourceManager, MediaSourceManager>(); - serviceCollection.AddSingleton<ISubtitleManager, SubtitleManager>(); + ServiceCollection.AddSingleton<ISubtitleManager, SubtitleManager>(); - serviceCollection.AddSingleton<IProviderManager, ProviderManager>(); + ServiceCollection.AddSingleton<IProviderManager, ProviderManager>(); // TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required - serviceCollection.AddTransient(provider => new Lazy<ILiveTvManager>(provider.GetRequiredService<ILiveTvManager>)); - serviceCollection.AddSingleton<IDtoService, DtoService>(); + ServiceCollection.AddTransient(provider => new Lazy<ILiveTvManager>(provider.GetRequiredService<ILiveTvManager>)); + ServiceCollection.AddSingleton<IDtoService, DtoService>(); - serviceCollection.AddSingleton<IChannelManager, ChannelManager>(); + ServiceCollection.AddSingleton<IChannelManager, ChannelManager>(); - serviceCollection.AddSingleton<ISessionManager, SessionManager>(); + ServiceCollection.AddSingleton<ISessionManager, SessionManager>(); - serviceCollection.AddSingleton<IDlnaManager, DlnaManager>(); + ServiceCollection.AddSingleton<IDlnaManager, DlnaManager>(); - serviceCollection.AddSingleton<ICollectionManager, CollectionManager>(); + ServiceCollection.AddSingleton<ICollectionManager, CollectionManager>(); - serviceCollection.AddSingleton<IPlaylistManager, PlaylistManager>(); + ServiceCollection.AddSingleton<IPlaylistManager, PlaylistManager>(); - serviceCollection.AddSingleton<ISyncPlayManager, SyncPlayManager>(); + ServiceCollection.AddSingleton<ISyncPlayManager, SyncPlayManager>(); - serviceCollection.AddSingleton<LiveTvDtoService>(); - serviceCollection.AddSingleton<ILiveTvManager, LiveTvManager>(); + ServiceCollection.AddSingleton<LiveTvDtoService>(); + ServiceCollection.AddSingleton<ILiveTvManager, LiveTvManager>(); - serviceCollection.AddSingleton<IUserViewManager, UserViewManager>(); + ServiceCollection.AddSingleton<IUserViewManager, UserViewManager>(); - serviceCollection.AddSingleton<INotificationManager, NotificationManager>(); + ServiceCollection.AddSingleton<INotificationManager, NotificationManager>(); - serviceCollection.AddSingleton<IDeviceDiscovery, DeviceDiscovery>(); + ServiceCollection.AddSingleton<IDeviceDiscovery, DeviceDiscovery>(); - serviceCollection.AddSingleton<IChapterManager, ChapterManager>(); + ServiceCollection.AddSingleton<IChapterManager, ChapterManager>(); - serviceCollection.AddSingleton<IEncodingManager, MediaEncoder.EncodingManager>(); + ServiceCollection.AddSingleton<IEncodingManager, MediaEncoder.EncodingManager>(); - serviceCollection.AddSingleton<IAuthorizationContext, AuthorizationContext>(); - serviceCollection.AddSingleton<ISessionContext, SessionContext>(); + ServiceCollection.AddSingleton<IAuthorizationContext, AuthorizationContext>(); + ServiceCollection.AddSingleton<ISessionContext, SessionContext>(); - serviceCollection.AddSingleton<IAuthService, AuthService>(); + ServiceCollection.AddSingleton<IAuthService, AuthService>(); + ServiceCollection.AddSingleton<IQuickConnect, QuickConnectManager>(); - serviceCollection.AddSingleton<ISubtitleEncoder, MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder>(); + ServiceCollection.AddSingleton<ISubtitleEncoder, MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder>(); - serviceCollection.AddSingleton<IResourceFileManager, ResourceFileManager>(); - serviceCollection.AddSingleton<EncodingHelper>(); + ServiceCollection.AddSingleton<IResourceFileManager, ResourceFileManager>(); + ServiceCollection.AddSingleton<EncodingHelper>(); - serviceCollection.AddSingleton<IAttachmentExtractor, MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor>(); + ServiceCollection.AddSingleton<IAttachmentExtractor, MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor>(); + + ServiceCollection.AddSingleton<TranscodingJobHelper>(); + ServiceCollection.AddScoped<MediaInfoHelper>(); + ServiceCollection.AddScoped<AudioHelper>(); + ServiceCollection.AddScoped<DynamicHlsHelper>(); } /// <summary> @@ -654,20 +665,14 @@ namespace Emby.Server.Implementations _mediaEncoder = Resolve<IMediaEncoder>(); _sessionManager = Resolve<ISessionManager>(); - _httpServer = Resolve<IHttpServer>(); - _httpClient = Resolve<IHttpClient>(); + _httpClientFactory = Resolve<IHttpClientFactory>(); - ((SqliteDisplayPreferencesRepository)Resolve<IDisplayPreferencesRepository>()).Initialize(); ((AuthenticationRepository)Resolve<IAuthenticationRepository>()).Initialize(); - ((SqliteUserRepository)Resolve<IUserRepository>()).Initialize(); SetStaticProperties(); - var userManager = (UserManager)Resolve<IUserManager>(); - userManager.Initialize(); - var userDataRepo = (SqliteUserDataRepository)Resolve<IUserDataRepository>(); - ((SqliteItemRepository)Resolve<IItemRepository>()).Initialize(userDataRepo, userManager); + ((SqliteItemRepository)Resolve<IItemRepository>()).Initialize(userDataRepo, Resolve<IUserManager>()); FindParts(); } @@ -750,7 +755,6 @@ namespace Emby.Server.Implementations BaseItem.ProviderManager = Resolve<IProviderManager>(); BaseItem.LocalizationManager = Resolve<ILocalizationManager>(); BaseItem.ItemRepository = Resolve<IItemRepository>(); - User.UserManager = Resolve<IUserManager>(); BaseItem.FileSystem = _fileSystemManager; BaseItem.UserDataManager = Resolve<IUserDataManager>(); BaseItem.ChannelManager = Resolve<IChannelManager>(); @@ -762,7 +766,6 @@ namespace Emby.Server.Implementations CollectionFolder.XmlSerializer = _xmlSerializer; CollectionFolder.JsonSerializer = Resolve<IJsonSerializer>(); CollectionFolder.ApplicationHost = this; - AuthenticatedAttribute.AuthService = Resolve<IAuthService>(); } /// <summary> @@ -782,7 +785,7 @@ namespace Emby.Server.Implementations .Where(i => i != null) .ToArray(); - _httpServer.Init(GetExportTypes<IService>(), GetExports<IWebSocketListener>(), GetUrlPrefixes()); + _urlPrefixes = GetUrlPrefixes().ToArray(); Resolve<ILibraryManager>().AddParts( GetExports<IResolverIgnoreRule>(), @@ -807,7 +810,6 @@ namespace Emby.Server.Implementations Resolve<IMediaSourceManager>().AddParts(GetExports<IMediaSourceProvider>()); Resolve<INotificationManager>().AddParts(GetExports<INotificationService>(), GetExports<INotificationTypeFactory>()); - Resolve<IUserManager>().AddParts(GetExports<IAuthenticationProvider>(), GetExports<IPasswordResetProvider>()); Resolve<IIsoManager>().AddParts(GetExports<IIsoMounter>()); } @@ -816,37 +818,7 @@ 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) { @@ -881,6 +853,11 @@ namespace Emby.Server.Implementations Logger.LogError(ex, "Error getting exported types from {Assembly}", ass.FullName); continue; } + catch (TypeLoadException ex) + { + Logger.LogError(ex, "Error loading types from {Assembly}.", ass.FullName); + continue; + } foreach (Type type in exportedTypes) { @@ -942,7 +919,7 @@ namespace Emby.Server.Implementations } } - if (!_httpServer.UrlPrefixes.SequenceEqual(GetUrlPrefixes(), StringComparer.OrdinalIgnoreCase)) + if (!_urlPrefixes.SequenceEqual(GetUrlPrefixes(), StringComparer.OrdinalIgnoreCase)) { requiresRestart = true; } @@ -964,7 +941,7 @@ namespace Emby.Server.Implementations } /// <summary> - /// Notifies that the kernel that a change has been made that requires a restart + /// Notifies that the kernel that a change has been made that requires a restart. /// </summary> public void NotifyPendingRestart() { @@ -1017,6 +994,119 @@ namespace Emby.Server.Implementations protected abstract void RestartInternal(); /// <summary> + /// Comparison function used in <see cref="GetPlugins" />. + /// </summary> + /// <param name="a">Item to compare.</param> + /// <param name="b">Item to compare with.</param> + /// <returns>Boolean result of the operation.</returns> + private static int VersionCompare( + (Version PluginVersion, string Name, string Path) a, + (Version PluginVersion, string Name, string Path) b) + { + int compare = string.Compare(a.Name, b.Name, true, CultureInfo.InvariantCulture); + + if (compare == 0) + { + return a.PluginVersion.CompareTo(b.PluginVersion); + } + + return compare; + } + + /// <summary> + /// Returns a list of plugins to install. + /// </summary> + /// <param name="path">Path to check.</param> + /// <param name="cleanup">True if an attempt should be made to delete old plugs.</param> + /// <returns>Enumerable list of dlls to load.</returns> + private IEnumerable<string> GetPlugins(string path, bool cleanup = true) + { + var dllList = new List<string>(); + var versions = new List<(Version PluginVersion, string Name, string Path)>(); + var directories = Directory.EnumerateDirectories(path, "*.*", SearchOption.TopDirectoryOnly); + string metafile; + + foreach (var dir in directories) + { + try + { + metafile = Path.Combine(dir, "meta.json"); + if (File.Exists(metafile)) + { + var manifest = _jsonSerializer.DeserializeFromFile<PluginManifest>(metafile); + + if (!Version.TryParse(manifest.TargetAbi, out var targetAbi)) + { + targetAbi = new Version(0, 0, 0, 1); + } + + if (!Version.TryParse(manifest.Version, out var version)) + { + version = new Version(0, 0, 0, 1); + } + + if (ApplicationVersion >= targetAbi) + { + // Only load Plugins if the plugin is built for this version or below. + versions.Add((version, manifest.Name, dir)); + } + } + else + { + // 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)) + { + // Versioned folder. + versions.Add((ver, metafile, dir)); + } + else + { + // 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 + { + continue; + } + } + + string lastName = string.Empty; + versions.Sort(VersionCompare); + // Traverse backwards through the list. + // The first item will be the latest version. + for (int x = versions.Count - 1; x >= 0; x--) + { + if (!string.Equals(lastName, versions[x].Name, StringComparison.OrdinalIgnoreCase)) + { + dllList.AddRange(Directory.EnumerateFiles(versions[x].Path, "*.dll", SearchOption.AllDirectories)); + lastName = versions[x].Name; + continue; + } + + if (!string.IsNullOrEmpty(lastName) && cleanup) + { + // Attempt a cleanup of old folders. + try + { + Logger.LogDebug("Deleting {Path}", versions[x].Path); + Directory.Delete(versions[x].Path, true); + } + catch (Exception e) + { + Logger.LogWarning(e, "Unable to delete {Path}", versions[x].Path); + } + } + } + + return dllList; + } + + /// <summary> /// Gets the composable part assemblies. /// </summary> /// <returns>IEnumerable{Assembly}.</returns> @@ -1024,7 +1114,7 @@ namespace Emby.Server.Implementations { if (Directory.Exists(ApplicationPaths.PluginsPath)) { - foreach (var file in Directory.EnumerateFiles(ApplicationPaths.PluginsPath, "*.dll", SearchOption.AllDirectories)) + foreach (var file in GetPlugins(ApplicationPaths.PluginsPath)) { Assembly plugAss; try @@ -1042,12 +1132,6 @@ namespace Emby.Server.Implementations } } - // Include composable parts in the Api assembly - yield return typeof(ApiEntryPoint).Assembly; - - // Include composable parts in the Dashboard assembly - yield return typeof(DashboardService).Assembly; - // Include composable parts in the Model assembly yield return typeof(SystemInfo).Assembly; @@ -1144,7 +1228,8 @@ namespace Emby.Server.Implementations Id = SystemId, OperatingSystem = OperatingSystem.Id.ToString(), ServerName = FriendlyName, - LocalAddress = localAddress + LocalAddress = localAddress, + StartupWizardCompleted = ConfigurationManager.CommonConfiguration.IsStartupWizardCompleted }; } @@ -1163,7 +1248,7 @@ namespace Emby.Server.Implementations return null; } - return GetLocalApiUrl(addresses.First()); + return GetLocalApiUrl(addresses[0]); } catch (Exception ex) { @@ -1236,13 +1321,13 @@ namespace Emby.Server.Implementations var addresses = ServerConfigurationManager .Configuration .LocalNetworkAddresses - .Select(NormalizeConfiguredLocalAddress) + .Select(x => NormalizeConfiguredLocalAddress(x)) .Where(i => i != null) .ToList(); if (addresses.Count == 0) { - addresses.AddRange(_networkManager.GetLocalIpAddresses(ServerConfigurationManager.Configuration.IgnoreVirtualInterfaces)); + addresses.AddRange(_networkManager.GetLocalIpAddresses()); } var resultList = new List<IPAddress>(); @@ -1257,8 +1342,7 @@ namespace Emby.Server.Implementations } } - var valid = await IsLocalIpAddressValidAsync(address, cancellationToken).ConfigureAwait(false); - if (valid) + if (await IsLocalIpAddressValidAsync(address, cancellationToken).ConfigureAwait(false)) { resultList.Add(address); @@ -1272,13 +1356,12 @@ namespace Emby.Server.Implementations return resultList; } - public IPAddress NormalizeConfiguredLocalAddress(string address) + public IPAddress NormalizeConfiguredLocalAddress(ReadOnlySpan<char> address) { var index = address.Trim('/').IndexOf('/'); - if (index != -1) { - address = address.Substring(index + 1); + address = address.Slice(index + 1); } if (IPAddress.TryParse(address.Trim('/'), out IPAddress result)) @@ -1308,25 +1391,17 @@ namespace Emby.Server.Implementations try { - using (var response = await _httpClient.SendAsync( - new HttpRequestOptions - { - Url = apiUrl, - LogErrorResponseBody = false, - BufferContent = false, - CancellationToken = cancellationToken - }, HttpMethod.Post).ConfigureAwait(false)) - { - using (var reader = new StreamReader(response.Content)) - { - var result = await reader.ReadToEndAsync().ConfigureAwait(false); - var valid = string.Equals(Name, result, StringComparison.OrdinalIgnoreCase); + using var request = new HttpRequestMessage(HttpMethod.Post, apiUrl); + using var response = await _httpClientFactory.CreateClient(NamedClient.Default) + .SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - _validAddressResults.AddOrUpdate(apiUrl, valid, (k, v) => valid); - Logger.LogDebug("Ping test result to {0}. Success: {1}", apiUrl, valid); - return valid; - } - } + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + var result = await System.Text.Json.JsonSerializer.DeserializeAsync<string>(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) { @@ -1404,6 +1479,20 @@ namespace Emby.Server.Implementations _plugins = list.ToArray(); } + public IEnumerable<Assembly> GetApiPluginAssemblies() + { + var assemblies = _allConcreteTypes + .Where(i => typeof(ControllerBase).IsAssignableFrom(i)) + .Select(i => i.Assembly) + .Distinct(); + + foreach (var assembly in assemblies) + { + Logger.LogDebug("Found API endpoints in plugin {Name}", assembly.FullName); + yield return assembly; + } + } + public virtual void LaunchUrl(string url) { if (!CanLaunchWebBrowser) @@ -1434,10 +1523,6 @@ namespace Emby.Server.Implementations } } - public virtual void EnableLoopback(string appName) - { - } - private bool _disposed = false; /// <summary> |
