FixAudioData.cs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. using System;
  2. using System.Globalization;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Threading;
  6. using Jellyfin.Data.Enums;
  7. using MediaBrowser.Controller;
  8. using MediaBrowser.Controller.Entities;
  9. using MediaBrowser.Controller.Entities.Audio;
  10. using MediaBrowser.Controller.Persistence;
  11. using MediaBrowser.Model.Entities;
  12. using Microsoft.Extensions.Logging;
  13. namespace Jellyfin.Server.Migrations.Routines
  14. {
  15. /// <summary>
  16. /// Fixes the data column of audio types to be deserializable.
  17. /// </summary>
  18. internal class FixAudioData : IMigrationRoutine
  19. {
  20. private const string DbFilename = "library.db";
  21. private readonly ILogger<FixAudioData> _logger;
  22. private readonly IServerApplicationPaths _applicationPaths;
  23. private readonly IItemRepository _itemRepository;
  24. public FixAudioData(
  25. IServerApplicationPaths applicationPaths,
  26. ILoggerFactory loggerFactory,
  27. IItemRepository itemRepository)
  28. {
  29. _applicationPaths = applicationPaths;
  30. _itemRepository = itemRepository;
  31. _logger = loggerFactory.CreateLogger<FixAudioData>();
  32. }
  33. /// <inheritdoc/>
  34. public Guid Id => Guid.Parse("{CF6FABC2-9FBE-4933-84A5-FFE52EF22A58}");
  35. /// <inheritdoc/>
  36. public string Name => "FixAudioData";
  37. /// <inheritdoc/>
  38. public bool PerformOnNewInstall => false;
  39. /// <inheritdoc/>
  40. public void Perform()
  41. {
  42. var dbPath = Path.Combine(_applicationPaths.DataPath, DbFilename);
  43. // Back up the database before modifying any entries
  44. for (int i = 1; ; i++)
  45. {
  46. var bakPath = string.Format(CultureInfo.InvariantCulture, "{0}.bak{1}", dbPath, i);
  47. if (!File.Exists(bakPath))
  48. {
  49. try
  50. {
  51. _logger.LogInformation("Backing up {Library} to {BackupPath}", DbFilename, bakPath);
  52. File.Copy(dbPath, bakPath);
  53. _logger.LogInformation("{Library} backed up to {BackupPath}", DbFilename, bakPath);
  54. break;
  55. }
  56. catch (Exception ex)
  57. {
  58. _logger.LogError(ex, "Cannot make a backup of {Library} at path {BackupPath}", DbFilename, bakPath);
  59. throw;
  60. }
  61. }
  62. }
  63. _logger.LogInformation("Backfilling audio lyrics data to database.");
  64. var startIndex = 0;
  65. var records = _itemRepository.GetCount(new InternalItemsQuery
  66. {
  67. IncludeItemTypes = [BaseItemKind.Audio],
  68. });
  69. while (startIndex < records)
  70. {
  71. var results = _itemRepository.GetItemList(new InternalItemsQuery
  72. {
  73. IncludeItemTypes = [BaseItemKind.Audio],
  74. StartIndex = startIndex,
  75. Limit = 5000,
  76. SkipDeserialization = true
  77. })
  78. .Cast<Audio>()
  79. .ToList();
  80. foreach (var audio in results)
  81. {
  82. var lyricMediaStreams = audio.GetMediaStreams().Where(s => s.Type == MediaStreamType.Lyric).Select(s => s.Path).ToList();
  83. if (lyricMediaStreams.Count > 0)
  84. {
  85. audio.HasLyrics = true;
  86. audio.LyricFiles = lyricMediaStreams;
  87. }
  88. }
  89. _itemRepository.SaveItems(results, CancellationToken.None);
  90. startIndex += results.Count;
  91. _logger.LogInformation("Backfilled data for {UpdatedRecords} of {TotalRecords} audio records", startIndex, records);
  92. }
  93. }
  94. }
  95. }