aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNiels van Velzen <nielsvanvelzen@users.noreply.github.com>2025-07-31 16:07:48 +0200
committerGitHub <noreply@github.com>2025-07-31 16:07:48 +0200
commitb00e381109d1d3b1d83da0ed678b8b27d70341d1 (patch)
tree013e66787574570a0b15b0b9295c94f1ea29b3a6
parentb8fb8bd608bc775d97b9bb55e2bbe20c7f6e6db9 (diff)
parentc8d2f436608c631e94461e70c3a7511031d32f58 (diff)
Merge pull request #14554 from JPVenson/feature/FixIsFolderMigration
Also migrate IsFolder
-rw-r--r--Jellyfin.Server/Migrations/Routines/MigrateLibraryDb.cs10
-rw-r--r--Jellyfin.Server/Migrations/Routines/ReseedFolderFlag.cs74
2 files changed, 83 insertions, 1 deletions
diff --git a/Jellyfin.Server/Migrations/Routines/MigrateLibraryDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateLibraryDb.cs
index e25c52786..e04a2737a 100644
--- a/Jellyfin.Server/Migrations/Routines/MigrateLibraryDb.cs
+++ b/Jellyfin.Server/Migrations/Routines/MigrateLibraryDb.cs
@@ -90,6 +90,9 @@ internal class MigrateLibraryDb : IDatabaseMigrationRoutine
operation.JellyfinDbContext.AncestorIds.ExecuteDelete();
}
+ // notify the other migration to just silently abort because the fix has been applied here already.
+ ReseedFolderFlag.RerunGuardFlag = true;
+
var legacyBaseItemWithUserKeys = new Dictionary<string, BaseItemEntity>();
connection.Open();
@@ -105,7 +108,7 @@ internal class MigrateLibraryDb : IDatabaseMigrationRoutine
Audio, ExternalServiceId, IsInMixedFolder, DateLastSaved, LockedFields, Studios, Tags, TrailerTypes, OriginalTitle, PrimaryVersionId,
DateLastMediaAdded, Album, LUFS, NormalizationGain, CriticRating, IsVirtualItem, SeriesName, UserDataKey, SeasonName, SeasonId, SeriesId,
PresentationUniqueKey, InheritedParentalRatingValue, ExternalSeriesId, Tagline, ProviderIds, Images, ProductionLocations, ExtraIds, TotalBitrate,
- ExtraType, Artists, AlbumArtists, ExternalId, SeriesPresentationUniqueKey, ShowId, OwnerId, MediaType, SortName, CleanName, UnratedType FROM TypedBaseItems
+ ExtraType, Artists, AlbumArtists, ExternalId, SeriesPresentationUniqueKey, ShowId, OwnerId, MediaType, SortName, CleanName, UnratedType, IsFolder FROM TypedBaseItems
""";
using (new TrackedMigrationStep("Loading TypedBaseItems", _logger))
{
@@ -1167,6 +1170,11 @@ internal class MigrateLibraryDb : IDatabaseMigrationRoutine
entity.UnratedType = unratedType;
}
+ if (reader.TryGetBoolean(index++, out var isFolder))
+ {
+ entity.IsFolder = isFolder;
+ }
+
var baseItem = BaseItemRepository.DeserializeBaseItem(entity, _logger, null, false);
var dataKeys = baseItem.GetUserDataKeys();
userDataKeys.AddRange(dataKeys);
diff --git a/Jellyfin.Server/Migrations/Routines/ReseedFolderFlag.cs b/Jellyfin.Server/Migrations/Routines/ReseedFolderFlag.cs
new file mode 100644
index 000000000..502763ac0
--- /dev/null
+++ b/Jellyfin.Server/Migrations/Routines/ReseedFolderFlag.cs
@@ -0,0 +1,74 @@
+#pragma warning disable RS0030 // Do not use banned APIs
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Emby.Server.Implementations.Data;
+using Jellyfin.Database.Implementations;
+using Jellyfin.Server.ServerSetupApp;
+using MediaBrowser.Controller;
+using Microsoft.Data.Sqlite;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Logging;
+
+namespace Jellyfin.Server.Migrations.Routines;
+
+[JellyfinMigration("2025-07-30T21:50:00", nameof(ReseedFolderFlag))]
+[JellyfinMigrationBackup(JellyfinDb = true)]
+internal class ReseedFolderFlag : IAsyncMigrationRoutine
+{
+ private const string DbFilename = "library.db.old";
+
+ private readonly IStartupLogger _logger;
+ private readonly IServerApplicationPaths _paths;
+ private readonly IDbContextFactory<JellyfinDbContext> _provider;
+
+ public ReseedFolderFlag(
+ IStartupLogger<MigrateLibraryDb> startupLogger,
+ IDbContextFactory<JellyfinDbContext> provider,
+ IServerApplicationPaths paths)
+ {
+ _logger = startupLogger;
+ _provider = provider;
+ _paths = paths;
+ }
+
+ internal static bool RerunGuardFlag { get; set; } = false;
+
+ public async Task PerformAsync(CancellationToken cancellationToken)
+ {
+ if (RerunGuardFlag)
+ {
+ _logger.LogInformation("Migration is skipped because it does not apply.");
+ return;
+ }
+
+ _logger.LogInformation("Migrating the IsFolder flag from library.db.old may take a while, do not stop Jellyfin.");
+
+ var dataPath = _paths.DataPath;
+ var libraryDbPath = Path.Combine(dataPath, DbFilename);
+ if (!File.Exists(libraryDbPath))
+ {
+ _logger.LogError("Cannot migrate IsFolder flag from {LibraryDb} as it does not exist. This migration expects the MigrateLibraryDb to run first.", libraryDbPath);
+ return;
+ }
+
+ var dbContext = await _provider.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
+ await using (dbContext.ConfigureAwait(false))
+ {
+ using var connection = new SqliteConnection($"Filename={libraryDbPath};Mode=ReadOnly");
+ var queryResult = connection.Query(
+ """
+ SELECT guid FROM TypedBaseItems
+ WHERE IsFolder = true
+ """)
+ .Select(entity => entity.GetGuid(0))
+ .ToList();
+ _logger.LogInformation("Migrating the IsFolder flag for {Count} items.", queryResult.Count);
+ foreach (var id in queryResult)
+ {
+ await dbContext.BaseItems.Where(e => e.Id == id).ExecuteUpdateAsync(e => e.SetProperty(f => f.IsFolder, true), cancellationToken).ConfigureAwait(false);
+ }
+ }
+ }
+}