diff --git a/API/Services/MetadataService.cs b/API/Services/MetadataService.cs index 9c24b1c38..6c1f0cf2a 100644 --- a/API/Services/MetadataService.cs +++ b/API/Services/MetadataService.cs @@ -76,159 +76,14 @@ public class MetadataService : IMetadataService return true; } - private void UpdateChapterMetadata(Chapter chapter, ICollection allPeople, ICollection allTags, ICollection allGenres, bool forceUpdate) + private void UpdateChapterLastModified(Chapter chapter, bool forceUpdate) { var firstFile = chapter.Files.OrderBy(x => x.Chapter).FirstOrDefault(); if (firstFile == null || _cacheHelper.HasFileNotChangedSinceCreationOrLastScan(chapter, forceUpdate, firstFile)) return; - UpdateChapterFromComicInfo(chapter, allPeople, allTags, allGenres, firstFile); firstFile.UpdateLastModified(); } - private void UpdateChapterFromComicInfo(Chapter chapter, ICollection allPeople, ICollection allTags, ICollection allGenres, MangaFile firstFile) - { - var comicInfo = _readingItemService.GetComicInfo(firstFile.FilePath, firstFile.Format); - if (comicInfo == null) return; - - chapter.AgeRating = ComicInfo.ConvertAgeRatingToEnum(comicInfo.AgeRating); - - if (!string.IsNullOrEmpty(comicInfo.Title)) - { - chapter.TitleName = comicInfo.Title.Trim(); - } - - if (!string.IsNullOrEmpty(comicInfo.Summary)) - { - chapter.Summary = comicInfo.Summary; - } - - if (!string.IsNullOrEmpty(comicInfo.LanguageISO)) - { - chapter.Language = comicInfo.LanguageISO; - } - - if (comicInfo.Count > 0) - { - chapter.TotalCount = comicInfo.Count; - } - - if (int.Parse(comicInfo.Number) > 0) - { - chapter.Count = int.Parse(comicInfo.Number); - } - - - - - if (comicInfo.Year > 0) - { - var day = Math.Max(comicInfo.Day, 1); - var month = Math.Max(comicInfo.Month, 1); - chapter.ReleaseDate = DateTime.Parse($"{month}/{day}/{comicInfo.Year}"); - } - - if (!string.IsNullOrEmpty(comicInfo.Colorist)) - { - var people = comicInfo.Colorist.Split(","); - PersonHelper.RemovePeople(chapter.People, people, PersonRole.Colorist); - PersonHelper.UpdatePeople(allPeople, people, PersonRole.Colorist, - person => PersonHelper.AddPersonIfNotExists(chapter.People, person)); - } - - if (!string.IsNullOrEmpty(comicInfo.Characters)) - { - var people = comicInfo.Characters.Split(","); - PersonHelper.RemovePeople(chapter.People, people, PersonRole.Character); - PersonHelper.UpdatePeople(allPeople, people, PersonRole.Character, - person => PersonHelper.AddPersonIfNotExists(chapter.People, person)); - } - - if (!string.IsNullOrEmpty(comicInfo.Translator)) - { - var people = comicInfo.Translator.Split(","); - PersonHelper.RemovePeople(chapter.People, people, PersonRole.Translator); - PersonHelper.UpdatePeople(allPeople, people, PersonRole.Translator, - person => PersonHelper.AddPersonIfNotExists(chapter.People, person)); - } - - if (!string.IsNullOrEmpty(comicInfo.Tags)) - { - var tags = comicInfo.Tags.Split(",").Select(s => s.Trim()).ToList(); - // Remove all tags that aren't matching between chapter tags and metadata - TagHelper.KeepOnlySameTagBetweenLists(chapter.Tags, tags.Select(t => DbFactory.Tag(t, false)).ToList()); - TagHelper.UpdateTag(allTags, tags, false, - (tag, added) => - { - chapter.Tags.Add(tag); - }); - } - - if (!string.IsNullOrEmpty(comicInfo.Writer)) - { - var people = comicInfo.Writer.Split(","); - PersonHelper.RemovePeople(chapter.People, people, PersonRole.Writer); - PersonHelper.UpdatePeople(allPeople, people, PersonRole.Writer, - person => PersonHelper.AddPersonIfNotExists(chapter.People, person)); - } - - if (!string.IsNullOrEmpty(comicInfo.Editor)) - { - var people = comicInfo.Editor.Split(","); - PersonHelper.RemovePeople(chapter.People, people, PersonRole.Editor); - PersonHelper.UpdatePeople(allPeople, people, PersonRole.Editor, - person => PersonHelper.AddPersonIfNotExists(chapter.People, person)); - } - - if (!string.IsNullOrEmpty(comicInfo.Inker)) - { - var people = comicInfo.Inker.Split(","); - PersonHelper.RemovePeople(chapter.People, people, PersonRole.Inker); - PersonHelper.UpdatePeople(allPeople, people, PersonRole.Inker, - person => PersonHelper.AddPersonIfNotExists(chapter.People, person)); - } - - if (!string.IsNullOrEmpty(comicInfo.Letterer)) - { - var people = comicInfo.Letterer.Split(","); - PersonHelper.RemovePeople(chapter.People, people, PersonRole.Letterer); - PersonHelper.UpdatePeople(allPeople, people, PersonRole.Letterer, - person => PersonHelper.AddPersonIfNotExists(chapter.People, person)); - } - - if (!string.IsNullOrEmpty(comicInfo.Penciller)) - { - var people = comicInfo.Penciller.Split(","); - PersonHelper.RemovePeople(chapter.People, people, PersonRole.Penciller); - PersonHelper.UpdatePeople(allPeople, people, PersonRole.Penciller, - person => PersonHelper.AddPersonIfNotExists(chapter.People, person)); - } - - if (!string.IsNullOrEmpty(comicInfo.CoverArtist)) - { - var people = comicInfo.CoverArtist.Split(","); - PersonHelper.RemovePeople(chapter.People, people, PersonRole.CoverArtist); - PersonHelper.UpdatePeople(allPeople, people, PersonRole.CoverArtist, - person => PersonHelper.AddPersonIfNotExists(chapter.People, person)); - } - - if (!string.IsNullOrEmpty(comicInfo.Publisher)) - { - var people = comicInfo.Publisher.Split(","); - PersonHelper.RemovePeople(chapter.People, people, PersonRole.Publisher); - PersonHelper.UpdatePeople(allPeople, people, PersonRole.Publisher, - person => PersonHelper.AddPersonIfNotExists(chapter.People, person)); - } - - if (!string.IsNullOrEmpty(comicInfo.Genre)) - { - var genres = comicInfo.Genre.Split(","); - GenreHelper.KeepOnlySameGenreBetweenLists(chapter.Genres, genres.Select(g => DbFactory.Genre(g, false)).ToList()); - GenreHelper.UpdateGenre(allGenres, genres, false, - genre => chapter.Genres.Add(genre)); - } - } - - /// /// Updates the cover image for a Volume /// @@ -283,93 +138,6 @@ public class MetadataService : IMetadataService series.CoverImage = firstCover?.CoverImage ?? coverImage; } - private static void UpdateSeriesMetadata(Series series, ICollection allPeople, ICollection allGenres, ICollection allTags, bool forceUpdate) - { - var isBook = series.Library.Type == LibraryType.Book; - var firstVolume = series.Volumes.OrderBy(c => c.Number, new ChapterSortComparer()).FirstWithChapters(isBook); - var firstChapter = firstVolume?.Chapters.GetFirstChapterWithFiles(); - - var firstFile = firstChapter?.Files.FirstOrDefault(); - if (firstFile == null) return; - if (Parser.Parser.IsPdf(firstFile.FilePath)) return; - - var chapters = series.Volumes.SelectMany(volume => volume.Chapters).ToList(); - - // Update Metadata based on Chapter metadata - series.Metadata.ReleaseYear = chapters.Min(c => c.ReleaseDate.Year); - - if (series.Metadata.ReleaseYear < 1000) - { - // Not a valid year, default to 0 - series.Metadata.ReleaseYear = 0; - } - - // Set the AgeRating as highest in all the comicInfos - series.Metadata.AgeRating = chapters.Max(chapter => chapter.AgeRating); - - - series.Metadata.Count = chapters.Max(chapter => chapter.TotalCount); - series.Metadata.PublicationStatus = PublicationStatus.OnGoing; - if (chapters.Max(chapter => chapter.Count) >= series.Metadata.Count && series.Metadata.Count > 0) - { - series.Metadata.PublicationStatus = PublicationStatus.Completed; - } - - if (!string.IsNullOrEmpty(firstChapter.Summary)) - { - series.Metadata.Summary = firstChapter.Summary; - } - - if (!string.IsNullOrEmpty(firstChapter.Language)) - { - series.Metadata.Language = firstChapter.Language; - } - - - // Handle People - foreach (var chapter in chapters) - { - PersonHelper.UpdatePeople(allPeople, chapter.People.Where(p => p.Role == PersonRole.Writer).Select(p => p.Name), PersonRole.Writer, - person => PersonHelper.AddPersonIfNotExists(series.Metadata.People, person)); - - PersonHelper.UpdatePeople(allPeople, chapter.People.Where(p => p.Role == PersonRole.CoverArtist).Select(p => p.Name), PersonRole.CoverArtist, - person => PersonHelper.AddPersonIfNotExists(series.Metadata.People, person)); - - PersonHelper.UpdatePeople(allPeople, chapter.People.Where(p => p.Role == PersonRole.Publisher).Select(p => p.Name), PersonRole.Publisher, - person => PersonHelper.AddPersonIfNotExists(series.Metadata.People, person)); - - PersonHelper.UpdatePeople(allPeople, chapter.People.Where(p => p.Role == PersonRole.Character).Select(p => p.Name), PersonRole.Character, - person => PersonHelper.AddPersonIfNotExists(series.Metadata.People, person)); - - PersonHelper.UpdatePeople(allPeople, chapter.People.Where(p => p.Role == PersonRole.Colorist).Select(p => p.Name), PersonRole.Colorist, - person => PersonHelper.AddPersonIfNotExists(series.Metadata.People, person)); - - PersonHelper.UpdatePeople(allPeople, chapter.People.Where(p => p.Role == PersonRole.Editor).Select(p => p.Name), PersonRole.Editor, - person => PersonHelper.AddPersonIfNotExists(series.Metadata.People, person)); - - PersonHelper.UpdatePeople(allPeople, chapter.People.Where(p => p.Role == PersonRole.Inker).Select(p => p.Name), PersonRole.Inker, - person => PersonHelper.AddPersonIfNotExists(series.Metadata.People, person)); - - PersonHelper.UpdatePeople(allPeople, chapter.People.Where(p => p.Role == PersonRole.Letterer).Select(p => p.Name), PersonRole.Letterer, - person => PersonHelper.AddPersonIfNotExists(series.Metadata.People, person)); - - PersonHelper.UpdatePeople(allPeople, chapter.People.Where(p => p.Role == PersonRole.Penciller).Select(p => p.Name), PersonRole.Penciller, - person => PersonHelper.AddPersonIfNotExists(series.Metadata.People, person)); - - PersonHelper.UpdatePeople(allPeople, chapter.People.Where(p => p.Role == PersonRole.Translator).Select(p => p.Name), PersonRole.Translator, - person => PersonHelper.AddPersonIfNotExists(series.Metadata.People, person)); - - TagHelper.UpdateTag(allTags, chapter.Tags.Select(t => t.Title), false, (tag, added) => - TagHelper.AddTagIfNotExists(series.Metadata.Tags, tag)); - - GenreHelper.UpdateGenre(allGenres, chapter.Genres.Select(t => t.Title), false, genre => - GenreHelper.AddGenreIfNotExists(series.Metadata.Genres, genre)); - } - - var people = chapters.SelectMany(c => c.People).ToList(); - PersonHelper.KeepOnlySamePeopleBetweenLists(series.Metadata.People, - people, person => series.Metadata.People.Remove(person)); - } /// /// @@ -391,7 +159,7 @@ public class MetadataService : IMetadataService { var chapterUpdated = UpdateChapterCoverImage(chapter, forceUpdate); // If cover was update, either the file has changed or first scan and we should force a metadata update - UpdateChapterMetadata(chapter, allPeople, allTags, allGenres, forceUpdate || chapterUpdated); + UpdateChapterLastModified(chapter, forceUpdate || chapterUpdated); if (index == 0 && chapterUpdated) { firstChapterUpdated = true; @@ -409,7 +177,6 @@ public class MetadataService : IMetadataService } UpdateSeriesCoverImage(series, firstVolumeUpdated || forceUpdate); - UpdateSeriesMetadata(series, allPeople, allGenres, allTags, forceUpdate); } catch (Exception ex) { diff --git a/API/Services/ReadingItemService.cs b/API/Services/ReadingItemService.cs index 12b02f163..4ab94e906 100644 --- a/API/Services/ReadingItemService.cs +++ b/API/Services/ReadingItemService.cs @@ -7,7 +7,7 @@ namespace API.Services; public interface IReadingItemService { - ComicInfo GetComicInfo(string filePath, MangaFormat format); + ComicInfo GetComicInfo(string filePath); int GetNumberOfPages(string filePath, MangaFormat format); string GetCoverImage(string fileFilePath, string fileName, MangaFormat format); void Extract(string fileFilePath, string targetDirectory, MangaFormat format, int imageCount = 1); @@ -34,13 +34,17 @@ public class ReadingItemService : IReadingItemService /// Gets the ComicInfo for the file if it exists. Null otherewise. /// /// Fully qualified path of file - /// Format of the file determines how we open it (epub vs comicinfo.xml) /// - public ComicInfo? GetComicInfo(string filePath, MangaFormat format) + public ComicInfo? GetComicInfo(string filePath) { - if (format is MangaFormat.Archive or MangaFormat.Epub) + if (Parser.Parser.IsEpub(filePath)) { - return Parser.Parser.IsEpub(filePath) ? _bookService.GetComicInfo(filePath) : _archiveService.GetComicInfo(filePath); + return _bookService.GetComicInfo(filePath); + } + + if (Parser.Parser.IsComicInfoExtension(filePath)) + { + return _archiveService.GetComicInfo(filePath); } return null; @@ -120,6 +124,13 @@ public class ReadingItemService : IReadingItemService } } + /// + /// Parses information out of a file. If file is a book (epub), it will use book metadata regardless of LibraryType + /// + /// + /// + /// + /// public ParserInfo Parse(string path, string rootPath, LibraryType type) { return Parser.Parser.IsEpub(path) ? _bookService.ParseInfo(path) : _defaultParser.Parse(path, rootPath, type); diff --git a/API/Services/Tasks/Scanner/ParseScannedFiles.cs b/API/Services/Tasks/Scanner/ParseScannedFiles.cs index 975179d86..9ec1865b3 100644 --- a/API/Services/Tasks/Scanner/ParseScannedFiles.cs +++ b/API/Services/Tasks/Scanner/ParseScannedFiles.cs @@ -59,20 +59,6 @@ namespace API.Services.Tasks.Scanner return existingKey != null ? parsedSeries[existingKey] : new List(); } - private ComicInfo GetComicInfo(string path) - { - if (Parser.Parser.IsEpub(path)) - { - return _readingItemService.GetComicInfo(path, MangaFormat.Epub); - } - - if (Parser.Parser.IsComicInfoExtension(path)) - { - return _readingItemService.GetComicInfo(path, MangaFormat.Archive); - } - return null; - } - /// /// Processes files found during a library scan. /// Populates a collection of for DB updates later. @@ -82,22 +68,12 @@ namespace API.Services.Tasks.Scanner /// Library type to determine parsing to perform private void ProcessFile(string path, string rootPath, LibraryType type) { - ParserInfo info = null; - // TODO: Emit event with what is being processed. It can look like Kavita isn't doing anything during file scan - if (Parser.Parser.IsEpub(path)) - { - info = _readingItemService.Parse(path, rootPath, type); - } - else - { - info = _readingItemService.Parse(path, rootPath, type); - } - - // If we couldn't match, log. But don't log if the file parses as a cover image + var info = _readingItemService.Parse(path, rootPath, type); if (info == null) { + // If the file is an image and literally a cover image, skip processing. if (!(Parser.Parser.IsImage(path) && Parser.Parser.IsCoverImage(path))) { _logger.LogWarning("[Scanner] Could not parse series from {Path}", path); @@ -105,15 +81,16 @@ namespace API.Services.Tasks.Scanner return; } - if (Parser.Parser.IsEpub(path) && Parser.Parser.ParseVolume(info.Series) != Parser.Parser.DefaultVolume) + + // This catches when original library type is Manga/Comic and when parsing with non + if (Parser.Parser.IsEpub(path) && Parser.Parser.ParseVolume(info.Series) != Parser.Parser.DefaultVolume) // Shouldn't this be info.Volume != DefaultVolume? { - info = _defaultParser.Parse(path, rootPath, LibraryType.Book); // TODO: Why do I reparse? + info = _defaultParser.Parse(path, rootPath, LibraryType.Book); var info2 = _readingItemService.Parse(path, rootPath, type); info.Merge(info2); } - // TODO: Think about doing this before the Fallback code to speed up - info.ComicInfo = GetComicInfo(path); + info.ComicInfo = _readingItemService.GetComicInfo(path); if (info.ComicInfo != null) { if (!string.IsNullOrEmpty(info.ComicInfo.Volume)) diff --git a/API/Services/Tasks/ScannerService.cs b/API/Services/Tasks/ScannerService.cs index 0841ac7bb..e58356ade 100644 --- a/API/Services/Tasks/ScannerService.cs +++ b/API/Services/Tasks/ScannerService.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using API.Comparators; using API.Data; +using API.Data.Metadata; using API.Data.Repositories; using API.Entities; using API.Entities.Enums; @@ -42,11 +43,13 @@ public class ScannerService : IScannerService private readonly IFileService _fileService; private readonly IDirectoryService _directoryService; private readonly IReadingItemService _readingItemService; + private readonly ICacheHelper _cacheHelper; private readonly NaturalSortComparer _naturalSort = new (); public ScannerService(IUnitOfWork unitOfWork, ILogger logger, IMetadataService metadataService, ICacheService cacheService, IHubContext messageHub, - IFileService fileService, IDirectoryService directoryService, IReadingItemService readingItemService) + IFileService fileService, IDirectoryService directoryService, IReadingItemService readingItemService, + ICacheHelper cacheHelper) { _unitOfWork = unitOfWork; _logger = logger; @@ -56,6 +59,7 @@ public class ScannerService : IScannerService _fileService = fileService; _directoryService = directoryService; _readingItemService = readingItemService; + _cacheHelper = cacheHelper; } [DisableConcurrentExecution(timeoutInSeconds: 360)] @@ -76,6 +80,10 @@ public class ScannerService : IScannerService return; } + var allPeople = await _unitOfWork.PersonRepository.GetAllPeople(); + var allGenres = await _unitOfWork.GenreRepository.GetAllGenresAsync(); + var allTags = await _unitOfWork.TagRepository.GetAllTagsAsync(); + var dirs = _directoryService.FindHighestDirectoriesFromFiles(folderPaths, files.Select(f => f.FilePath).ToList()); _logger.LogInformation("Beginning file scan on {SeriesName}", series.Name); @@ -140,7 +148,8 @@ public class ScannerService : IScannerService try { - UpdateSeries(series, parsedSeries); + UpdateSeries(series, parsedSeries, allPeople, allTags, allGenres, library.Type); + await CommitAndSend(totalFiles, parsedSeries, sw, scanElapsedTime, series); } catch (Exception ex) @@ -296,6 +305,10 @@ public class ScannerService : IScannerService var stopwatch = Stopwatch.StartNew(); var totalTime = 0L; + var allPeople = await _unitOfWork.PersonRepository.GetAllPeople(); + var allGenres = await _unitOfWork.GenreRepository.GetAllGenresAsync(); + var allTags = await _unitOfWork.TagRepository.GetAllTagsAsync(); + // Update existing series _logger.LogInformation("[ScannerService] Updating existing series for {LibraryName}. Total Items: {TotalSize}. Total Chunks: {TotalChunks} with {ChunkSize} size", library.Name, chunkInfo.TotalSize, chunkInfo.TotalChunks, chunkInfo.ChunkSize); @@ -334,7 +347,7 @@ public class ScannerService : IScannerService var librarySeries = cleanedSeries.ToList(); Parallel.ForEach(librarySeries, (series) => { - UpdateSeries(series, parsedSeries); + UpdateSeries(series, parsedSeries, allPeople, allTags, allGenres, library.Type); }); try @@ -407,11 +420,12 @@ public class ScannerService : IScannerService newSeries.Add(s); } + var i = 0; foreach(var series in newSeries) { _logger.LogDebug("[ScannerService] Processing series {SeriesName}", series.OriginalName); - UpdateSeries(series, parsedSeries); + UpdateSeries(series, parsedSeries, allPeople, allTags, allGenres, library.Type); _unitOfWork.SeriesRepository.Attach(series); try { @@ -440,14 +454,15 @@ public class ScannerService : IScannerService newSeries.Count, stopwatch.ElapsedMilliseconds, library.Name); } - private void UpdateSeries(Series series, Dictionary> parsedSeries) + private void UpdateSeries(Series series, Dictionary> parsedSeries, + ICollection allPeople, ICollection allTags, ICollection allGenres, LibraryType libraryType) { try { _logger.LogInformation("[ScannerService] Processing series {SeriesName}", series.OriginalName); var parsedInfos = ParseScannedFiles.GetInfosByName(parsedSeries, series); - UpdateVolumes(series, parsedInfos); + UpdateVolumes(series, parsedInfos, allPeople, allTags, allGenres); series.Pages = series.Volumes.Sum(v => v.Pages); series.NormalizedName = Parser.Parser.Normalize(series.Name); @@ -458,6 +473,8 @@ public class ScannerService : IScannerService } series.OriginalName ??= parsedInfos[0].Series; series.SortName ??= parsedInfos[0].SeriesSort; + + UpdateSeriesMetadata(series, allPeople, allGenres, allTags, libraryType); } catch (Exception ex) { @@ -471,8 +488,97 @@ public class ScannerService : IScannerService } + private static void UpdateSeriesMetadata(Series series, ICollection allPeople, ICollection allGenres, ICollection allTags, LibraryType libraryType) + { + var isBook = libraryType == LibraryType.Book; + var firstVolume = series.Volumes.OrderBy(c => c.Number, new ChapterSortComparer()).FirstWithChapters(isBook); + var firstChapter = firstVolume?.Chapters.GetFirstChapterWithFiles(); - private void UpdateVolumes(Series series, IList parsedInfos) + var firstFile = firstChapter?.Files.FirstOrDefault(); + if (firstFile == null) return; + if (Parser.Parser.IsPdf(firstFile.FilePath)) return; + + var chapters = series.Volumes.SelectMany(volume => volume.Chapters).ToList(); + + // Update Metadata based on Chapter metadata + series.Metadata.ReleaseYear = chapters.Min(c => c.ReleaseDate.Year); + + if (series.Metadata.ReleaseYear < 1000) + { + // Not a valid year, default to 0 + series.Metadata.ReleaseYear = 0; + } + + // Set the AgeRating as highest in all the comicInfos + series.Metadata.AgeRating = chapters.Max(chapter => chapter.AgeRating); + + + series.Metadata.Count = chapters.Max(chapter => chapter.TotalCount); + series.Metadata.PublicationStatus = PublicationStatus.OnGoing; + if (chapters.Max(chapter => chapter.Count) >= series.Metadata.Count && series.Metadata.Count > 0) + { + series.Metadata.PublicationStatus = PublicationStatus.Completed; + } + + if (!string.IsNullOrEmpty(firstChapter.Summary)) + { + series.Metadata.Summary = firstChapter.Summary; + } + + if (!string.IsNullOrEmpty(firstChapter.Language)) + { + series.Metadata.Language = firstChapter.Language; + } + + + // Handle People + foreach (var chapter in chapters) + { + PersonHelper.UpdatePeople(allPeople, chapter.People.Where(p => p.Role == PersonRole.Writer).Select(p => p.Name), PersonRole.Writer, + person => PersonHelper.AddPersonIfNotExists(series.Metadata.People, person)); + + PersonHelper.UpdatePeople(allPeople, chapter.People.Where(p => p.Role == PersonRole.CoverArtist).Select(p => p.Name), PersonRole.CoverArtist, + person => PersonHelper.AddPersonIfNotExists(series.Metadata.People, person)); + + PersonHelper.UpdatePeople(allPeople, chapter.People.Where(p => p.Role == PersonRole.Publisher).Select(p => p.Name), PersonRole.Publisher, + person => PersonHelper.AddPersonIfNotExists(series.Metadata.People, person)); + + PersonHelper.UpdatePeople(allPeople, chapter.People.Where(p => p.Role == PersonRole.Character).Select(p => p.Name), PersonRole.Character, + person => PersonHelper.AddPersonIfNotExists(series.Metadata.People, person)); + + PersonHelper.UpdatePeople(allPeople, chapter.People.Where(p => p.Role == PersonRole.Colorist).Select(p => p.Name), PersonRole.Colorist, + person => PersonHelper.AddPersonIfNotExists(series.Metadata.People, person)); + + PersonHelper.UpdatePeople(allPeople, chapter.People.Where(p => p.Role == PersonRole.Editor).Select(p => p.Name), PersonRole.Editor, + person => PersonHelper.AddPersonIfNotExists(series.Metadata.People, person)); + + PersonHelper.UpdatePeople(allPeople, chapter.People.Where(p => p.Role == PersonRole.Inker).Select(p => p.Name), PersonRole.Inker, + person => PersonHelper.AddPersonIfNotExists(series.Metadata.People, person)); + + PersonHelper.UpdatePeople(allPeople, chapter.People.Where(p => p.Role == PersonRole.Letterer).Select(p => p.Name), PersonRole.Letterer, + person => PersonHelper.AddPersonIfNotExists(series.Metadata.People, person)); + + PersonHelper.UpdatePeople(allPeople, chapter.People.Where(p => p.Role == PersonRole.Penciller).Select(p => p.Name), PersonRole.Penciller, + person => PersonHelper.AddPersonIfNotExists(series.Metadata.People, person)); + + PersonHelper.UpdatePeople(allPeople, chapter.People.Where(p => p.Role == PersonRole.Translator).Select(p => p.Name), PersonRole.Translator, + person => PersonHelper.AddPersonIfNotExists(series.Metadata.People, person)); + + TagHelper.UpdateTag(allTags, chapter.Tags.Select(t => t.Title), false, (tag, added) => + TagHelper.AddTagIfNotExists(series.Metadata.Tags, tag)); + + GenreHelper.UpdateGenre(allGenres, chapter.Genres.Select(t => t.Title), false, genre => + GenreHelper.AddGenreIfNotExists(series.Metadata.Genres, genre)); + } + + var people = chapters.SelectMany(c => c.People).ToList(); + PersonHelper.KeepOnlySamePeopleBetweenLists(series.Metadata.People, + people, person => series.Metadata.People.Remove(person)); + } + + + + private void UpdateVolumes(Series series, IList parsedInfos, ICollection allPeople, ICollection allTags, ICollection allGenres) { var startingVolumeCount = series.Volumes.Count; // Add new volumes and update chapters per volume @@ -492,6 +598,22 @@ public class ScannerService : IScannerService var infos = parsedInfos.Where(p => p.Volumes == volumeNumber).ToArray(); UpdateChapters(volume, infos); volume.Pages = volume.Chapters.Sum(c => c.Pages); + + // Update all the metadata on the Chapters + foreach (var chapter in volume.Chapters) + { + var firstFile = chapter.Files.OrderBy(x => x.Chapter).FirstOrDefault(); + if (firstFile == null || _cacheHelper.HasFileNotChangedSinceCreationOrLastScan(chapter, false, firstFile)) continue; + try + { + var firstChapterInfo = infos.SingleOrDefault(i => i.FullFilePath.Equals(firstFile.FilePath)); + UpdateChapterFromComicInfo(chapter, allPeople, allTags, allGenres, firstChapterInfo?.ComicInfo); + } + catch (Exception ex) + { + _logger.LogError(ex, "There was some issue when updating chapter's metadata"); + } + } } // Remove existing volumes that aren't in parsedInfos @@ -593,11 +715,6 @@ public class ScannerService : IScannerService } } - private MangaFile CreateMangaFile(ParserInfo info) - { - return DbFactory.MangaFile(info.FullFilePath, info.Format, _readingItemService.GetNumberOfPages(info.FullFilePath, info.Format)); - } - private void AddOrUpdateFileForChapter(Chapter chapter, ParserInfo info) { chapter.Files ??= new List(); @@ -607,14 +724,166 @@ public class ScannerService : IScannerService existingFile.Format = info.Format; if (!_fileService.HasFileBeenModifiedSince(existingFile.FilePath, existingFile.LastModified) && existingFile.Pages != 0) return; existingFile.Pages = _readingItemService.GetNumberOfPages(info.FullFilePath, info.Format); - //existingFile.UpdateLastModified(); // We skip updating DB here so that metadata refresh can do it + // We skip updating DB here with last modified time so that metadata refresh can do it } else { - var file = CreateMangaFile(info); + var file = DbFactory.MangaFile(info.FullFilePath, info.Format, _readingItemService.GetNumberOfPages(info.FullFilePath, info.Format)); if (file == null) return; chapter.Files.Add(file); } } + + private void UpdateChapterFromComicInfo(Chapter chapter, ICollection allPeople, ICollection allTags, ICollection allGenres, ComicInfo? info) + { + var firstFile = chapter.Files.OrderBy(x => x.Chapter).FirstOrDefault(); + if (firstFile == null || + _cacheHelper.HasFileNotChangedSinceCreationOrLastScan(chapter, false, firstFile)) return; + + var comicInfo = info; + if (info == null) + { + comicInfo = _readingItemService.GetComicInfo(firstFile.FilePath); + } + + if (comicInfo == null) return; + + chapter.AgeRating = ComicInfo.ConvertAgeRatingToEnum(comicInfo.AgeRating); + + if (!string.IsNullOrEmpty(comicInfo.Title)) + { + chapter.TitleName = comicInfo.Title.Trim(); + } + + if (!string.IsNullOrEmpty(comicInfo.Summary)) + { + chapter.Summary = comicInfo.Summary; + } + + if (!string.IsNullOrEmpty(comicInfo.LanguageISO)) + { + chapter.Language = comicInfo.LanguageISO; + } + + if (comicInfo.Count > 0) + { + chapter.TotalCount = comicInfo.Count; + } + + if (!string.IsNullOrEmpty(comicInfo.Number) && int.Parse(comicInfo.Number) > 0) + { + chapter.Count = int.Parse(comicInfo.Number); + } + + + + + if (comicInfo.Year > 0) + { + var day = Math.Max(comicInfo.Day, 1); + var month = Math.Max(comicInfo.Month, 1); + chapter.ReleaseDate = DateTime.Parse($"{month}/{day}/{comicInfo.Year}"); + } + + if (!string.IsNullOrEmpty(comicInfo.Colorist)) + { + var people = comicInfo.Colorist.Split(","); + PersonHelper.RemovePeople(chapter.People, people, PersonRole.Colorist); + PersonHelper.UpdatePeople(allPeople, people, PersonRole.Colorist, + person => PersonHelper.AddPersonIfNotExists(chapter.People, person)); + } + + if (!string.IsNullOrEmpty(comicInfo.Characters)) + { + var people = comicInfo.Characters.Split(","); + PersonHelper.RemovePeople(chapter.People, people, PersonRole.Character); + PersonHelper.UpdatePeople(allPeople, people, PersonRole.Character, + person => PersonHelper.AddPersonIfNotExists(chapter.People, person)); + } + + if (!string.IsNullOrEmpty(comicInfo.Translator)) + { + var people = comicInfo.Translator.Split(","); + PersonHelper.RemovePeople(chapter.People, people, PersonRole.Translator); + PersonHelper.UpdatePeople(allPeople, people, PersonRole.Translator, + person => PersonHelper.AddPersonIfNotExists(chapter.People, person)); + } + + if (!string.IsNullOrEmpty(comicInfo.Tags)) + { + var tags = comicInfo.Tags.Split(",").Select(s => s.Trim()).ToList(); + // Remove all tags that aren't matching between chapter tags and metadata + TagHelper.KeepOnlySameTagBetweenLists(chapter.Tags, tags.Select(t => DbFactory.Tag(t, false)).ToList()); + TagHelper.UpdateTag(allTags, tags, false, + (tag, added) => + { + chapter.Tags.Add(tag); + }); + } + + if (!string.IsNullOrEmpty(comicInfo.Writer)) + { + var people = comicInfo.Writer.Split(","); + PersonHelper.RemovePeople(chapter.People, people, PersonRole.Writer); + PersonHelper.UpdatePeople(allPeople, people, PersonRole.Writer, + person => PersonHelper.AddPersonIfNotExists(chapter.People, person)); + } + + if (!string.IsNullOrEmpty(comicInfo.Editor)) + { + var people = comicInfo.Editor.Split(","); + PersonHelper.RemovePeople(chapter.People, people, PersonRole.Editor); + PersonHelper.UpdatePeople(allPeople, people, PersonRole.Editor, + person => PersonHelper.AddPersonIfNotExists(chapter.People, person)); + } + + if (!string.IsNullOrEmpty(comicInfo.Inker)) + { + var people = comicInfo.Inker.Split(","); + PersonHelper.RemovePeople(chapter.People, people, PersonRole.Inker); + PersonHelper.UpdatePeople(allPeople, people, PersonRole.Inker, + person => PersonHelper.AddPersonIfNotExists(chapter.People, person)); + } + + if (!string.IsNullOrEmpty(comicInfo.Letterer)) + { + var people = comicInfo.Letterer.Split(","); + PersonHelper.RemovePeople(chapter.People, people, PersonRole.Letterer); + PersonHelper.UpdatePeople(allPeople, people, PersonRole.Letterer, + person => PersonHelper.AddPersonIfNotExists(chapter.People, person)); + } + + if (!string.IsNullOrEmpty(comicInfo.Penciller)) + { + var people = comicInfo.Penciller.Split(","); + PersonHelper.RemovePeople(chapter.People, people, PersonRole.Penciller); + PersonHelper.UpdatePeople(allPeople, people, PersonRole.Penciller, + person => PersonHelper.AddPersonIfNotExists(chapter.People, person)); + } + + if (!string.IsNullOrEmpty(comicInfo.CoverArtist)) + { + var people = comicInfo.CoverArtist.Split(","); + PersonHelper.RemovePeople(chapter.People, people, PersonRole.CoverArtist); + PersonHelper.UpdatePeople(allPeople, people, PersonRole.CoverArtist, + person => PersonHelper.AddPersonIfNotExists(chapter.People, person)); + } + + if (!string.IsNullOrEmpty(comicInfo.Publisher)) + { + var people = comicInfo.Publisher.Split(","); + PersonHelper.RemovePeople(chapter.People, people, PersonRole.Publisher); + PersonHelper.UpdatePeople(allPeople, people, PersonRole.Publisher, + person => PersonHelper.AddPersonIfNotExists(chapter.People, person)); + } + + if (!string.IsNullOrEmpty(comicInfo.Genre)) + { + var genres = comicInfo.Genre.Split(","); + GenreHelper.KeepOnlySameGenreBetweenLists(chapter.Genres, genres.Select(g => DbFactory.Genre(g, false)).ToList()); + GenreHelper.UpdateGenre(allGenres, genres, false, + genre => chapter.Genres.Add(genre)); + } + } }