ReseedFolderFlag.cs 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. #pragma warning disable RS0030 // Do not use banned APIs
  2. using System.IO;
  3. using System.Linq;
  4. using System.Threading;
  5. using System.Threading.Tasks;
  6. using Emby.Server.Implementations.Data;
  7. using Jellyfin.Database.Implementations;
  8. using Jellyfin.Server.ServerSetupApp;
  9. using MediaBrowser.Controller;
  10. using Microsoft.Data.Sqlite;
  11. using Microsoft.EntityFrameworkCore;
  12. using Microsoft.Extensions.Logging;
  13. namespace Jellyfin.Server.Migrations.Routines;
  14. [JellyfinMigration("2025-07-30T21:50:00", nameof(ReseedFolderFlag))]
  15. [JellyfinMigrationBackup(JellyfinDb = true)]
  16. internal class ReseedFolderFlag : IAsyncMigrationRoutine
  17. {
  18. private const string DbFilename = "library.db.old";
  19. private readonly IStartupLogger _logger;
  20. private readonly IServerApplicationPaths _paths;
  21. private readonly IDbContextFactory<JellyfinDbContext> _provider;
  22. public ReseedFolderFlag(
  23. IStartupLogger<MigrateLibraryDb> startupLogger,
  24. IDbContextFactory<JellyfinDbContext> provider,
  25. IServerApplicationPaths paths)
  26. {
  27. _logger = startupLogger;
  28. _provider = provider;
  29. _paths = paths;
  30. }
  31. internal static bool RerunGuardFlag { get; set; } = false;
  32. public async Task PerformAsync(CancellationToken cancellationToken)
  33. {
  34. if (RerunGuardFlag)
  35. {
  36. _logger.LogInformation("Migration is skipped because it does not apply.");
  37. return;
  38. }
  39. _logger.LogInformation("Migrating the IsFolder flag from library.db.old may take a while, do not stop Jellyfin.");
  40. var dataPath = _paths.DataPath;
  41. var libraryDbPath = Path.Combine(dataPath, DbFilename);
  42. if (!File.Exists(libraryDbPath))
  43. {
  44. _logger.LogError("Cannot migrate IsFolder flag from {LibraryDb} as it does not exist. This migration expects the MigrateLibraryDb to run first.", libraryDbPath);
  45. return;
  46. }
  47. var dbContext = await _provider.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
  48. await using (dbContext.ConfigureAwait(false))
  49. {
  50. using var connection = new SqliteConnection($"Filename={libraryDbPath};Mode=ReadOnly");
  51. var queryResult = connection.Query(
  52. """
  53. SELECT guid FROM TypedBaseItems
  54. WHERE IsFolder = true
  55. """)
  56. .Select(entity => entity.GetGuid(0))
  57. .ToList();
  58. _logger.LogInformation("Migrating the IsFolder flag for {Count} items.", queryResult.Count);
  59. foreach (var id in queryResult)
  60. {
  61. await dbContext.BaseItems.Where(e => e.Id == id).ExecuteUpdateAsync(e => e.SetProperty(f => f.IsFolder, true), cancellationToken).ConfigureAwait(false);
  62. }
  63. }
  64. }
  65. }