mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-07-06 22:32:57 +01:00
Merge pull request #16861 from JPVenson/fix/fixMigrations12.0
Reorder Migrations and fix failing migration
This commit is contained in:
@@ -26,7 +26,7 @@ namespace Jellyfin.Server.Implementations.Item;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles mapping between BaseItemEntity (database) and BaseItemDto (domain) objects.
|
/// Handles mapping between BaseItemEntity (database) and BaseItemDto (domain) objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static class BaseItemMapper
|
public static class BaseItemMapper
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This holds all the types in the running assemblies
|
/// This holds all the types in the running assemblies
|
||||||
|
|||||||
8
Jellyfin.Server/GlobalSuppressions.cs
Normal file
8
Jellyfin.Server/GlobalSuppressions.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// This file is used by Code Analysis to maintain SuppressMessage
|
||||||
|
// attributes that are applied to this project.
|
||||||
|
// Project-level suppressions either have no target or are given
|
||||||
|
// a specific target and scoped to a namespace, type, member, etc.
|
||||||
|
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1649:File name should match first type name", Justification = "Migration files should follow the EFCore standard in regards to naming.", Scope = "namespaceanddescendants", Target = "~N:Jellyfin.Server.Migrations.Routines")]
|
||||||
@@ -4,6 +4,7 @@ using System.Linq;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Jellyfin.Database.Implementations;
|
using Jellyfin.Database.Implementations;
|
||||||
|
using Jellyfin.Server.Implementations.Item;
|
||||||
using Jellyfin.Server.Migrations.Stages;
|
using Jellyfin.Server.Migrations.Stages;
|
||||||
using Jellyfin.Server.ServerSetupApp;
|
using Jellyfin.Server.ServerSetupApp;
|
||||||
using MediaBrowser.Controller.Channels;
|
using MediaBrowser.Controller.Channels;
|
||||||
@@ -23,7 +24,7 @@ namespace Jellyfin.Server.Migrations.Routines;
|
|||||||
/// Removes orphaned extras (items with OwnerId pointing to non-existent items).
|
/// Removes orphaned extras (items with OwnerId pointing to non-existent items).
|
||||||
/// Must run before EF migrations that add FK constraints on OwnerId.
|
/// Must run before EF migrations that add FK constraints on OwnerId.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JellyfinMigration("2026-01-13T23:00:00", nameof(CleanupOrphanedExtras), Stage = JellyfinMigrationStageTypes.CoreInitialisation)]
|
[JellyfinMigration("2026-01-13T23:00:00", nameof(CleanupOrphanedExtras), Stage = JellyfinMigrationStageTypes.AppInitialisation)]
|
||||||
[JellyfinMigrationBackup(JellyfinDb = true)]
|
[JellyfinMigrationBackup(JellyfinDb = true)]
|
||||||
public class CleanupOrphanedExtras : IAsyncMigrationRoutine
|
public class CleanupOrphanedExtras : IAsyncMigrationRoutine
|
||||||
{
|
{
|
||||||
@@ -37,39 +38,14 @@ public class CleanupOrphanedExtras : IAsyncMigrationRoutine
|
|||||||
/// <param name="logger">The startup logger.</param>
|
/// <param name="logger">The startup logger.</param>
|
||||||
/// <param name="dbContextFactory">The database context factory.</param>
|
/// <param name="dbContextFactory">The database context factory.</param>
|
||||||
/// <param name="libraryManager">The library manager.</param>
|
/// <param name="libraryManager">The library manager.</param>
|
||||||
/// <param name="itemRepository">The item repository.</param>
|
|
||||||
/// <param name="itemCountService">The item count service.</param>
|
|
||||||
/// <param name="channelManager">The channel manager.</param>
|
|
||||||
/// <param name="recordingsManager">The recordings manager.</param>
|
|
||||||
/// <param name="mediaSourceManager">The media source manager.</param>
|
|
||||||
/// <param name="mediaSegmentManager">The media segments manager.</param>
|
|
||||||
/// <param name="configurationManager">The configuration manager.</param>
|
|
||||||
/// <param name="fileSystem">The file system.</param>
|
|
||||||
public CleanupOrphanedExtras(
|
public CleanupOrphanedExtras(
|
||||||
IStartupLogger<CleanupOrphanedExtras> logger,
|
IStartupLogger<CleanupOrphanedExtras> logger,
|
||||||
IDbContextFactory<JellyfinDbContext> dbContextFactory,
|
IDbContextFactory<JellyfinDbContext> dbContextFactory,
|
||||||
ILibraryManager libraryManager,
|
ILibraryManager libraryManager)
|
||||||
IItemRepository itemRepository,
|
|
||||||
IItemCountService itemCountService,
|
|
||||||
IChannelManager channelManager,
|
|
||||||
IRecordingsManager recordingsManager,
|
|
||||||
IMediaSourceManager mediaSourceManager,
|
|
||||||
IMediaSegmentManager mediaSegmentManager,
|
|
||||||
IServerConfigurationManager configurationManager,
|
|
||||||
IFileSystem fileSystem)
|
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_dbContextFactory = dbContextFactory;
|
_dbContextFactory = dbContextFactory;
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
BaseItem.LibraryManager ??= libraryManager;
|
|
||||||
BaseItem.ItemRepository ??= itemRepository;
|
|
||||||
BaseItem.ItemCountService ??= itemCountService;
|
|
||||||
BaseItem.ChannelManager ??= channelManager;
|
|
||||||
BaseItem.MediaSourceManager ??= mediaSourceManager;
|
|
||||||
BaseItem.MediaSegmentManager ??= mediaSegmentManager;
|
|
||||||
BaseItem.ConfigurationManager ??= configurationManager;
|
|
||||||
BaseItem.FileSystem ??= fileSystem;
|
|
||||||
Video.RecordingsManager ??= recordingsManager;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -78,12 +54,19 @@ public class CleanupOrphanedExtras : IAsyncMigrationRoutine
|
|||||||
var context = await _dbContextFactory.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
|
var context = await _dbContextFactory.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
|
||||||
await using (context.ConfigureAwait(false))
|
await using (context.ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
|
var placeholderOwner = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
||||||
|
#pragma warning disable RS0030 // Do not use banned APIs
|
||||||
var orphanedItemIds = await context.BaseItems
|
var orphanedItemIds = await context.BaseItems
|
||||||
.Where(b => b.OwnerId.HasValue && !b.OwnerId.Value.Equals(Guid.Empty))
|
.Where(b => b.OwnerId.HasValue && b.OwnerId == placeholderOwner)
|
||||||
.Where(b => !context.BaseItems.Any(parent => parent.Id.Equals(b.OwnerId!.Value)))
|
.Select(b => new
|
||||||
.Select(b => b.Id)
|
{
|
||||||
|
b.Id,
|
||||||
|
b.Path,
|
||||||
|
b.Type
|
||||||
|
})
|
||||||
.ToListAsync(cancellationToken)
|
.ToListAsync(cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
#pragma warning restore RS0030 // Do not use banned APIs
|
||||||
|
|
||||||
if (orphanedItemIds.Count == 0)
|
if (orphanedItemIds.Count == 0)
|
||||||
{
|
{
|
||||||
@@ -97,11 +80,16 @@ public class CleanupOrphanedExtras : IAsyncMigrationRoutine
|
|||||||
var itemsToDelete = new List<BaseItem>();
|
var itemsToDelete = new List<BaseItem>();
|
||||||
foreach (var itemId in orphanedItemIds)
|
foreach (var itemId in orphanedItemIds)
|
||||||
{
|
{
|
||||||
var item = _libraryManager.GetItemById(itemId);
|
itemsToDelete.Add(BaseItemMapper.DeserializeBaseItem(
|
||||||
if (item is not null)
|
new Database.Implementations.Entities.BaseItemEntity()
|
||||||
{
|
{
|
||||||
itemsToDelete.Add(item);
|
Id = itemId.Id,
|
||||||
}
|
Path = itemId.Path,
|
||||||
|
Type = itemId.Type
|
||||||
|
},
|
||||||
|
_logger,
|
||||||
|
null,
|
||||||
|
true)!);
|
||||||
}
|
}
|
||||||
|
|
||||||
_libraryManager.DeleteItemsUnsafeFast(itemsToDelete);
|
_libraryManager.DeleteItemsUnsafeFast(itemsToDelete);
|
||||||
@@ -284,10 +284,16 @@ public class MergeDuplicatePeople : IAsyncMigrationRoutine
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await context.Peoples
|
var idx = 0;
|
||||||
.Where(p => idsToDelete.Contains(p.Id))
|
foreach (var item in idsToDelete.Chunk(200))
|
||||||
.ExecuteDeleteAsync(cancellationToken)
|
{
|
||||||
.ConfigureAwait(false);
|
idx++; // humans count at one
|
||||||
|
_logger.LogInformation("Remove batch {BatchNo}/{MaxBatches} duplicate Peoples.", idx, idsToDelete.Count / 200);
|
||||||
|
await context.Peoples
|
||||||
|
.Where(p => item.Contains(p.Id))
|
||||||
|
.ExecuteDeleteAsync(cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
_logger.LogInformation("Removed {Count} duplicate Peoples rows.", idsToDelete.Count);
|
_logger.LogInformation("Removed {Count} duplicate Peoples rows.", idsToDelete.Count);
|
||||||
}
|
}
|
||||||
@@ -270,6 +270,9 @@ namespace Jellyfin.Database.Providers.Sqlite.Migrations
|
|||||||
b.Property<string>("OfficialRating")
|
b.Property<string>("OfficialRating")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("OriginalLanguage")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<string>("OriginalTitle")
|
b.Property<string>("OriginalTitle")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
|||||||
@@ -23,12 +23,40 @@ namespace Jellyfin.Database.Providers.Sqlite.Migrations
|
|||||||
name: "BaseItemEntityId",
|
name: "BaseItemEntityId",
|
||||||
table: "BaseItems");
|
table: "BaseItems");
|
||||||
|
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
UPDATE BaseItems
|
||||||
|
SET OwnerId = '00000000-0000-0000-0000-000000000001'
|
||||||
|
WHERE OwnerId IS NOT NULL
|
||||||
|
AND OwnerId NOT IN (SELECT Id FROM BaseItems);
|
||||||
|
""");
|
||||||
|
|
||||||
migrationBuilder.AddForeignKey(
|
migrationBuilder.AddForeignKey(
|
||||||
name: "FK_BaseItems_BaseItems_OwnerId",
|
name: "FK_BaseItems_BaseItems_OwnerId",
|
||||||
table: "BaseItems",
|
table: "BaseItems",
|
||||||
column: "OwnerId",
|
column: "OwnerId",
|
||||||
principalTable: "BaseItems",
|
principalTable: "BaseItems",
|
||||||
principalColumn: "Id");
|
principalColumn: "Id");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<bool>(
|
||||||
|
name: "IsOriginal",
|
||||||
|
table: "MediaStreamInfos",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: false);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "OriginalLanguage",
|
||||||
|
table: "BaseItems",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "BaseItems",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: new Guid("00000000-0000-0000-0000-000000000001"),
|
||||||
|
column: "OriginalLanguage",
|
||||||
|
value: null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -62,6 +90,14 @@ namespace Jellyfin.Database.Providers.Sqlite.Migrations
|
|||||||
column: "BaseItemEntityId",
|
column: "BaseItemEntityId",
|
||||||
principalTable: "BaseItems",
|
principalTable: "BaseItems",
|
||||||
principalColumn: "Id");
|
principalColumn: "Id");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "IsOriginal",
|
||||||
|
table: "MediaStreamInfos");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "OriginalLanguage",
|
||||||
|
table: "BaseItems");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -267,6 +267,9 @@ namespace Jellyfin.Database.Providers.Sqlite.Migrations
|
|||||||
b.Property<string>("OfficialRating")
|
b.Property<string>("OfficialRating")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("OriginalLanguage")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<string>("OriginalTitle")
|
b.Property<string>("OriginalTitle")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
|||||||
@@ -267,6 +267,9 @@ namespace Jellyfin.Database.Providers.Sqlite.Migrations
|
|||||||
b.Property<string>("OfficialRating")
|
b.Property<string>("OfficialRating")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("OriginalLanguage")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<string>("OriginalTitle")
|
b.Property<string>("OriginalTitle")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
|||||||
@@ -267,6 +267,9 @@ namespace Jellyfin.Database.Providers.Sqlite.Migrations
|
|||||||
b.Property<string>("OfficialRating")
|
b.Property<string>("OfficialRating")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("OriginalLanguage")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<string>("OriginalTitle")
|
b.Property<string>("OriginalTitle")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
|||||||
@@ -267,6 +267,9 @@ namespace Jellyfin.Database.Providers.Sqlite.Migrations
|
|||||||
b.Property<string>("OfficialRating")
|
b.Property<string>("OfficialRating")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("OriginalLanguage")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<string>("OriginalTitle")
|
b.Property<string>("OriginalTitle")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
|||||||
@@ -267,6 +267,9 @@ namespace Jellyfin.Database.Providers.Sqlite.Migrations
|
|||||||
b.Property<string>("OfficialRating")
|
b.Property<string>("OfficialRating")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("OriginalLanguage")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<string>("OriginalTitle")
|
b.Property<string>("OriginalTitle")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
|||||||
@@ -267,6 +267,9 @@ namespace Jellyfin.Database.Providers.Sqlite.Migrations
|
|||||||
b.Property<string>("OfficialRating")
|
b.Property<string>("OfficialRating")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("OriginalLanguage")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<string>("OriginalTitle")
|
b.Property<string>("OriginalTitle")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
|||||||
@@ -267,6 +267,9 @@ namespace Jellyfin.Database.Providers.Sqlite.Migrations
|
|||||||
b.Property<string>("OfficialRating")
|
b.Property<string>("OfficialRating")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("OriginalLanguage")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<string>("OriginalTitle")
|
b.Property<string>("OriginalTitle")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
|||||||
@@ -267,6 +267,9 @@ namespace Jellyfin.Database.Providers.Sqlite.Migrations
|
|||||||
b.Property<string>("OfficialRating")
|
b.Property<string>("OfficialRating")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("OriginalLanguage")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<string>("OriginalTitle")
|
b.Property<string>("OriginalTitle")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,47 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Jellyfin.Database.Providers.Sqlite.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddOriginalLanguage : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.AddColumn<bool>(
|
|
||||||
name: "IsOriginal",
|
|
||||||
table: "MediaStreamInfos",
|
|
||||||
type: "INTEGER",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: false);
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<string>(
|
|
||||||
name: "OriginalLanguage",
|
|
||||||
table: "BaseItems",
|
|
||||||
type: "TEXT",
|
|
||||||
nullable: true);
|
|
||||||
|
|
||||||
migrationBuilder.UpdateData(
|
|
||||||
table: "BaseItems",
|
|
||||||
keyColumn: "Id",
|
|
||||||
keyValue: new Guid("00000000-0000-0000-0000-000000000001"),
|
|
||||||
column: "OriginalLanguage",
|
|
||||||
value: null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "IsOriginal",
|
|
||||||
table: "MediaStreamInfos");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "OriginalLanguage",
|
|
||||||
table: "BaseItems");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user