|
@@ -12,11 +12,8 @@ using System.Linq;
|
|
|
using System.Net;
|
|
|
using System.Reflection;
|
|
|
using System.Security.Cryptography.X509Certificates;
|
|
|
-using System.Threading;
|
|
|
using System.Threading.Tasks;
|
|
|
-using Emby.Dlna;
|
|
|
using Emby.Dlna.Main;
|
|
|
-using Emby.Dlna.Ssdp;
|
|
|
using Emby.Naming.Common;
|
|
|
using Emby.Photos;
|
|
|
using Emby.Server.Implementations.Channels;
|
|
@@ -59,7 +56,6 @@ using MediaBrowser.Controller.Chapters;
|
|
|
using MediaBrowser.Controller.ClientEvent;
|
|
|
using MediaBrowser.Controller.Collections;
|
|
|
using MediaBrowser.Controller.Configuration;
|
|
|
-using MediaBrowser.Controller.Dlna;
|
|
|
using MediaBrowser.Controller.Drawing;
|
|
|
using MediaBrowser.Controller.Dto;
|
|
|
using MediaBrowser.Controller.Entities;
|
|
@@ -83,7 +79,6 @@ using MediaBrowser.LocalMetadata.Savers;
|
|
|
using MediaBrowser.MediaEncoding.BdInfo;
|
|
|
using MediaBrowser.MediaEncoding.Subtitles;
|
|
|
using MediaBrowser.Model.Cryptography;
|
|
|
-using MediaBrowser.Model.Dlna;
|
|
|
using MediaBrowser.Model.Globalization;
|
|
|
using MediaBrowser.Model.IO;
|
|
|
using MediaBrowser.Model.MediaInfo;
|
|
@@ -112,7 +107,7 @@ namespace Emby.Server.Implementations
|
|
|
/// <summary>
|
|
|
/// Class CompositionRoot.
|
|
|
/// </summary>
|
|
|
- public abstract class ApplicationHost : IServerApplicationHost, IAsyncDisposable, IDisposable
|
|
|
+ public abstract class ApplicationHost : IServerApplicationHost, IDisposable
|
|
|
{
|
|
|
/// <summary>
|
|
|
/// The disposable parts.
|
|
@@ -120,14 +115,12 @@ namespace Emby.Server.Implementations
|
|
|
private readonly ConcurrentDictionary<IDisposable, byte> _disposableParts = new();
|
|
|
private readonly DeviceId _deviceId;
|
|
|
|
|
|
- private readonly IFileSystem _fileSystemManager;
|
|
|
private readonly IConfiguration _startupConfig;
|
|
|
private readonly IXmlSerializer _xmlSerializer;
|
|
|
private readonly IStartupOptions _startupOptions;
|
|
|
private readonly IPluginManager _pluginManager;
|
|
|
|
|
|
private List<Type> _creatingInstances;
|
|
|
- private ISessionManager _sessionManager;
|
|
|
|
|
|
/// <summary>
|
|
|
/// Gets or sets all concrete types.
|
|
@@ -135,7 +128,7 @@ namespace Emby.Server.Implementations
|
|
|
/// <value>All concrete types.</value>
|
|
|
private Type[] _allConcreteTypes;
|
|
|
|
|
|
- private bool _disposed = false;
|
|
|
+ private bool _disposed;
|
|
|
|
|
|
/// <summary>
|
|
|
/// Initializes a new instance of the <see cref="ApplicationHost"/> class.
|
|
@@ -154,10 +147,8 @@ namespace Emby.Server.Implementations
|
|
|
LoggerFactory = loggerFactory;
|
|
|
_startupOptions = options;
|
|
|
_startupConfig = startupConfig;
|
|
|
- _fileSystemManager = new ManagedFileSystem(LoggerFactory.CreateLogger<ManagedFileSystem>(), applicationPaths);
|
|
|
|
|
|
Logger = LoggerFactory.CreateLogger<ApplicationHost>();
|
|
|
- _fileSystemManager.AddShortcutHandler(new MbLinkShortcutHandler(_fileSystemManager));
|
|
|
_deviceId = new DeviceId(ApplicationPaths, LoggerFactory);
|
|
|
|
|
|
ApplicationVersion = typeof(ApplicationHost).Assembly.GetName().Version;
|
|
@@ -165,13 +156,15 @@ namespace Emby.Server.Implementations
|
|
|
ApplicationUserAgent = Name.Replace(' ', '-') + "/" + ApplicationVersionString;
|
|
|
|
|
|
_xmlSerializer = new MyXmlSerializer();
|
|
|
- ConfigurationManager = new ServerConfigurationManager(ApplicationPaths, LoggerFactory, _xmlSerializer, _fileSystemManager);
|
|
|
+ ConfigurationManager = new ServerConfigurationManager(ApplicationPaths, LoggerFactory, _xmlSerializer);
|
|
|
_pluginManager = new PluginManager(
|
|
|
LoggerFactory.CreateLogger<PluginManager>(),
|
|
|
this,
|
|
|
ConfigurationManager.Configuration,
|
|
|
ApplicationPaths.PluginsPath,
|
|
|
ApplicationVersion);
|
|
|
+
|
|
|
+ _disposableParts.TryAdd((PluginManager)_pluginManager, byte.MinValue);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
@@ -186,23 +179,16 @@ namespace Emby.Server.Implementations
|
|
|
|
|
|
public bool CoreStartupHasCompleted { get; private set; }
|
|
|
|
|
|
- public virtual bool CanLaunchWebBrowser => Environment.UserInteractive
|
|
|
- && !_startupOptions.IsService
|
|
|
- && (OperatingSystem.IsWindows() || OperatingSystem.IsMacOS());
|
|
|
-
|
|
|
/// <summary>
|
|
|
/// Gets the <see cref="INetworkManager"/> singleton instance.
|
|
|
/// </summary>
|
|
|
public INetworkManager NetManager { get; private set; }
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// Gets a value indicating whether this instance has changes that require the entire application to restart.
|
|
|
- /// </summary>
|
|
|
- /// <value><c>true</c> if this instance has pending application restart; otherwise, <c>false</c>.</value>
|
|
|
+ /// <inheritdoc />
|
|
|
public bool HasPendingRestart { get; private set; }
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
- public bool IsShuttingDown { get; private set; }
|
|
|
+ public bool ShouldRestart { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// Gets the logger.
|
|
@@ -406,11 +392,9 @@ namespace Emby.Server.Implementations
|
|
|
/// <summary>
|
|
|
/// Runs the startup tasks.
|
|
|
/// </summary>
|
|
|
- /// <param name="cancellationToken">The cancellation token.</param>
|
|
|
/// <returns><see cref="Task" />.</returns>
|
|
|
- public async Task RunStartupTasksAsync(CancellationToken cancellationToken)
|
|
|
+ public async Task RunStartupTasksAsync()
|
|
|
{
|
|
|
- cancellationToken.ThrowIfCancellationRequested();
|
|
|
Logger.LogInformation("Running startup tasks");
|
|
|
|
|
|
Resolve<ITaskManager>().AddTasks(GetExports<IScheduledTask>(false));
|
|
@@ -424,8 +408,6 @@ namespace Emby.Server.Implementations
|
|
|
|
|
|
var entryPoints = GetExports<IServerEntryPoint>();
|
|
|
|
|
|
- cancellationToken.ThrowIfCancellationRequested();
|
|
|
-
|
|
|
var stopWatch = new Stopwatch();
|
|
|
stopWatch.Start();
|
|
|
|
|
@@ -435,8 +417,6 @@ namespace Emby.Server.Implementations
|
|
|
Logger.LogInformation("Core startup complete");
|
|
|
CoreStartupHasCompleted = true;
|
|
|
|
|
|
- cancellationToken.ThrowIfCancellationRequested();
|
|
|
-
|
|
|
stopWatch.Restart();
|
|
|
|
|
|
await Task.WhenAll(StartEntryPoints(entryPoints, false)).ConfigureAwait(false);
|
|
@@ -466,7 +446,7 @@ namespace Emby.Server.Implementations
|
|
|
|
|
|
ConfigurationManager.AddParts(GetExports<IConfigurationFactory>());
|
|
|
|
|
|
- NetManager = new NetworkManager(ConfigurationManager, LoggerFactory.CreateLogger<NetworkManager>());
|
|
|
+ NetManager = new NetworkManager(ConfigurationManager, _startupConfig, LoggerFactory.CreateLogger<NetworkManager>());
|
|
|
|
|
|
// Initialize runtime stat collection
|
|
|
if (ConfigurationManager.Configuration.EnableMetrics)
|
|
@@ -475,8 +455,8 @@ namespace Emby.Server.Implementations
|
|
|
}
|
|
|
|
|
|
var networkConfiguration = ConfigurationManager.GetNetworkConfiguration();
|
|
|
- HttpPort = networkConfiguration.HttpServerPortNumber;
|
|
|
- HttpsPort = networkConfiguration.HttpsPortNumber;
|
|
|
+ HttpPort = networkConfiguration.InternalHttpPort;
|
|
|
+ HttpsPort = networkConfiguration.InternalHttpsPort;
|
|
|
|
|
|
// Safeguard against invalid configuration
|
|
|
if (HttpPort == HttpsPort)
|
|
@@ -509,7 +489,11 @@ namespace Emby.Server.Implementations
|
|
|
serviceCollection.AddSingleton(_pluginManager);
|
|
|
serviceCollection.AddSingleton<IApplicationPaths>(ApplicationPaths);
|
|
|
|
|
|
- serviceCollection.AddSingleton(_fileSystemManager);
|
|
|
+ serviceCollection.AddSingleton<IFileSystem, ManagedFileSystem>();
|
|
|
+ serviceCollection.AddSingleton<IShortcutHandler, MbLinkShortcutHandler>();
|
|
|
+
|
|
|
+ serviceCollection.AddScoped<ISystemManager, SystemManager>();
|
|
|
+
|
|
|
serviceCollection.AddSingleton<TmdbClientManager>();
|
|
|
|
|
|
serviceCollection.AddSingleton(NetManager);
|
|
@@ -575,8 +559,6 @@ namespace Emby.Server.Implementations
|
|
|
|
|
|
serviceCollection.AddSingleton<ISessionManager, SessionManager>();
|
|
|
|
|
|
- serviceCollection.AddSingleton<IDlnaManager, DlnaManager>();
|
|
|
-
|
|
|
serviceCollection.AddSingleton<ICollectionManager, CollectionManager>();
|
|
|
|
|
|
serviceCollection.AddSingleton<IPlaylistManager, PlaylistManager>();
|
|
@@ -588,8 +570,6 @@ namespace Emby.Server.Implementations
|
|
|
|
|
|
serviceCollection.AddSingleton<IUserViewManager, UserViewManager>();
|
|
|
|
|
|
- serviceCollection.AddSingleton<IDeviceDiscovery, DeviceDiscovery>();
|
|
|
-
|
|
|
serviceCollection.AddSingleton<IChapterManager, ChapterManager>();
|
|
|
|
|
|
serviceCollection.AddSingleton<IEncodingManager, MediaEncoder.EncodingManager>();
|
|
@@ -633,8 +613,6 @@ namespace Emby.Server.Implementations
|
|
|
var localizationManager = (LocalizationManager)Resolve<ILocalizationManager>();
|
|
|
await localizationManager.LoadAll().ConfigureAwait(false);
|
|
|
|
|
|
- _sessionManager = Resolve<ISessionManager>();
|
|
|
-
|
|
|
SetStaticProperties();
|
|
|
|
|
|
FindParts();
|
|
@@ -685,7 +663,7 @@ namespace Emby.Server.Implementations
|
|
|
BaseItem.ProviderManager = Resolve<IProviderManager>();
|
|
|
BaseItem.LocalizationManager = Resolve<ILocalizationManager>();
|
|
|
BaseItem.ItemRepository = Resolve<IItemRepository>();
|
|
|
- BaseItem.FileSystem = _fileSystemManager;
|
|
|
+ BaseItem.FileSystem = Resolve<IFileSystem>();
|
|
|
BaseItem.UserDataManager = Resolve<IUserDataManager>();
|
|
|
BaseItem.ChannelManager = Resolve<IChannelManager>();
|
|
|
Video.LiveTvManager = Resolve<ILiveTvManager>();
|
|
@@ -785,8 +763,8 @@ namespace Emby.Server.Implementations
|
|
|
if (HttpPort != 0 && HttpsPort != 0)
|
|
|
{
|
|
|
// Need to restart if ports have changed
|
|
|
- if (networkConfiguration.HttpServerPortNumber != HttpPort
|
|
|
- || networkConfiguration.HttpsPortNumber != HttpsPort)
|
|
|
+ if (networkConfiguration.InternalHttpPort != HttpPort
|
|
|
+ || networkConfiguration.InternalHttpsPort != HttpsPort)
|
|
|
{
|
|
|
if (ConfigurationManager.Configuration.IsPortAuthorized)
|
|
|
{
|
|
@@ -855,38 +833,6 @@ namespace Emby.Server.Implementations
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// Restarts this instance.
|
|
|
- /// </summary>
|
|
|
- public void Restart()
|
|
|
- {
|
|
|
- if (IsShuttingDown)
|
|
|
- {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- IsShuttingDown = true;
|
|
|
- _pluginManager.UnloadAssemblies();
|
|
|
-
|
|
|
- Task.Run(async () =>
|
|
|
- {
|
|
|
- try
|
|
|
- {
|
|
|
- await _sessionManager.SendServerRestartNotification(CancellationToken.None).ConfigureAwait(false);
|
|
|
- }
|
|
|
- catch (Exception ex)
|
|
|
- {
|
|
|
- Logger.LogError(ex, "Error sending server restart notification");
|
|
|
- }
|
|
|
-
|
|
|
- Logger.LogInformation("Calling RestartInternal");
|
|
|
-
|
|
|
- RestartInternal();
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- protected abstract void RestartInternal();
|
|
|
-
|
|
|
/// <summary>
|
|
|
/// Gets the composable part assemblies.
|
|
|
/// </summary>
|
|
@@ -942,49 +888,6 @@ namespace Emby.Server.Implementations
|
|
|
|
|
|
protected abstract IEnumerable<Assembly> GetAssembliesWithPartsInternal();
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// Gets the system status.
|
|
|
- /// </summary>
|
|
|
- /// <param name="request">Where this request originated.</param>
|
|
|
- /// <returns>SystemInfo.</returns>
|
|
|
- public SystemInfo GetSystemInfo(HttpRequest request)
|
|
|
- {
|
|
|
- return new SystemInfo
|
|
|
- {
|
|
|
- HasPendingRestart = HasPendingRestart,
|
|
|
- IsShuttingDown = IsShuttingDown,
|
|
|
- Version = ApplicationVersionString,
|
|
|
- WebSocketPortNumber = HttpPort,
|
|
|
- CompletedInstallations = Resolve<IInstallationManager>().CompletedInstallations.ToArray(),
|
|
|
- Id = SystemId,
|
|
|
- ProgramDataPath = ApplicationPaths.ProgramDataPath,
|
|
|
- WebPath = ApplicationPaths.WebPath,
|
|
|
- LogPath = ApplicationPaths.LogDirectoryPath,
|
|
|
- ItemsByNamePath = ApplicationPaths.InternalMetadataPath,
|
|
|
- InternalMetadataPath = ApplicationPaths.InternalMetadataPath,
|
|
|
- CachePath = ApplicationPaths.CachePath,
|
|
|
- CanLaunchWebBrowser = CanLaunchWebBrowser,
|
|
|
- TranscodingTempPath = ConfigurationManager.GetTranscodePath(),
|
|
|
- ServerName = FriendlyName,
|
|
|
- LocalAddress = GetSmartApiUrl(request),
|
|
|
- SupportsLibraryMonitor = true,
|
|
|
- PackageName = _startupOptions.PackageName
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- public PublicSystemInfo GetPublicSystemInfo(HttpRequest request)
|
|
|
- {
|
|
|
- return new PublicSystemInfo
|
|
|
- {
|
|
|
- Version = ApplicationVersionString,
|
|
|
- ProductName = ApplicationProductName,
|
|
|
- Id = SystemId,
|
|
|
- ServerName = FriendlyName,
|
|
|
- LocalAddress = GetSmartApiUrl(request),
|
|
|
- StartupWizardCompleted = ConfigurationManager.CommonConfiguration.IsStartupWizardCompleted
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
/// <inheritdoc/>
|
|
|
public string GetSmartApiUrl(IPAddress remoteAddr)
|
|
|
{
|
|
@@ -995,18 +898,20 @@ namespace Emby.Server.Implementations
|
|
|
return PublishedServerUrl.Trim('/');
|
|
|
}
|
|
|
|
|
|
- string smart = NetManager.GetBindInterface(remoteAddr, out var port);
|
|
|
+ string smart = NetManager.GetBindAddress(remoteAddr, out var port);
|
|
|
return GetLocalApiUrl(smart.Trim('/'), null, port);
|
|
|
}
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
public string GetSmartApiUrl(HttpRequest request)
|
|
|
{
|
|
|
- // Return the host in the HTTP request as the API url
|
|
|
+ // Return the host in the HTTP request as the API URL if not configured otherwise
|
|
|
if (ConfigurationManager.GetNetworkConfiguration().EnablePublishedServerUriByRequest)
|
|
|
{
|
|
|
int? requestPort = request.Host.Port;
|
|
|
- if ((requestPort == 80 && string.Equals(request.Scheme, "http", StringComparison.OrdinalIgnoreCase)) || (requestPort == 443 && string.Equals(request.Scheme, "https", StringComparison.OrdinalIgnoreCase)))
|
|
|
+ if (requestPort is null
|
|
|
+ || (requestPort == 80 && string.Equals(request.Scheme, "http", StringComparison.OrdinalIgnoreCase))
|
|
|
+ || (requestPort == 443 && string.Equals(request.Scheme, "https", StringComparison.OrdinalIgnoreCase)))
|
|
|
{
|
|
|
requestPort = -1;
|
|
|
}
|
|
@@ -1027,15 +932,15 @@ namespace Emby.Server.Implementations
|
|
|
return PublishedServerUrl.Trim('/');
|
|
|
}
|
|
|
|
|
|
- string smart = NetManager.GetBindInterface(hostname, out var port);
|
|
|
+ string smart = NetManager.GetBindAddress(hostname, out var port);
|
|
|
return GetLocalApiUrl(smart.Trim('/'), null, port);
|
|
|
}
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
- public string GetApiUrlForLocalAccess(IPObject hostname = null, bool allowHttps = true)
|
|
|
+ public string GetApiUrlForLocalAccess(IPAddress ipAddress = null, bool allowHttps = true)
|
|
|
{
|
|
|
// With an empty source, the port will be null
|
|
|
- var smart = NetManager.GetBindInterface(hostname ?? IPHost.None, out _);
|
|
|
+ var smart = NetManager.GetBindAddress(ipAddress, out _, false);
|
|
|
var scheme = !allowHttps ? Uri.UriSchemeHttp : null;
|
|
|
int? port = !allowHttps ? HttpPort : null;
|
|
|
return GetLocalApiUrl(smart, scheme, port);
|
|
@@ -1063,30 +968,6 @@ namespace Emby.Server.Implementations
|
|
|
}.ToString().TrimEnd('/');
|
|
|
}
|
|
|
|
|
|
- /// <inheritdoc />
|
|
|
- public async Task Shutdown()
|
|
|
- {
|
|
|
- if (IsShuttingDown)
|
|
|
- {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- IsShuttingDown = true;
|
|
|
-
|
|
|
- try
|
|
|
- {
|
|
|
- await _sessionManager.SendServerShutdownNotification(CancellationToken.None).ConfigureAwait(false);
|
|
|
- }
|
|
|
- catch (Exception ex)
|
|
|
- {
|
|
|
- Logger.LogError(ex, "Error sending server shutdown notification");
|
|
|
- }
|
|
|
-
|
|
|
- ShutdownInternal();
|
|
|
- }
|
|
|
-
|
|
|
- protected abstract void ShutdownInternal();
|
|
|
-
|
|
|
public IEnumerable<Assembly> GetApiPluginAssemblies()
|
|
|
{
|
|
|
var assemblies = _allConcreteTypes
|
|
@@ -1150,52 +1031,5 @@ namespace Emby.Server.Implementations
|
|
|
|
|
|
_disposed = true;
|
|
|
}
|
|
|
-
|
|
|
- public async ValueTask DisposeAsync()
|
|
|
- {
|
|
|
- await DisposeAsyncCore().ConfigureAwait(false);
|
|
|
- Dispose(false);
|
|
|
- GC.SuppressFinalize(this);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Used to perform asynchronous cleanup of managed resources or for cascading calls to <see cref="DisposeAsync"/>.
|
|
|
- /// </summary>
|
|
|
- /// <returns>A ValueTask.</returns>
|
|
|
- protected virtual async ValueTask DisposeAsyncCore()
|
|
|
- {
|
|
|
- var type = GetType();
|
|
|
-
|
|
|
- Logger.LogInformation("Disposing {Type}", type.Name);
|
|
|
-
|
|
|
- foreach (var (part, _) in _disposableParts)
|
|
|
- {
|
|
|
- var partType = part.GetType();
|
|
|
- if (partType == type)
|
|
|
- {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- Logger.LogInformation("Disposing {Type}", partType.Name);
|
|
|
-
|
|
|
- try
|
|
|
- {
|
|
|
- part.Dispose();
|
|
|
- }
|
|
|
- catch (Exception ex)
|
|
|
- {
|
|
|
- Logger.LogError(ex, "Error disposing {Type}", partType.Name);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (_sessionManager != null)
|
|
|
- {
|
|
|
- // used for closing websockets
|
|
|
- foreach (var session in _sessionManager.Sessions)
|
|
|
- {
|
|
|
- await session.DisposeAsync().ConfigureAwait(false);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
}
|
|
|
}
|