diff options
| author | Shadowghost <Ghost_of_Stone@web.de> | 2026-08-05 12:22:55 +0200 |
|---|---|---|
| committer | Shadowghost <Ghost_of_Stone@web.de> | 2026-08-05 13:21:57 +0200 |
| commit | 61e75599b30cd346eab743529271ab3e002f0474 (patch) | |
| tree | e552741eb659ecfdbf36b6f8bcf1b1cc1ce70ee8 /Jellyfin.Server.Implementations | |
| parent | b2485af6e9b29c8a6491deb934569e591b4858d2 (diff) | |
Project the lowered person credit values once when updating people
Diffstat (limited to 'Jellyfin.Server.Implementations')
| -rw-r--r-- | Jellyfin.Server.Implementations/Item/PeopleRepository.cs | 56 |
1 files changed, 36 insertions, 20 deletions
diff --git a/Jellyfin.Server.Implementations/Item/PeopleRepository.cs b/Jellyfin.Server.Implementations/Item/PeopleRepository.cs index ef96952f67..05c8bffd66 100644 --- a/Jellyfin.Server.Implementations/Item/PeopleRepository.cs +++ b/Jellyfin.Server.Implementations/Item/PeopleRepository.cs @@ -117,13 +117,17 @@ public class PeopleRepository(IDbContextFactory<JellyfinDbContext> dbProvider, I person.Role = person.Role?.Trim() ?? string.Empty; } + // Project the values every comparison below needs once, so neither the case folding nor the + // enum formatting is repeated per candidate. + var credits = people.Select(e => (Person: e, LoweredName: e.Name.ToLowerInvariant(), PersonType: e.Type.ToString(), LoweredRole: e.Role.ToLowerInvariant())); + // multiple metadata providers can provide the _same_ credit; dedupe case-insensitively. // The role is part of the key because one person can hold several credits of the same type // on an item, e.g. a Writer credited for both the Novel and the Screenplay. - people = people.DistinctBy(e => e.Name.ToLowerInvariant() + "-" + e.Type + "-" + e.Role.ToLowerInvariant()).ToArray(); + var distinctCredits = credits.DistinctBy(e => (e.LoweredName, e.PersonType, e.LoweredRole)).ToArray(); - var distinctPersons = people.DistinctBy(e => e.Name.ToLowerInvariant() + "-" + e.Type).ToArray(); - var personKeys = distinctPersons.Select(e => e.Name.ToLowerInvariant() + "-" + e.Type).ToArray(); + var distinctPersons = distinctCredits.DistinctBy(e => (e.LoweredName, e.PersonType)).ToArray(); + var personKeys = distinctPersons.Select(e => e.LoweredName + "-" + e.PersonType).ToArray(); using var context = _dbProvider.CreateDbContext(); using var transaction = context.Database.BeginTransaction(); @@ -136,24 +140,44 @@ public class PeopleRepository(IDbContextFactory<JellyfinDbContext> dbProvider, I .Select(f => f.item) .ToArray(); + var existingPersonKeys = existingPersons.Select(e => (e.Name.ToLowerInvariant(), e.PersonType ?? string.Empty)).ToHashSet(); + var toAdd = distinctPersons - .Where(e => !existingPersons.Any(f => string.Equals(f.Name, e.Name, StringComparison.OrdinalIgnoreCase) && f.PersonType == e.Type.ToString())) - .Select(Map) + .Where(e => !existingPersonKeys.Contains((e.LoweredName, e.PersonType))) + .Select(e => Map(e.Person)) .ToArray(); context.Peoples.AddRange(toAdd); context.SaveChanges(); - var personsEntities = toAdd.Concat(existingPersons).ToArray(); + // The Peoples table can hold case-only duplicates, so keep the first match per key just as + // the previous First() lookup did. + var personsEntities = new Dictionary<(string LoweredName, string PersonType), People>(); + foreach (var entity in toAdd.Concat(existingPersons)) + { + personsEntities.TryAdd((entity.Name.ToLowerInvariant(), entity.PersonType ?? string.Empty), entity); + } var existingMaps = context.PeopleBaseItemMap.Include(e => e.People).Where(e => e.ItemId == itemId).ToList(); + var existingMapsByCredit = new Dictionary<(string LoweredName, string PersonType, string LoweredRole), PeopleBaseItemMap>(); + foreach (var map in existingMaps) + { + existingMapsByCredit.TryAdd((map.People.Name.ToLowerInvariant(), map.People.PersonType ?? string.Empty, map.Role?.ToLowerInvariant() ?? string.Empty), map); + } var listOrder = 0; - foreach (var person in people) + foreach (var credit in distinctCredits) { - var entityPerson = personsEntities.First(e => string.Equals(e.Name, person.Name, StringComparison.OrdinalIgnoreCase) && e.PersonType == person.Type.ToString()); - var existingMap = existingMaps.FirstOrDefault(e => string.Equals(e.People.Name, person.Name, StringComparison.OrdinalIgnoreCase) && e.People.PersonType == person.Type.ToString() && e.Role == person.Role); - if (existingMap is null) + var entityPerson = personsEntities[(credit.LoweredName, credit.PersonType)]; + if (existingMapsByCredit.TryGetValue((credit.LoweredName, credit.PersonType, credit.LoweredRole), out var existingMap)) + { + // Update the order for existing mappings + existingMap.ListOrder = listOrder; + existingMap.SortOrder = credit.Person.SortOrder; + // person mapping already exists so remove from list + existingMaps.Remove(existingMap); + } + else { context.PeopleBaseItemMap.Add(new PeopleBaseItemMap() { @@ -162,18 +186,10 @@ public class PeopleRepository(IDbContextFactory<JellyfinDbContext> dbProvider, I People = null!, PeopleId = entityPerson.Id, ListOrder = listOrder, - SortOrder = person.SortOrder, - Role = person.Role + SortOrder = credit.Person.SortOrder, + Role = credit.Person.Role }); } - else - { - // Update the order for existing mappings - existingMap.ListOrder = listOrder; - existingMap.SortOrder = person.SortOrder; - // person mapping already exists so remove from list - existingMaps.Remove(existingMap); - } listOrder++; } |
