Merge remote-tracking branch 'upstream/master' into optimize-db-helper-memory

# Conflicts:
#	src/Jellyfin.Database/Jellyfin.Database.Providers.Sqlite/Migrations/JellyfinDbModelSnapshot.cs
This commit is contained in:
Shadowghost
2026-08-22 07:01:58 +02:00
52 changed files with 3027 additions and 259 deletions
@@ -318,13 +318,14 @@ public class ItemCountService : IItemCountService
var parentIdsArray = parentIds.ToArray();
var hierarchicalCounts = dbContext.BaseItems
.Where(b => b.ParentId.HasValue && parentIdsArray.Contains(b.ParentId.Value))
.Where(b => b.ParentId.HasValue)
.WhereOneOrMany(parentIdsArray, b => b.ParentId!.Value)
.GroupBy(b => b.ParentId!.Value)
.Select(g => new { ParentId = g.Key, Count = g.Count() })
.ToDictionary(x => x.ParentId, x => x.Count);
var linkedCounts = dbContext.LinkedChildren
.Where(lc => parentIdsArray.Contains(lc.ParentId))
.WhereOneOrMany(parentIdsArray, lc => lc.ParentId)
.GroupBy(lc => lc.ParentId)
.Select(g => new { ParentId = g.Key, Count = g.Count() })
.ToDictionary(x => x.ParentId, x => x.Count);
@@ -538,7 +538,7 @@ public class ItemPersistenceService : IItemPersistenceService
var childIdsToCheck = resolvedChildren.Select(c => c.ChildId).Distinct().ToList();
var existingChildIds = childIdsToCheck.Count > 0
? context.BaseItems
.Where(e => childIdsToCheck.Contains(e.Id))
.WhereOneOrMany(childIdsToCheck, e => e.Id)
.Select(e => e.Id)
.ToHashSet()
: [];
@@ -225,17 +225,8 @@ namespace Jellyfin.Server.Implementations.Users
?? throw new ResourceNotFoundException(nameof(user.Id));
dbContext.Entry(dbUser).CurrentValues.SetValues(user);
dbUser.Permissions.Clear();
foreach (var permission in user.Permissions)
{
dbUser.Permissions.Add(new Permission(permission.Kind, permission.Value));
}
dbUser.Preferences.Clear();
foreach (var preference in user.Preferences)
{
dbUser.Preferences.Add(new Preference(preference.Kind, preference.Value));
}
SyncPermissions(dbUser, user.Permissions);
SyncPreferences(dbUser, user.Preferences);
dbUser.AccessSchedules.Clear();
foreach (var accessSchedule in user.AccessSchedules)
@@ -269,6 +260,60 @@ namespace Jellyfin.Server.Implementations.Users
}
}
private static void SyncPermissions(User dbUser, ICollection<Permission> source)
{
var incoming = new Dictionary<PermissionKind, bool>();
foreach (var permission in source)
{
incoming[permission.Kind] = permission.Value;
}
foreach (var existing in dbUser.Permissions)
{
if (incoming.Remove(existing.Kind, out var value))
{
// EF only marks the row modified if the value actually differs, so an update that
// touches nothing but the user row - a session activity stamp - writes no children.
existing.Value = value;
}
else
{
dbUser.Permissions.Remove(existing);
}
}
foreach (var (kind, value) in incoming)
{
dbUser.Permissions.Add(new Permission(kind, value));
}
}
private static void SyncPreferences(User dbUser, ICollection<Preference> source)
{
var incoming = new Dictionary<PreferenceKind, string>();
foreach (var preference in source)
{
incoming[preference.Kind] = preference.Value;
}
foreach (var existing in dbUser.Preferences)
{
if (incoming.Remove(existing.Kind, out var value))
{
existing.Value = value;
}
else
{
dbUser.Preferences.Remove(existing);
}
}
foreach (var (kind, value) in incoming)
{
dbUser.Preferences.Add(new Preference(kind, value));
}
}
internal async Task<User> CreateUserInternalAsync(string name, JellyfinDbContext dbContext)
{
// TODO: Remove after user item data is migrated.