< Summary - Jellyfin

Information
Class: Emby.Server.Implementations.ApplicationHost
Assembly: Emby.Server.Implementations
File(s): /srv/git/jellyfin/Emby.Server.Implementations/ApplicationHost.cs
Line coverage
75%
Covered lines: 289
Uncovered lines: 96
Coverable lines: 385
Total lines: 1053
Line coverage: 75%
Branch coverage
45%
Covered branches: 58
Total branches: 128
Branch coverage: 45.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 4/8/2026 - 12:11:47 AM Line coverage: 72.1% (225/312) Branch coverage: 39.4% (41/104) Total lines: 10144/19/2026 - 12:14:27 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: 1053 4/8/2026 - 12:11:47 AM Line coverage: 72.1% (225/312) Branch coverage: 39.4% (41/104) Total lines: 10144/19/2026 - 12:14:27 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: 1053

Coverage delta

Coverage delta 10 -10

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%2290.9%
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%110100%
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.Drawing;
 43using Jellyfin.MediaEncoding.Hls.Playlist;
 44using Jellyfin.Networking.Manager;
 45using Jellyfin.Networking.Udp;
 46using Jellyfin.Server.Implementations.FullSystemBackup;
 47using Jellyfin.Server.Implementations.Item;
 48using Jellyfin.Server.Implementations.MediaSegments;
 49using Jellyfin.Server.Implementations.SystemBackupService;
 50using MediaBrowser.Common;
 51using MediaBrowser.Common.Configuration;
 52using MediaBrowser.Common.Events;
 53using MediaBrowser.Common.Net;
 54using MediaBrowser.Common.Plugins;
 55using MediaBrowser.Common.Updates;
 56using MediaBrowser.Controller;
 57using MediaBrowser.Controller.Channels;
 58using MediaBrowser.Controller.Chapters;
 59using MediaBrowser.Controller.ClientEvent;
 60using MediaBrowser.Controller.Collections;
 61using MediaBrowser.Controller.Configuration;
 62using MediaBrowser.Controller.Drawing;
 63using MediaBrowser.Controller.Dto;
 64using MediaBrowser.Controller.Entities;
 65using MediaBrowser.Controller.Entities.TV;
 66using MediaBrowser.Controller.IO;
 67using MediaBrowser.Controller.Library;
 68using MediaBrowser.Controller.LibraryTaskScheduler;
 69using MediaBrowser.Controller.LiveTv;
 70using MediaBrowser.Controller.Lyrics;
 71using MediaBrowser.Controller.MediaEncoding;
 72using MediaBrowser.Controller.MediaSegments;
 73using MediaBrowser.Controller.Net;
 74using MediaBrowser.Controller.Persistence;
 75using MediaBrowser.Controller.Playlists;
 76using MediaBrowser.Controller.Providers;
 77using MediaBrowser.Controller.QuickConnect;
 78using MediaBrowser.Controller.Resolvers;
 79using MediaBrowser.Controller.Session;
 80using MediaBrowser.Controller.Sorting;
 81using MediaBrowser.Controller.Subtitles;
 82using MediaBrowser.Controller.SyncPlay;
 83using MediaBrowser.Controller.TV;
 84using MediaBrowser.LocalMetadata.Savers;
 85using MediaBrowser.MediaEncoding.BdInfo;
 86using MediaBrowser.MediaEncoding.Subtitles;
 87using MediaBrowser.MediaEncoding.Transcoding;
 88using MediaBrowser.Model.Cryptography;
 89using MediaBrowser.Model.Globalization;
 90using MediaBrowser.Model.IO;
 91using MediaBrowser.Model.MediaInfo;
 92using MediaBrowser.Model.Net;
 93using MediaBrowser.Model.Serialization;
 94using MediaBrowser.Model.System;
 95using MediaBrowser.Model.Tasks;
 96using MediaBrowser.Providers.Books;
 97using MediaBrowser.Providers.Books.ComicBookInfo;
 98using MediaBrowser.Providers.Books.ComicInfo;
 99using MediaBrowser.Providers.Lyric;
 100using MediaBrowser.Providers.Manager;
 101using MediaBrowser.Providers.Plugins.ListenBrainz;
 102using MediaBrowser.Providers.Plugins.ListenBrainz.Api;
 103using MediaBrowser.Providers.Plugins.Tmdb;
 104using MediaBrowser.Providers.Plugins.Tmdb.Movies;
 105using MediaBrowser.Providers.Plugins.Tmdb.TV;
 106using MediaBrowser.Providers.Subtitles;
 107using MediaBrowser.XbmcMetadata.Providers;
 108using Microsoft.AspNetCore.Http;
 109using Microsoft.AspNetCore.Mvc;
 110using Microsoft.Extensions.Configuration;
 111using Microsoft.Extensions.DependencyInjection;
 112using Microsoft.Extensions.Logging;
 113using Prometheus.DotNetRuntime;
 114using static MediaBrowser.Controller.Extensions.ConfigurationExtensions;
 115using IConfigurationManager = MediaBrowser.Common.Configuration.IConfigurationManager;
 116using WebSocketManager = Emby.Server.Implementations.HttpServer.WebSocketManager;
 117
 118namespace Emby.Server.Implementations
 119{
 120    /// <summary>
 121    /// Class CompositionRoot.
 122    /// </summary>
 123    public abstract class ApplicationHost : IServerApplicationHost, IDisposable
 124    {
 125        /// <summary>
 126        /// The disposable parts.
 127        /// </summary>
 22128        private readonly ConcurrentBag<IDisposable> _disposableParts = new();
 129        private readonly DeviceId _deviceId;
 130
 131        private readonly IConfiguration _startupConfig;
 132        private readonly IXmlSerializer _xmlSerializer;
 133        private readonly IStartupOptions _startupOptions;
 134        private readonly PluginManager _pluginManager;
 135
 136        private List<Type> _creatingInstances;
 137
 138        /// <summary>
 139        /// Gets or sets all concrete types.
 140        /// </summary>
 141        /// <value>All concrete types.</value>
 142        private Type[] _allConcreteTypes;
 143
 144        private bool _disposed;
 145
 146        /// <summary>
 147        /// Initializes a new instance of the <see cref="ApplicationHost"/> class.
 148        /// </summary>
 149        /// <param name="applicationPaths">Instance of the <see cref="IServerApplicationPaths"/> interface.</param>
 150        /// <param name="loggerFactory">Instance of the <see cref="ILoggerFactory"/> interface.</param>
 151        /// <param name="options">Instance of the <see cref="IStartupOptions"/> interface.</param>
 152        /// <param name="startupConfig">The <see cref="IConfiguration" /> interface.</param>
 153        protected ApplicationHost(
 154            IServerApplicationPaths applicationPaths,
 155            ILoggerFactory loggerFactory,
 156            IStartupOptions options,
 157            IConfiguration startupConfig)
 158        {
 159            ApplicationPaths = applicationPaths;
 160            LoggerFactory = loggerFactory;
 22161            _startupOptions = options;
 22162            _startupConfig = startupConfig;
 163
 22164            Logger = LoggerFactory.CreateLogger<ApplicationHost>();
 22165            _deviceId = new DeviceId(ApplicationPaths, LoggerFactory.CreateLogger<DeviceId>());
 166
 22167            ApplicationVersion = typeof(ApplicationHost).Assembly.GetName().Version;
 22168            ApplicationVersionString = ApplicationVersion.ToString(3);
 22169            ApplicationUserAgent = Name.Replace(' ', '-') + "/" + ApplicationVersionString;
 170
 22171            _xmlSerializer = new MyXmlSerializer();
 22172            ConfigurationManager = new ServerConfigurationManager(ApplicationPaths, LoggerFactory, _xmlSerializer);
 22173            _pluginManager = new PluginManager(
 22174                LoggerFactory.CreateLogger<PluginManager>(),
 22175                this,
 22176                ConfigurationManager.Configuration,
 22177                ApplicationPaths.PluginsPath,
 22178                ApplicationVersion);
 22179        }
 180
 181        /// <summary>
 182        /// Occurs when [has pending restart changed].
 183        /// </summary>
 184        public event EventHandler HasPendingRestartChanged;
 185
 186        /// <summary>
 187        /// Gets the value of the PublishedServerUrl setting.
 188        /// </summary>
 0189        private string PublishedServerUrl => _startupConfig[AddressOverrideKey];
 190
 191        public bool CoreStartupHasCompleted { get; private set; }
 192
 193        /// <summary>
 194        /// Gets the <see cref="INetworkManager"/> singleton instance.
 195        /// </summary>
 196        public INetworkManager NetManager { get; private set; }
 197
 198        /// <inheritdoc />
 199        public bool HasPendingRestart { get; private set; }
 200
 201        /// <inheritdoc />
 202        public bool ShouldRestart { get; set; }
 203
 204        /// <summary>
 205        /// Gets the logger.
 206        /// </summary>
 207        protected ILogger<ApplicationHost> Logger { get; }
 208
 209        /// <summary>
 210        /// Gets the logger factory.
 211        /// </summary>
 212        protected ILoggerFactory LoggerFactory { get; }
 213
 214        /// <summary>
 215        /// Gets the application paths.
 216        /// </summary>
 217        /// <value>The application paths.</value>
 218        protected IServerApplicationPaths ApplicationPaths { get; }
 219
 220        /// <summary>
 221        /// Gets the configuration manager.
 222        /// </summary>
 223        /// <value>The configuration manager.</value>
 224        public ServerConfigurationManager ConfigurationManager { get; }
 225
 226        /// <summary>
 227        /// Gets or sets the service provider.
 228        /// </summary>
 229        public IServiceProvider ServiceProvider { get; set; }
 230
 231        /// <summary>
 232        /// Gets the http port for the webhost.
 233        /// </summary>
 234        public int HttpPort { get; private set; }
 235
 236        /// <summary>
 237        /// Gets the https port for the webhost.
 238        /// </summary>
 239        public int HttpsPort { get; private set; }
 240
 241        /// <inheritdoc />
 242        public Version ApplicationVersion { get; }
 243
 244        /// <inheritdoc />
 245        public string ApplicationVersionString { get; }
 246
 247        /// <summary>
 248        /// Gets the current application user agent.
 249        /// </summary>
 250        /// <value>The application user agent.</value>
 251        public string ApplicationUserAgent { get; }
 252
 253        /// <summary>
 254        /// Gets the email address for use within a comment section of a user agent field.
 255        /// Presently used to provide contact information to MusicBrainz service.
 256        /// </summary>
 22257        public string ApplicationUserAgentAddress => "team@jellyfin.org";
 258
 259        /// <summary>
 260        /// Gets the current application name.
 261        /// </summary>
 262        /// <value>The application name.</value>
 22263        public string ApplicationProductName { get; } = FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly().Locat
 264
 126265        public string SystemId => _deviceId.Value;
 266
 267        /// <inheritdoc/>
 66268        public string Name => ApplicationProductName;
 269
 270        private string CertificatePath { get; set; }
 271
 272        public X509Certificate2 Certificate { get; private set; }
 273
 274        /// <inheritdoc/>
 0275        public bool ListenWithHttps => Certificate is not null && ConfigurationManager.GetNetworkConfiguration().EnableH
 276
 277        public string FriendlyName =>
 0278            string.IsNullOrEmpty(ConfigurationManager.Configuration.ServerName)
 0279                ? Environment.MachineName
 0280                : ConfigurationManager.Configuration.ServerName;
 281
 282        public string RestoreBackupPath { get; set; }
 283
 284        public string ExpandVirtualPath(string path)
 285        {
 76286            if (path is null)
 287            {
 0288                return null;
 289            }
 290
 76291            var appPaths = ApplicationPaths;
 292
 76293            return path.Replace(appPaths.VirtualDataPath, appPaths.DataPath, StringComparison.OrdinalIgnoreCase)
 76294                .Replace(appPaths.VirtualInternalMetadataPath, appPaths.InternalMetadataPath, StringComparison.OrdinalIg
 295        }
 296
 297        public string ReverseVirtualPath(string path)
 298        {
 118299            var appPaths = ApplicationPaths;
 300
 118301            return path.Replace(appPaths.DataPath, appPaths.VirtualDataPath, StringComparison.OrdinalIgnoreCase)
 118302                .Replace(appPaths.InternalMetadataPath, appPaths.VirtualInternalMetadataPath, StringComparison.OrdinalIg
 303        }
 304
 305        /// <summary>
 306        /// Creates the instance safe.
 307        /// </summary>
 308        /// <param name="type">The type.</param>
 309        /// <returns>System.Object.</returns>
 310        protected object CreateInstanceSafe(Type type)
 311        {
 4818312            _creatingInstances ??= new List<Type>();
 313
 4818314            if (_creatingInstances.Contains(type))
 315            {
 0316                Logger.LogError("DI Loop detected in the attempted creation of {Type}", type.FullName);
 0317                foreach (var entry in _creatingInstances)
 318                {
 0319                    Logger.LogError("Called from: {TypeName}", entry.FullName);
 320                }
 321
 0322                _pluginManager.FailPlugin(type.Assembly);
 323
 0324                throw new TypeLoadException("DI Loop detected");
 325            }
 326
 327            try
 328            {
 4818329                _creatingInstances.Add(type);
 4818330                Logger.LogDebug("Creating instance of {Type}", type);
 4818331                return ServiceProvider is null
 4818332                    ? Activator.CreateInstance(type)
 4818333                    : ActivatorUtilities.CreateInstance(ServiceProvider, type);
 334            }
 0335            catch (Exception ex)
 336            {
 0337                Logger.LogError(ex, "Error creating {Type}", type);
 338                // If this is a plugin fail it.
 0339                _pluginManager.FailPlugin(type.Assembly);
 0340                return null;
 341            }
 342            finally
 343            {
 4818344                _creatingInstances.Remove(type);
 4818345            }
 4818346        }
 347
 348        /// <summary>
 349        /// Resolves this instance.
 350        /// </summary>
 351        /// <typeparam name="T">The type.</typeparam>
 352        /// <returns>``0.</returns>
 572353        public T Resolve<T>() => ServiceProvider.GetService<T>();
 354
 355        /// <inheritdoc/>
 356        public IEnumerable<Type> GetExportTypes<T>()
 357        {
 440358            var currentType = typeof(T);
 440359            var numberOfConcreteTypes = _allConcreteTypes.Length;
 925760360            for (var i = 0; i < numberOfConcreteTypes; i++)
 361            {
 462440362                var type = _allConcreteTypes[i];
 462440363                if (currentType.IsAssignableFrom(type))
 364                {
 5038365                    yield return type;
 366                }
 367            }
 440368        }
 369
 370        /// <inheritdoc />
 371        public IReadOnlyCollection<T> GetExports<T>(bool manageLifetime = true)
 372        {
 373            // Convert to list so this isn't executed for each iteration
 352374            var parts = GetExportTypes<T>()
 352375                .Select(CreateInstanceSafe)
 352376                .Where(i => i is not null)
 352377                .Cast<T>()
 352378                .ToList();
 379
 352380            if (manageLifetime)
 381            {
 660382                foreach (var part in parts.OfType<IDisposable>())
 383                {
 0384                    _disposableParts.Add(part);
 385                }
 386            }
 387
 352388            return parts;
 389        }
 390
 391        /// <inheritdoc />
 392        public IReadOnlyCollection<T> GetExports<T>(CreationDelegateFactory defaultFunc, bool manageLifetime = true)
 393        {
 394            // Convert to list so this isn't executed for each iteration
 22395            var parts = GetExportTypes<T>()
 22396                .Select(i => defaultFunc(i))
 22397                .Where(i => i is not null)
 22398                .Cast<T>()
 22399                .ToList();
 400
 22401            if (manageLifetime)
 402            {
 88403                foreach (var part in parts.OfType<IDisposable>())
 404                {
 22405                    _disposableParts.Add(part);
 406                }
 407            }
 408
 22409            return parts;
 410        }
 411
 412        /// <summary>
 413        /// Runs the startup tasks.
 414        /// </summary>
 415        /// <returns><see cref="Task" />.</returns>
 416        public Task RunStartupTasksAsync()
 417        {
 22418            Logger.LogInformation("Running startup tasks");
 419
 22420            Resolve<ITaskManager>().AddTasks(GetExports<IScheduledTask>(false));
 421
 22422            ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated;
 22423            ConfigurationManager.NamedConfigurationUpdated += OnConfigurationUpdated;
 424
 22425            var ffmpegValid = Resolve<IMediaEncoder>().SetFFmpegPath();
 426
 22427            if (!ffmpegValid)
 428            {
 0429                throw new FfmpegException("Failed to find valid ffmpeg");
 430            }
 431
 22432            Logger.LogInformation("ServerId: {ServerId}", SystemId);
 22433            Logger.LogInformation("Core startup complete");
 22434            CoreStartupHasCompleted = true;
 435
 22436            return Task.CompletedTask;
 437        }
 438
 439        /// <inheritdoc/>
 440        public void Init(IServiceCollection serviceCollection)
 441        {
 22442            DiscoverTypes();
 443
 22444            ConfigurationManager.AddParts(GetExports<IConfigurationFactory>());
 445
 22446            NetManager = new NetworkManager(ConfigurationManager, _startupConfig, LoggerFactory.CreateLogger<NetworkMana
 447
 448            // Initialize runtime stat collection
 22449            if (ConfigurationManager.Configuration.EnableMetrics)
 450            {
 0451                _disposableParts.Add(DotNetRuntimeStatsBuilder.Default().StartCollecting());
 452            }
 453
 22454            var networkConfiguration = ConfigurationManager.GetNetworkConfiguration();
 22455            HttpPort = networkConfiguration.InternalHttpPort;
 22456            HttpsPort = networkConfiguration.InternalHttpsPort;
 457
 458            // Safeguard against invalid configuration
 22459            if (HttpPort == HttpsPort)
 460            {
 0461                HttpPort = NetworkConfiguration.DefaultHttpPort;
 0462                HttpsPort = NetworkConfiguration.DefaultHttpsPort;
 463            }
 464
 22465            CertificatePath = networkConfiguration.CertificatePath;
 22466            Certificate = GetCertificate(CertificatePath, networkConfiguration.CertificatePassword);
 467
 22468            RegisterServices(serviceCollection);
 469
 22470            _pluginManager.RegisterServices(serviceCollection);
 22471        }
 472
 473        /// <summary>
 474        /// Registers services/resources with the service collection that will be available via DI.
 475        /// </summary>
 476        /// <param name="serviceCollection">Instance of the <see cref="IServiceCollection"/> interface.</param>
 477        protected virtual void RegisterServices(IServiceCollection serviceCollection)
 478        {
 22479            serviceCollection.AddSingleton(_startupOptions);
 480
 22481            serviceCollection.AddMemoryCache();
 482
 22483            serviceCollection.AddSingleton<IServerConfigurationManager>(ConfigurationManager);
 22484            serviceCollection.AddSingleton<IConfigurationManager>(ConfigurationManager);
 22485            serviceCollection.AddSingleton<IApplicationHost>(this);
 22486            serviceCollection.AddSingleton<IPluginManager>(_pluginManager);
 22487            serviceCollection.AddSingleton<IApplicationPaths>(ApplicationPaths);
 22488            serviceCollection.AddSingleton<IBackupService, BackupService>();
 489
 22490            serviceCollection.AddSingleton<IFileSystem, ManagedFileSystem>();
 22491            serviceCollection.AddSingleton<IShortcutHandler, MbLinkShortcutHandler>();
 492
 22493            serviceCollection.AddScoped<ISystemManager, SystemManager>();
 494
 22495            serviceCollection.AddSingleton<TmdbClientManager>();
 22496            serviceCollection.AddSingleton<TmdbMovieSimilarProvider>();
 22497            serviceCollection.AddSingleton<TmdbSeriesSimilarProvider>();
 498
 22499            serviceCollection.AddSingleton<ListenBrainzLabsClient>();
 22500            serviceCollection.AddSingleton<ListenBrainzSimilarArtistProvider>();
 501
 502            // register the generic local metadata provider for comic files
 22503            serviceCollection.AddSingleton<ComicProvider>();
 504
 505            // register the actual implementations of the local metadata provider for comic files
 22506            serviceCollection.AddSingleton<IComicProvider, ComicBookInfoProvider>();
 22507            serviceCollection.AddSingleton<IComicProvider, ExternalComicInfoProvider>();
 22508            serviceCollection.AddSingleton<IComicProvider, InternalComicInfoProvider>();
 509
 22510            serviceCollection.AddSingleton(NetManager);
 511
 22512            serviceCollection.AddSingleton<ITaskManager, TaskManager>();
 513
 22514            serviceCollection.AddSingleton(_xmlSerializer);
 515
 22516            serviceCollection.AddSingleton<ICryptoProvider, CryptographyProvider>();
 517
 22518            serviceCollection.AddSingleton<ISocketFactory, SocketFactory>();
 519
 22520            serviceCollection.AddSingleton<IInstallationManager, InstallationManager>();
 521
 22522            serviceCollection.AddSingleton<IServerApplicationHost>(this);
 22523            serviceCollection.AddSingleton(ApplicationPaths);
 524
 22525            serviceCollection.AddSingleton<ILocalizationManager, LocalizationManager>();
 526
 22527            serviceCollection.AddSingleton<IBlurayExaminer, BdInfoExaminer>();
 528
 22529            serviceCollection.AddSingleton<IUserDataManager, UserDataManager>();
 530
 22531            serviceCollection.AddSingleton<BaseItemRepository>();
 22532            serviceCollection.AddSingleton<IItemRepository>(sp => sp.GetRequiredService<BaseItemRepository>());
 22533            serviceCollection.AddSingleton<IItemQueryHelpers>(sp => sp.GetRequiredService<BaseItemRepository>());
 22534            serviceCollection.AddSingleton<IItemPersistenceService, ItemPersistenceService>();
 22535            serviceCollection.AddSingleton<INextUpService, NextUpService>();
 22536            serviceCollection.AddSingleton<IItemCountService, ItemCountService>();
 22537            serviceCollection.AddSingleton<ILinkedChildrenService, LinkedChildrenService>();
 22538            serviceCollection.AddSingleton<IPeopleRepository, PeopleRepository>();
 22539            serviceCollection.AddSingleton<IChapterRepository, ChapterRepository>();
 22540            serviceCollection.AddSingleton<IMediaAttachmentRepository, MediaAttachmentRepository>();
 22541            serviceCollection.AddSingleton<IMediaStreamRepository, MediaStreamRepository>();
 22542            serviceCollection.AddSingleton<IKeyframeRepository, KeyframeRepository>();
 22543            serviceCollection.AddSingleton<IItemTypeLookup, ItemTypeLookup>();
 544
 22545            serviceCollection.AddSingleton<IMediaEncoder, MediaBrowser.MediaEncoding.Encoder.MediaEncoder>();
 22546            serviceCollection.AddSingleton<EncodingHelper>();
 22547            serviceCollection.AddSingleton<IPathManager, PathManager>();
 22548            serviceCollection.AddSingleton<IExternalDataManager, ExternalDataManager>();
 549
 550            // TODO: Refactor to eliminate the circular dependencies here so that Lazy<T> isn't required
 22551            serviceCollection.AddTransient(provider => new Lazy<ILibraryMonitor>(provider.GetRequiredService<ILibraryMon
 22552            serviceCollection.AddTransient(provider => new Lazy<IProviderManager>(provider.GetRequiredService<IProviderM
 22553            serviceCollection.AddTransient(provider => new Lazy<IUserViewManager>(provider.GetRequiredService<IUserViewM
 22554            serviceCollection.AddTransient(provider => new Lazy<IExternalDataManager>(provider.GetRequiredService<IExter
 22555            serviceCollection.AddSingleton<ILibraryManager, LibraryManager>();
 22556            serviceCollection.AddSingleton<NamingOptions>();
 22557            serviceCollection.AddSingleton<VideoListResolver>();
 558
 22559            serviceCollection.AddSingleton<IMusicManager, MusicManager>();
 560
 22561            serviceCollection.AddSingleton<ILibraryMonitor, LibraryMonitor>();
 22562            serviceCollection.AddSingleton<DotIgnoreIgnoreRule>();
 563
 22564            serviceCollection.AddSingleton<ISimilarItemsManager, SimilarItemsManager>();
 565
 22566            serviceCollection.AddSingleton<ISearchManager, SearchManager>();
 22567            serviceCollection.AddSingleton<ISearchProvider, SqlSearchProvider>();
 568
 22569            serviceCollection.AddSingleton<IWebSocketManager, WebSocketManager>();
 570
 22571            serviceCollection.AddSingleton<IImageProcessor, ImageProcessor>();
 572
 22573            serviceCollection.AddSingleton<ITVSeriesManager, TVSeriesManager>();
 574
 22575            serviceCollection.AddSingleton<IMediaSourceManager, MediaSourceManager>();
 576
 22577            serviceCollection.AddSingleton<ISubtitleManager, SubtitleManager>();
 22578            serviceCollection.AddSingleton<ILyricManager, LyricManager>();
 579
 22580            serviceCollection.AddSingleton<IProviderManager, ProviderManager>();
 581
 582            // TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
 22583            serviceCollection.AddTransient(provider => new Lazy<ILiveTvManager>(provider.GetRequiredService<ILiveTvManag
 22584            serviceCollection.AddSingleton<IDtoService, DtoService>();
 585
 22586            serviceCollection.AddSingleton<ISessionManager, SessionManager>();
 587
 22588            serviceCollection.AddSingleton<ICollectionManager, CollectionManager>();
 22589            serviceCollection.AddSingleton<ILimitedConcurrencyLibraryScheduler, LimitedConcurrencyLibraryScheduler>();
 590
 22591            serviceCollection.AddSingleton<IPlaylistManager, PlaylistManager>();
 592
 22593            serviceCollection.AddSingleton<ISyncPlayManager, SyncPlayManager>();
 594
 22595            serviceCollection.AddSingleton<IUserViewManager, UserViewManager>();
 596
 22597            serviceCollection.AddSingleton<IChapterManager, ChapterManager>();
 598
 22599            serviceCollection.AddSingleton<IAuthService, AuthService>();
 22600            serviceCollection.AddSingleton<IQuickConnect, QuickConnectManager>();
 601
 22602            serviceCollection.AddSingleton<ISubtitleParser, SubtitleEditParser>();
 22603            serviceCollection.AddSingleton<ISubtitleEncoder, SubtitleEncoder>();
 22604            serviceCollection.AddSingleton<IKeyframeManager, KeyframeManager>();
 605
 22606            serviceCollection.AddSingleton<IAttachmentExtractor, MediaBrowser.MediaEncoding.Attachments.AttachmentExtrac
 607
 22608            serviceCollection.AddSingleton<ITranscodeManager, TranscodeManager>();
 22609            serviceCollection.AddScoped<MediaInfoHelper>();
 22610            serviceCollection.AddScoped<AudioHelper>();
 22611            serviceCollection.AddScoped<DynamicHlsHelper>();
 22612            serviceCollection.AddScoped<IClientEventLogger, ClientEventLogger>();
 22613            serviceCollection.AddSingleton<IDirectoryService, DirectoryService>();
 614
 22615            serviceCollection.AddSingleton<IMediaSegmentManager, MediaSegmentManager>();
 22616        }
 617
 618        /// <summary>
 619        /// Create services registered with the service container that need to be initialized at application startup.
 620        /// </summary>
 621        /// <param name="startupConfig">The configuration used to initialise the application.</param>
 622        /// <returns>A task representing the service initialization operation.</returns>
 623        public async Task InitializeServices(IConfiguration startupConfig)
 624        {
 22625            var localizationManager = (LocalizationManager)Resolve<ILocalizationManager>();
 22626            await localizationManager.LoadAll().ConfigureAwait(false);
 627
 22628            SetStaticProperties();
 629
 22630            FindParts();
 22631        }
 632
 633        private X509Certificate2 GetCertificate(string path, string password)
 634        {
 22635            if (string.IsNullOrWhiteSpace(path))
 636            {
 22637                return null;
 638            }
 639
 640            try
 641            {
 0642                if (!File.Exists(path))
 643                {
 0644                    return null;
 645                }
 646
 647                // Don't use an empty string password
 0648                password = string.IsNullOrWhiteSpace(password) ? null : password;
 649
 0650                var localCert = X509CertificateLoader.LoadPkcs12FromFile(path, password, X509KeyStorageFlags.UserKeySet)
 0651                if (!localCert.HasPrivateKey)
 652                {
 0653                    Logger.LogError("No private key included in SSL cert {CertificateLocation}.", path);
 0654                    return null;
 655                }
 656
 0657                return localCert;
 658            }
 0659            catch (Exception ex)
 660            {
 0661                Logger.LogError(ex, "Error loading cert from {CertificateLocation}", path);
 0662                return null;
 663            }
 0664        }
 665
 666        /// <summary>
 667        /// Dirty hacks.
 668        /// </summary>
 669        private void SetStaticProperties()
 670        {
 671            // For now there's no real way to inject these properly
 22672            BaseItem.ChapterManager = Resolve<IChapterManager>();
 22673            BaseItem.ChannelManager = Resolve<IChannelManager>();
 22674            BaseItem.ConfigurationManager = ConfigurationManager;
 22675            BaseItem.FileSystem = Resolve<IFileSystem>();
 22676            BaseItem.ItemRepository = Resolve<IItemRepository>();
 22677            BaseItem.ItemCountService = Resolve<IItemCountService>();
 22678            BaseItem.LibraryManager = Resolve<ILibraryManager>();
 22679            BaseItem.LocalizationManager = Resolve<ILocalizationManager>();
 22680            BaseItem.Logger = Resolve<ILogger<BaseItem>>();
 22681            BaseItem.MediaSegmentManager = Resolve<IMediaSegmentManager>();
 22682            BaseItem.MediaSourceManager = Resolve<IMediaSourceManager>();
 22683            BaseItem.ProviderManager = Resolve<IProviderManager>();
 22684            BaseItem.UserDataManager = Resolve<IUserDataManager>();
 22685            CollectionFolder.XmlSerializer = _xmlSerializer;
 22686            CollectionFolder.ApplicationHost = this;
 22687            Folder.UserViewManager = Resolve<IUserViewManager>();
 22688            Folder.CollectionManager = Resolve<ICollectionManager>();
 22689            Folder.LimitedConcurrencyLibraryScheduler = Resolve<ILimitedConcurrencyLibraryScheduler>();
 22690            Episode.MediaEncoder = Resolve<IMediaEncoder>();
 22691            UserView.TVSeriesManager = Resolve<ITVSeriesManager>();
 22692            Video.RecordingsManager = Resolve<IRecordingsManager>();
 22693        }
 694
 695        /// <summary>
 696        /// Finds plugin components and register them with the appropriate services.
 697        /// </summary>
 698        private void FindParts()
 699        {
 22700            if (!ConfigurationManager.Configuration.IsPortAuthorized)
 701            {
 22702                ConfigurationManager.Configuration.IsPortAuthorized = true;
 22703                ConfigurationManager.SaveConfiguration();
 704            }
 705
 22706            _pluginManager.CreatePlugins();
 707
 22708            Resolve<ILibraryManager>().AddParts(
 22709                GetExports<IResolverIgnoreRule>(),
 22710                GetExports<IItemResolver>(),
 22711                GetExports<IIntroProvider>(),
 22712                GetExports<IBaseItemComparer>(),
 22713                GetExports<ILibraryPostScanTask>());
 714
 22715            Resolve<IProviderManager>().AddParts(
 22716                GetExports<IImageProvider>(),
 22717                GetExports<IMetadataService>(),
 22718                GetExports<IMetadataProvider>(),
 22719                GetExports<IMetadataSaver>(),
 22720                GetExports<IExternalId>(),
 22721                GetExports<IExternalUrlProvider>());
 722
 22723            Resolve<IMediaSourceManager>().AddParts(GetExports<IMediaSourceProvider>());
 724
 22725            Resolve<ISimilarItemsManager>().AddParts(GetExports<ISimilarItemsProvider>());
 22726            Resolve<ISearchManager>().AddParts(GetExports<ISearchProvider>());
 22727        }
 728
 729        /// <summary>
 730        /// Discovers the types.
 731        /// </summary>
 732        protected void DiscoverTypes()
 733        {
 22734            Logger.LogInformation("Loading assemblies");
 735
 22736            _allConcreteTypes = GetTypes(GetComposablePartAssemblies()).ToArray();
 22737        }
 738
 739        private IEnumerable<Type> GetTypes(IEnumerable<Assembly> assemblies)
 740        {
 748741            foreach (var ass in assemblies)
 742            {
 743                Type[] exportedTypes;
 744                try
 745                {
 352746                    exportedTypes = ass.GetExportedTypes();
 352747                }
 0748                catch (FileNotFoundException ex)
 749                {
 0750                    Logger.LogError(ex, "Error getting exported types from {Assembly}", ass.FullName);
 0751                    _pluginManager.FailPlugin(ass);
 0752                    continue;
 753                }
 0754                catch (TypeLoadException ex)
 755                {
 0756                    Logger.LogError(ex, "Error loading types from {Assembly}.", ass.FullName);
 0757                    _pluginManager.FailPlugin(ass);
 0758                    continue;
 759                }
 760
 67276761                foreach (Type type in exportedTypes)
 762                {
 33286763                    if (type.IsClass && !type.IsAbstract && !type.IsInterface && !type.IsGenericType)
 764                    {
 23122765                        yield return type;
 766                    }
 767                }
 768            }
 22769        }
 770
 771        /// <summary>
 772        /// Called when [configuration updated].
 773        /// </summary>
 774        /// <param name="sender">The sender.</param>
 775        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
 776        private void OnConfigurationUpdated(object sender, EventArgs e)
 777        {
 19778            var requiresRestart = false;
 19779            var networkConfiguration = ConfigurationManager.GetNetworkConfiguration();
 780
 781            // Don't do anything if these haven't been set yet
 19782            if (HttpPort != 0 && HttpsPort != 0)
 783            {
 784                // Need to restart if ports have changed
 19785                if (networkConfiguration.InternalHttpPort != HttpPort
 19786                    || networkConfiguration.InternalHttpsPort != HttpsPort)
 787                {
 0788                    if (ConfigurationManager.Configuration.IsPortAuthorized)
 789                    {
 0790                        ConfigurationManager.Configuration.IsPortAuthorized = false;
 0791                        ConfigurationManager.SaveConfiguration();
 792
 0793                        requiresRestart = true;
 794                    }
 795                }
 796            }
 797
 19798            if (ValidateSslCertificate(networkConfiguration))
 799            {
 0800                requiresRestart = true;
 801            }
 802
 19803            if (requiresRestart)
 804            {
 0805                Logger.LogInformation("App needs to be restarted due to configuration change.");
 806
 0807                NotifyPendingRestart();
 808            }
 19809        }
 810
 811        /// <summary>
 812        /// Validates the SSL certificate.
 813        /// </summary>
 814        /// <param name="networkConfig">The new configuration.</param>
 815        /// <exception cref="FileNotFoundException">The certificate path doesn't exist.</exception>
 816        private bool ValidateSslCertificate(NetworkConfiguration networkConfig)
 817        {
 19818            var newPath = networkConfig.CertificatePath;
 819
 19820            if (!string.IsNullOrWhiteSpace(newPath)
 19821                && !string.Equals(CertificatePath, newPath, StringComparison.Ordinal))
 822            {
 0823                if (File.Exists(newPath))
 824                {
 0825                    return true;
 826                }
 827
 0828                throw new FileNotFoundException(
 0829                    string.Format(
 0830                        CultureInfo.InvariantCulture,
 0831                        "Certificate file '{0}' does not exist.",
 0832                        newPath));
 833            }
 834
 19835            return false;
 836        }
 837
 838        /// <summary>
 839        /// Notifies the kernel that a change has been made that requires a restart.
 840        /// </summary>
 841        public void NotifyPendingRestart()
 842        {
 0843            Logger.LogInformation("App needs to be restarted.");
 844
 0845            var changed = !HasPendingRestart;
 846
 0847            HasPendingRestart = true;
 848
 0849            if (changed)
 850            {
 0851                EventHelper.QueueEventIfNotNull(HasPendingRestartChanged, this, EventArgs.Empty, Logger);
 852            }
 0853        }
 854
 855        /// <summary>
 856        /// Gets the composable part assemblies.
 857        /// </summary>
 858        /// <returns>IEnumerable{Assembly}.</returns>
 859        protected IEnumerable<Assembly> GetComposablePartAssemblies()
 860        {
 44861            foreach (var p in _pluginManager.LoadAssemblies())
 862            {
 0863                yield return p;
 864            }
 865
 866            // Include composable parts in the Model assembly
 22867            yield return typeof(SystemInfo).Assembly;
 868
 869            // Include composable parts in the Common assembly
 22870            yield return typeof(IApplicationHost).Assembly;
 871
 872            // Include composable parts in the Controller assembly
 22873            yield return typeof(IServerApplicationHost).Assembly;
 874
 875            // Include composable parts in the Providers assembly
 22876            yield return typeof(ProviderManager).Assembly;
 877
 878            // Include composable parts in the Photos assembly
 22879            yield return typeof(PhotoProvider).Assembly;
 880
 881            // Emby.Server implementations
 22882            yield return typeof(InstallationManager).Assembly;
 883
 884            // MediaEncoding
 22885            yield return typeof(MediaBrowser.MediaEncoding.Encoder.MediaEncoder).Assembly;
 886
 887            // Local metadata
 22888            yield return typeof(BoxSetXmlSaver).Assembly;
 889
 890            // Xbmc
 22891            yield return typeof(ArtistNfoProvider).Assembly;
 892
 893            // Network
 22894            yield return typeof(NetworkManager).Assembly;
 895
 896            // Hls
 22897            yield return typeof(DynamicHlsPlaylistGenerator).Assembly;
 898
 264899            foreach (var i in GetAssembliesWithPartsInternal())
 900            {
 110901                yield return i;
 902            }
 22903        }
 904
 905        protected abstract IEnumerable<Assembly> GetAssembliesWithPartsInternal();
 906
 907        /// <inheritdoc/>
 908        public string GetSmartApiUrl(IPAddress remoteAddr)
 909        {
 910            // Published server ends with a /
 0911            if (!string.IsNullOrEmpty(PublishedServerUrl))
 912            {
 913                // Published server ends with a '/', so we need to remove it.
 0914                return PublishedServerUrl.Trim('/');
 915            }
 916
 0917            string smart = NetManager.GetBindAddress(remoteAddr, out var port);
 0918            return GetLocalApiUrl(smart.Trim('/'), null, port);
 919        }
 920
 921        /// <inheritdoc/>
 922        public string GetSmartApiUrl(HttpRequest request)
 923        {
 924            // Return the host in the HTTP request as the API URL if not configured otherwise
 0925            if (ConfigurationManager.GetNetworkConfiguration().EnablePublishedServerUriByRequest)
 926            {
 0927                int? requestPort = request.Host.Port;
 0928                if (requestPort is null
 0929                    || (requestPort == 80 && string.Equals(request.Scheme, "http", StringComparison.OrdinalIgnoreCase))
 0930                    || (requestPort == 443 && string.Equals(request.Scheme, "https", StringComparison.OrdinalIgnoreCase)
 931                {
 0932                    requestPort = -1;
 933                }
 934
 0935                return GetLocalApiUrl(request.Host.Host, request.Scheme, requestPort);
 936            }
 937
 0938            return GetSmartApiUrl(request.HttpContext.Connection.RemoteIpAddress ?? IPAddress.Loopback);
 939        }
 940
 941        /// <inheritdoc/>
 942        public string GetSmartApiUrl(string hostname)
 943        {
 944            // Published server ends with a /
 0945            if (!string.IsNullOrEmpty(PublishedServerUrl))
 946            {
 947                // Published server ends with a '/', so we need to remove it.
 0948                return PublishedServerUrl.Trim('/');
 949            }
 950
 0951            string smart = NetManager.GetBindAddress(hostname, out var port);
 0952            return GetLocalApiUrl(smart.Trim('/'), null, port);
 953        }
 954
 955        /// <inheritdoc/>
 956        public string GetApiUrlForLocalAccess(IPAddress ipAddress = null, bool allowHttps = true)
 957        {
 958            // With an empty source, the port will be null
 0959            var smart = NetManager.GetBindAddress(ipAddress, out _, false);
 0960            var scheme = !allowHttps ? Uri.UriSchemeHttp : null;
 0961            int? port = !allowHttps ? HttpPort : null;
 0962            return GetLocalApiUrl(smart, scheme, port);
 963        }
 964
 965        /// <inheritdoc/>
 966        public string GetLocalApiUrl(string hostname, string scheme = null, int? port = null)
 967        {
 968            // If the smartAPI doesn't start with http then treat it as a host or ip.
 0969            if (hostname.StartsWith("http", StringComparison.OrdinalIgnoreCase))
 970            {
 0971                return hostname.TrimEnd('/');
 972            }
 973
 974            // NOTE: If no BaseUrl is set then UriBuilder appends a trailing slash, but if there is no BaseUrl it does
 975            // not. For consistency, always trim the trailing slash.
 0976            scheme ??= ListenWithHttps ? Uri.UriSchemeHttps : Uri.UriSchemeHttp;
 0977            var isHttps = string.Equals(scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase);
 0978            return new UriBuilder
 0979            {
 0980                Scheme = scheme,
 0981                Host = hostname,
 0982                Port = port ?? (isHttps ? HttpsPort : HttpPort),
 0983                Path = ConfigurationManager.GetNetworkConfiguration().BaseUrl
 0984            }.ToString().TrimEnd('/');
 985        }
 986
 987        public IEnumerable<Assembly> GetApiPluginAssemblies()
 988        {
 22989            var assemblies = _allConcreteTypes
 22990                .Where(i => typeof(ControllerBase).IsAssignableFrom(i))
 22991                .Select(i => i.Assembly)
 22992                .Distinct();
 993
 132994            foreach (var assembly in assemblies)
 995            {
 44996                Logger.LogDebug("Found API endpoints in plugin {Name}", assembly.FullName);
 44997                yield return assembly;
 998            }
 22999        }
 1000
 1001        /// <inheritdoc />
 1002        public void Dispose()
 1003        {
 221004            Dispose(true);
 221005            GC.SuppressFinalize(this);
 221006        }
 1007
 1008        /// <summary>
 1009        /// Releases unmanaged and - optionally - managed resources.
 1010        /// </summary>
 1011        /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release o
 1012        protected virtual void Dispose(bool dispose)
 1013        {
 221014            if (_disposed)
 1015            {
 01016                return;
 1017            }
 1018
 221019            if (dispose)
 1020            {
 221021                var type = GetType();
 1022
 221023                Logger.LogInformation("Disposing {Type}", type.Name);
 1024
 881025                foreach (var part in _disposableParts.ToArray())
 1026                {
 221027                    var partType = part.GetType();
 221028                    if (partType == type)
 1029                    {
 1030                        continue;
 1031                    }
 1032
 221033                    Logger.LogInformation("Disposing {Type}", partType.Name);
 1034
 1035                    try
 1036                    {
 221037                        part.Dispose();
 221038                    }
 01039                    catch (Exception ex)
 1040                    {
 01041                        Logger.LogError(ex, "Error disposing {Type}", partType.Name);
 01042                    }
 1043                }
 1044
 221045                _disposableParts.Clear();
 1046
 221047                _pluginManager?.Dispose();
 1048            }
 1049
 221050            _disposed = true;
 221051        }
 1052    }
 1053}