< Summary - Jellyfin

Information
Class: Emby.Server.Implementations.ApplicationHost
Assembly: Emby.Server.Implementations
File(s): /srv/git/jellyfin/Emby.Server.Implementations/ApplicationHost.cs
Line coverage
74%
Covered lines: 295
Uncovered lines: 101
Coverable lines: 396
Total lines: 1076
Line coverage: 74.4%
Branch coverage
44%
Covered branches: 60
Total branches: 134
Branch coverage: 44.7%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 5/2/2026 - 12:12:50 AM Line coverage: 73.5% (267/363) Branch coverage: 49.2% (62/126) Total lines: 10145/4/2026 - 12:15:16 AM Line coverage: 74% (274/370) Branch coverage: 49.2% (62/126) Total lines: 10215/5/2026 - 12:15:44 AM Line coverage: 74.1% (275/371) Branch coverage: 49.2% (62/126) Total lines: 10225/12/2026 - 12:15:57 AM Line coverage: 74.1% (275/371) Branch coverage: 49.2% (63/128) Total lines: 10225/16/2026 - 12:15:55 AM Line coverage: 74.6% (282/378) Branch coverage: 49.2% (63/128) Total lines: 10385/20/2026 - 12:15:44 AM Line coverage: 74.6% (282/378) Branch coverage: 45.3% (58/128) Total lines: 10386/8/2026 - 12:16:15 AM Line coverage: 74.8% (285/381) Branch coverage: 45.3% (58/128) Total lines: 10426/28/2026 - 12:15:35 AM Line coverage: 75% (289/385) Branch coverage: 45.3% (58/128) Total lines: 10537/22/2026 - 12:16:22 AM Line coverage: 74.6% (295/395) Branch coverage: 45.4% (60/132) Total lines: 10758/3/2026 - 12:16:46 AM Line coverage: 74.4% (295/396) Branch coverage: 44.7% (60/134) Total lines: 1076 5/2/2026 - 12:12:50 AM Line coverage: 73.5% (267/363) Branch coverage: 49.2% (62/126) Total lines: 10145/4/2026 - 12:15:16 AM Line coverage: 74% (274/370) Branch coverage: 49.2% (62/126) Total lines: 10215/5/2026 - 12:15:44 AM Line coverage: 74.1% (275/371) Branch coverage: 49.2% (62/126) Total lines: 10225/12/2026 - 12:15:57 AM Line coverage: 74.1% (275/371) Branch coverage: 49.2% (63/128) Total lines: 10225/16/2026 - 12:15:55 AM Line coverage: 74.6% (282/378) Branch coverage: 49.2% (63/128) Total lines: 10385/20/2026 - 12:15:44 AM Line coverage: 74.6% (282/378) Branch coverage: 45.3% (58/128) Total lines: 10386/8/2026 - 12:16:15 AM Line coverage: 74.8% (285/381) Branch coverage: 45.3% (58/128) Total lines: 10426/28/2026 - 12:15:35 AM Line coverage: 75% (289/385) Branch coverage: 45.3% (58/128) Total lines: 10537/22/2026 - 12:16:22 AM Line coverage: 74.6% (295/395) Branch coverage: 45.4% (60/132) Total lines: 10758/3/2026 - 12:16:46 AM Line coverage: 74.4% (295/396) Branch coverage: 44.7% (60/134) Total lines: 1076

Coverage delta

Coverage delta 4 -4

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_PublishedServerUrl()100%210%
get_ApplicationUserAgentAddress()100%11100%
get_SystemId()100%11100%
get_Name()100%11100%
get_ListenWithHttps()0%620%
get_FriendlyName()0%620%
ExpandVirtualPath(...)50%2280%
ReverseVirtualPath(...)100%11100%
CreateInstanceSafe(...)62.5%15852.63%
Resolve()100%11100%
GetExportTypes()100%44100%
GetExports(...)83.33%6688.88%
GetExports(...)100%66100%
RunStartupTasksAsync()50%2291.66%
EnsureStartupWizardIntegrity()50%5455.55%
Init(...)50%4481.25%
RegisterServices(...)100%11100%
InitializeServices()100%11100%
GetCertificate(...)12.5%48814.28%
SetStaticProperties()100%11100%
FindParts()50%22100%
DiscoverTypes()100%11100%
GetTypes()100%341246.66%
OnConfigurationUpdated(...)42.85%341453.33%
ValidateSslCertificate(...)33.33%15636.36%
NotifyPendingRestart()0%620%
GetComposablePartAssemblies()75%4493.75%
GetSmartApiUrl(...)0%620%
GetSmartApiUrl(...)0%210140%
GetSmartApiUrl(...)0%620%
GetApiUrlForLocalAccess(...)0%2040%
GetLocalApiUrl(...)0%156120%
GetApiPluginAssemblies()100%22100%
Dispose()100%11100%
Dispose(...)70%111077.77%

File(s)

/srv/git/jellyfin/Emby.Server.Implementations/ApplicationHost.cs

