diff --git a/API/Data/Repositories/PersonRepository.cs b/API/Data/Repositories/PersonRepository.cs index cc81713e3..0882ec4c3 100644 --- a/API/Data/Repositories/PersonRepository.cs +++ b/API/Data/Repositories/PersonRepository.cs @@ -17,6 +17,7 @@ namespace API.Data.Repositories; public interface IPersonRepository { void Attach(Person person); + void Attach(IEnumerable person); void Remove(Person person); void Remove(ChapterPeople person); void Remove(SeriesMetadataPeople person); @@ -41,6 +42,7 @@ public interface IPersonRepository Task> GetSeriesKnownFor(int personId); Task> GetChaptersForPersonByRole(int personId, int userId, PersonRole role); + Task> GetPeopleByNames(List normalizedNames); } public class PersonRepository : IPersonRepository @@ -59,6 +61,11 @@ public void Attach(Person person) _context.Person.Attach(person); } + public void Attach(IEnumerable person) + { + _context.Person.AttachRange(person); + } + public void Remove(Person person) { _context.Person.Remove(person); @@ -259,6 +266,14 @@ public async Task> GetChaptersForPersonByRole(int person .ToListAsync(); } + public async Task> GetPeopleByNames(List normalizedNames) + { + return await _context.Person + .Where(p => normalizedNames.Contains(p.NormalizedName)) + .OrderBy(p => p.Name) + .ToListAsync(); + } + public async Task> GetAllPeople() { diff --git a/API/Data/Repositories/SeriesRepository.cs b/API/Data/Repositories/SeriesRepository.cs index 0c982f1e6..9eef9483d 100644 --- a/API/Data/Repositories/SeriesRepository.cs +++ b/API/Data/Repositories/SeriesRepository.cs @@ -44,6 +44,9 @@ public enum SeriesIncludes { None = 1, Volumes = 2, + /// + /// This will include all necessary includes + /// Metadata = 4, Related = 8, Library = 16, @@ -51,8 +54,7 @@ public enum SeriesIncludes ExternalReviews = 64, ExternalRatings = 128, ExternalRecommendations = 256, - ExternalMetadata = 512 - + ExternalMetadata = 512, } /// @@ -533,14 +535,6 @@ public async Task SearchSeries(int userId, bool isAdmin, I .SingleOrDefaultAsync(); } - public async Task GetSeriesByIdForUserAsync(int seriesId, int userId, SeriesIncludes includes = SeriesIncludes.Volumes | SeriesIncludes.Metadata) - { - return await _context.Series - .Where(s => s.Id == seriesId) - .Includes(includes) - .SingleOrDefaultAsync(); - } - /// /// Returns Full Series including all external links /// diff --git a/API/Extensions/QueryExtensions/IncludesExtensions.cs b/API/Extensions/QueryExtensions/IncludesExtensions.cs index fa342d0f0..983f6798e 100644 --- a/API/Extensions/QueryExtensions/IncludesExtensions.cs +++ b/API/Extensions/QueryExtensions/IncludesExtensions.cs @@ -162,17 +162,16 @@ public static IQueryable Includes(this IQueryable query, if (includeFlags.HasFlag(SeriesIncludes.Metadata)) { - query = query.Include(s => s.Metadata) - .ThenInclude(m => m.CollectionTags.OrderBy(g => g.NormalizedTitle)) + query = query .Include(s => s.Metadata) .ThenInclude(m => m.Genres.OrderBy(g => g.NormalizedTitle)) .Include(s => s.Metadata) .ThenInclude(m => m.People) + .ThenInclude(smp => smp.Person) .Include(s => s.Metadata) .ThenInclude(m => m.Tags.OrderBy(g => g.NormalizedTitle)); } - return query.AsSplitQuery(); } diff --git a/API/Helpers/PersonHelper.cs b/API/Helpers/PersonHelper.cs index 288670f8f..f03a95a7b 100644 --- a/API/Helpers/PersonHelper.cs +++ b/API/Helpers/PersonHelper.cs @@ -6,6 +6,7 @@ using API.DTOs; using API.Entities; using API.Entities.Enums; +using API.Entities.Metadata; using API.Extensions; using API.Helpers.Builders; @@ -16,6 +17,134 @@ namespace API.Helpers; public static class PersonHelper { + public static async Task UpdateSeriesMetadataPeopleAsync(SeriesMetadata metadata, ICollection metadataPeople, + IEnumerable chapterPeople, PersonRole role, IUnitOfWork unitOfWork) + { + // Normalize and group by person + var peopleToAdd = chapterPeople + .Where(cp => cp.Role == role) + .Select(cp => cp.Person) + .ToList(); + + var modification = false; + + // Remove any people who are not part of the new list + var peopleToRemove = metadataPeople + .Where(mp => mp.Role == role && peopleToAdd.TrueForAll(p => p.NormalizedName != mp.Person.NormalizedName)) + .ToList(); + + foreach (var personToRemove in peopleToRemove) + { + metadataPeople.Remove(personToRemove); + modification = true; + } + + // Add new people if they do not already exist + foreach (var person in peopleToAdd) + { + var existingPerson = metadataPeople + .FirstOrDefault(mp => mp.Person.NormalizedName == person.NormalizedName && mp.Role == role); + + if (existingPerson == null) + { + // Check if the person already exists in the database + var dbPerson = await unitOfWork.PersonRepository.GetPersonByName(person.Name); + + if (dbPerson == null) + { + // Create a new Person entity if not found in the database + dbPerson = new PersonBuilder(person.Name).Build(); + + // Attach and save the new Person entity + unitOfWork.DataContext.Person.Attach(dbPerson); + await unitOfWork.CommitAsync(); + } + + // Add the person to the SeriesMetadataPeople collection + metadataPeople.Add(new SeriesMetadataPeople + { + PersonId = dbPerson.Id, + Person = dbPerson, + SeriesMetadataId = metadata.Id, + SeriesMetadata = metadata, + Role = role + }); + modification = true; + } + } + + // Commit the changes if any modifications were made + if (modification) + { + await unitOfWork.CommitAsync(); + } + } + + + public static async Task UpdateSeriesMetadataPeopleAsync(SeriesMetadata metadata, ICollection metadataPeople, + IEnumerable seriesPeople, PersonRole role, IUnitOfWork unitOfWork) + { + // Normalize and group by person + var peopleToAdd = seriesPeople + .Where(cp => cp.Role == role) + .Select(cp => cp.Person) + .ToList(); + + var modification = false; + + // Remove any people who are not part of the new list + var peopleToRemove = metadataPeople + .Where(mp => mp.Role == role && peopleToAdd.TrueForAll(p => p.NormalizedName != mp.Person.NormalizedName)) + .ToList(); + + foreach (var personToRemove in peopleToRemove) + { + metadataPeople.Remove(personToRemove); + modification = true; + } + + // Add new people if they do not already exist + foreach (var person in peopleToAdd) + { + var existingPerson = metadataPeople + .FirstOrDefault(mp => mp.Person.NormalizedName == person.NormalizedName && mp.Role == role); + + if (existingPerson == null) + { + // Check if the person already exists in the database + var dbPerson = await unitOfWork.PersonRepository.GetPersonByName(person.Name); + + if (dbPerson == null) + { + // Create a new Person entity if not found in the database + dbPerson = new PersonBuilder(person.Name).Build(); + + // Attach and save the new Person entity + unitOfWork.DataContext.Person.Attach(dbPerson); + await unitOfWork.CommitAsync(); + } + + // Add the person to the SeriesMetadataPeople collection + metadataPeople.Add(new SeriesMetadataPeople + { + PersonId = dbPerson.Id, + Person = dbPerson, + SeriesMetadataId = metadata.Id, + SeriesMetadata = metadata, + Role = role + }); + modification = true; + } + } + + // Commit the changes if any modifications were made + if (modification) + { + await unitOfWork.CommitAsync(); + } + } + + public static async Task UpdateChapterPeopleAsync(Chapter chapter, IList people, PersonRole role, IUnitOfWork unitOfWork) { diff --git a/API/Services/SeriesService.cs b/API/Services/SeriesService.cs index cd3f5ae56..9140d26e6 100644 --- a/API/Services/SeriesService.cs +++ b/API/Services/SeriesService.cs @@ -111,7 +111,7 @@ public async Task UpdateSeriesMetadata(UpdateSeriesMetadataDto updateSerie try { var seriesId = updateSeriesMetadataDto.SeriesMetadata.SeriesId; - var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId); + var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId, SeriesIncludes.Metadata); if (series == null) return false; series.Metadata ??= new SeriesMetadataBuilder() @@ -199,81 +199,83 @@ public async Task UpdateSeriesMetadata(UpdateSeriesMetadataDto updateSerie if (updateSeriesMetadataDto.SeriesMetadata != null) { - // TODO: Implement People support - // if (PersonHelper.HasAnyPeople(updateSeriesMetadataDto.SeriesMetadata)) - // { - // void HandleAddPerson(Person person) - // { - // PersonHelper.AddPersonIfNotExists(series.Metadata.People, person); - // } - // - // - // series.Metadata.People ??= new List(); - // var allWriters = await _unitOfWork.PersonRepository.GetAllPeopleByRoleAndNames(PersonRole.Writer, - // updateSeriesMetadataDto.SeriesMetadata!.Writers.Select(p => Parser.Normalize(p.Name))); - // PersonHelper.UpdatePeopleList(PersonRole.Writer, updateSeriesMetadataDto.SeriesMetadata.Writers, series, allWriters.AsReadOnly(), - // HandleAddPerson, () => series.Metadata.WriterLocked = true); - // - // var allCharacters = await _unitOfWork.PersonRepository.GetAllPeopleByRoleAndNames(PersonRole.Character, - // updateSeriesMetadataDto.SeriesMetadata!.Characters.Select(p => Parser.Normalize(p.Name))); - // PersonHelper.UpdatePeopleList(PersonRole.Character, updateSeriesMetadataDto.SeriesMetadata.Characters, series, allCharacters.AsReadOnly(), - // HandleAddPerson, () => series.Metadata.CharacterLocked = true); - // - // var allColorists = await _unitOfWork.PersonRepository.GetAllPeopleByRoleAndNames(PersonRole.Colorist, - // updateSeriesMetadataDto.SeriesMetadata!.Colorists.Select(p => Parser.Normalize(p.Name))); - // PersonHelper.UpdatePeopleList(PersonRole.Colorist, updateSeriesMetadataDto.SeriesMetadata.Colorists, series, allColorists.AsReadOnly(), - // HandleAddPerson, () => series.Metadata.ColoristLocked = true); - // - // var allEditors = await _unitOfWork.PersonRepository.GetAllPeopleByRoleAndNames(PersonRole.Editor, - // updateSeriesMetadataDto.SeriesMetadata!.Editors.Select(p => Parser.Normalize(p.Name))); - // PersonHelper.UpdatePeopleList(PersonRole.Editor, updateSeriesMetadataDto.SeriesMetadata.Editors, series, allEditors.AsReadOnly(), - // HandleAddPerson, () => series.Metadata.EditorLocked = true); - // - // var allInkers = await _unitOfWork.PersonRepository.GetAllPeopleByRoleAndNames(PersonRole.Inker, - // updateSeriesMetadataDto.SeriesMetadata!.Inkers.Select(p => Parser.Normalize(p.Name))); - // PersonHelper.UpdatePeopleList(PersonRole.Inker, updateSeriesMetadataDto.SeriesMetadata.Inkers, series, allInkers.AsReadOnly(), - // HandleAddPerson, () => series.Metadata.InkerLocked = true); - // - // var allLetterers = await _unitOfWork.PersonRepository.GetAllPeopleByRoleAndNames(PersonRole.Letterer, - // updateSeriesMetadataDto.SeriesMetadata!.Letterers.Select(p => Parser.Normalize(p.Name))); - // PersonHelper.UpdatePeopleList(PersonRole.Letterer, updateSeriesMetadataDto.SeriesMetadata.Letterers, series, allLetterers.AsReadOnly(), - // HandleAddPerson, () => series.Metadata.LettererLocked = true); - // - // var allPencillers = await _unitOfWork.PersonRepository.GetAllPeopleByRoleAndNames(PersonRole.Penciller, - // updateSeriesMetadataDto.SeriesMetadata!.Pencillers.Select(p => Parser.Normalize(p.Name))); - // PersonHelper.UpdatePeopleList(PersonRole.Penciller, updateSeriesMetadataDto.SeriesMetadata.Pencillers, series, allPencillers.AsReadOnly(), - // HandleAddPerson, () => series.Metadata.PencillerLocked = true); - // - // var allPublishers = await _unitOfWork.PersonRepository.GetAllPeopleByRoleAndNames(PersonRole.Publisher, - // updateSeriesMetadataDto.SeriesMetadata!.Publishers.Select(p => Parser.Normalize(p.Name))); - // PersonHelper.UpdatePeopleList(PersonRole.Publisher, updateSeriesMetadataDto.SeriesMetadata.Publishers, series, allPublishers.AsReadOnly(), - // HandleAddPerson, () => series.Metadata.PublisherLocked = true); - // - // var allImprints = await _unitOfWork.PersonRepository.GetAllPeopleByRoleAndNames(PersonRole.Imprint, - // updateSeriesMetadataDto.SeriesMetadata!.Imprints.Select(p => Parser.Normalize(p.Name))); - // PersonHelper.UpdatePeopleList(PersonRole.Imprint, updateSeriesMetadataDto.SeriesMetadata.Imprints, series, allImprints.AsReadOnly(), - // HandleAddPerson, () => series.Metadata.ImprintLocked = true); - // - // var allTeams = await _unitOfWork.PersonRepository.GetAllPeopleByRoleAndNames(PersonRole.Team, - // updateSeriesMetadataDto.SeriesMetadata!.Imprints.Select(p => Parser.Normalize(p.Name))); - // PersonHelper.UpdatePeopleList(PersonRole.Team, updateSeriesMetadataDto.SeriesMetadata.Teams, series, allTeams.AsReadOnly(), - // HandleAddPerson, () => series.Metadata.TeamLocked = true); - // - // var allLocations = await _unitOfWork.PersonRepository.GetAllPeopleByRoleAndNames(PersonRole.Location, - // updateSeriesMetadataDto.SeriesMetadata!.Imprints.Select(p => Parser.Normalize(p.Name))); - // PersonHelper.UpdatePeopleList(PersonRole.Location, updateSeriesMetadataDto.SeriesMetadata.Locations, series, allLocations.AsReadOnly(), - // HandleAddPerson, () => series.Metadata.LocationLocked = true); - // - // var allTranslators = await _unitOfWork.PersonRepository.GetAllPeopleByRoleAndNames(PersonRole.Translator, - // updateSeriesMetadataDto.SeriesMetadata!.Translators.Select(p => Parser.Normalize(p.Name))); - // PersonHelper.UpdatePeopleList(PersonRole.Translator, updateSeriesMetadataDto.SeriesMetadata.Translators, series, allTranslators.AsReadOnly(), - // HandleAddPerson, () => series.Metadata.TranslatorLocked = true); - // - // var allCoverArtists = await _unitOfWork.PersonRepository.GetAllPeopleByRoleAndNames(PersonRole.CoverArtist, - // updateSeriesMetadataDto.SeriesMetadata!.CoverArtists.Select(p => Parser.Normalize(p.Name))); - // PersonHelper.UpdatePeopleList(PersonRole.CoverArtist, updateSeriesMetadataDto.SeriesMetadata.CoverArtists, series, allCoverArtists.AsReadOnly(), - // HandleAddPerson, () => series.Metadata.CoverArtistLocked = true); - // } + if (PersonHelper.HasAnyPeople(updateSeriesMetadataDto.SeriesMetadata)) + { + series.Metadata.People ??= new List(); + + // Writers + if (!series.Metadata.WriterLocked) + { + await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Writers, PersonRole.Writer); + } + + // Cover Artists + if (!series.Metadata.CoverArtistLocked) + { + await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.CoverArtists, PersonRole.CoverArtist); + } + + // Colorists + if (!series.Metadata.ColoristLocked) + { + await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Colorists, PersonRole.Colorist); + } + + // Editors + if (!series.Metadata.EditorLocked) + { + await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Editors, PersonRole.Editor); + } + + // Inkers + if (!series.Metadata.InkerLocked) + { + await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Inkers, PersonRole.Inker); + } + + // Letterers + if (!series.Metadata.LettererLocked) + { + await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Letterers, PersonRole.Letterer); + } + + // Pencillers + if (!series.Metadata.PencillerLocked) + { + await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Pencillers, PersonRole.Penciller); + } + + // Publishers + if (!series.Metadata.PublisherLocked) + { + await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Publishers, PersonRole.Publisher); + } + + // Imprints + if (!series.Metadata.ImprintLocked) + { + await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Imprints, PersonRole.Imprint); + } + + // Teams + if (!series.Metadata.TeamLocked) + { + await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Teams, PersonRole.Team); + } + + // Locations + if (!series.Metadata.LocationLocked) + { + await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Locations, PersonRole.Location); + } + + // Translators + if (!series.Metadata.TranslatorLocked) + { + await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Translators, PersonRole.Translator); + } + + } series.Metadata.AgeRatingLocked = updateSeriesMetadataDto.SeriesMetadata.AgeRatingLocked; series.Metadata.PublicationStatusLocked = updateSeriesMetadataDto.SeriesMetadata.PublicationStatusLocked; @@ -323,6 +325,90 @@ public async Task UpdateSeriesMetadata(UpdateSeriesMetadataDto updateSerie return false; } + /// + /// Exclusively for Series Update API + /// + /// + /// + /// + private async Task HandlePeopleUpdateAsync(SeriesMetadata metadata, ICollection peopleDtos, PersonRole role) + { + // Normalize all names from the DTOs + var normalizedNames = peopleDtos.Select(p => Parser.Normalize(p.Name)).ToList(); + + // Bulk select people who already exist in the database + var existingPeople = await _unitOfWork.PersonRepository.GetPeopleByNames(normalizedNames); + + // Use a dictionary for quick lookups + var existingPeopleDictionary = existingPeople.ToDictionary(p => p.NormalizedName, p => p); + + // List to track people that will be added to the metadata + var peopleToAdd = new List(); + + foreach (var personDto in peopleDtos) + { + var normalizedPersonName = Parser.Normalize(personDto.Name); + + // Check if the person exists in the dictionary + if (existingPeopleDictionary.TryGetValue(normalizedPersonName, out _)) continue; + + // Person doesn't exist, so create a new one + var newPerson = new Person + { + Name = personDto.Name, + NormalizedName = normalizedPersonName + }; + + peopleToAdd.Add(newPerson); + existingPeopleDictionary[normalizedPersonName] = newPerson; + } + + // Add any new people to the database in bulk + if (peopleToAdd.Count != 0) + { + _unitOfWork.PersonRepository.Attach(peopleToAdd); + } + + // Now that we have all the people (new and existing), update the SeriesMetadataPeople + UpdateSeriesMetadataPeople(metadata, metadata.People, existingPeopleDictionary.Values, role); + } + + private static void UpdateSeriesMetadataPeople(SeriesMetadata metadata, ICollection metadataPeople, IEnumerable people, PersonRole role) + { + var peopleToAdd = people.ToList(); + + // Remove any people in the existing metadataPeople for this role that are no longer present in the input list + var peopleToRemove = metadataPeople + .Where(mp => mp.Role == role && peopleToAdd.TrueForAll(p => p.NormalizedName != mp.Person.NormalizedName)) + .ToList(); + + foreach (var personToRemove in peopleToRemove) + { + metadataPeople.Remove(personToRemove); + } + + // Add new people for this role if they don't already exist + foreach (var person in peopleToAdd) + { + var existingPersonEntry = metadataPeople + .FirstOrDefault(mp => mp.Person.NormalizedName == person.NormalizedName && mp.Role == role); + + if (existingPersonEntry == null) + { + metadataPeople.Add(new SeriesMetadataPeople + { + PersonId = person.Id, + Person = person, + SeriesMetadataId = metadata.Id, + SeriesMetadata = metadata, + Role = role + }); + } + } + } + + + /// /// /// @@ -386,6 +472,7 @@ public async Task DeleteMultipleSeries(IList seriesIds) allChapterIds.AddRange(mapping.Value); } + // NOTE: This isn't getting all the people and whatnot currently var series = await _unitOfWork.SeriesRepository.GetSeriesByIdsAsync(seriesIds); _unitOfWork.SeriesRepository.Remove(series); diff --git a/API/Services/Tasks/Scanner/ProcessSeries.cs b/API/Services/Tasks/Scanner/ProcessSeries.cs index e380503e4..abc5749ad 100644 --- a/API/Services/Tasks/Scanner/ProcessSeries.cs +++ b/API/Services/Tasks/Scanner/ProcessSeries.cs @@ -371,79 +371,79 @@ private async Task UpdateSeriesMetadata(Series series, Library library) if (!series.Metadata.WriterLocked) { var chapterPeople = chapters.SelectMany(c => c.People.Where(p => p.Role == PersonRole.Writer)).ToList(); - UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Writer); + await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Writer); } if (!series.Metadata.ColoristLocked) { var chapterPeople = chapters.SelectMany(c => c.People.Where(p => p.Role == PersonRole.Colorist)).ToList(); - UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Colorist); + await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Colorist); } if (!series.Metadata.PublisherLocked) { var chapterPeople = chapters.SelectMany(c => c.People.Where(p => p.Role == PersonRole.Publisher)).ToList(); - UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Publisher); + await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Publisher); } if (!series.Metadata.CoverArtistLocked) { var chapterPeople = chapters.SelectMany(c => c.People.Where(p => p.Role == PersonRole.CoverArtist)).ToList(); - UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.CoverArtist); + await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.CoverArtist); } if (!series.Metadata.CharacterLocked) { var chapterPeople = chapters.SelectMany(c => c.People.Where(p => p.Role == PersonRole.Character)).ToList(); - UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Character); + await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Character); } if (!series.Metadata.EditorLocked) { var chapterPeople = chapters.SelectMany(c => c.People.Where(p => p.Role == PersonRole.Editor)).ToList(); - UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Editor); + await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Editor); } if (!series.Metadata.InkerLocked) { var chapterPeople = chapters.SelectMany(c => c.People.Where(p => p.Role == PersonRole.Inker)).ToList(); - UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Inker); + await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Inker); } if (!series.Metadata.ImprintLocked) { var chapterPeople = chapters.SelectMany(c => c.People.Where(p => p.Role == PersonRole.Imprint)).ToList(); - UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Imprint); + await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Imprint); } if (!series.Metadata.TeamLocked) { var chapterPeople = chapters.SelectMany(c => c.People.Where(p => p.Role == PersonRole.Team)).ToList(); - UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Team); + await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Team); } if (!series.Metadata.LocationLocked) { var chapterPeople = chapters.SelectMany(c => c.People.Where(p => p.Role == PersonRole.Location)).ToList(); - UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Location); + await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Location); } if (!series.Metadata.LettererLocked) { var chapterPeople = chapters.SelectMany(c => c.People.Where(p => p.Role == PersonRole.Letterer)).ToList(); - UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Letterer); + await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Letterer); } if (!series.Metadata.PencillerLocked) { var chapterPeople = chapters.SelectMany(c => c.People.Where(p => p.Role == PersonRole.Penciller)).ToList(); - UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Penciller); + await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Penciller); } if (!series.Metadata.TranslatorLocked) { var chapterPeople = chapters.SelectMany(c => c.People.Where(p => p.Role == PersonRole.Translator)).ToList(); - UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Translator); + await UpdateSeriesMetadataPeople(series.Metadata, series.Metadata.People, chapterPeople, PersonRole.Translator); } @@ -523,42 +523,10 @@ private static void UpdateSeriesMetadataGenres(ICollection metadataGenres - private static void UpdateSeriesMetadataPeople(SeriesMetadata metadata, ICollection metadataPeople, IEnumerable chapterPeople, PersonRole role) + private async Task UpdateSeriesMetadataPeople(SeriesMetadata metadata, ICollection metadataPeople, + IEnumerable chapterPeople, PersonRole role) { - // Normalize and group by person - var peopleToAdd = chapterPeople - .Where(cp => cp.Role == role) - .Select(cp => cp.Person) - .ToList(); - - // Remove any people who are not part of the new list - var peopleToRemove = metadataPeople - .Where(mp => mp.Role == role && peopleToAdd.TrueForAll(p => p.NormalizedName != mp.Person.NormalizedName)) - .ToList(); - - foreach (var personToRemove in peopleToRemove) - { - metadataPeople.Remove(personToRemove); - } - - // Add new people if they do not already exist - foreach (var person in peopleToAdd) - { - var existingPerson = metadataPeople - .FirstOrDefault(mp => mp.Person.NormalizedName == person.NormalizedName && mp.Role == role); - - if (existingPerson == null) - { - metadataPeople.Add(new SeriesMetadataPeople - { - PersonId = person.Id, - Person = person, - SeriesMetadataId = metadata.Id, - SeriesMetadata = metadata, - Role = role - }); - } - } + await PersonHelper.UpdateSeriesMetadataPeopleAsync(metadata, metadataPeople, chapterPeople, role, _unitOfWork); } private void DeterminePublicationStatus(Series series, List chapters)