aboutsummaryrefslogtreecommitdiff
path: root/Jellyfin.Server/Migrations/Routines
diff options
context:
space:
mode:
Diffstat (limited to 'Jellyfin.Server/Migrations/Routines')
-rw-r--r--Jellyfin.Server/Migrations/Routines/CleanMusicArtist.cs47
-rw-r--r--Jellyfin.Server/Migrations/Routines/DisableLegacyAuthorization.cs32
-rw-r--r--Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs18
-rw-r--r--Jellyfin.Server/Migrations/Routines/MigrateAuthenticationDb.cs21
-rw-r--r--Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs18
-rw-r--r--Jellyfin.Server/Migrations/Routines/MigrateKeyframeData.cs25
-rw-r--r--Jellyfin.Server/Migrations/Routines/MigrateLibraryDb.cs2
-rw-r--r--Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs19
-rw-r--r--Jellyfin.Server/Migrations/Routines/MoveExtractedFiles.cs24
9 files changed, 197 insertions, 9 deletions
diff --git a/Jellyfin.Server/Migrations/Routines/CleanMusicArtist.cs b/Jellyfin.Server/Migrations/Routines/CleanMusicArtist.cs
new file mode 100644
index 0000000000..d5c5f3d929
--- /dev/null
+++ b/Jellyfin.Server/Migrations/Routines/CleanMusicArtist.cs
@@ -0,0 +1,47 @@
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
+using Jellyfin.Database.Implementations;
+using Jellyfin.Server.ServerSetupApp;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Logging;
+
+namespace Jellyfin.Server.Migrations.Routines;
+
+/// <summary>
+/// Cleans up all Music artists that have been migrated in the 10.11 RC migrations.
+/// </summary>
+[JellyfinMigration("2025-10-09T20:00:00", nameof(CleanMusicArtist))]
+[JellyfinMigrationBackup(JellyfinDb = true)]
+public class CleanMusicArtist : IAsyncMigrationRoutine
+{
+ private readonly IStartupLogger<CleanMusicArtist> _startupLogger;
+ private readonly IDbContextFactory<JellyfinDbContext> _dbContextFactory;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CleanMusicArtist"/> class.
+ /// </summary>
+ /// <param name="startupLogger">The startup logger.</param>
+ /// <param name="dbContextFactory">The Db context factory.</param>
+ public CleanMusicArtist(IStartupLogger<CleanMusicArtist> startupLogger, IDbContextFactory<JellyfinDbContext> dbContextFactory)
+ {
+ _startupLogger = startupLogger;
+ _dbContextFactory = dbContextFactory;
+ }
+
+ /// <inheritdoc/>
+ public async Task PerformAsync(CancellationToken cancellationToken)
+ {
+ var context = await _dbContextFactory.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
+ await using (context.ConfigureAwait(false))
+ {
+ var peoples = context.Peoples.Where(e => e.PersonType == nameof(PersonKind.Artist) || e.PersonType == nameof(PersonKind.AlbumArtist));
+ _startupLogger.LogInformation("Delete {Number} Artist and Album Artist person types from db", await peoples.CountAsync(cancellationToken).ConfigureAwait(false));
+
+ await peoples
+ .ExecuteDeleteAsync(cancellationToken)
+ .ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server/Migrations/Routines/DisableLegacyAuthorization.cs b/Jellyfin.Server/Migrations/Routines/DisableLegacyAuthorization.cs
new file mode 100644
index 0000000000..6edfcbcfd5
--- /dev/null
+++ b/Jellyfin.Server/Migrations/Routines/DisableLegacyAuthorization.cs
@@ -0,0 +1,32 @@
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Configuration;
+
+namespace Jellyfin.Server.Migrations.Routines;
+
+/// <summary>
+/// Migration to disable legacy authorization in the system config.
+/// </summary>
+[JellyfinMigration("2025-11-18T16:00:00", nameof(DisableLegacyAuthorization))]
+public class DisableLegacyAuthorization : IAsyncMigrationRoutine
+{
+ private readonly IServerConfigurationManager _serverConfigurationManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DisableLegacyAuthorization"/> class.
+ /// </summary>
+ /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
+ public DisableLegacyAuthorization(IServerConfigurationManager serverConfigurationManager)
+ {
+ _serverConfigurationManager = serverConfigurationManager;
+ }
+
+ /// <inheritdoc />
+ public Task PerformAsync(CancellationToken cancellationToken)
+ {
+ _serverConfigurationManager.Configuration.EnableLegacyAuthorization = false;
+ _serverConfigurationManager.SaveConfiguration();
+
+ return Task.CompletedTask;
+ }
+}
diff --git a/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs
index a954d307e1..8c8563190d 100644
--- a/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs
+++ b/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs
@@ -55,9 +55,25 @@ namespace Jellyfin.Server.Migrations.Routines
};
var dataPath = _paths.DataPath;
- using (var connection = new SqliteConnection($"Filename={Path.Combine(dataPath, DbFilename)}"))
+ var activityLogPath = Path.Combine(dataPath, DbFilename);
+ if (!File.Exists(activityLogPath))
+ {
+ _logger.LogWarning("{ActivityLogDb} doesn't exist, nothing to migrate", activityLogPath);
+ return;
+ }
+
+ using (var connection = new SqliteConnection($"Filename={activityLogPath}"))
{
connection.Open();
+ var tableQuery = connection.Query("SELECT count(*) FROM sqlite_master WHERE type='table' AND name='ActivityLog';");
+ foreach (var row in tableQuery)
+ {
+ if (row.GetInt32(0) == 0)
+ {
+ _logger.LogWarning("Table 'ActivityLog' doesn't exist in {ActivityLogPath}, nothing to migrate", activityLogPath);
+ return;
+ }
+ }
using var userDbConnection = new SqliteConnection($"Filename={Path.Combine(dataPath, "users.db")}");
userDbConnection.Open();
diff --git a/Jellyfin.Server/Migrations/Routines/MigrateAuthenticationDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateAuthenticationDb.cs
index c6699c21df..0de775e03a 100644
--- a/Jellyfin.Server/Migrations/Routines/MigrateAuthenticationDb.cs
+++ b/Jellyfin.Server/Migrations/Routines/MigrateAuthenticationDb.cs
@@ -50,9 +50,28 @@ namespace Jellyfin.Server.Migrations.Routines
public void Perform()
{
var dataPath = _appPaths.DataPath;
- using (var connection = new SqliteConnection($"Filename={Path.Combine(dataPath, DbFilename)}"))
+ var dbFilePath = Path.Combine(dataPath, DbFilename);
+
+ if (!File.Exists(dbFilePath))
+ {
+ _logger.LogWarning("{Path} doesn't exist, nothing to migrate", dbFilePath);
+ return;
+ }
+
+ using (var connection = new SqliteConnection($"Filename={dbFilePath}"))
{
connection.Open();
+
+ var tableQuery = connection.Query("SELECT count(*) FROM sqlite_master WHERE type='table' AND name='Tokens';");
+ foreach (var row in tableQuery)
+ {
+ if (row.GetInt32(0) == 0)
+ {
+ _logger.LogWarning("Table 'Tokens' doesn't exist in {Path}, nothing to migrate", dbFilePath);
+ return;
+ }
+ }
+
using var dbContext = _dbProvider.CreateDbContext();
var authenticatedDevices = connection.Query("SELECT * FROM Tokens");
diff --git a/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs
index 0d9952ce97..ffd06fea0d 100644
--- a/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs
+++ b/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs
@@ -78,9 +78,27 @@ namespace Jellyfin.Server.Migrations.Routines
var displayPrefs = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var customDisplayPrefs = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var dbFilePath = Path.Combine(_paths.DataPath, DbFilename);
+
+ if (!File.Exists(dbFilePath))
+ {
+ _logger.LogWarning("{Path} doesn't exist, nothing to migrate", dbFilePath);
+ return;
+ }
+
using (var connection = new SqliteConnection($"Filename={dbFilePath}"))
{
connection.Open();
+
+ var tableQuery = connection.Query("SELECT count(*) FROM sqlite_master WHERE type='table' AND name='userdisplaypreferences';");
+ foreach (var row in tableQuery)
+ {
+ if (row.GetInt32(0) == 0)
+ {
+ _logger.LogWarning("Table 'userdisplaypreferences' doesn't exist in {Path}, nothing to migrate", dbFilePath);
+ return;
+ }
+ }
+
using var dbContext = _provider.CreateDbContext();
var results = connection.Query("SELECT * FROM userdisplaypreferences");
diff --git a/Jellyfin.Server/Migrations/Routines/MigrateKeyframeData.cs b/Jellyfin.Server/Migrations/Routines/MigrateKeyframeData.cs
index c199ee4d6b..aa55309264 100644
--- a/Jellyfin.Server/Migrations/Routines/MigrateKeyframeData.cs
+++ b/Jellyfin.Server/Migrations/Routines/MigrateKeyframeData.cs
@@ -122,6 +122,16 @@ public class MigrateKeyframeData : IDatabaseMigrationRoutine
{
lastWriteTimeUtc = File.GetLastWriteTimeUtc(filePath);
}
+ catch (ArgumentOutOfRangeException e)
+ {
+ _logger.LogDebug("Skipping {Path}: {Exception}", filePath, e.Message);
+ return null;
+ }
+ catch (UnauthorizedAccessException e)
+ {
+ _logger.LogDebug("Skipping {Path}: {Exception}", filePath, e.Message);
+ return null;
+ }
catch (IOException e)
{
_logger.LogDebug("Skipping {Path}: {Exception}", filePath, e.Message);
@@ -135,14 +145,21 @@ public class MigrateKeyframeData : IDatabaseMigrationRoutine
return Path.Join(keyframeCachePath, prefix, filename);
}
- private static bool TryReadFromCache(string? cachePath, [NotNullWhen(true)] out MediaEncoding.Keyframes.KeyframeData? cachedResult)
+ private bool TryReadFromCache(string? cachePath, [NotNullWhen(true)] out MediaEncoding.Keyframes.KeyframeData? cachedResult)
{
if (File.Exists(cachePath))
{
- var bytes = File.ReadAllBytes(cachePath);
- cachedResult = JsonSerializer.Deserialize<MediaEncoding.Keyframes.KeyframeData>(bytes, _jsonOptions);
+ try
+ {
+ var bytes = File.ReadAllBytes(cachePath);
+ cachedResult = JsonSerializer.Deserialize<MediaEncoding.Keyframes.KeyframeData>(bytes, _jsonOptions);
- return cachedResult is not null;
+ return cachedResult is not null;
+ }
+ catch (JsonException jsonException)
+ {
+ _logger.LogWarning(jsonException, "Failed to read {Path}", cachePath);
+ }
}
cachedResult = null;
diff --git a/Jellyfin.Server/Migrations/Routines/MigrateLibraryDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateLibraryDb.cs
index b90da9f7d3..d221d18531 100644
--- a/Jellyfin.Server/Migrations/Routines/MigrateLibraryDb.cs
+++ b/Jellyfin.Server/Migrations/Routines/MigrateLibraryDb.cs
@@ -383,8 +383,6 @@ internal class MigrateLibraryDb : IDatabaseMigrationRoutine
});
}
- baseItemIds.Clear();
-
foreach (var item in peopleCache)
{
operation.JellyfinDbContext.Peoples.Add(item.Value.Person);
diff --git a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs
index e5584fb947..8c3361ee16 100644
--- a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs
+++ b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs
@@ -57,11 +57,28 @@ public class MigrateUserDb : IMigrationRoutine
public void Perform()
{
var dataPath = _paths.DataPath;
+ var userDbPath = Path.Combine(dataPath, DbFilename);
+ if (!File.Exists(userDbPath))
+ {
+ _logger.LogWarning("{UserDbPath} doesn't exist, nothing to migrate", userDbPath);
+ return;
+ }
+
_logger.LogInformation("Migrating the user database may take a while, do not stop Jellyfin.");
- using (var connection = new SqliteConnection($"Filename={Path.Combine(dataPath, DbFilename)}"))
+ using (var connection = new SqliteConnection($"Filename={userDbPath}"))
{
connection.Open();
+ var tableQuery = connection.Query("SELECT count(*) FROM sqlite_master WHERE type='table' AND name='LocalUsersv2';");
+ foreach (var row in tableQuery)
+ {
+ if (row.GetInt32(0) == 0)
+ {
+ _logger.LogWarning("Table 'LocalUsersv2' doesn't exist in {UserDbPath}, nothing to migrate", userDbPath);
+ return;
+ }
+ }
+
using var dbContext = _provider.CreateDbContext();
var queryResult = connection.Query("SELECT * FROM LocalUsersv2");
diff --git a/Jellyfin.Server/Migrations/Routines/MoveExtractedFiles.cs b/Jellyfin.Server/Migrations/Routines/MoveExtractedFiles.cs
index 8b394dd7aa..fbf9c16377 100644
--- a/Jellyfin.Server/Migrations/Routines/MoveExtractedFiles.cs
+++ b/Jellyfin.Server/Migrations/Routines/MoveExtractedFiles.cs
@@ -224,6 +224,18 @@ public class MoveExtractedFiles : IAsyncMigrationRoutine
return null;
}
+ catch (UnauthorizedAccessException e)
+ {
+ _logger.LogDebug("Skipping subtitle at index {Index} for {Path}: {Exception}", attachmentStreamIndex, mediaPath, e.Message);
+
+ return null;
+ }
+ catch (ArgumentOutOfRangeException e)
+ {
+ _logger.LogDebug("Skipping attachment at index {Index} for {Path}: {Exception}", attachmentStreamIndex, mediaPath, e.Message);
+
+ return null;
+ }
filename = (mediaPath + attachmentStreamIndex.ToString(CultureInfo.InvariantCulture) + "_" + date.Value.Ticks.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("D", CultureInfo.InvariantCulture);
}
@@ -263,6 +275,18 @@ public class MoveExtractedFiles : IAsyncMigrationRoutine
{
date = File.GetLastWriteTimeUtc(path);
}
+ catch (ArgumentOutOfRangeException e)
+ {
+ _logger.LogDebug("Skipping subtitle at index {Index} for {Path}: {Exception}", streamIndex, path, e.Message);
+
+ return null;
+ }
+ catch (UnauthorizedAccessException e)
+ {
+ _logger.LogDebug("Skipping subtitle at index {Index} for {Path}: {Exception}", streamIndex, path, e.Message);
+
+ return null;
+ }
catch (IOException e)
{
_logger.LogDebug("Skipping subtitle at index {Index} for {Path}: {Exception}", streamIndex, path, e.Message);