aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJPVenson <github@jpb.email>2026-03-01 13:10:32 +0000
committerJPVenson <github@jpb.email>2026-03-01 13:10:32 +0000
commite70eaf8bc1470436fdafb7fef6e23898b4a3423b (patch)
tree1225e94b022dae4248b8af2f850ab35c185ecc14
parentf680495ca377b20488cc8133a054317d6daf48fc (diff)
Add startup mode to migrate or seed the database on cmd
-rw-r--r--Jellyfin.Server/Configuration/StartupMode.cs24
-rw-r--r--Jellyfin.Server/Migrations/JellyfinMigrationService.cs6
-rw-r--r--Jellyfin.Server/Program.cs28
-rw-r--r--Jellyfin.Server/StartupOptions.cs8
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs2
5 files changed, 54 insertions, 14 deletions
diff --git a/Jellyfin.Server/Configuration/StartupMode.cs b/Jellyfin.Server/Configuration/StartupMode.cs
new file mode 100644
index 0000000000..f4d63652d8
--- /dev/null
+++ b/Jellyfin.Server/Configuration/StartupMode.cs
@@ -0,0 +1,24 @@
+using MediaBrowser.Model.Configuration;
+
+namespace Jellyfin.Server.Configuration;
+
+/// <summary>
+/// Defines types for usage with the <see cref="StartupOptions.StartupMode"/>.
+/// </summary>
+public enum StartupMode
+{
+ /// <summary>
+ /// Default startup mode, runs the jellyfin server in normal operation.
+ /// </summary>
+ MediaServer = 0,
+
+ /// <summary>
+ /// Attempts to Migrate the selected database only then shuts down.
+ /// </summary>
+ MigrateDatabase = 1,
+
+ /// <summary>
+ /// Runs the Database seed function regardless of <see cref="BaseApplicationConfiguration.IsStartupWizardCompleted"/> state.
+ /// </summary>
+ SeedDatabase = 2
+}
diff --git a/Jellyfin.Server/Migrations/JellyfinMigrationService.cs b/Jellyfin.Server/Migrations/JellyfinMigrationService.cs
index 188d3c4a9a..9d70ef1208 100644
--- a/Jellyfin.Server/Migrations/JellyfinMigrationService.cs
+++ b/Jellyfin.Server/Migrations/JellyfinMigrationService.cs
@@ -90,7 +90,7 @@ internal class JellyfinMigrationService
private HashSet<MigrationStage> Migrations { get; set; }
- public async Task CheckFirstTimeRunOrMigration(IApplicationPaths appPaths)
+ public async Task CheckFirstTimeRunOrMigration(IApplicationPaths appPaths, StartupOptions startupOptions)
{
var logger = _startupLogger.With(_loggerFactory.CreateLogger<JellyfinMigrationService>()).BeginGroup($"Migration Startup");
logger.LogInformation("Initialise Migration service.");
@@ -98,9 +98,9 @@ internal class JellyfinMigrationService
var serverConfig = File.Exists(appPaths.SystemConfigurationFilePath)
? (ServerConfiguration)xmlSerializer.DeserializeFromFile(typeof(ServerConfiguration), appPaths.SystemConfigurationFilePath)!
: new ServerConfiguration();
- if (!serverConfig.IsStartupWizardCompleted)
+ if (!serverConfig.IsStartupWizardCompleted || startupOptions.StartupMode is Configuration.StartupMode.SeedDatabase)
{
- logger.LogInformation("System initialisation detected. Seed data.");
+ logger.LogInformation("System initialization detected. Seed data. Startup mode is: {StartupMode}", startupOptions.StartupMode ?? Configuration.StartupMode.MediaServer);
var flatApplyMigrations = Migrations.SelectMany(e => e.Where(f => !f.Metadata.RunMigrationOnSetup)).ToArray();
var dbContext = await _dbContextFactory.CreateDbContextAsync().ConfigureAwait(false);
diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs
index 93f71fdc69..e774c16510 100644
--- a/Jellyfin.Server/Program.cs
+++ b/Jellyfin.Server/Program.cs
@@ -137,7 +137,7 @@ namespace Jellyfin.Server
StartupHelpers.PerformStaticInitialization();
- await ApplyStartupMigrationAsync(appPaths, startupConfig).ConfigureAwait(false);
+ await ApplyStartupMigrationAsync(appPaths, startupConfig, options).ConfigureAwait(false);
do
{
@@ -214,13 +214,17 @@ namespace Jellyfin.Server
{
configurationCompleted = true;
await _setupServer!.StopAsync().ConfigureAwait(false);
- await _jellyfinHost.StartAsync().ConfigureAwait(false);
- if (!OperatingSystem.IsWindows() && startupConfig.UseUnixSocket())
+ if (options.StartupMode is null or Configuration.StartupMode.MediaServer)
{
- var socketPath = StartupHelpers.GetUnixSocketPath(startupConfig, appPaths);
+ await _jellyfinHost.StartAsync().ConfigureAwait(false);
- StartupHelpers.SetUnixSocketPermissions(startupConfig, socketPath, _logger);
+ if (!OperatingSystem.IsWindows() && startupConfig.UseUnixSocket())
+ {
+ var socketPath = StartupHelpers.GetUnixSocketPath(startupConfig, appPaths);
+
+ StartupHelpers.SetUnixSocketPermissions(startupConfig, socketPath, _logger);
+ }
}
}
catch (Exception)
@@ -229,11 +233,14 @@ namespace Jellyfin.Server
throw;
}
- await appHost.RunStartupTasksAsync().ConfigureAwait(false);
+ if (options.StartupMode is null or Configuration.StartupMode.MediaServer)
+ {
+ await appHost.RunStartupTasksAsync().ConfigureAwait(false);
+ _logger.LogInformation("Startup complete {Time:g}", Stopwatch.GetElapsedTime(_startTimestamp));
- _logger.LogInformation("Startup complete {Time:g}", Stopwatch.GetElapsedTime(_startTimestamp));
+ await _jellyfinHost.WaitForShutdownAsync().ConfigureAwait(false);
+ }
- await _jellyfinHost.WaitForShutdownAsync().ConfigureAwait(false);
_restartOnShutdown = appHost.ShouldRestart;
_restoreFromBackup = appHost.RestoreBackupPath;
}
@@ -274,8 +281,9 @@ namespace Jellyfin.Server
/// </remarks>
/// <param name="appPaths">Application Paths.</param>
/// <param name="startupConfig">Startup Config.</param>
+ /// <param name="startupOptions">The applications startup options.</param>
/// <returns>A task.</returns>
- public static async Task ApplyStartupMigrationAsync(ServerApplicationPaths appPaths, IConfiguration startupConfig)
+ public static async Task ApplyStartupMigrationAsync(ServerApplicationPaths appPaths, IConfiguration startupConfig, StartupOptions startupOptions)
{
_migrationLogger = StartupLogger.Logger.BeginGroup<JellyfinMigrationService>($"Migration Service");
var startupConfigurationManager = new ServerConfigurationManager(appPaths, _loggerFactory, new MyXmlSerializer());
@@ -293,7 +301,7 @@ namespace Jellyfin.Server
PrepareDatabaseProvider(startupService);
var jellyfinMigrationService = ActivatorUtilities.CreateInstance<JellyfinMigrationService>(startupService);
- await jellyfinMigrationService.CheckFirstTimeRunOrMigration(appPaths).ConfigureAwait(false);
+ await jellyfinMigrationService.CheckFirstTimeRunOrMigration(appPaths, startupOptions).ConfigureAwait(false);
await jellyfinMigrationService.MigrateStepAsync(Migrations.Stages.JellyfinMigrationStageTypes.PreInitialisation, startupService).ConfigureAwait(false);
}
diff --git a/Jellyfin.Server/StartupOptions.cs b/Jellyfin.Server/StartupOptions.cs
index 4890ccbb2e..4716bc1746 100644
--- a/Jellyfin.Server/StartupOptions.cs
+++ b/Jellyfin.Server/StartupOptions.cs
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using CommandLine;
using Emby.Server.Implementations;
+using Jellyfin.Server.Configuration;
using static MediaBrowser.Controller.Extensions.ConfigurationExtensions;
namespace Jellyfin.Server
@@ -80,6 +81,13 @@ namespace Jellyfin.Server
public string? RestoreArchive { get; set; }
/// <summary>
+ /// Gets or sets the mode of operation the server should perform when started.
+ /// Defaults to: <see cref="StartupMode.MediaServer"/>.
+ /// </summary>
+ [Option("mode", Required = false, HelpText = "Mode which selects what action the jellyfin server should perform when started.")]
+ public StartupMode? StartupMode { get; set; }
+
+ /// <summary>
/// Gets the command line options as a dictionary that can be used in the .NET configuration system.
/// </summary>
/// <returns>The configuration dictionary.</returns>
diff --git a/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs b/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs
index 0952fb8b63..54f443de2d 100644
--- a/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs
+++ b/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs
@@ -111,7 +111,7 @@ namespace Jellyfin.Server.Integration.Tests
var appHost = (TestAppHost)host.Services.GetRequiredService<IApplicationHost>();
appHost.ServiceProvider = host.Services;
var applicationPaths = appHost.ServiceProvider.GetRequiredService<IApplicationPaths>();
- Program.ApplyStartupMigrationAsync((ServerApplicationPaths)applicationPaths, appHost.ServiceProvider.GetRequiredService<IConfiguration>()).GetAwaiter().GetResult();
+ Program.ApplyStartupMigrationAsync((ServerApplicationPaths)applicationPaths, appHost.ServiceProvider.GetRequiredService<IConfiguration>(), new()).GetAwaiter().GetResult();
Program.ApplyCoreMigrationsAsync(appHost.ServiceProvider, Migrations.Stages.JellyfinMigrationStageTypes.CoreInitialisation).GetAwaiter().GetResult();
appHost.InitializeServices(Mock.Of<IConfiguration>()).GetAwaiter().GetResult();
Program.ApplyCoreMigrationsAsync(appHost.ServiceProvider, Migrations.Stages.JellyfinMigrationStageTypes.AppInitialisation).GetAwaiter().GetResult();