#LineLine coverage
 1#nullable disable
 2
 3#pragma warning disable CS1591
 4
 5using System;
 6using System.Collections.Concurrent;
 7using System.Collections.Generic;
 8using System.Diagnostics;
 9using System.Globalization;
 10using System.IO;
 11using System.Linq;
 12using System.Net;
 13using System.Reflection;
 14using System.Security.Cryptography.X509Certificates;
 15using System.Threading.Tasks;
 16using Emby.Naming.Common;
 17using Emby.Naming.Video;
 18using Emby.Photos;
 19using Emby.Server.Implementations.Chapters;
 20using Emby.Server.Implementations.Collections;
 21using Emby.Server.Implementations.Configuration;
 22using Emby.Server.Implementations.Cryptography;
 23using Emby.Server.Implementations.Data;
 24using Emby.Server.Implementations.Devices;
 25using Emby.Server.Implementations.Dto;
 26using Emby.Server.Implementations.HttpServer.Security;
 27using Emby.Server.Implementations.IO;
 28using Emby.Server.Implementations.Library;
 29using Emby.Server.Implementations.Library.Search;
 30using Emby.Server.Implementations.Library.SimilarItems;
 31using Emby.Server.Implementations.Localization;
 32using Emby.Server.Implementations.Playlists;
 33using Emby.Server.Implementations.Plugins;
 34using Emby.Server.Implementations.QuickConnect;
 35using Emby.Server.Implementations.ScheduledTasks;
 36using Emby.Server.Implementations.Serialization;
 37using Emby.Server.Implementations.Session;
 38using Emby.Server.Implementations.SyncPlay;
 39using Emby.Server.Implementations.TV;
 40using Emby.Server.Implementations.Updates;
 41using Jellyfin.Api.Helpers;
 42using Jellyfin.Data;
 43using Jellyfin.Database.Implementations.Enums;
 44using Jellyfin.Drawing;
 45using Jellyfin.MediaEncoding.Hls.Playlist;
 46using Jellyfin.Networking.Manager;
 47using Jellyfin.Networking.Udp;
 48using Jellyfin.Server.Implementations.FullSystemBackup;
 49using Jellyfin.Server.Implementations.Item;
 50using Jellyfin.Server.Implementations.MediaSegments;
 51using Jellyfin.Server.Implementations.SystemBackupService;
 52using MediaBrowser.Common;
 53using MediaBrowser.Common.Configuration;
 54using MediaBrowser.Common.Events;
 55using MediaBrowser.Common.Net;
 56using MediaBrowser.Common.Plugins;
 57using MediaBrowser.Common.Updates;
 58using MediaBrowser.Controller;
 59using MediaBrowser.Controller.Channels;
 60using MediaBrowser.Controller.Chapters;
 61using MediaBrowser.Controller.ClientEvent;
 62using MediaBrowser.Controller.Collections;
 63using MediaBrowser.Controller.Configuration;
 64using MediaBrowser.Controller.Drawing;
 65using MediaBrowser.Controller.Dto;
 66using MediaBrowser.Controller.Entities;
 67using MediaBrowser.Controller.Entities.TV;
 68using MediaBrowser.Controller.IO;
 69using MediaBrowser.Controller.Library;
 70using MediaBrowser.Controller.LibraryTaskScheduler;
 71using MediaBrowser.Controller.LiveTv;
 72using MediaBrowser.Controller.Lyrics;
 73using MediaBrowser.Controller.MediaEncoding;
 74using MediaBrowser.Controller.MediaSegments;
 75using MediaBrowser.Controller.Net;
 76using MediaBrowser.Controller.Persistence;
 77using MediaBrowser.Controller.Playlists;
 78using MediaBrowser.Controller.Providers;
 79using MediaBrowser.Controller.QuickConnect;
 80using MediaBrowser.Controller.Resolvers;
 81using MediaBrowser.Controller.Session;
 82using MediaBrowser.Controller.Sorting;
 83using MediaBrowser.Controller.Subtitles;
 84using MediaBrowser.Controller.SyncPlay;
 85using MediaBrowser.Controller.TV;
 86using MediaBrowser.LocalMetadata.Savers;
 87using MediaBrowser.MediaEncoding.BdInfo;
 88using MediaBrowser.MediaEncoding.Subtitles;
 89using MediaBrowser.MediaEncoding.Transcoding;
 90using MediaBrowser.Model.Cryptography;
 91using MediaBrowser.Model.Globalization;
 92using MediaBrowser.Model.IO;
 93using MediaBrowser.Model.MediaInfo;
 94using MediaBrowser.Model.Net;
 95using MediaBrowser.Model.Serialization;
 96using MediaBrowser.Model.System;
 97using MediaBrowser.Model.Tasks;
 98using MediaBrowser.Providers.Books;
 99using MediaBrowser.Providers.Books.ComicBookInfo;
 100using MediaBrowser.Providers.Books.ComicInfo;
 101using MediaBrowser.Providers.Lyric;
 102using MediaBrowser.Providers.Manager;
 103using MediaBrowser.Providers.Plugins.ListenBrainz;
 104using MediaBrowser.Providers.Plugins.ListenBrainz.Api;
 105using MediaBrowser.Providers.Plugins.Tmdb;
 106using MediaBrowser.Providers.Plugins.Tmdb.Movies;
 107using MediaBrowser.Providers.Plugins.Tmdb.TV;
 108using MediaBrowser.Providers.Subtitles;
 109using MediaBrowser.XbmcMetadata.Providers;
 110using Microsoft.AspNetCore.Http;
 111using Microsoft.AspNetCore.Mvc;
 112using Microsoft.Extensions.Configuration;
 113using Microsoft.Extensions.DependencyInjection;
 114using Microsoft.Extensions.Logging;
 115using Prometheus.DotNetRuntime;
 116using static MediaBrowser.Controller.Extensions.ConfigurationExtensions;
 117using IConfigurationManager = MediaBrowser.Common.Configuration.IConfigurationManager;
 118using WebSocketManager = Emby.Server.Implementations.HttpServer.WebSocketManager;
 119
 120namespace Emby.Server.Implementations
 121{
 122    /// <summary>
 123    /// Class CompositionRoot.
 124    /// </summary>
 125    public abstract class ApplicationHost : IServerApplicationHost, IDisposable
 126    {
 127        /// <summary>
 128        /// The disposable parts.
 129        /// </summary>
 22130        private readonly ConcurrentBag<IDisposable> _disposableParts = new();
 131        private readonly DeviceId _deviceId;
 132
 133        private readonly IConfiguration _startupConfig;
 134        private readonly IXmlSerializer _xmlSerializer;
 135        private readonly IStartupOptions _startupOptions;
 136        private readonly PluginManager _pluginManager;
 137
 138        private List<Type> _creatingInstances;
 139
 140        /// <summary>
 141        /// Gets or sets all concrete types.
 142        /// </summary>
 143        /// <value>All concrete types.</value>
 144        private Type[] _allConcreteTypes;
 145
 146        private bool _disposed;
 147
 148        /// <summary>
 149        /// Initializes a new instance of the <see cref="ApplicationHost"/> class.
 150        /// </summary>
 151        /// <param name="applicationPaths">Instance of the <see cref="IServerApplicationPaths"/> interface.</param>
 152        /// <param name="loggerFactory">Instance of the <see cref="ILoggerFactory"/> interface.</param>
 153        /// <param name="options">Instance of the <see cref="IStartupOptions"/> interface.</param>
 154        /// <param name="startupConfig">The <see cref="IConfiguration" /> interface.</param>
 155        protected ApplicationHost(
 156            IServerApplicationPaths applicationPaths,
 157            ILoggerFactory loggerFactory,
 158            IStartupOptions options,
 159            IConfiguration startupConfig)
 160        {
 161            ApplicationPaths = applicationPaths;
 162            LoggerFactory = loggerFactory;
 22163            _startupOptions = options;
 22164            _startupConfig = startupConfig;
 165
 22166            Logger = LoggerFactory.CreateLogger<ApplicationHost>();
 22167            _deviceId = new DeviceId(ApplicationPaths, LoggerFactory.CreateLogger<DeviceId>());
 168
 22169            ApplicationVersion = typeof(ApplicationHost).Assembly.GetName().Version;
 22170            ApplicationVersionString = ApplicationVersion.ToString(3);
 22171            ApplicationUserAgent = Name.Replace(' ', '-') + "/" + ApplicationVersionString;
 172
 22173            _xmlSerializer = new MyXmlSerializer();
 22174            ConfigurationManager = new ServerConfigurationManager(ApplicationPaths, LoggerFactory, _xmlSerializer);
 22175            _pluginManager = new PluginManager(
 22176                LoggerFactory.CreateLogger<PluginManager>(),
 22177                this,
 22178                ConfigurationManager.Configuration,
 22179                ApplicationPaths.PluginsPath,
 22180                ApplicationVersion);
 22181        }
 182
 183        /// <summary>
 184        /// Occurs when [has pending restart changed].
 185        /// </summary>
 186        public event EventHandler HasPendingRestartChanged;
 187
 188        /// <summary>
 189        /// Gets the value of the PublishedServerUrl setting.
 190        /// </summary>
 0191        private string PublishedServerUrl => _startupConfig[AddressOverrideKey];
 192
 193        public bool CoreStartupHasCompleted { get; private set; }
 194
 195        /// <summary>
 196        /// Gets the <see cref="INetworkManager"/> singleton instance.
 197        /// </summary>
 198        public INetworkManager NetManager { get; private set; }
 199
 200        /// <inheritdoc />
 201        public bool HasPendingRestart { get; private set; }
 202
 203        /// <inheritdoc />
 204        public bool ShouldRestart { get; set; }
 205
 206        /// <summary>
 207        /// Gets the logger.
 208        /// </summary>
 209        protected ILogger<ApplicationHost> Logger { get; }
 210
 211        /// <summary>
 212        /// Gets the logger factory.
 213        /// </summary>
 214        protected ILoggerFactory LoggerFactory { get; }
 215
 216        /// <summary>
 217        /// Gets the application paths.
 218        /// </summary>
 219        /// <value>The application paths.</value>
 220        protected IServerApplicationPaths ApplicationPaths { get; }
 221
 222        /// <summary>
 223        /// Gets the configuration manager.
 224        /// </summary>
 225        /// <value>The configuration manager.</value>
 226        public ServerConfigurationManager ConfigurationManager { get; }
 227
 228        /// <summary>
 229        /// Gets or sets the service provider.
 230        /// </summary>
 231        public IServiceProvider ServiceProvider { get; set; }
 232
 233        /// <summary>
 234        /// Gets the http port for the webhost.
 235        /// </summary>
 236        public int HttpPort { get; private set; }
 237
 238        /// <summary>
 239        /// Gets the https port for the webhost.
 240        /// </summary>
 241        public int HttpsPort { get; private set; }
 242
 243        /// <inheritdoc />
 244        public Version ApplicationVersion { get; }
 245
 246        /// <inheritdoc />
 247        public string ApplicationVersionString { get; }
 248
 249        /// <summary>
 250        /// Gets the current application user agent.
 251        /// </summary>
 252        /// <value>The application user agent.</value>
 253        public string ApplicationUserAgent { get; }
 254
 255        /// <summary>
 256        /// Gets the email address for use within a comment section of a user agent field.
 257        /// Presently used to provide contact information to MusicBrainz service.
 258        /// </summary>
 22259        public string ApplicationUserAgentAddress => "team@jellyfin.org";
 260
 261        /// <summary>
 262        /// Gets the current application name.
 263        /// </summary>
 264        /// <value>The application name.</value>
 22265        public string ApplicationProductName { get; } = FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly().Locat
 266
 125267        public string SystemId => _deviceId.Value;
 268
 269        /// <inheritdoc/>
 66270        public string Name => ApplicationProductName;
 271
 272        private string CertificatePath { get; set; }
 273
 274        public X509Certificate2 Certificate { get; private set; }
 275
 276        /// <inheritdoc/>
 0277        public bool ListenWithHttps => Certificate is not null && ConfigurationManager.GetNetworkConfiguration().EnableH
 278
 279        public string FriendlyName =>
 0280            string.IsNullOrEmpty(ConfigurationManager.Configuration.ServerName)
 0281                ? Environment.MachineName
 0282                : ConfigurationManager.Configuration.ServerName;
 283
 284        public string RestoreBackupPath { get; set; }
 285
 286        public string ExpandVirtualPath(string path)
 287        {
 75288            if (path is null)
 289            {
 0290                return null;
 291            }
 292
 75293            var appPaths = ApplicationPaths;
 294
 75295            return path.Replace(appPaths.VirtualDataPath, appPaths.DataPath, StringComparison.OrdinalIgnoreCase)
 75296                .Replace(appPaths.VirtualInternalMetadataPath, appPaths.InternalMetadataPath, StringComparison.OrdinalIg
 297        }
 298
 299        public string ReverseVirtualPath(string path)
 300        {
 115301            var appPaths = ApplicationPaths;
 302
 115303            return path.Replace(appPaths.DataPath, appPaths.VirtualDataPath, StringComparison.OrdinalIgnoreCase)
 115304                .Replace(appPaths.InternalMetadataPath, appPaths.VirtualInternalMetadataPath, StringComparison.OrdinalIg
 305        }
 306
 307        /// <summary>
 308        /// Creates the instance safe.
 309        /// </summary>
 310        /// <param name="type">The type.</param>
 311        /// <returns>System.Object.</returns>
 312        protected object CreateInstanceSafe(Type type)
 313        {
 4884314            _creatingInstances ??= new List<Type>();
 315
 4884316            if (_creatingInstances.Contains(type))
 317            {
 0318                Logger.LogError("DI Loop detected in the attempted creation of {Type}", type.FullName);
 0319                foreach (var entry in _creatingInstances)
 320                {
 0321                    Logger.LogError("Called from: {TypeName}", entry.FullName);
 322                }
 323
 0324                _pluginManager.FailPlugin(type.Assembly);
 325
 0326                throw new TypeLoadException("DI Loop detected");
 327            }
 328
 329            try
 330            {
 4884331                _creatingInstances.Add(type);
 4884332                Logger.LogDebug("Creating instance of {Type}", type);
 4884333                return ServiceProvider is null
 4884334                    ? Activator.CreateInstance(type)
 4884335                    : ActivatorUtilities.CreateInstance(ServiceProvider, type);
 336            }
 0337            catch (Exception ex)
 338            {
 0339                Logger.LogError(ex, "Error creating {Type}", type);
 340                // If this is a plugin fail it.
 0341                _pluginManager.FailPlugin(type.Assembly);
 0342                return null;
 343            }
 344            finally
 345            {
 4884346                _creatingInstances.Remove(type);
 4884347            }
 4884348        }
 349
 350        /// <summary>
 351        /// Resolves this instance.
 352        /// </summary>
 353        /// <typeparam name="T">The type.</typeparam>
 354        /// <returns>``0.</returns>
 594355        public T Resolve<T>() => ServiceProvider.GetService<T>();
 356
 357        /// <inheritdoc/>
 358        public IEnumerable<Type> GetExportTypes<T>()
 359        {
 440360            var currentType = typeof(T);
 440361            var numberOfConcreteTypes = _allConcreteTypes.Length;
 930160362            for (var i = 0; i < numberOfConcreteTypes; i++)
 363            {
 464640364                var type = _allConcreteTypes[i];
 464640365                if (currentType.IsAssignableFrom(type))
 366                {
 5104367                    yield return type;
 368                }
 369            }
 440370        }
 371
 372        /// <inheritdoc />
 373        public IReadOnlyCollection<T> GetExports<T>(bool manageLifetime = true)
 374        {
 375            // Convert to list so this isn't executed for each iteration
 352376            var parts = GetExportTypes<T>()
 352377                .Select(CreateInstanceSafe)
 352378                .Where(i => i is not null)
 352379                .Cast<T>()
 352380                .ToList();
 381
 352382            if (manageLifetime)
 383            {
 660384                foreach (var part in parts.OfType<IDisposable>())
 385                {
 0386                    _disposableParts.Add(part);
 387                }
 388            }
 389
 352390            return parts;
 391        }
 392
 393        /// <inheritdoc />
 394        public IReadOnlyCollection<T> GetExports<T>(CreationDelegateFactory defaultFunc, bool manageLifetime = true)
 395        {
 396            // Convert to list so this isn't executed for each iteration
 22397            var parts = GetExportTypes<T>()
 22398                .Select(i => defaultFunc(i))
 22399                .Where(i => i is not null)
 22400                .Cast<T>()
 22401                .ToList();
 402
 22403            if (manageLifetime)
 404            {
 88405                foreach (var part in parts.OfType<IDisposable>())
 406                {
 22407                    _disposableParts.Add(part);
 408                }
 409            }
 410
 22411            return parts;
 412        }
 413
 414        /// <summary>
 415        /// Runs the startup tasks.
 416        /// </summary>
 417        /// <returns><see cref="Task" />.</returns>
 418        public Task RunStartupTasksAsync()
 419        {
 22420            Logger.LogInformation("Running startup tasks");
 421
 22422            EnsureStartupWizardIntegrity();
 423
 22424            Resolve<ITaskManager>().AddTasks(GetExports<IScheduledTask>(false));
 425
 22426            ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated;
 22427            ConfigurationManager.NamedConfigurationUpdated += OnConfigurationUpdated;
 428
 22429            var ffmpegValid = Resolve<IMediaEncoder>().SetFFmpegPath();
 430
 22431            if (!ffmpegValid)
 432            {
 0433                throw new FfmpegException("Failed to find valid ffmpeg");
 434            }
 435
 22436            Logger.LogInformation("ServerId: {ServerId}", SystemId);
 22437            Logger.LogInformation("Core startup complete");
 22438            CoreStartupHasCompleted = true;
 439
 22440            return Task.CompletedTask;
 441        }
 442
 443        private void EnsureStartupWizardIntegrity()
 444        {
 22445            if (ConfigurationManager.CommonConfiguration.IsStartupWizardCompleted)
 446            {
 0447                return;
 448            }
 449
 22450            var hasConfiguredAdministrator = Resolve<IUserManager>().GetUsers()
 22451                .Any(user => user.HasPermission(PermissionKind.IsAdministrator) && !string.IsNullOrEmpty(user.Password))
 452
 22453            if (hasConfiguredAdministrator)
 454            {
 0455                Logger.LogWarning("The startup wizard is marked incomplete but a configured administrator already exists
 0456                ConfigurationManager.Configuration.IsStartupWizardCompleted = true;
 0457                ConfigurationManager.SaveConfiguration();
 458            }
 22459        }
 460
 461        /// <inheritdoc/>
 462        public void Init(IServiceCollection serviceCollection)
 463        {
 22464            DiscoverTypes();
 465
 22466            ConfigurationManager.AddParts(GetExports<IConfigurationFactory>());
 467
 22468            NetManager = new NetworkManager(ConfigurationManager, _startupConfig, LoggerFactory.CreateLogger<NetworkMana
 469
 470            // Initialize runtime stat collection
 22471            if (ConfigurationManager.Configuration.EnableMetrics)
 472            {
 0473                _disposableParts.Add(DotNetRuntimeStatsBuilder.Default().StartCollecting());
 474            }
 475
 22476            var networkConfiguration = ConfigurationManager.GetNetworkConfiguration();
 22477            HttpPort = networkConfiguration.InternalHttpPort;
 22478            HttpsPort = networkConfiguration.InternalHttpsPort;
 479
 480            // Safeguard against invalid configuration
 22481            if (HttpPort == HttpsPort)
 482            {
 0483                HttpPort = NetworkConfiguration.DefaultHttpPort;
 0484                HttpsPort = NetworkConfiguration.DefaultHttpsPort;
 485            }
 486
 22487            CertificatePath = networkConfiguration.CertificatePath;
 22488            Certificate = GetCertificate(CertificatePath, networkConfiguration.CertificatePassword);
 489
 22490            RegisterServices(serviceCollection);
 491
 22492            _pluginManager.RegisterServices(serviceCollection);
 22493        }
 494
 495        /// <summary>
 496        /// Registers services/resources with the service collection that will be available via DI.
 497        /// </summary>
 498        /// <param name="serviceCollection">Instance of the <see cref="IServiceCollection"/> interface.</param>
 499        protected virtual void RegisterServices(IServiceCollection serviceCollection)
 500        {
 22501            serviceCollection.AddSingleton(_startupOptions);
 502
 22503            serviceCollection.AddMemoryCache();
 504
 22505            serviceCollection.AddSingleton<IServerConfigurationManager>(ConfigurationManager);
 22506            serviceCollection.AddSingleton<IConfigurationManager>(ConfigurationManager);
 22507            serviceCollection.AddSingleton<IApplicationHost>(this);
 22508            serviceCollection.AddSingleton<IPluginManager>(_pluginManager);
 22509            serviceCollection.AddSingleton<IApplicationPaths>(ApplicationPaths);
 22510            serviceCollection.AddSingleton<IBackupService, BackupService>();
 511
 22512            serviceCollection.AddSingleton<IFileSystem, ManagedFileSystem>();
 22513            serviceCollection.AddSingleton<IShortcutHandler, MbLinkShortcutHandler>();
 514
 22515            serviceCollection.AddScoped<ISystemManager, SystemManager>();
 516
 22517            serviceCollection.AddSingleton<TmdbClientManager>();
 22518            serviceCollection.AddSingleton<TmdbMovieSimilarProvider>();
 22519            serviceCollection.AddSingleton<TmdbSeriesSimilarProvider>();
 520
 22521            serviceCollection.AddSingleton<ListenBrainzLabsClient>();
 22522            serviceCollection.AddSingleton<ListenBrainzSimilarArtistProvider>();
 523
 524            // register the generic local metadata provider for comic files
 22525            serviceCollection.AddSingleton<ComicProvider>();
 526
 527            // register the actual implementations of the local metadata provider for comic files
 22528            serviceCollection.AddSingleton<IComicProvider, ComicBookInfoProvider>();
 22529            serviceCollection.AddSingleton<IComicProvider, ExternalComicInfoProvider>();
 22530            serviceCollection.AddSingleton<IComicProvider, InternalComicInfoProvider>();
 531
 22532            serviceCollection.AddSingleton(NetManager);
 533
 22534            serviceCollection.AddSingleton<ITaskManager, TaskManager>();
 535
 22536            serviceCollection.AddSingleton(_xmlSerializer);
 537
 22538            serviceCollection.AddSingleton<ICryptoProvider, CryptographyProvider>();
 539
 22540            serviceCollection.AddSingleton<ISocketFactory, SocketFactory>();
 541
 22542            serviceCollection.AddSingleton<IInstallationManager, InstallationManager>();
 543
 22544            serviceCollection.AddSingleton<IServerApplicationHost>(this);
 22545            serviceCollection.AddSingleton(ApplicationPaths);
 546
 22547            serviceCollection.AddSingleton<ILocalizationManager, LocalizationManager>();
 548
 22549            serviceCollection.AddSingleton<IBlurayExaminer, BdInfoExaminer>();
 550
 22551            serviceCollection.AddSingleton<IUserDataManager, UserDataManager>();
 552
 22553            serviceCollection.AddSingleton<BaseItemRepository>();
 22554            serviceCollection.AddSingleton<IItemRepository>(sp => sp.GetRequiredService<BaseItemRepository>());
 22555            serviceCollection.AddSingleton<IItemQueryHelpers>(sp => sp.GetRequiredService<BaseItemRepository>());
 22556            serviceCollection.AddSingleton<IItemPersistenceService, ItemPersistenceService>();
 22557            serviceCollection.AddSingleton<INextUpService, NextUpService>();
 22558            serviceCollection.AddSingleton<IItemCountService, ItemCountService>();
 22559            serviceCollection.AddSingleton<ILinkedChildrenService, LinkedChildrenService>();
 22560            serviceCollection.AddSingleton<IPeopleRepository, PeopleRepository>();
 22561            serviceCollection.AddSingleton<IChapterRepository, ChapterRepository>();
 22562            serviceCollection.AddSingleton<IMediaAttachmentRepository, MediaAttachmentRepository>();
 22563            serviceCollection.AddSingleton<IMediaStreamRepository, MediaStreamRepository>();
 22564            serviceCollection.AddSingleton<IKeyframeRepository, KeyframeRepository>();
 22565            serviceCollection.AddSingleton<IItemTypeLookup, ItemTypeLookup>();
 566
 22567            serviceCollection.AddSingleton<IMediaEncoder, MediaBrowser.MediaEncoding.Encoder.MediaEncoder>();
 22568            serviceCollection.AddSingleton<EncodingHelper>();
 22569            serviceCollection.AddSingleton<IPathManager, PathManager>();
 22570            serviceCollection.AddSingleton<IExternalDataManager, ExternalDataManager>();
 571
 572            // TODO: Refactor to eliminate the circular dependencies here so that Lazy<T> isn't required
 22573            serviceCollection.AddTransient(provider => new Lazy<ILibraryMonitor>(provider.GetRequiredService<ILibraryMon
 22574            serviceCollection.AddTransient(provider => new Lazy<IProviderManager>(provider.GetRequiredService<IProviderM
 22575            serviceCollection.AddTransient(provider => new Lazy<IUserViewManager>(provider.GetRequiredService<IUserViewM
 22576            serviceCollection.AddTransient(provider => new Lazy<IExternalDataManager>(provider.GetRequiredService<IExter
 22577            serviceCollection.AddSingleton<ILibraryManager, LibraryManager>();
 22578            serviceCollection.AddSingleton<NamingOptions>();
 22579            serviceCollection.AddSingleton<VideoListResolver>();
 580
 22581            serviceCollection.AddSingleton<IMusicManager, MusicManager>();
 582
 22583            serviceCollection.AddSingleton<ILibraryMonitor, LibraryMonitor>();
 22584            serviceCollection.AddSingleton<DotIgnoreIgnoreRule>();
 585
 22586            serviceCollection.AddSingleton<ISimilarItemsManager, SimilarItemsManager>();
 587
 22588            serviceCollection.AddSingleton<ISearchManager, SearchManager>();
 22589            serviceCollection.AddSingleton<ISearchProvider, SqlSearchProvider>();
 590
 22591            serviceCollection.AddSingleton<IWebSocketManager, WebSocketManager>();
 592
 22593            serviceCollection.AddSingleton<IImageProcessor, ImageProcessor>();
 594
 22595            serviceCollection.AddSingleton<ITVSeriesManager, TVSeriesManager>();
 596
 22597            serviceCollection.AddSingleton<IMediaSourceManager, MediaSourceManager>();
 598
 22599            serviceCollection.AddSingleton<ISubtitleManager, SubtitleManager>();
 22600            serviceCollection.AddSingleton<ILyricManager, LyricManager>();
 601
 22602            serviceCollection.AddSingleton<IProviderManager, ProviderManager>();
 603
 604            // TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
 22605            serviceCollection.AddTransient(provider => new Lazy<ILiveTvManager>(provider.GetRequiredService<ILiveTvManag
 22606            serviceCollection.AddSingleton<IDtoService, DtoService>();
 607
 22608            serviceCollection.AddSingleton<ISessionManager, SessionManager>();
 609
 22610            serviceCollection.AddSingleton<ICollectionManager, CollectionManager>();
 22611            serviceCollection.AddSingleton<ILimitedConcurrencyLibraryScheduler, LimitedConcurrencyLibraryScheduler>();
 612
 22613            serviceCollection.AddSingleton<IPlaylistManager, PlaylistManager>();
 614
 22615            serviceCollection.AddSingleton<ISyncPlayManager, SyncPlayManager>();
 616
 22617            serviceCollection.AddSingleton<IUserViewManager, UserViewManager>();
 618
 22619            serviceCollection.AddSingleton<IChapterManager, ChapterManager>();
 620
 22621            serviceCollection.AddSingleton<IAuthService, AuthService>();
 22622            serviceCollection.AddSingleton<IQuickConnect, QuickConnectManager>();
 623
 22624            serviceCollection.AddSingleton<ISubtitleParser, SubtitleEditParser>();
 22625            serviceCollection.AddSingleton<ISubtitleEncoder, SubtitleEncoder>();
 22626            serviceCollection.AddSingleton<IKeyframeManager, KeyframeManager>();
 627
 22628            serviceCollection.AddSingleton<IAttachmentExtractor, MediaBrowser.MediaEncoding.Attachments.AttachmentExtrac
 629
 22630            serviceCollection.AddSingleton<ITranscodeManager, TranscodeManager>();
 22631            serviceCollection.AddScoped<MediaInfoHelper>();
 22632            serviceCollection.AddScoped<AudioHelper>();
 22633            serviceCollection.AddScoped<DynamicHlsHelper>();
 22634            serviceCollection.AddScoped<IClientEventLogger, ClientEventLogger>();
 22635            serviceCollection.AddSingleton<IDirectoryService, DirectoryService>();
 636
 22637            serviceCollection.AddSingleton<IMediaSegmentManager, MediaSegmentManager>();
 22638        }
 639
 640        /// <summary>
 641        /// Create services registered with the service container that need to be initialized at application startup.
 642        /// </summary>
 643        /// <param name="startupConfig">The configuration used to initialise the application.</param>
 644        /// <returns>A task representing the service initialization operation.</returns>
 645        public async Task InitializeServices(IConfiguration startupConfig)
 646        {
 22647            var localizationManager = (LocalizationManager)Resolve<ILocalizationManager>();
 22648            await localizationManager.LoadAll().ConfigureAwait(false);
 649
 22650            SetStaticProperties();
 651
 22652            FindParts();
 22653        }
 654
 655        private X509Certificate2 GetCertificate(string path, string password)
 656        {
 22657            if (string.IsNullOrWhiteSpace(path))
 658            {
 22659                return null;
 660            }
 661
 662            try
 663            {
 0664                if (!File.Exists(path))
 665                {
 0666                    return null;
 667                }
 668
 669                // Don't use an empty string password
 0670                password = string.IsNullOrWhiteSpace(password) ? null : password;
 671
 0672                var localCert = X509CertificateLoader.LoadPkcs12FromFile(path, password, X509KeyStorageFlags.UserKeySet)
 0673                if (!localCert.HasPrivateKey)
 674                {
 0675                    Logger.LogError("No private key included in SSL cert {CertificateLocation}.", path);
 0676                    return null;
 677                }
 678
 0679                return localCert;
 680            }
 0681            catch (Exception ex)
 682            {
 0683                Logger.LogError(ex, "Error loading cert from {CertificateLocation}", path);
 0684                return null;
 685            }
 0686        }
 687
 688        /// <summary>
 689        /// Dirty hacks.
 690        /// </summary>
 691        private void SetStaticProperties()
 692        {
 693            // For now there's no real way to inject these properly
 22694            BaseItem.ChapterManager = Resolve<IChapterManager>();
 22695            BaseItem.ChannelManager = Resolve<IChannelManager>();
 22696            BaseItem.ConfigurationManager = ConfigurationManager;
 22697            BaseItem.FileSystem = Resolve<IFileSystem>();
 22698            BaseItem.ItemRepository = Resolve<IItemRepository>();
 22699            BaseItem.ItemCountService = Resolve<IItemCountService>();
 22700            BaseItem.LibraryManager = Resolve<ILibraryManager>();
 22701            BaseItem.LocalizationManager = Resolve<ILocalizationManager>();
 22702            BaseItem.Logger = Resolve<ILogger<BaseItem>>();
 22703            BaseItem.MediaSegmentManager = Resolve<IMediaSegmentManager>();
 22704            BaseItem.MediaSourceManager = Resolve<IMediaSourceManager>();
 22705            BaseItem.ProviderManager = Resolve<IProviderManager>();
 22706            BaseItem.UserDataManager = Resolve<IUserDataManager>();
 22707            CollectionFolder.XmlSerializer = _xmlSerializer;
 22708            CollectionFolder.ApplicationHost = this;
 22709            Folder.UserViewManager = Resolve<IUserViewManager>();
 22710            Folder.CollectionManager = Resolve<ICollectionManager>();
 22711            Folder.LimitedConcurrencyLibraryScheduler = Resolve<ILimitedConcurrencyLibraryScheduler>();
 22712            Episode.MediaEncoder = Resolve<IMediaEncoder>();
 22713            UserView.TVSeriesManager = Resolve<ITVSeriesManager>();
 22714            Video.RecordingsManager = Resolve<IRecordingsManager>();
 22715        }
 716
 717        /// <summary>
 718        /// Finds plugin components and register them with the appropriate services.
 719        /// </summary>
 720        private void FindParts()
 721        {
 22722            if (!ConfigurationManager.Configuration.IsPortAuthorized)
 723            {
 22724                ConfigurationManager.Configuration.IsPortAuthorized = true;
 22725                ConfigurationManager.SaveConfiguration();
 726            }
 727
 22728            _pluginManager.CreatePlugins();
 729
 22730            Resolve<ILibraryManager>().AddParts(
 22731                GetExports<IResolverIgnoreRule>(),
 22732                GetExports<IItemResolver>(),
 22733                GetExports<IIntroProvider>(),
 22734                GetExports<IBaseItemComparer>(),
 22735                GetExports<ILibraryPostScanTask>());
 736
 22737            Resolve<IProviderManager>().AddParts(
 22738                GetExports<IImageProvider>(),
 22739                GetExports<IMetadataService>(),
 22740                GetExports<IMetadataProvider>(),
 22741                GetExports<IMetadataSaver>(),
 22742                GetExports<IExternalId>(),
 22743                GetExports<IExternalUrlProvider>());
 744
 22745            Resolve<IMediaSourceManager>().AddParts(GetExports<IMediaSourceProvider>());
 746
 22747            Resolve<ISimilarItemsManager>().AddParts(GetExports<ISimilarItemsProvider>());
 22748            Resolve<ISearchManager>().AddParts(GetExports<ISearchProvider>());
 22749        }
 750
 751        /// <summary>
 752        /// Discovers the types.
 753        /// </summary>
 754        protected void DiscoverTypes()
 755        {
 22756            Logger.LogInformation("Loading assemblies");
 757
 22758            _allConcreteTypes = GetTypes(GetComposablePartAssemblies()).ToArray();
 22759        }
 760
 761        private IEnumerable<Type> GetTypes(IEnumerable<Assembly> assemblies)
 762        {
 748763            foreach (var ass in assemblies)
 764            {
 765                Type[] exportedTypes;
 766                try
 767                {
 352768                    exportedTypes = ass.GetExportedTypes();
 352769                }
 0770                catch (FileNotFoundException ex)
 771                {
 0772                    Logger.LogError(ex, "Error getting exported types from {Assembly}", ass.FullName);
 0773                    _pluginManager.FailPlugin(ass);
 0774                    continue;
 775                }
 0776                catch (TypeLoadException ex)
 777                {
 0778                    Logger.LogError(ex, "Error loading types from {Assembly}.", ass.FullName);
 0779                    _pluginManager.FailPlugin(ass);
 0780                    continue;
 781                }
 782
 67496783                foreach (Type type in exportedTypes)
 784                {
 33396785                    if (type.IsClass && !type.IsAbstract && !type.IsInterface && !type.IsGenericType)
 786                    {
 23232787                        yield return type;
 788                    }
 789                }
 790            }
 22791        }
 792
 793        /// <summary>
 794        /// Called when [configuration updated].
 795        /// </summary>
 796        /// <param name="sender">The sender.</param>
 797        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
 798        private void OnConfigurationUpdated(object sender, EventArgs e)
 799        {
 19800            var requiresRestart = false;
 19801            var networkConfiguration = ConfigurationManager.GetNetworkConfiguration();
 802
 803            // Don't do anything if these haven't been set yet
 19804            if (HttpPort != 0 && HttpsPort != 0)
 805            {
 806                // Need to restart if ports have changed
 19807                if (networkConfiguration.InternalHttpPort != HttpPort
 19808                    || networkConfiguration.InternalHttpsPort != HttpsPort)
 809                {
 0810                    if (ConfigurationManager.Configuration.IsPortAuthorized)
 811                    {
 0812                        ConfigurationManager.Configuration.IsPortAuthorized = false;
 0813                        ConfigurationManager.SaveConfiguration();
 814
 0815                        requiresRestart = true;
 816                    }
 817                }
 818            }
 819
 19820            if (ValidateSslCertificate(networkConfiguration))
 821            {
 0822                requiresRestart = true;
 823            }
 824
 19825            if (requiresRestart)
 826            {
 0827                Logger.LogInformation("App needs to be restarted due to configuration change.");
 828
 0829                NotifyPendingRestart();
 830            }
 19831        }
 832
 833        /// <summary>
 834        /// Validates the SSL certificate.
 835        /// </summary>
 836        /// <param name="networkConfig">The new configuration.</param>
 837        /// <exception cref="FileNotFoundException">The certificate path doesn't exist.</exception>
 838        private bool ValidateSslCertificate(NetworkConfiguration networkConfig)
 839        {
 19840            var newPath = networkConfig.CertificatePath;
 841
 19842            if (!string.IsNullOrWhiteSpace(newPath)
 19843                && !string.Equals(CertificatePath, newPath, StringComparison.Ordinal))
 844            {
 0845                if (File.Exists(newPath))
 846                {
 0847                    return true;
 848                }
 849
 0850                throw new FileNotFoundException(
 0851                    string.Format(
 0852                        CultureInfo.InvariantCulture,
 0853                        "Certificate file '{0}' does not exist.",
 0854                        newPath));
 855            }
 856
 19857            return false;
 858        }
 859
 860        /// <summary>
 861        /// Notifies the kernel that a change has been made that requires a restart.
 862        /// </summary>
 863        public void NotifyPendingRestart()
 864        {
 0865            Logger.LogInformation("App needs to be restarted.");
 866
 0867            var changed = !HasPendingRestart;
 868
 0869            HasPendingRestart = true;
 870
 0871            if (changed)
 872            {
 0873                EventHelper.QueueEventIfNotNull(HasPendingRestartChanged, this, EventArgs.Empty, Logger);
 874            }
 0875        }
 876
 877        /// <summary>
 878        /// Gets the composable part assemblies.
 879        /// </summary>
 880        /// <returns>IEnumerable{Assembly}.</returns>
 881        protected IEnumerable<Assembly> GetComposablePartAssemblies()
 882        {
 44883            foreach (var p in _pluginManager.LoadAssemblies())
 884            {
 0885                yield return p;
 886            }
 887
 888            // Include composable parts in the Model assembly
 22889            yield return typeof(SystemInfo).Assembly;
 890
 891            // Include composable parts in the Common assembly
 22892            yield return typeof(IApplicationHost).Assembly;
 893
 894            // Include composable parts in the Controller assembly
 22895            yield return typeof(IServerApplicationHost).Assembly;
 896
 897            // Include composable parts in the Providers assembly
 22898            yield return typeof(ProviderManager).Assembly;
 899
 900            // Include composable parts in the Photos assembly
 22901            yield return typeof(PhotoProvider).Assembly;
 902
 903            // Emby.Server implementations
 22904            yield return typeof(InstallationManager).Assembly;
 905
 906            // MediaEncoding
 22907            yield return typeof(MediaBrowser.MediaEncoding.Encoder.MediaEncoder).Assembly;
 908
 909            // Local metadata
 22910            yield return typeof(BoxSetXmlSaver).Assembly;
 911
 912            // Xbmc
 22913            yield return typeof(ArtistNfoProvider).Assembly;
 914
 915            // Network
 22916            yield return typeof(NetworkManager).Assembly;
 917
 918            // Hls
 22919            yield return typeof(DynamicHlsPlaylistGenerator).Assembly;
 920
 264921            foreach (var i in GetAssembliesWithPartsInternal())
 922            {
 110923                yield return i;
 924            }
 22925        }
 926
 927        protected abstract IEnumerable<Assembly> GetAssembliesWithPartsInternal();
 928
 929        /// <inheritdoc/>
 930        public string GetSmartApiUrl(IPAddress remoteAddr)
 931        {
 932            // Published server ends with a /
 0933            if (!string.IsNullOrEmpty(PublishedServerUrl))
 934            {
 935                // Published server ends with a '/', so we need to remove it.
 0936                return PublishedServerUrl.Trim('/');
 937            }
 938
 0939            string smart = NetManager.GetBindAddress(remoteAddr, out var port);
 0940            return GetLocalApiUrl(smart.Trim('/'), null, port);
 941        }
 942
 943        /// <inheritdoc/>
 944        public string GetSmartApiUrl(HttpRequest request)
 945        {
 946            // Return the host in the HTTP request as the API URL if not configured otherwise
 0947            if (ConfigurationManager.GetNetworkConfiguration().EnablePublishedServerUriByRequest)
 948            {
 0949                int? requestPort = request.Host.Port;
 0950                if (requestPort is null
 0951                    || (requestPort == 80 && string.Equals(request.Scheme, "http", StringComparison.OrdinalIgnoreCase))
 0952                    || (requestPort == 443 && string.Equals(request.Scheme, "https", StringComparison.OrdinalIgnoreCase)
 953                {
 0954                    requestPort = -1;
 955                }
 956
 0957                return GetLocalApiUrl(request.Host.Host, request.Scheme, requestPort);
 958            }
 959
 0960            return GetSmartApiUrl(request.HttpContext.Connection.RemoteIpAddress ?? IPAddress.Loopback);
 961        }
 962
 963        /// <inheritdoc/>
 964        public string GetSmartApiUrl(string hostname)
 965        {
 966            // Published server ends with a /
 0967            if (!string.IsNullOrEmpty(PublishedServerUrl))
 968            {
 969                // Published server ends with a '/', so we need to remove it.
 0970                return PublishedServerUrl.Trim('/');
 971            }
 972
 0973            string smart = NetManager.GetBindAddress(hostname, out var port);
 0974            return GetLocalApiUrl(smart.Trim('/'), null, port);
 975        }
 976
 977        /// <inheritdoc/>
 978        public string GetApiUrlForLocalAccess(IPAddress ipAddress = null, bool allowHttps = true)
 979        {
 980            // With an empty source, the port will be null
 0981            var smart = NetManager.GetBindAddress(ipAddress, out _, false);
 0982            var scheme = !allowHttps ? Uri.UriSchemeHttp : null;
 0983            int? port = !allowHttps ? HttpPort : null;
 0984            return GetLocalApiUrl(smart, scheme, port);
 985        }
 986
 987        /// <inheritdoc/>
 988        public string GetLocalApiUrl(string hostname, string scheme = null, int? port = null)
 989        {
 990            // If the smartAPI isn't already a complete URL then treat it as a host or ip.
 0991            if (hostname.StartsWith("http://", StringComparison.OrdinalIgnoreCase)
 0992                || hostname.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
 993            {
 0994                return hostname.TrimEnd('/');
 995            }
 996
 997            // NOTE: If no BaseUrl is set then UriBuilder appends a trailing slash, but if there is no BaseUrl it does
 998            // not. For consistency, always trim the trailing slash.
 0999            scheme ??= ListenWithHttps ? Uri.UriSchemeHttps : Uri.UriSchemeHttp;
 01000            var isHttps = string.Equals(scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase);
 01001            return new UriBuilder
 01002            {
 01003                Scheme = scheme,
 01004                Host = hostname,
 01005                Port = port ?? (isHttps ? HttpsPort : HttpPort),
 01006                Path = ConfigurationManager.GetNetworkConfiguration().BaseUrl
 01007            }.ToString().TrimEnd('/');
 1008        }
 1009
 1010        public IEnumerable<Assembly> GetApiPluginAssemblies()
 1011        {
 221012            var assemblies = _allConcreteTypes
 221013                .Where(i => typeof(ControllerBase).IsAssignableFrom(i))
 221014                .Select(i => i.Assembly)
 221015                .Distinct();
 1016
 1321017            foreach (var assembly in assemblies)
 1018            {
 441019                Logger.LogDebug("Found API endpoints in plugin {Name}", assembly.FullName);
 441020                yield return assembly;
 1021            }
 221022        }
 1023
 1024        /// <inheritdoc />
 1025        public void Dispose()
 1026        {
 221027            Dispose(true);
 221028            GC.SuppressFinalize(this);
 221029        }
 1030
 1031        /// <summary>
 1032        /// Releases unmanaged and - optionally - managed resources.
 1033        /// </summary>
 1034        /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release o
 1035        protected virtual void Dispose(bool dispose)
 1036        {
 221037            if (_disposed)
 1038            {
 01039                return;
 1040            }
 1041
 221042            if (dispose)
 1043            {
 221044                var type = GetType();
 1045
 221046                Logger.LogInformation("Disposing {Type}", type.Name);
 1047
 881048                foreach (var part in _disposableParts.ToArray())
 1049                {
 221050                    var partType = part.GetType();
 221051                    if (partType == type)
 1052                    {
 1053                        continue;
 1054                    }
 1055
 221056                    Logger.LogInformation("Disposing {Type}", partType.Name);
 1057
 1058                    try
 1059                    {
 221060                        part.Dispose();
 221061                    }
 01062                    catch (Exception ex)
 1063                    {
 01064                        Logger.LogError(ex, "Error disposing {Type}", partType.Name);
 01065                    }
 1066                }
 1067
 221068                _disposableParts.Clear();
 1069
 221070                _pluginManager?.Dispose();
 1071            }
 1072
 221073            _disposed = true;
 221074        }
 1075    }
 1076}