More Metadata Stuff (#3537)

This commit is contained in:
Joe Milazzo 2025-02-08 15:37:12 -06:00 committed by GitHub
parent 8d3dcc637e
commit 53b13da0c9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
34 changed files with 4123 additions and 129 deletions

View file

@ -45,7 +45,7 @@ public interface IExternalMetadataService
/// <param name="seriesId"></param>
/// <param name="libraryType"></param>
/// <returns></returns>
Task GetNewSeriesData(int seriesId, LibraryType libraryType);
Task FetchSeriesMetadata(int seriesId, LibraryType libraryType);
Task<IList<MalStackDto>> GetStacksForUser(int userId);
Task<IList<ExternalSeriesMatchDto>> MatchSeries(MatchSeriesDto dto);
@ -118,7 +118,7 @@ public class ExternalMetadataService : IExternalMetadataService
foreach (var seriesId in ids)
{
var libraryType = libTypes[seriesId];
await GetNewSeriesData(seriesId, libraryType);
await FetchSeriesMetadata(seriesId, libraryType);
await Task.Delay(1500);
count++;
}
@ -131,7 +131,7 @@ public class ExternalMetadataService : IExternalMetadataService
/// </summary>
/// <param name="seriesId"></param>
/// <param name="libraryType"></param>
public async Task GetNewSeriesData(int seriesId, LibraryType libraryType)
public async Task FetchSeriesMetadata(int seriesId, LibraryType libraryType)
{
if (!IsPlusEligible(libraryType)) return;
if (!await _licenseService.HasActiveLicense()) return;
@ -146,8 +146,9 @@ public class ExternalMetadataService : IExternalMetadataService
}
_logger.LogDebug("Prefetching Kavita+ data for Series {SeriesId}", seriesId);
// Prefetch SeriesDetail data
var metadata = await GetSeriesDetailPlus(seriesId, libraryType);
await GetSeriesDetailPlus(seriesId, libraryType);
}
@ -211,7 +212,7 @@ public class ExternalMetadataService : IExternalMetadataService
Format = series.Format == MangaFormat.Epub ? PlusMediaFormat.LightNovel : PlusMediaFormat.Manga,
Query = dto.Query,
SeriesName = series.Name,
AlternativeNames = altNames,
AlternativeNames = altNames.Where(s => !string.IsNullOrEmpty(s)).ToList(),
Year = series.Metadata.ReleaseYear,
AniListId = potentialAnilistId ?? ScrobblingService.GetAniListId(series),
MalId = potentialMalId ?? ScrobblingService.GetMalId(series),
@ -403,7 +404,7 @@ public class ExternalMetadataService : IExternalMetadataService
var result = await (Configuration.KavitaPlusApiUrl + "/api/metadata/v2/series-detail")
.WithKavitaPlusHeaders(license)
.PostJsonAsync(data)
.ReceiveJson<SeriesDetailPlusApiDto>();
.ReceiveJson<SeriesDetailPlusApiDto>(); // This returns an AniListSeries and Match returns ExternalSeriesDto
// Clear out existing results
@ -446,6 +447,8 @@ public class ExternalMetadataService : IExternalMetadataService
var madeMetadataModification = false;
if (result.Series != null && series.Library.AllowMetadataMatching)
{
externalSeriesMetadata.Series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId);
madeMetadataModification = await WriteExternalMetadataToSeries(result.Series, seriesId);
if (madeMetadataModification)
{
@ -499,21 +502,41 @@ public class ExternalMetadataService : IExternalMetadataService
{
var settings = await _unitOfWork.SettingsRepository.GetMetadataSettingDto();
if (!settings.Enabled) return false;
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId, SeriesIncludes.Metadata | SeriesIncludes.Related);
if (series == null) return false;
var defaultAdmin = await _unitOfWork.UserRepository.GetDefaultAdminUser();
_logger.LogInformation("Writing External metadata to Series {SeriesName}", series.Name);
var madeModification = false;
if (!series.Metadata.SummaryLocked && string.IsNullOrEmpty(series.Metadata.Summary) && settings.EnableSummary)
if (settings.EnableLocalizedName && (!series.LocalizedNameLocked || settings.HasOverride(MetadataSettingField.LocalizedName)))
{
// We need to make the best appropriate guess
if (externalMetadata.Name == series.Name)
{
// Choose closest (usually last) synonym
series.LocalizedName = externalMetadata.Synonyms.Last();
}
else
{
series.LocalizedName = externalMetadata.Name;
}
madeModification = true;
}
if (settings.EnableSummary && (!series.Metadata.SummaryLocked ||
settings.HasOverride(MetadataSettingField.Summary)))
{
series.Metadata.Summary = CleanSummary(externalMetadata.Summary);
madeModification = true;
}
if (settings.EnableStartDate && !series.Metadata.ReleaseYearLocked && externalMetadata.StartDate.HasValue)
if (settings.EnableStartDate && externalMetadata.StartDate.HasValue && (!series.Metadata.ReleaseYearLocked ||
settings.HasOverride(MetadataSettingField.StartDate)))
{
series.Metadata.ReleaseYear = externalMetadata.StartDate.Value.Year;
madeModification = true;
@ -543,7 +566,7 @@ public class ExternalMetadataService : IExternalMetadataService
.Where(g => !settings.Blacklist.Contains(g))
.ToList();
if (settings.EnableGenres && !series.Metadata.GenresLocked && processedGenres.Count > 0)
if (settings.EnableGenres && processedGenres.Count > 0 && (!series.Metadata.GenresLocked || settings.HasOverride(MetadataSettingField.Genres)))
{
_logger.LogDebug("Found {GenreCount} genres for {SeriesName}", processedGenres.Count, series.Name);
var allGenres = (await _unitOfWork.GenreRepository.GetAllGenresByNamesAsync(processedGenres.Select(Parser.Normalize))).ToList();
@ -578,7 +601,7 @@ public class ExternalMetadataService : IExternalMetadataService
.ToList();
// Set the tags for the series and ensure they are in the DB
if (settings.EnableTags && !series.Metadata.TagsLocked && processedTags.Count > 0)
if (settings.EnableTags && processedTags.Count > 0 && (!series.Metadata.TagsLocked || settings.HasOverride(MetadataSettingField.Tags)))
{
_logger.LogDebug("Found {TagCount} tags for {SeriesName}", processedTags.Count, series.Name);
var allTags = (await _unitOfWork.TagRepository.GetAllTagsByNameAsync(processedTags.Select(Parser.Normalize)))
@ -596,7 +619,7 @@ public class ExternalMetadataService : IExternalMetadataService
#region Age Rating
if (!series.Metadata.AgeRatingLocked)
if (!series.Metadata.AgeRatingLocked || settings.HasOverride(MetadataSettingField.AgeRating))
{
try
{
@ -644,64 +667,92 @@ public class ExternalMetadataService : IExternalMetadataService
// Roles: Character Design, Story, Art
var allWriters = externalMetadata.Staff
var upstreamWriters = externalMetadata.Staff
.Where(s => s.Role is "Story" or "Story & Art")
.ToList();
var writers = allWriters
var writers = upstreamWriters
.Select(w => new PersonDto()
{
Name = w.Name,
AniListId = ScrobblingService.ExtractId<int>(w.Url, ScrobblingService.AniListStaffWebsite),
Description = CleanSummary(w.Description),
})
.Concat(series.Metadata.People.Where(p => p.Role == PersonRole.Writer).Select(p => _mapper.Map<PersonDto>(p)))
.Concat(series.Metadata.People
.Where(p => p.Role == PersonRole.Writer)
.Where(p => !p.KavitaPlusConnection)
.Select(p => _mapper.Map<PersonDto>(p.Person))
)
.DistinctBy(p => Parser.Normalize(p.Name))
.ToList();
// NOTE: PersonRoles can be a hashset
if (!series.Metadata.WriterLocked && writers.Count > 0 && settings.PersonRoles.Contains(PersonRole.Writer))
if (writers.Count > 0 && settings.IsPersonAllowed(PersonRole.Writer) && (!series.Metadata.WriterLocked || settings.HasOverride(MetadataSettingField.People)))
{
await SeriesService.HandlePeopleUpdateAsync(series.Metadata, writers, PersonRole.Writer, _unitOfWork);
foreach (var person in series.Metadata.People.Where(p => p.Role == PersonRole.Writer))
{
var meta = upstreamWriters.FirstOrDefault(c => c.Name == person.Person.Name);
person.OrderWeight = 0;
if (meta != null)
{
person.KavitaPlusConnection = true;
}
}
_unitOfWork.SeriesRepository.Update(series);
await _unitOfWork.CommitAsync();
await DownloadAndSetCovers(allWriters);
await DownloadAndSetCovers(upstreamWriters);
madeModification = true;
}
var allArtists = externalMetadata.Staff
var upstreamArtists = externalMetadata.Staff
.Where(s => s.Role is "Art" or "Story & Art")
.ToList();
var artists = allArtists
var artists = upstreamArtists
.Select(w => new PersonDto()
{
Name = w.Name,
AniListId = ScrobblingService.ExtractId<int>(w.Url, ScrobblingService.AniListStaffWebsite),
Description = CleanSummary(w.Description),
})
.Concat(series.Metadata.People.Where(p => p.Role == PersonRole.CoverArtist).Select(p => _mapper.Map<PersonDto>(p)))
.Concat(series.Metadata.People
.Where(p => p.Role == PersonRole.CoverArtist)
.Where(p => !p.KavitaPlusConnection)
.Select(p => _mapper.Map<PersonDto>(p.Person))
)
.DistinctBy(p => Parser.Normalize(p.Name))
.ToList();
if (!series.Metadata.CoverArtistLocked && artists.Count > 0 && settings.PersonRoles.Contains(PersonRole.CoverArtist))
if (artists.Count > 0 && settings.IsPersonAllowed(PersonRole.CoverArtist) && (!series.Metadata.CoverArtistLocked || settings.HasOverride(MetadataSettingField.People)))
{
await SeriesService.HandlePeopleUpdateAsync(series.Metadata, artists, PersonRole.CoverArtist, _unitOfWork);
foreach (var person in series.Metadata.People.Where(p => p.Role == PersonRole.CoverArtist))
{
var meta = upstreamArtists.FirstOrDefault(c => c.Name == person.Person.Name);
person.OrderWeight = 0;
if (meta != null)
{
person.KavitaPlusConnection = true;
}
}
// Download the image and save it
_unitOfWork.SeriesRepository.Update(series);
await _unitOfWork.CommitAsync();
await DownloadAndSetCovers(allArtists);
await DownloadAndSetCovers(upstreamArtists);
madeModification = true;
}
if (externalMetadata.Characters != null && settings.PersonRoles.Contains(PersonRole.Character))
if (externalMetadata.Characters != null && settings.IsPersonAllowed(PersonRole.Character) && (!series.Metadata.CharacterLocked ||
settings.HasOverride(MetadataSettingField.People)))
{
var characters = externalMetadata.Characters
.Select(w => new PersonDto()
@ -710,27 +761,50 @@ public class ExternalMetadataService : IExternalMetadataService
AniListId = ScrobblingService.ExtractId<int>(w.Url, ScrobblingService.AniListCharacterWebsite),
Description = CleanSummary(w.Description),
})
.Concat(series.Metadata.People.Where(p => p.Role == PersonRole.Character).Select(p => _mapper.Map<PersonDto>(p)))
.Concat(series.Metadata.People
.Where(p => p.Role == PersonRole.Character)
// Need to ensure existing people are retained, but we overwrite anything from a bad match
.Where(p => !p.KavitaPlusConnection)
.Select(p => _mapper.Map<PersonDto>(p.Person))
)
.DistinctBy(p => Parser.Normalize(p.Name))
.ToList();
if (!series.Metadata.CharacterLocked && characters.Count > 0)
if (characters.Count > 0)
{
await SeriesService.HandlePeopleUpdateAsync(series.Metadata, characters, PersonRole.Character, _unitOfWork);
foreach (var spPerson in series.Metadata.People.Where(p => p.Role == PersonRole.Character))
{
// Set a sort order based on their role
var characterMeta = externalMetadata.Characters?.FirstOrDefault(c => c.Name == spPerson.Person.Name);
spPerson.OrderWeight = 0;
if (characterMeta != null)
{
spPerson.KavitaPlusConnection = true;
spPerson.OrderWeight = characterMeta.Role switch
{
CharacterRole.Main => 0,
CharacterRole.Supporting => 1,
CharacterRole.Background => 2,
_ => 99 // Default for unknown roles
};
}
}
// Download the image and save it
_unitOfWork.SeriesRepository.Update(series);
await _unitOfWork.CommitAsync();
foreach (var character in externalMetadata.Characters)
foreach (var character in externalMetadata.Characters ?? [])
{
var aniListId = ScrobblingService.ExtractId<int>(character.Url, ScrobblingService.AniListCharacterWebsite);
if (aniListId <= 0) continue;
var person = await _unitOfWork.PersonRepository.GetPersonByAniListId(aniListId);
if (person != null && !string.IsNullOrEmpty(character.ImageUrl) && string.IsNullOrEmpty(person.CoverImage))
{
await _coverDbService.SetPersonCoverImage(person, character.ImageUrl, false);
await _coverDbService.SetPersonCoverByUrl(person, character.ImageUrl, false);
}
}
@ -743,7 +817,8 @@ public class ExternalMetadataService : IExternalMetadataService
#region Publication Status
if (!series.Metadata.PublicationStatusLocked && settings.EnablePublicationStatus)
if (settings.EnablePublicationStatus && (!series.Metadata.PublicationStatusLocked ||
settings.HasOverride(MetadataSettingField.PublicationStatus)))
{
try
{
@ -765,7 +840,6 @@ public class ExternalMetadataService : IExternalMetadataService
if (settings.EnableRelationships && externalMetadata.Relations != null && defaultAdmin != null)
{
foreach (var relation in externalMetadata.Relations)
{
var relatedSeries = await _unitOfWork.SeriesRepository.GetSeriesByAnyName(
@ -817,9 +891,20 @@ public class ExternalMetadataService : IExternalMetadataService
}
#endregion
#region Series Cover
// This must not allow cover image locked to be off after downloading, else it will call every time a match is hit
if (!string.IsNullOrEmpty(externalMetadata.CoverUrl) && (!series.CoverImageLocked || settings.HasOverride(MetadataSettingField.Covers)))
{
await DownloadSeriesCovers(series, externalMetadata.CoverUrl);
}
#endregion
return madeModification;
}
private static RelationKind GetReverseRelation(RelationKind relation)
{
return relation switch
@ -830,6 +915,18 @@ public class ExternalMetadataService : IExternalMetadataService
};
}
private async Task DownloadSeriesCovers(Series series, string coverUrl)
{
try
{
await _coverDbService.SetSeriesCoverByUrl(series, coverUrl, false);
}
catch (Exception ex)
{
_logger.LogError(ex, "There was an exception downloading cover image for Series {SeriesName} ({SeriesId})", series.Name, series.Id);
}
}
private async Task DownloadAndSetCovers(List<SeriesStaffDto> people)
{
foreach (var staff in people)
@ -839,7 +936,7 @@ public class ExternalMetadataService : IExternalMetadataService
var person = await _unitOfWork.PersonRepository.GetPersonByAniListId(aniListId.Value);
if (person != null && !string.IsNullOrEmpty(staff.ImageUrl) && string.IsNullOrEmpty(person.CoverImage))
{
await _coverDbService.SetPersonCoverImage(person, staff.ImageUrl, false);
await _coverDbService.SetPersonCoverByUrl(person, staff.ImageUrl, false);
}
}
}
@ -929,7 +1026,13 @@ public class ExternalMetadataService : IExternalMetadataService
return mapping.DestinationValue ?? (mapping.ExcludeFromSource ? null : value);
}
private static AgeRating DetermineAgeRating(IEnumerable<string> values, Dictionary<string, AgeRating> mappings)
/// <summary>
/// Returns the highest age rating from all tags/genres based on user-supplied mappings
/// </summary>
/// <param name="values">A combo of all tags/genres</param>
/// <param name="mappings"></param>
/// <returns></returns>
public static AgeRating DetermineAgeRating(IEnumerable<string> values, Dictionary<string, AgeRating> mappings)
{
// Find highest age rating from mappings
mappings ??= new Dictionary<string, AgeRating>();

View file

@ -121,12 +121,6 @@ public class SeriesService : ISeriesService
series.Metadata ??= new SeriesMetadataBuilder()
.Build();
if (series.Metadata.AgeRating != updateSeriesMetadataDto.SeriesMetadata.AgeRating)
{
series.Metadata.AgeRating = updateSeriesMetadataDto.SeriesMetadata.AgeRating;
series.Metadata.AgeRatingLocked = true;
}
if (NumberHelper.IsValidYear(updateSeriesMetadataDto.SeriesMetadata.ReleaseYear) && series.Metadata.ReleaseYear != updateSeriesMetadataDto.SeriesMetadata.ReleaseYear)
{
series.Metadata.ReleaseYear = updateSeriesMetadataDto.SeriesMetadata.ReleaseYear;
@ -173,7 +167,7 @@ public class SeriesService : ISeriesService
updateSeriesMetadataDto.SeriesMetadata.Genres.Count != 0)
{
var allGenres = (await _unitOfWork.GenreRepository.GetAllGenresByNamesAsync(updateSeriesMetadataDto.SeriesMetadata.Genres.Select(t => Parser.Normalize(t.Title)))).ToList();
series.Metadata.Genres ??= new List<Genre>();
series.Metadata.Genres ??= [];
GenreHelper.UpdateGenreList(updateSeriesMetadataDto.SeriesMetadata?.Genres, series, allGenres, genre =>
{
series.Metadata.Genres.Add(genre);
@ -181,7 +175,7 @@ public class SeriesService : ISeriesService
}
else
{
series.Metadata.Genres = new List<Genre>();
series.Metadata.Genres = [];
}
@ -190,7 +184,7 @@ public class SeriesService : ISeriesService
var allTags = (await _unitOfWork.TagRepository
.GetAllTagsByNameAsync(updateSeriesMetadataDto.SeriesMetadata.Tags.Select(t => Parser.Normalize(t.Title))))
.ToList();
series.Metadata.Tags ??= new List<Tag>();
series.Metadata.Tags ??= [];
TagHelper.UpdateTagList(updateSeriesMetadataDto.SeriesMetadata?.Tags, series, allTags, tag =>
{
series.Metadata.Tags.Add(tag);
@ -198,14 +192,33 @@ public class SeriesService : ISeriesService
}
else
{
series.Metadata.Tags = new List<Tag>();
series.Metadata.Tags = [];
}
if (series.Metadata.AgeRating != updateSeriesMetadataDto.SeriesMetadata?.AgeRating)
{
series.Metadata.AgeRating = updateSeriesMetadataDto.SeriesMetadata?.AgeRating ?? AgeRating.Unknown;
series.Metadata.AgeRatingLocked = true;
}
else
{
if (!series.Metadata.AgeRatingLocked)
{
var metadataSettings = await _unitOfWork.SettingsRepository.GetMetadataSettingDto();
var allTags = series.Metadata.Tags.Select(t => t.Title).Concat(series.Metadata.Genres.Select(g => g.Title));
var updatedRating = ExternalMetadataService.DetermineAgeRating(allTags, metadataSettings.AgeRatingMappings);
if (updatedRating > series.Metadata.AgeRating)
{
series.Metadata.AgeRating = updatedRating;
}
}
}
if (updateSeriesMetadataDto.SeriesMetadata != null)
{
if (PersonHelper.HasAnyPeople(updateSeriesMetadataDto.SeriesMetadata))
{
series.Metadata.People ??= new List<SeriesMetadataPeople>();
series.Metadata.People ??= [];
// Writers
if (!series.Metadata.WriterLocked)
@ -279,6 +292,12 @@ public class SeriesService : ISeriesService
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Translators, PersonRole.Translator, _unitOfWork);
}
// Characters
if (!series.Metadata.CharacterLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Characters, PersonRole.Character, _unitOfWork);
}
}
series.Metadata.AgeRatingLocked = updateSeriesMetadataDto.SeriesMetadata.AgeRatingLocked;
@ -295,6 +314,7 @@ public class SeriesService : ISeriesService
series.Metadata.PencillerLocked = updateSeriesMetadataDto.SeriesMetadata.PencillerLocked;
series.Metadata.PublisherLocked = updateSeriesMetadataDto.SeriesMetadata.PublisherLocked;
series.Metadata.TranslatorLocked = updateSeriesMetadataDto.SeriesMetadata.TranslatorLocked;
series.Metadata.LocationLocked = updateSeriesMetadataDto.SeriesMetadata.LocationLocked;
series.Metadata.CoverArtistLocked = updateSeriesMetadataDto.SeriesMetadata.CoverArtistLocked;
series.Metadata.WriterLocked = updateSeriesMetadataDto.SeriesMetadata.WriterLocked;
series.Metadata.SummaryLocked = updateSeriesMetadataDto.SeriesMetadata.SummaryLocked;

View file

@ -28,7 +28,8 @@ public interface ICoverDbService
Task<string> DownloadPublisherImageAsync(string publisherName, EncodeFormat encodeFormat);
Task<string?> DownloadPersonImageAsync(Person person, EncodeFormat encodeFormat);
Task<string?> DownloadPersonImageAsync(Person person, EncodeFormat encodeFormat, string url);
Task SetPersonCoverImage(Person person, string url, bool fromBase64 = true);
Task SetPersonCoverByUrl(Person person, string url, bool fromBase64 = true);
Task SetSeriesCoverByUrl(Series series, string url, bool fromBase64 = true);
}
@ -460,7 +461,8 @@ public class CoverDbService : ICoverDbService
return null;
}
public async Task SetPersonCoverImage(Person person, string url, bool fromBase64 = true)
public async Task SetPersonCoverByUrl(Person person, string url, bool fromBase64 = true)
{
if (!string.IsNullOrEmpty(url))
{
@ -490,6 +492,42 @@ public class CoverDbService : ICoverDbService
}
}
/// <summary>
/// Sets the series cover by url
/// </summary>
/// <param name="series"></param>
/// <param name="url"></param>
/// <param name="fromBase64"></param>
public async Task SetSeriesCoverByUrl(Series series, string url, bool fromBase64 = true)
{
if (!string.IsNullOrEmpty(url))
{
var filePath = await CreateThumbnail(url, $"{ImageService.GetSeriesFormat(series.Id)}", fromBase64);
if (!string.IsNullOrEmpty(filePath))
{
series.CoverImage = filePath;
series.CoverImageLocked = true;
_imageService.UpdateColorScape(series);
_unitOfWork.SeriesRepository.Update(series);
}
}
else
{
series.CoverImage = string.Empty;
series.CoverImageLocked = false;
_imageService.UpdateColorScape(series);
_unitOfWork.SeriesRepository.Update(series);
}
if (_unitOfWork.HasChanges())
{
await _unitOfWork.CommitAsync();
await _eventHub.SendMessageAsync(MessageFactory.CoverUpdate,
MessageFactory.CoverUpdateEvent(series.Id, MessageFactoryEntityTypes.Series), false);
}
}
private async Task<string> CreateThumbnail(string url, string filename, bool fromBase64 = true)
{
var settings = await _unitOfWork.SettingsRepository.GetSettingsDtoAsync();

View file

@ -194,7 +194,7 @@ public class ProcessSeries : IProcessSeries
{
// See if any recommendations can link up to the series and pre-fetch external metadata for the series
BackgroundJob.Enqueue(() =>
_externalMetadataService.GetNewSeriesData(series.Id, series.Library.Type));
_externalMetadataService.FetchSeriesMetadata(series.Id, series.Library.Type));
await _eventHub.SendMessageAsync(MessageFactory.SeriesAdded,
MessageFactory.SeriesAddedEvent(series.Id, series.Name, series.LibraryId), false);
@ -298,7 +298,19 @@ public class ProcessSeries : IProcessSeries
}
// Set the AgeRating as highest in all the comicInfos
if (!series.Metadata.AgeRatingLocked) series.Metadata.AgeRating = chapters.Max(chapter => chapter.AgeRating);
if (!series.Metadata.AgeRatingLocked)
{
series.Metadata.AgeRating = chapters.Max(chapter => chapter.AgeRating);
// Get the MetadataSettings and apply Age Rating Mappings here
var metadataSettings = await _unitOfWork.SettingsRepository.GetMetadataSettingDto();
var allTags = series.Metadata.Tags.Select(t => t.Title).Concat(series.Metadata.Genres.Select(g => g.Title));
var updatedRating = ExternalMetadataService.DetermineAgeRating(allTags, metadataSettings.AgeRatingMappings);
if (updatedRating > series.Metadata.AgeRating)
{
series.Metadata.AgeRating = updatedRating;
}
}
DeterminePublicationStatus(series, chapters);
@ -318,7 +330,6 @@ public class ProcessSeries : IProcessSeries
await UpdateCollectionTags(series, firstChapter);
}
#region PeopleAndTagsAndGenres
if (!series.Metadata.WriterLocked)
{
@ -414,6 +425,7 @@ public class ProcessSeries : IProcessSeries
}
#endregion
}
private async Task UpdateCollectionTags(Series series, Chapter firstChapter)