From 7d983ae0dd13e7754ffa8060cfb59e3340f7b17a Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Mon, 20 May 2024 22:51:38 +0200 Subject: [PATCH] Mark Audio as RequiresDeserialization and backfill data --- .../Data/SqliteItemRepository.cs | 13 +-- Jellyfin.Server/Migrations/MigrationRunner.cs | 3 +- .../Migrations/Routines/FixAudioData.cs | 104 ++++++++++++++++++ .../Entities/InternalItemsQuery.cs | 3 + 4 files changed, 115 insertions(+), 8 deletions(-) create mode 100644 Jellyfin.Server/Migrations/Routines/FixAudioData.cs diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 34d753093d..4f19ec347f 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -1298,16 +1298,15 @@ namespace Emby.Server.Implementations.Data && type != typeof(Book) && type != typeof(LiveTvProgram) && type != typeof(AudioBook) - && type != typeof(Audio) && type != typeof(MusicAlbum); } private BaseItem GetItem(SqliteDataReader reader, InternalItemsQuery query) { - return GetItem(reader, query, HasProgramAttributes(query), HasEpisodeAttributes(query), HasServiceName(query), HasStartDate(query), HasTrailerTypes(query), HasArtistFields(query), HasSeriesFields(query)); + return GetItem(reader, query, HasProgramAttributes(query), HasEpisodeAttributes(query), HasServiceName(query), HasStartDate(query), HasTrailerTypes(query), HasArtistFields(query), HasSeriesFields(query), false); } - private BaseItem GetItem(SqliteDataReader reader, InternalItemsQuery query, bool enableProgramAttributes, bool hasEpisodeAttributes, bool hasServiceName, bool queryHasStartDate, bool hasTrailerTypes, bool hasArtistFields, bool hasSeriesFields) + private BaseItem GetItem(SqliteDataReader reader, InternalItemsQuery query, bool enableProgramAttributes, bool hasEpisodeAttributes, bool hasServiceName, bool queryHasStartDate, bool hasTrailerTypes, bool hasArtistFields, bool hasSeriesFields, bool skipDeserialization) { var typeString = reader.GetString(0); @@ -1320,7 +1319,7 @@ namespace Emby.Server.Implementations.Data BaseItem item = null; - if (TypeRequiresDeserialization(type)) + if (TypeRequiresDeserialization(type) && !skipDeserialization) { try { @@ -2562,7 +2561,7 @@ namespace Emby.Server.Implementations.Data foreach (var row in statement.ExecuteQuery()) { - var item = GetItem(row, query, hasProgramAttributes, hasEpisodeAttributes, hasServiceName, hasStartDate, hasTrailerTypes, hasArtistFields, hasSeriesFields); + var item = GetItem(row, query, hasProgramAttributes, hasEpisodeAttributes, hasServiceName, hasStartDate, hasTrailerTypes, hasArtistFields, hasSeriesFields, query.SkipDeserialization); if (item is not null) { items.Add(item); @@ -2774,7 +2773,7 @@ namespace Emby.Server.Implementations.Data foreach (var row in statement.ExecuteQuery()) { - var item = GetItem(row, query, hasProgramAttributes, hasEpisodeAttributes, hasServiceName, hasStartDate, hasTrailerTypes, hasArtistFields, hasSeriesFields); + var item = GetItem(row, query, hasProgramAttributes, hasEpisodeAttributes, hasServiceName, hasStartDate, hasTrailerTypes, hasArtistFields, hasSeriesFields, false); if (item is not null) { list.Add(item); @@ -5021,7 +5020,7 @@ AND Type = @InternalPersonType)"); foreach (var row in statement.ExecuteQuery()) { - var item = GetItem(row, query, hasProgramAttributes, hasEpisodeAttributes, hasServiceName, hasStartDate, hasTrailerTypes, hasArtistFields, hasSeriesFields); + var item = GetItem(row, query, hasProgramAttributes, hasEpisodeAttributes, hasServiceName, hasStartDate, hasTrailerTypes, hasArtistFields, hasSeriesFields, false); if (item is not null) { var countStartColumn = columns.Count - 1; diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs index 44aa430444..81fecc9a13 100644 --- a/Jellyfin.Server/Migrations/MigrationRunner.cs +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -44,7 +44,8 @@ namespace Jellyfin.Server.Migrations typeof(Routines.FixPlaylistOwner), typeof(Routines.MigrateRatingLevels), typeof(Routines.AddDefaultCastReceivers), - typeof(Routines.UpdateDefaultPluginRepository) + typeof(Routines.UpdateDefaultPluginRepository), + typeof(Routines.FixAudioData), }; /// diff --git a/Jellyfin.Server/Migrations/Routines/FixAudioData.cs b/Jellyfin.Server/Migrations/Routines/FixAudioData.cs new file mode 100644 index 0000000000..74f7e9c3e8 --- /dev/null +++ b/Jellyfin.Server/Migrations/Routines/FixAudioData.cs @@ -0,0 +1,104 @@ +using System; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Threading; +using Jellyfin.Data.Enums; +using MediaBrowser.Controller; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Persistence; +using MediaBrowser.Model.Entities; +using Microsoft.Extensions.Logging; + +namespace Jellyfin.Server.Migrations.Routines +{ + /// + /// Fixes the data column of audio types to be deserializable. + /// + internal class FixAudioData : IMigrationRoutine + { + private const string DbFilename = "library.db"; + private readonly ILogger _logger; + private readonly IServerApplicationPaths _applicationPaths; + private readonly IItemRepository _itemRepository; + + public FixAudioData( + IServerApplicationPaths applicationPaths, + ILoggerFactory loggerFactory, + IItemRepository itemRepository) + { + _applicationPaths = applicationPaths; + _itemRepository = itemRepository; + _logger = loggerFactory.CreateLogger(); + } + + /// + public Guid Id => Guid.Parse("{CF6FABC2-9FBE-4933-84A5-FFE52EF22A58}"); + + /// + public string Name => "FixAudioData"; + + /// + public bool PerformOnNewInstall => false; + + /// + public void Perform() + { + var dbPath = Path.Combine(_applicationPaths.DataPath, DbFilename); + + // Back up the database before modifying any entries + for (int i = 1; ; i++) + { + var bakPath = string.Format(CultureInfo.InvariantCulture, "{0}.bak{1}", dbPath, i); + if (!File.Exists(bakPath)) + { + try + { + File.Copy(dbPath, bakPath); + _logger.LogInformation("Library database backed up to {BackupPath}", bakPath); + break; + } + catch (Exception ex) + { + _logger.LogError(ex, "Cannot make a backup of {Library} at path {BackupPath}", DbFilename, bakPath); + throw; + } + } + } + + _logger.LogInformation("Backfilling audio lyrics data to database."); + var startIndex = 0; + var records = _itemRepository.GetCount(new InternalItemsQuery + { + IncludeItemTypes = [BaseItemKind.Audio], + }); + + while (startIndex < records) + { + var results = _itemRepository.GetItemList(new InternalItemsQuery + { + IncludeItemTypes = [BaseItemKind.Audio], + StartIndex = startIndex, + Limit = 100, + SkipDeserialization = true + }) + .Cast