mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-08-07 06:37:09 +02:00
188 lines
8.0 KiB
C#
188 lines
8.0 KiB
C#
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using Emby.Server.Implementations.Data;
|
|
using MediaBrowser.Controller;
|
|
using Microsoft.Data.Sqlite;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace Jellyfin.Server.Migrations.Routines;
|
|
|
|
/// <summary>
|
|
/// Updates the library.db to version 10.10.z in preperation for the EFCore migration.
|
|
/// Replaces the migration code from the old SqliteItemRepository and SqliteUserDataRepository.
|
|
/// </summary>
|
|
[JellyfinMigration("2025-04-20T19:15:00", nameof(UpdateDatabaseToTenDotTen))]
|
|
[JellyfinMigrationBackup(LegacyLibraryDb = true)]
|
|
internal class UpdateDatabaseToTenDotTen : IDatabaseMigrationRoutine
|
|
{
|
|
private const string DbFilename = "library.db";
|
|
private readonly ILogger<UpdateDatabaseToTenDotTen> _logger;
|
|
private readonly IServerApplicationPaths _paths;
|
|
private readonly List<(string TableName, string ColumnName, string Type)> _libraryDbTableColumns =
|
|
[
|
|
("AncestorIds", "AncestorIdText", "Text"),
|
|
|
|
("TypedBaseItems", "Path", "Text"),
|
|
("TypedBaseItems", "StartDate", "DATETIME"),
|
|
("TypedBaseItems", "EndDate", "DATETIME"),
|
|
("TypedBaseItems", "ChannelId", "Text"),
|
|
("TypedBaseItems", "IsMovie", "BIT"),
|
|
("TypedBaseItems", "CommunityRating", "Float"),
|
|
("TypedBaseItems", "CustomRating", "Text"),
|
|
("TypedBaseItems", "IndexNumber", "INT"),
|
|
("TypedBaseItems", "IsLocked", "BIT"),
|
|
("TypedBaseItems", "Name", "Text"),
|
|
("TypedBaseItems", "OfficialRating", "Text"),
|
|
("TypedBaseItems", "MediaType", "Text"),
|
|
("TypedBaseItems", "Overview", "Text"),
|
|
("TypedBaseItems", "ParentIndexNumber", "INT"),
|
|
("TypedBaseItems", "PremiereDate", "DATETIME"),
|
|
("TypedBaseItems", "ProductionYear", "INT"),
|
|
("TypedBaseItems", "ParentId", "GUID"),
|
|
("TypedBaseItems", "Genres", "Text"),
|
|
("TypedBaseItems", "SortName", "Text"),
|
|
("TypedBaseItems", "ForcedSortName", "Text"),
|
|
("TypedBaseItems", "RunTimeTicks", "BIGINT"),
|
|
("TypedBaseItems", "DateCreated", "DATETIME"),
|
|
("TypedBaseItems", "DateModified", "DATETIME"),
|
|
("TypedBaseItems", "IsSeries", "BIT"),
|
|
("TypedBaseItems", "EpisodeTitle", "Text"),
|
|
("TypedBaseItems", "IsRepeat", "BIT"),
|
|
("TypedBaseItems", "PreferredMetadataLanguage", "Text"),
|
|
("TypedBaseItems", "PreferredMetadataCountryCode", "Text"),
|
|
("TypedBaseItems", "DateLastRefreshed", "DATETIME"),
|
|
("TypedBaseItems", "DateLastSaved", "DATETIME"),
|
|
("TypedBaseItems", "IsInMixedFolder", "BIT"),
|
|
("TypedBaseItems", "LockedFields", "Text"),
|
|
("TypedBaseItems", "Studios", "Text"),
|
|
("TypedBaseItems", "Audio", "Text"),
|
|
("TypedBaseItems", "ExternalServiceId", "Text"),
|
|
("TypedBaseItems", "Tags", "Text"),
|
|
("TypedBaseItems", "IsFolder", "BIT"),
|
|
("TypedBaseItems", "InheritedParentalRatingValue", "INT"),
|
|
("TypedBaseItems", "UnratedType", "Text"),
|
|
("TypedBaseItems", "TopParentId", "Text"),
|
|
("TypedBaseItems", "TrailerTypes", "Text"),
|
|
("TypedBaseItems", "CriticRating", "Float"),
|
|
("TypedBaseItems", "CleanName", "Text"),
|
|
("TypedBaseItems", "PresentationUniqueKey", "Text"),
|
|
("TypedBaseItems", "OriginalTitle", "Text"),
|
|
("TypedBaseItems", "PrimaryVersionId", "Text"),
|
|
("TypedBaseItems", "DateLastMediaAdded", "DATETIME"),
|
|
("TypedBaseItems", "Album", "Text"),
|
|
("TypedBaseItems", "LUFS", "Float"),
|
|
("TypedBaseItems", "NormalizationGain", "Float"),
|
|
("TypedBaseItems", "IsVirtualItem", "BIT"),
|
|
("TypedBaseItems", "SeriesName", "Text"),
|
|
("TypedBaseItems", "UserDataKey", "Text"),
|
|
("TypedBaseItems", "SeasonName", "Text"),
|
|
("TypedBaseItems", "SeasonId", "GUID"),
|
|
("TypedBaseItems", "SeriesId", "GUID"),
|
|
("TypedBaseItems", "ExternalSeriesId", "Text"),
|
|
("TypedBaseItems", "Tagline", "Text"),
|
|
("TypedBaseItems", "ProviderIds", "Text"),
|
|
("TypedBaseItems", "Images", "Text"),
|
|
("TypedBaseItems", "ProductionLocations", "Text"),
|
|
("TypedBaseItems", "ExtraIds", "Text"),
|
|
("TypedBaseItems", "TotalBitrate", "INT"),
|
|
("TypedBaseItems", "ExtraType", "Text"),
|
|
("TypedBaseItems", "Artists", "Text"),
|
|
("TypedBaseItems", "AlbumArtists", "Text"),
|
|
("TypedBaseItems", "ExternalId", "Text"),
|
|
("TypedBaseItems", "SeriesPresentationUniqueKey", "Text"),
|
|
("TypedBaseItems", "ShowId", "Text"),
|
|
("TypedBaseItems", "OwnerId", "Text"),
|
|
("TypedBaseItems", "Width", "INT"),
|
|
("TypedBaseItems", "Height", "INT"),
|
|
("TypedBaseItems", "Size", "BIGINT"),
|
|
|
|
("ItemValues", "CleanValue", "Text"),
|
|
|
|
("Chapters2", "ImageDateModified", "DATETIME"),
|
|
|
|
("mediastreams", "IsAvc", "BIT"),
|
|
("mediastreams", "TimeBase", "TEXT"),
|
|
("mediastreams", "CodecTimeBase", "TEXT"),
|
|
("mediastreams", "Title", "TEXT"),
|
|
("mediastreams", "NalLengthSize", "TEXT"),
|
|
("mediastreams", "Comment", "TEXT"),
|
|
("mediastreams", "CodecTag", "TEXT"),
|
|
("mediastreams", "PixelFormat", "TEXT"),
|
|
("mediastreams", "BitDepth", "INT"),
|
|
("mediastreams", "RefFrames", "INT"),
|
|
("mediastreams", "KeyFrames", "TEXT"),
|
|
("mediastreams", "IsAnamorphic", "BIT"),
|
|
("mediastreams", "ColorPrimaries", "TEXT"),
|
|
("mediastreams", "ColorSpace", "TEXT"),
|
|
("mediastreams", "ColorTransfer", "TEXT"),
|
|
("mediastreams", "DvVersionMajor", "INT"),
|
|
("mediastreams", "DvVersionMinor", "INT"),
|
|
("mediastreams", "DvProfile", "INT"),
|
|
("mediastreams", "DvLevel", "INT"),
|
|
("mediastreams", "RpuPresentFlag", "INT"),
|
|
("mediastreams", "ElPresentFlag", "INT"),
|
|
("mediastreams", "BlPresentFlag", "INT"),
|
|
("mediastreams", "DvBlSignalCompatibilityId", "INT"),
|
|
("mediastreams", "IsHearingImpaired", "BIT"),
|
|
("mediastreams", "Rotation", "INT")
|
|
];
|
|
|
|
public UpdateDatabaseToTenDotTen(
|
|
ILogger<UpdateDatabaseToTenDotTen> logger,
|
|
IServerApplicationPaths paths)
|
|
{
|
|
_logger = logger;
|
|
_paths = paths;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public void Perform()
|
|
{
|
|
var dataPath = _paths.DataPath;
|
|
|
|
var dbPath = Path.Combine(dataPath, DbFilename);
|
|
|
|
_logger.LogInformation("Prepare database for EFCore migration: update to version 10.10.z");
|
|
|
|
// add missing columns
|
|
using var connection = new SqliteConnection($"Filename={dbPath}");
|
|
connection.Open();
|
|
using (var transaction = connection.BeginTransaction())
|
|
{
|
|
var existingColumns = GetExistingColumns(connection);
|
|
|
|
_libraryDbTableColumns
|
|
.Where(col => !existingColumns.Exists(exist => col.TableName == exist.TableName && col.ColumnName == exist.ColumnName))
|
|
.ToList()
|
|
.ForEach(col => connection.Execute($"ALTER TABLE {col.TableName} ADD COLUMN {col.ColumnName} {col.Type} NULL"));
|
|
|
|
transaction.Commit();
|
|
}
|
|
|
|
_logger.LogInformation("Database was successfully updated");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a list of all existing columns.
|
|
/// </summary>
|
|
private List<(string TableName, string ColumnName)> GetExistingColumns(SqliteConnection connection)
|
|
{
|
|
var existingColumns = new List<(string TableName, string ColumnName)>();
|
|
var existingColumnsQuery = @"SELECT t.name AS table_name, c.name AS column_name
|
|
FROM sqlite_master t
|
|
JOIN pragma_table_info(t.name) c
|
|
WHERE t.name IN ('AncestorIds', 'TypedBaseItems', 'ItemValues', 'Chapters2', 'mediastreams')";
|
|
|
|
foreach (var row in connection.Query(existingColumnsQuery))
|
|
{
|
|
if (row.TryGetString(0, out var tableName) && row.TryGetString(1, out var columnName))
|
|
{
|
|
existingColumns.Add((tableName, columnName));
|
|
}
|
|
}
|
|
|
|
return existingColumns;
|
|
}
|
|
}
|