mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-05-22 00:27:09 +01:00
Reorder migration handling for extra column
This commit is contained in:
@@ -26,7 +26,7 @@ namespace Jellyfin.Server.Implementations.Item;
|
||||
/// <summary>
|
||||
/// Handles mapping between BaseItemEntity (database) and BaseItemDto (domain) objects.
|
||||
/// </summary>
|
||||
internal static class BaseItemMapper
|
||||
public static class BaseItemMapper
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.Tasks;
|
||||
using Jellyfin.Database.Implementations;
|
||||
using Jellyfin.Server.Implementations.Item;
|
||||
using Jellyfin.Server.Migrations.Stages;
|
||||
using Jellyfin.Server.ServerSetupApp;
|
||||
using MediaBrowser.Controller.Channels;
|
||||
@@ -23,7 +24,7 @@ namespace Jellyfin.Server.Migrations.Routines;
|
||||
/// Removes orphaned extras (items with OwnerId pointing to non-existent items).
|
||||
/// Must run before EF migrations that add FK constraints on OwnerId.
|
||||
/// </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)]
|
||||
public class CleanupOrphanedExtras : IAsyncMigrationRoutine
|
||||
{
|
||||
@@ -37,39 +38,14 @@ public class CleanupOrphanedExtras : IAsyncMigrationRoutine
|
||||
/// <param name="logger">The startup logger.</param>
|
||||
/// <param name="dbContextFactory">The database context factory.</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(
|
||||
IStartupLogger<CleanupOrphanedExtras> logger,
|
||||
IDbContextFactory<JellyfinDbContext> dbContextFactory,
|
||||
ILibraryManager libraryManager,
|
||||
IItemRepository itemRepository,
|
||||
IItemCountService itemCountService,
|
||||
IChannelManager channelManager,
|
||||
IRecordingsManager recordingsManager,
|
||||
IMediaSourceManager mediaSourceManager,
|
||||
IMediaSegmentManager mediaSegmentManager,
|
||||
IServerConfigurationManager configurationManager,
|
||||
IFileSystem fileSystem)
|
||||
ILibraryManager libraryManager)
|
||||
{
|
||||
_logger = logger;
|
||||
_dbContextFactory = dbContextFactory;
|
||||
_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/>
|
||||
@@ -78,12 +54,19 @@ public class CleanupOrphanedExtras : IAsyncMigrationRoutine
|
||||
var context = await _dbContextFactory.CreateDbContextAsync(cancellationToken).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
|
||||
.Where(b => b.OwnerId.HasValue && !b.OwnerId.Value.Equals(Guid.Empty))
|
||||
.Where(b => !context.BaseItems.Any(parent => parent.Id.Equals(b.OwnerId!.Value)))
|
||||
.Select(b => b.Id)
|
||||
.Where(b => b.OwnerId.HasValue && b.OwnerId == placeholderOwner)
|
||||
.Select(b => new
|
||||
{
|
||||
b.Id,
|
||||
b.Path,
|
||||
b.Type
|
||||
})
|
||||
.ToListAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
#pragma warning restore RS0030 // Do not use banned APIs
|
||||
|
||||
if (orphanedItemIds.Count == 0)
|
||||
{
|
||||
@@ -97,11 +80,16 @@ public class CleanupOrphanedExtras : IAsyncMigrationRoutine
|
||||
var itemsToDelete = new List<BaseItem>();
|
||||
foreach (var itemId in orphanedItemIds)
|
||||
{
|
||||
var item = _libraryManager.GetItemById(itemId);
|
||||
if (item is not null)
|
||||
{
|
||||
itemsToDelete.Add(item);
|
||||
}
|
||||
itemsToDelete.Add(BaseItemMapper.DeserializeBaseItem(
|
||||
new Database.Implementations.Entities.BaseItemEntity()
|
||||
{
|
||||
Id = itemId.Id,
|
||||
Path = itemId.Path,
|
||||
Type = itemId.Type
|
||||
},
|
||||
_logger,
|
||||
null,
|
||||
true)!);
|
||||
}
|
||||
|
||||
_libraryManager.DeleteItemsUnsafeFast(itemsToDelete);
|
||||
|
||||
@@ -270,6 +270,9 @@ namespace Jellyfin.Database.Providers.Sqlite.Migrations
|
||||
b.Property<string>("OfficialRating")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OriginalLanguage")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OriginalTitle")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
|
||||
@@ -23,12 +23,40 @@ namespace Jellyfin.Database.Providers.Sqlite.Migrations
|
||||
name: "BaseItemEntityId",
|
||||
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(
|
||||
name: "FK_BaseItems_BaseItems_OwnerId",
|
||||
table: "BaseItems",
|
||||
column: "OwnerId",
|
||||
principalTable: "BaseItems",
|
||||
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 />
|
||||
@@ -62,6 +90,14 @@ namespace Jellyfin.Database.Providers.Sqlite.Migrations
|
||||
column: "BaseItemEntityId",
|
||||
principalTable: "BaseItems",
|
||||
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")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OriginalLanguage")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OriginalTitle")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
|
||||
@@ -267,6 +267,9 @@ namespace Jellyfin.Database.Providers.Sqlite.Migrations
|
||||
b.Property<string>("OfficialRating")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OriginalLanguage")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OriginalTitle")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
|
||||
@@ -267,6 +267,9 @@ namespace Jellyfin.Database.Providers.Sqlite.Migrations
|
||||
b.Property<string>("OfficialRating")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OriginalLanguage")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OriginalTitle")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
|
||||
@@ -267,6 +267,9 @@ namespace Jellyfin.Database.Providers.Sqlite.Migrations
|
||||
b.Property<string>("OfficialRating")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OriginalLanguage")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OriginalTitle")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
|
||||
@@ -267,6 +267,9 @@ namespace Jellyfin.Database.Providers.Sqlite.Migrations
|
||||
b.Property<string>("OfficialRating")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OriginalLanguage")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OriginalTitle")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
|
||||
@@ -267,6 +267,9 @@ namespace Jellyfin.Database.Providers.Sqlite.Migrations
|
||||
b.Property<string>("OfficialRating")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OriginalLanguage")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OriginalTitle")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
|
||||
@@ -267,6 +267,9 @@ namespace Jellyfin.Database.Providers.Sqlite.Migrations
|
||||
b.Property<string>("OfficialRating")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OriginalLanguage")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OriginalTitle")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
|
||||
@@ -267,6 +267,9 @@ namespace Jellyfin.Database.Providers.Sqlite.Migrations
|
||||
b.Property<string>("OfficialRating")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OriginalLanguage")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OriginalTitle")
|
||||
.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