diff options
Diffstat (limited to 'Jellyfin.Server')
| -rw-r--r-- | Jellyfin.Server/CoreAppHost.cs | 14 | ||||
| -rw-r--r-- | Jellyfin.Server/Jellyfin.Server.csproj | 5 | ||||
| -rw-r--r-- | Jellyfin.Server/Migrations/IMigrationRoutine.cs | 4 | ||||
| -rw-r--r-- | Jellyfin.Server/Migrations/MigrationRunner.cs | 21 | ||||
| -rw-r--r-- | Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs | 11 | ||||
| -rw-r--r-- | Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs | 17 | ||||
| -rw-r--r-- | Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs | 133 | ||||
| -rw-r--r-- | Jellyfin.Server/Migrations/Routines/RemoveDuplicateExtras.cs | 79 | ||||
| -rw-r--r-- | Jellyfin.Server/Program.cs | 109 | ||||
| -rw-r--r-- | Jellyfin.Server/Startup.cs | 1 |
10 files changed, 339 insertions, 55 deletions
diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index f678e714c..331a32c73 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -1,12 +1,17 @@ using System; using System.Collections.Generic; +using System.IO; using System.Reflection; using Emby.Drawing; using Emby.Server.Implementations; using Jellyfin.Drawing.Skia; +using Jellyfin.Server.Implementations; +using Jellyfin.Server.Implementations.Activity; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Drawing; +using MediaBrowser.Model.Activity; using MediaBrowser.Model.IO; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -56,6 +61,15 @@ namespace Jellyfin.Server Logger.LogWarning($"Skia not available. Will fallback to {nameof(NullImageEncoder)}."); } + // TODO: Set up scoping and use AddDbContextPool + serviceCollection.AddDbContext<JellyfinDb>( + options => options.UseSqlite($"Filename={Path.Combine(ApplicationPaths.DataPath, "jellyfin.db")}"), + ServiceLifetime.Transient); + + serviceCollection.AddSingleton<JellyfinDbProvider>(); + + serviceCollection.AddSingleton<IActivityManager, ActivityManager>(); + base.RegisterServices(serviceCollection); } diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 88114d999..c93aa837e 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -41,8 +41,8 @@ <ItemGroup> <PackageReference Include="CommandLineParser" Version="2.7.82" /> - <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.3" /> - <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.3" /> + <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.4" /> + <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.4" /> <PackageReference Include="prometheus-net" Version="3.5.0" /> <PackageReference Include="prometheus-net.AspNetCore" Version="3.5.0" /> <PackageReference Include="Serilog.AspNetCore" Version="3.2.0" /> @@ -60,6 +60,7 @@ <ProjectReference Include="..\Emby.Drawing\Emby.Drawing.csproj" /> <ProjectReference Include="..\Emby.Server.Implementations\Emby.Server.Implementations.csproj" /> <ProjectReference Include="..\Jellyfin.Drawing.Skia\Jellyfin.Drawing.Skia.csproj" /> + <ProjectReference Include="..\Jellyfin.Server.Implementations\Jellyfin.Server.Implementations.csproj" /> </ItemGroup> </Project> diff --git a/Jellyfin.Server/Migrations/IMigrationRoutine.cs b/Jellyfin.Server/Migrations/IMigrationRoutine.cs index eab995d67..b79fdeac0 100644 --- a/Jellyfin.Server/Migrations/IMigrationRoutine.cs +++ b/Jellyfin.Server/Migrations/IMigrationRoutine.cs @@ -21,8 +21,6 @@ namespace Jellyfin.Server.Migrations /// <summary> /// Execute the migration routine. /// </summary> - /// <param name="host">Host that hosts current version.</param> - /// <param name="logger">Host logger.</param> - public void Perform(CoreAppHost host, ILogger logger); + public void Perform(); } } diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs index b5ea04dca..473f62737 100644 --- a/Jellyfin.Server/Migrations/MigrationRunner.cs +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using MediaBrowser.Common.Configuration; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace Jellyfin.Server.Migrations @@ -13,10 +14,12 @@ namespace Jellyfin.Server.Migrations /// <summary> /// The list of known migrations, in order of applicability. /// </summary> - internal static readonly IMigrationRoutine[] Migrations = + private static readonly Type[] _migrationTypes = { - new Routines.DisableTranscodingThrottling(), - new Routines.CreateUserLoggingConfigFile() + typeof(Routines.DisableTranscodingThrottling), + typeof(Routines.CreateUserLoggingConfigFile), + typeof(Routines.MigrateActivityLogDb), + typeof(Routines.RemoveDuplicateExtras) }; /// <summary> @@ -27,6 +30,10 @@ namespace Jellyfin.Server.Migrations public static void Run(CoreAppHost host, ILoggerFactory loggerFactory) { var logger = loggerFactory.CreateLogger<MigrationRunner>(); + var migrations = _migrationTypes + .Select(m => ActivatorUtilities.CreateInstance(host.ServiceProvider, m)) + .OfType<IMigrationRoutine>() + .ToArray(); var migrationOptions = ((IConfigurationManager)host.ServerConfigurationManager).GetConfiguration<MigrationOptions>(MigrationsListStore.StoreKey); if (!host.ServerConfigurationManager.Configuration.IsStartupWizardCompleted && migrationOptions.Applied.Count == 0) @@ -34,16 +41,16 @@ namespace Jellyfin.Server.Migrations // If startup wizard is not finished, this is a fresh install. // Don't run any migrations, just mark all of them as applied. logger.LogInformation("Marking all known migrations as applied because this is a fresh install"); - migrationOptions.Applied.AddRange(Migrations.Select(m => (m.Id, m.Name))); + migrationOptions.Applied.AddRange(migrations.Select(m => (m.Id, m.Name))); host.ServerConfigurationManager.SaveConfiguration(MigrationsListStore.StoreKey, migrationOptions); return; } var appliedMigrationIds = migrationOptions.Applied.Select(m => m.Id).ToHashSet(); - for (var i = 0; i < Migrations.Length; i++) + for (var i = 0; i < migrations.Length; i++) { - var migrationRoutine = Migrations[i]; + var migrationRoutine = migrations[i]; if (appliedMigrationIds.Contains(migrationRoutine.Id)) { logger.LogDebug("Skipping migration '{Name}' since it is already applied", migrationRoutine.Name); @@ -54,7 +61,7 @@ namespace Jellyfin.Server.Migrations try { - migrationRoutine.Perform(host, logger); + migrationRoutine.Perform(); } catch (Exception ex) { diff --git a/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs b/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs index 3bc32c047..89514c89b 100644 --- a/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs +++ b/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs @@ -36,6 +36,13 @@ namespace Jellyfin.Server.Migrations.Routines @"{""Serilog"":{""MinimumLevel"":""Information"",""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}""}},{""Name"":""Async"",""Args"":{""configure"":[{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""retainedFileCountLimit"":3,""rollOnFileSizeLimit"":true,""fileSizeLimitBytes"":100000000,""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}:{Message}{NewLine}{Exception}""}}]}}],""Enrich"":[""FromLogContext"",""WithThreadId""]}}", }; + private readonly IApplicationPaths _appPaths; + + public CreateUserLoggingConfigFile(IApplicationPaths appPaths) + { + _appPaths = appPaths; + } + /// <inheritdoc/> public Guid Id => Guid.Parse("{EF103419-8451-40D8-9F34-D1A8E93A1679}"); @@ -43,9 +50,9 @@ namespace Jellyfin.Server.Migrations.Routines public string Name => "CreateLoggingConfigHeirarchy"; /// <inheritdoc/> - public void Perform(CoreAppHost host, ILogger logger) + public void Perform() { - var logDirectory = host.Resolve<IApplicationPaths>().ConfigurationDirectoryPath; + var logDirectory = _appPaths.ConfigurationDirectoryPath; var existingConfigPath = Path.Combine(logDirectory, "logging.json"); // If the existing logging.json config file is unmodified, then 'reset' it by moving it to 'logging.old.json' diff --git a/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs b/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs index 6f8e4a8ff..b2e957d5b 100644 --- a/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs +++ b/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs @@ -10,6 +10,15 @@ namespace Jellyfin.Server.Migrations.Routines /// </summary> internal class DisableTranscodingThrottling : IMigrationRoutine { + private readonly ILogger _logger; + private readonly IConfigurationManager _configManager; + + public DisableTranscodingThrottling(ILogger<DisableTranscodingThrottling> logger, IConfigurationManager configManager) + { + _logger = logger; + _configManager = configManager; + } + /// <inheritdoc/> public Guid Id => Guid.Parse("{4124C2CD-E939-4FFB-9BE9-9B311C413638}"); @@ -17,16 +26,16 @@ namespace Jellyfin.Server.Migrations.Routines public string Name => "DisableTranscodingThrottling"; /// <inheritdoc/> - public void Perform(CoreAppHost host, ILogger logger) + public void Perform() { // Set EnableThrottling to false since it wasn't used before and may introduce issues - var encoding = ((IConfigurationManager)host.ServerConfigurationManager).GetConfiguration<EncodingOptions>("encoding"); + var encoding = _configManager.GetConfiguration<EncodingOptions>("encoding"); if (encoding.EnableThrottling) { - logger.LogInformation("Disabling transcoding throttling during migration"); + _logger.LogInformation("Disabling transcoding throttling during migration"); encoding.EnableThrottling = false; - host.ServerConfigurationManager.SaveConfiguration("encoding", encoding); + _configManager.SaveConfiguration("encoding", encoding); } } } diff --git a/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs new file mode 100644 index 000000000..b3cc29708 --- /dev/null +++ b/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Emby.Server.Implementations.Data; +using Jellyfin.Data.Entities; +using Jellyfin.Server.Implementations; +using MediaBrowser.Controller; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using SQLitePCL.pretty; + +namespace Jellyfin.Server.Migrations.Routines +{ + /// <summary> + /// The migration routine for migrating the activity log database to EF Core. + /// </summary> + public class MigrateActivityLogDb : IMigrationRoutine + { + private const string DbFilename = "activitylog.db"; + + private readonly ILogger<MigrateActivityLogDb> _logger; + private readonly JellyfinDbProvider _provider; + private readonly IServerApplicationPaths _paths; + + /// <summary> + /// Initializes a new instance of the <see cref="MigrateActivityLogDb"/> class. + /// </summary> + /// <param name="logger">The logger.</param> + /// <param name="paths">The server application paths.</param> + /// <param name="provider">The database provider.</param> + public MigrateActivityLogDb(ILogger<MigrateActivityLogDb> logger, IServerApplicationPaths paths, JellyfinDbProvider provider) + { + _logger = logger; + _provider = provider; + _paths = paths; + } + + /// <inheritdoc/> + public Guid Id => Guid.Parse("3793eb59-bc8c-456c-8b9f-bd5a62a42978"); + + /// <inheritdoc/> + public string Name => "MigrateActivityLogDatabase"; + + /// <inheritdoc/> + public void Perform() + { + var logLevelDictionary = new Dictionary<string, LogLevel>(StringComparer.OrdinalIgnoreCase) + { + { "None", LogLevel.None }, + { "Trace", LogLevel.Trace }, + { "Debug", LogLevel.Debug }, + { "Information", LogLevel.Information }, + { "Info", LogLevel.Information }, + { "Warn", LogLevel.Warning }, + { "Warning", LogLevel.Warning }, + { "Error", LogLevel.Error }, + { "Critical", LogLevel.Critical } + }; + + var dataPath = _paths.DataPath; + using (var connection = SQLite3.Open( + Path.Combine(dataPath, DbFilename), + ConnectionFlags.ReadOnly, + null)) + { + _logger.LogWarning("Migrating the activity database may take a while, do not stop Jellyfin."); + using var dbContext = _provider.CreateContext(); + + var queryResult = connection.Query("SELECT * FROM ActivityLog ORDER BY Id ASC"); + + // Make sure that the database is empty in case of failed migration due to power outages, etc. + dbContext.ActivityLogs.RemoveRange(dbContext.ActivityLogs); + dbContext.SaveChanges(); + // Reset the autoincrement counter + dbContext.Database.ExecuteSqlRaw("UPDATE sqlite_sequence SET seq = 0 WHERE name = 'ActivityLog';"); + dbContext.SaveChanges(); + + var newEntries = queryResult.Select(entry => + { + if (!logLevelDictionary.TryGetValue(entry[8].ToString(), out var severity)) + { + severity = LogLevel.Trace; + } + + var newEntry = new ActivityLog( + entry[1].ToString(), + entry[4].ToString(), + entry[6].SQLiteType == SQLiteType.Null ? Guid.Empty : Guid.Parse(entry[6].ToString())) + { + DateCreated = entry[7].ReadDateTime(), + LogSeverity = severity + }; + + if (entry[2].SQLiteType != SQLiteType.Null) + { + newEntry.Overview = entry[2].ToString(); + } + + if (entry[3].SQLiteType != SQLiteType.Null) + { + newEntry.ShortOverview = entry[3].ToString(); + } + + if (entry[5].SQLiteType != SQLiteType.Null) + { + newEntry.ItemId = entry[5].ToString(); + } + + return newEntry; + }); + + dbContext.ActivityLogs.AddRange(newEntries); + dbContext.SaveChanges(); + } + + try + { + File.Move(Path.Combine(dataPath, DbFilename), Path.Combine(dataPath, DbFilename + ".old")); + + var journalPath = Path.Combine(dataPath, DbFilename + "-journal"); + if (File.Exists(journalPath)) + { + File.Move(journalPath, Path.Combine(dataPath, DbFilename + ".old-journal")); + } + } + catch (IOException e) + { + _logger.LogError(e, "Error renaming legacy activity log database to 'activitylog.db.old'"); + } + } + } +} diff --git a/Jellyfin.Server/Migrations/Routines/RemoveDuplicateExtras.cs b/Jellyfin.Server/Migrations/Routines/RemoveDuplicateExtras.cs new file mode 100644 index 000000000..e95536388 --- /dev/null +++ b/Jellyfin.Server/Migrations/Routines/RemoveDuplicateExtras.cs @@ -0,0 +1,79 @@ +using System; +using System.Globalization; +using System.IO; + +using MediaBrowser.Controller; +using Microsoft.Extensions.Logging; +using SQLitePCL.pretty; + +namespace Jellyfin.Server.Migrations.Routines +{ + /// <summary> + /// Remove duplicate entries which were caused by a bug where a file was considered to be an "Extra" to itself. + /// </summary> + internal class RemoveDuplicateExtras : IMigrationRoutine + { + private const string DbFilename = "library.db"; + private readonly ILogger _logger; + private readonly IServerApplicationPaths _paths; + + public RemoveDuplicateExtras(ILogger<RemoveDuplicateExtras> logger, IServerApplicationPaths paths) + { + _logger = logger; + _paths = paths; + } + + /// <inheritdoc/> + public Guid Id => Guid.Parse("{ACBE17B7-8435-4A83-8B64-6FCF162CB9BD}"); + + /// <inheritdoc/> + public string Name => "RemoveDuplicateExtras"; + + /// <inheritdoc/> + public void Perform() + { + var dataPath = _paths.DataPath; + var dbPath = Path.Combine(dataPath, DbFilename); + using (var connection = SQLite3.Open( + dbPath, + ConnectionFlags.ReadWrite, + null)) + { + // Query the database for the ids of duplicate extras + var queryResult = connection.Query("SELECT t1.Path FROM TypedBaseItems AS t1, TypedBaseItems AS t2 WHERE t1.Path=t2.Path AND t1.Type!=t2.Type AND t1.Type='MediaBrowser.Controller.Entities.Video'"); + var bads = string.Join(", ", queryResult.SelectScalarString()); + + // Do nothing if no duplicate extras were detected + if (bads.Length == 0) + { + _logger.LogInformation("No duplicate extras detected, skipping migration."); + return; + } + + // Back up the database before deleting any entries + for (int i = 1; ; i++) + { + var bakPath = string.Format(CultureInfo.InvariantCulture, "{0}.bak{1}", dbPath, i); + if (!File.Exists(bakPath)) + { + try + { + File.Copy(dbPath, bakPath); + _logger.LogInformation("Library database backed up to {BackupPath}", bakPath); + break; + } + catch (Exception ex) + { + _logger.LogError(ex, "Cannot make a backup of {Library} at path {BackupPath}", DbFilename, bakPath); + throw; + } + } + } + + // Delete all duplicate extras + _logger.LogInformation("Removing found duplicated extras for the following items: {DuplicateExtras}", bads); + connection.Execute("DELETE FROM TypedBaseItems WHERE rowid IN (SELECT t1.rowid FROM TypedBaseItems AS t1, TypedBaseItems AS t2 WHERE t1.Path=t2.Path AND t1.Type!=t2.Type AND t1.Type='MediaBrowser.Controller.Entities.Video')"); + } + } + } +} diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 9635cc6ec..b9895386f 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -10,14 +10,11 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using CommandLine; -using Emby.Drawing; using Emby.Server.Implementations; using Emby.Server.Implementations.HttpServer; using Emby.Server.Implementations.IO; using Emby.Server.Implementations.Networking; -using Jellyfin.Drawing.Skia; using MediaBrowser.Common.Configuration; -using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Extensions; using MediaBrowser.WebDashboard.Api; using Microsoft.AspNetCore.Hosting; @@ -161,23 +158,7 @@ namespace Jellyfin.Server ApplicationHost.LogEnvironmentInfo(_logger, appPaths); - // Make sure we have all the code pages we can get - // Ref: https://docs.microsoft.com/en-us/dotnet/api/system.text.codepagesencodingprovider.instance?view=netcore-3.0#remarks - Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); - - // Increase the max http request limit - // The default connection limit is 10 for ASP.NET hosted applications and 2 for all others. - ServicePointManager.DefaultConnectionLimit = Math.Max(96, ServicePointManager.DefaultConnectionLimit); - - // Disable the "Expect: 100-Continue" header by default - // http://stackoverflow.com/questions/566437/http-post-returns-the-error-417-expectation-failed-c - ServicePointManager.Expect100Continue = false; - - Batteries_V2.Init(); - if (raw.sqlite3_enable_shared_cache(1) != raw.SQLITE_OK) - { - _logger.LogWarning("Failed to enable shared cache for SQLite"); - } + PerformStaticInitialization(); var appHost = new CoreAppHost( appPaths, @@ -205,7 +186,7 @@ namespace Jellyfin.Server ServiceCollection serviceCollection = new ServiceCollection(); appHost.Init(serviceCollection); - var webHost = CreateWebHostBuilder(appHost, serviceCollection, options, startupConfig, appPaths).Build(); + var webHost = new WebHostBuilder().ConfigureWebHostBuilder(appHost, serviceCollection, options, startupConfig, appPaths).Build(); // Re-use the web host service provider in the app host since ASP.NET doesn't allow a custom service collection. appHost.ServiceProvider = webHost.Services; @@ -250,14 +231,49 @@ namespace Jellyfin.Server } } - private static IWebHostBuilder CreateWebHostBuilder( + /// <summary> + /// Call static initialization methods for the application. + /// </summary> + public static void PerformStaticInitialization() + { + // Make sure we have all the code pages we can get + // Ref: https://docs.microsoft.com/en-us/dotnet/api/system.text.codepagesencodingprovider.instance?view=netcore-3.0#remarks + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + + // Increase the max http request limit + // The default connection limit is 10 for ASP.NET hosted applications and 2 for all others. + ServicePointManager.DefaultConnectionLimit = Math.Max(96, ServicePointManager.DefaultConnectionLimit); + + // Disable the "Expect: 100-Continue" header by default + // http://stackoverflow.com/questions/566437/http-post-returns-the-error-417-expectation-failed-c + ServicePointManager.Expect100Continue = false; + + Batteries_V2.Init(); + if (raw.sqlite3_enable_shared_cache(1) != raw.SQLITE_OK) + { + _logger.LogWarning("Failed to enable shared cache for SQLite"); + } + } + + /// <summary> + /// Configure the web host builder. + /// </summary> + /// <param name="builder">The builder to configure.</param> + /// <param name="appHost">The application host.</param> + /// <param name="serviceCollection">The application service collection.</param> + /// <param name="commandLineOpts">The command line options passed to the application.</param> + /// <param name="startupConfig">The application configuration.</param> + /// <param name="appPaths">The application paths.</param> + /// <returns>The configured web host builder.</returns> + public static IWebHostBuilder ConfigureWebHostBuilder( + this IWebHostBuilder builder, ApplicationHost appHost, IServiceCollection serviceCollection, StartupOptions commandLineOpts, IConfiguration startupConfig, IApplicationPaths appPaths) { - return new WebHostBuilder() + return builder .UseKestrel((builderContext, options) => { var addresses = appHost.ServerConfigurationManager @@ -278,8 +294,7 @@ namespace Jellyfin.Server { _logger.LogInformation("Kestrel listening on {IpAddress}", address); options.Listen(address, appHost.HttpPort); - - if (appHost.EnableHttps && appHost.Certificate != null) + if (appHost.ListenWithHttps) { options.Listen(address, appHost.HttpsPort, listenOptions => { @@ -289,11 +304,18 @@ namespace Jellyfin.Server } else if (builderContext.HostingEnvironment.IsDevelopment()) { - options.Listen(address, appHost.HttpsPort, listenOptions => + try { - listenOptions.UseHttps(); - listenOptions.Protocols = HttpProtocols.Http1AndHttp2; - }); + options.Listen(address, appHost.HttpsPort, listenOptions => + { + listenOptions.UseHttps(); + listenOptions.Protocols = HttpProtocols.Http1AndHttp2; + }); + } + catch (InvalidOperationException ex) + { + _logger.LogError(ex, "Failed to listen to HTTPS using the ASP.NET Core HTTPS development certificate. Please ensure it has been installed and set as trusted."); + } } } } @@ -302,7 +324,7 @@ namespace Jellyfin.Server _logger.LogInformation("Kestrel listening on all interfaces"); options.ListenAnyIP(appHost.HttpPort); - if (appHost.EnableHttps && appHost.Certificate != null) + if (appHost.ListenWithHttps) { options.ListenAnyIP(appHost.HttpsPort, listenOptions => { @@ -312,11 +334,18 @@ namespace Jellyfin.Server } else if (builderContext.HostingEnvironment.IsDevelopment()) { - options.ListenAnyIP(appHost.HttpsPort, listenOptions => + try { - listenOptions.UseHttps(); - listenOptions.Protocols = HttpProtocols.Http1AndHttp2; - }); + options.ListenAnyIP(appHost.HttpsPort, listenOptions => + { + listenOptions.UseHttps(); + listenOptions.Protocols = HttpProtocols.Http1AndHttp2; + }); + } + catch (InvalidOperationException ex) + { + _logger.LogError(ex, "Failed to listen to HTTPS using the ASP.NET Core HTTPS development certificate. Please ensure it has been installed and set as trusted."); + } } } }) @@ -496,7 +525,9 @@ namespace Jellyfin.Server /// Initialize the logging configuration file using the bundled resource file as a default if it doesn't exist /// already. /// </summary> - private static async Task InitLoggingConfigFile(IApplicationPaths appPaths) + /// <param name="appPaths">The application paths.</param> + /// <returns>A task representing the creation of the configuration file, or a completed task if the file already exists.</returns> + public static async Task InitLoggingConfigFile(IApplicationPaths appPaths) { // Do nothing if the config file already exists string configPath = Path.Combine(appPaths.ConfigurationDirectoryPath, LoggingConfigFileDefault); @@ -516,7 +547,13 @@ namespace Jellyfin.Server await resource.CopyToAsync(dst).ConfigureAwait(false); } - private static IConfiguration CreateAppConfiguration(StartupOptions commandLineOpts, IApplicationPaths appPaths) + /// <summary> + /// Create the application configuration. + /// </summary> + /// <param name="commandLineOpts">The command line options passed to the program.</param> + /// <param name="appPaths">The application paths.</param> + /// <returns>The application configuration.</returns> + public static IConfiguration CreateAppConfiguration(StartupOptions commandLineOpts, IApplicationPaths appPaths) { return new ConfigurationBuilder() .ConfigureAppConfiguration(commandLineOpts, appPaths) diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs index 8bcfd1350..5f9a5c161 100644 --- a/Jellyfin.Server/Startup.cs +++ b/Jellyfin.Server/Startup.cs @@ -64,7 +64,6 @@ namespace Jellyfin.Server app.UseResponseCompression(); // TODO app.UseMiddleware<WebSocketMiddleware>(); - app.Use(serverApplicationHost.ExecuteWebsocketHandlerAsync); // TODO use when old API is removed: app.UseAuthentication(); app.UseJellyfinApiSwagger(); |
