Optimize PostgreSQL dead tuple handling for media scans

Introduce bulk operation extensions and transaction helpers for efficient, batched EF Core operations. Refactor MediaStreamInfo schema by splitting audio/video details into dedicated tables, reducing table bloat and improving update performance. Add migration for data movement and schema changes. Update EF Core models and context for new structure. Enhance PostgreSQL provider configuration and add comprehensive documentation and monitoring guides. Includes minor middleware and code cleanups.
This commit is contained in:
2026-04-30 15:52:37 -04:00
parent 6d5282208b
commit 57d6342524
20 changed files with 1995 additions and 339 deletions
@@ -121,14 +121,5 @@ namespace Jellyfin.Server.Extensions
{
return appBuilder.UseMiddleware<RobotsRedirectionMiddleware>();
}
/// <summary>
/// Adds WebSocket authentication to the application pipeline.
/// </summary>
/// <param name="appBuilder">The application builder.</param>
/// <returns>The updated application builder.</returns>
public static IApplicationBuilder UseWebSocketAuthentication(this IApplicationBuilder appBuilder)
{
return appBuilder.UseMiddleware<WebSocketAuthenticationMiddleware>();
}
}
}
@@ -596,19 +596,16 @@ internal class MigrateLibraryDb : IDatabaseMigrationRoutine
/// <returns>MediaStream.</returns>
private MediaStreamInfo GetMediaStream(SqliteDataReader reader)
{
var streamType = Enum.Parse<MediaStreamTypeEntity>(reader.GetString(2));
var itemId = reader.GetGuid(0);
var streamIndex = reader.GetInt32(1);
var item = new MediaStreamInfo
{
StreamIndex = reader.GetInt32(1),
StreamType = Enum.Parse<MediaStreamTypeEntity>(reader.GetString(2)),
StreamIndex = streamIndex,
StreamType = streamType,
Item = null!,
ItemId = reader.GetGuid(0),
AspectRatio = null!,
ChannelLayout = null!,
Codec = null!,
IsInterlaced = false,
Language = null!,
Path = null!,
Profile = null!,
ItemId = itemId,
};
if (reader.TryGetString(3, out var codec))
@@ -621,92 +618,30 @@ internal class MigrateLibraryDb : IDatabaseMigrationRoutine
item.Language = language;
}
if (reader.TryGetString(5, out var channelLayout))
{
item.ChannelLayout = channelLayout;
}
if (reader.TryGetString(6, out var profile))
{
item.Profile = profile;
}
if (reader.TryGetString(7, out var aspectRatio))
{
item.AspectRatio = aspectRatio;
}
if (reader.TryGetString(8, out var path))
{
item.Path = path;
}
item.IsInterlaced = reader.GetBoolean(9);
if (reader.TryGetInt32(10, out var bitrate))
{
item.BitRate = bitrate;
}
if (reader.TryGetInt32(11, out var channels))
{
item.Channels = channels;
}
if (reader.TryGetInt32(12, out var sampleRate))
{
item.SampleRate = sampleRate;
}
item.IsDefault = reader.GetBoolean(13);
item.IsForced = reader.GetBoolean(14);
item.IsExternal = reader.GetBoolean(15);
if (reader.TryGetInt32(16, out var width))
{
item.Width = width;
}
if (reader.TryGetInt32(17, out var height))
{
item.Height = height;
}
if (reader.TryGetSingle(18, out var averageFrameRate))
{
item.AverageFrameRate = averageFrameRate;
}
if (reader.TryGetSingle(19, out var realFrameRate))
{
item.RealFrameRate = realFrameRate;
}
if (reader.TryGetSingle(20, out var level))
{
item.Level = level;
}
if (reader.TryGetString(21, out var pixelFormat))
{
item.PixelFormat = pixelFormat;
}
if (reader.TryGetInt32(22, out var bitDepth))
{
item.BitDepth = bitDepth;
}
if (reader.TryGetBoolean(23, out var isAnamorphic))
{
item.IsAnamorphic = isAnamorphic;
}
if (reader.TryGetInt32(24, out var refFrames))
{
item.RefFrames = refFrames;
}
if (reader.TryGetString(25, out var codecTag))
{
item.CodecTag = codecTag;
@@ -717,11 +652,6 @@ internal class MigrateLibraryDb : IDatabaseMigrationRoutine
item.Comment = comment;
}
if (reader.TryGetString(27, out var nalLengthSize))
{
item.NalLengthSize = nalLengthSize;
}
if (reader.TryGetBoolean(28, out var isAVC))
{
item.IsAvc = isAVC;
@@ -742,68 +672,151 @@ internal class MigrateLibraryDb : IDatabaseMigrationRoutine
item.CodecTimeBase = codecTimeBase;
}
if (reader.TryGetString(32, out var colorPrimaries))
if (streamType == MediaStreamTypeEntity.Audio)
{
item.ColorPrimaries = colorPrimaries;
}
var audio = new AudioStreamDetails
{
ItemId = itemId,
StreamIndex = streamIndex,
};
if (reader.TryGetString(33, out var colorSpace))
if (reader.TryGetString(5, out var channelLayout))
{
audio.ChannelLayout = channelLayout;
}
if (reader.TryGetInt32(11, out var channels))
{
audio.Channels = channels;
}
if (reader.TryGetInt32(12, out var sampleRate))
{
audio.SampleRate = sampleRate;
}
audio.IsHearingImpaired = reader.TryGetBoolean(43, out var hearingImpaired) && hearingImpaired;
item.AudioDetails = audio;
}
else if (streamType == MediaStreamTypeEntity.Video)
{
item.ColorSpace = colorSpace;
var video = new VideoStreamDetails
{
ItemId = itemId,
StreamIndex = streamIndex,
};
if (reader.TryGetString(7, out var aspectRatio))
{
video.AspectRatio = aspectRatio;
}
video.IsInterlaced = reader.GetBoolean(9);
if (reader.TryGetInt32(16, out var width))
{
video.Width = width;
}
if (reader.TryGetInt32(17, out var height))
{
video.Height = height;
}
if (reader.TryGetSingle(18, out var averageFrameRate))
{
video.AverageFrameRate = averageFrameRate;
}
if (reader.TryGetSingle(19, out var realFrameRate))
{
video.RealFrameRate = realFrameRate;
}
if (reader.TryGetString(21, out var pixelFormat))
{
video.PixelFormat = pixelFormat;
}
if (reader.TryGetInt32(22, out var bitDepth))
{
video.BitDepth = bitDepth;
}
if (reader.TryGetBoolean(23, out var isAnamorphic))
{
video.IsAnamorphic = isAnamorphic;
}
if (reader.TryGetInt32(24, out var refFrames))
{
video.RefFrames = refFrames;
}
if (reader.TryGetString(27, out var nalLengthSize))
{
video.NalLengthSize = nalLengthSize;
}
if (reader.TryGetString(32, out var colorPrimaries))
{
video.ColorPrimaries = colorPrimaries;
}
if (reader.TryGetString(33, out var colorSpace))
{
video.ColorSpace = colorSpace;
}
if (reader.TryGetString(34, out var colorTransfer))
{
video.ColorTransfer = colorTransfer;
}
if (reader.TryGetInt32(35, out var dvVersionMajor))
{
video.DvVersionMajor = dvVersionMajor;
}
if (reader.TryGetInt32(36, out var dvVersionMinor))
{
video.DvVersionMinor = dvVersionMinor;
}
if (reader.TryGetInt32(37, out var dvProfile))
{
video.DvProfile = dvProfile;
}
if (reader.TryGetInt32(38, out var dvLevel))
{
video.DvLevel = dvLevel;
}
if (reader.TryGetInt32(39, out var rpuPresentFlag))
{
video.RpuPresentFlag = rpuPresentFlag;
}
if (reader.TryGetInt32(40, out var elPresentFlag))
{
video.ElPresentFlag = elPresentFlag;
}
if (reader.TryGetInt32(41, out var blPresentFlag))
{
video.BlPresentFlag = blPresentFlag;
}
if (reader.TryGetInt32(42, out var dvBlSignalCompatibilityId))
{
video.DvBlSignalCompatibilityId = dvBlSignalCompatibilityId;
}
item.VideoDetails = video;
}
if (reader.TryGetString(34, out var colorTransfer))
{
item.ColorTransfer = colorTransfer;
}
if (reader.TryGetInt32(35, out var dvVersionMajor))
{
item.DvVersionMajor = dvVersionMajor;
}
if (reader.TryGetInt32(36, out var dvVersionMinor))
{
item.DvVersionMinor = dvVersionMinor;
}
if (reader.TryGetInt32(37, out var dvProfile))
{
item.DvProfile = dvProfile;
}
if (reader.TryGetInt32(38, out var dvLevel))
{
item.DvLevel = dvLevel;
}
if (reader.TryGetInt32(39, out var rpuPresentFlag))
{
item.RpuPresentFlag = rpuPresentFlag;
}
if (reader.TryGetInt32(40, out var elPresentFlag))
{
item.ElPresentFlag = elPresentFlag;
}
if (reader.TryGetInt32(41, out var blPresentFlag))
{
item.BlPresentFlag = blPresentFlag;
}
if (reader.TryGetInt32(42, out var dvBlSignalCompatibilityId))
{
item.DvBlSignalCompatibilityId = dvBlSignalCompatibilityId;
}
item.IsHearingImpaired = reader.TryGetBoolean(43, out var result) && result;
// if (reader.TryGetInt32(44, out var rotation))
// {
// item.Rotation = rotation;
// }
return item;
}