More Filtering and Support for ComicInfo v2.1 (draft) Tags (#851)
* Added a reoccuring task to cleanup db entries that might be abandoned. On library page, the Library in question will be prepoulated. * Laid out the foundation for customized sorting. Added all series page to the UI when clicking on Libraries section header on home page so user can apply any filtering they like. * When filtering, the current library filter will now automatically filter out the options for people and genres. * Implemented Sorting controls * Clear now clears sorting and read progress. Sorting is disabled on deck and recently added. * Fixed an issue where all-series page couldn't click to open series * Don't let the user unselect the last read progress. Added new comicinfo v2.1 draft tags. * Hooked in Translator tag into backend and UI. * Fixed an issue where you could open multiple typeaheads at the same time * Integrated Translator and Tags ComicInfo extension fields. Started work on a badge expander. * Reworked a bit more on badge expander. Added the UI code for Age Rating and Tags * Integrated backend for Tags, Translator, and Age Rating * Metadata tags now collapse if more than 4 present * Some code cleanup * Made the not read badge slightly smaller
This commit is contained in:
parent
21da5d8134
commit
94bad97511
71 changed files with 4324 additions and 207 deletions
|
|
@ -76,16 +76,16 @@ public class MetadataService : IMetadataService
|
|||
return true;
|
||||
}
|
||||
|
||||
private void UpdateChapterMetadata(Chapter chapter, ICollection<Person> allPeople, bool forceUpdate)
|
||||
private void UpdateChapterMetadata(Chapter chapter, ICollection<Person> allPeople, ICollection<Tag> allTags, bool forceUpdate)
|
||||
{
|
||||
var firstFile = chapter.Files.OrderBy(x => x.Chapter).FirstOrDefault();
|
||||
if (firstFile == null || _cacheHelper.HasFileNotChangedSinceCreationOrLastScan(chapter, forceUpdate, firstFile)) return;
|
||||
|
||||
UpdateChapterFromComicInfo(chapter, allPeople, firstFile);
|
||||
UpdateChapterFromComicInfo(chapter, allPeople, allTags, firstFile);
|
||||
firstFile.UpdateLastModified();
|
||||
}
|
||||
|
||||
private void UpdateChapterFromComicInfo(Chapter chapter, ICollection<Person> allPeople, MangaFile firstFile)
|
||||
private void UpdateChapterFromComicInfo(Chapter chapter, ICollection<Person> allPeople, ICollection<Tag> allTags, MangaFile firstFile)
|
||||
{
|
||||
// TODO: Think about letting the higher level loop have access for series to avoid duplicate IO operations
|
||||
var comicInfo = _readingItemService.GetComicInfo(firstFile.FilePath, firstFile.Format);
|
||||
|
|
@ -98,7 +98,7 @@ public class MetadataService : IMetadataService
|
|||
chapter.TitleName = comicInfo.Title.Trim();
|
||||
}
|
||||
|
||||
if (comicInfo.Year > 0 && comicInfo.Month > 0)
|
||||
if (comicInfo.Year > 0)
|
||||
{
|
||||
var day = Math.Max(comicInfo.Day, 1);
|
||||
var month = Math.Max(comicInfo.Month, 1);
|
||||
|
|
@ -113,6 +113,26 @@ public class MetadataService : IMetadataService
|
|||
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(",");
|
||||
|
|
@ -198,7 +218,6 @@ public class MetadataService : IMetadataService
|
|||
{
|
||||
if (series == null) return;
|
||||
|
||||
// NOTE: This will fail if we replace the cover of the first volume on a first scan. Because the series will already have a cover image
|
||||
if (!_cacheHelper.ShouldUpdateCoverImage(_directoryService.FileSystem.Path.Join(_directoryService.CoverImageDirectory, series.CoverImage), null, series.Created, forceUpdate, series.CoverImageLocked))
|
||||
return;
|
||||
|
||||
|
|
@ -223,7 +242,7 @@ public class MetadataService : IMetadataService
|
|||
series.CoverImage = firstCover?.CoverImage ?? coverImage;
|
||||
}
|
||||
|
||||
private void UpdateSeriesMetadata(Series series, ICollection<Person> allPeople, ICollection<Genre> allGenres, bool forceUpdate)
|
||||
private void UpdateSeriesMetadata(Series series, ICollection<Person> allPeople, ICollection<Genre> allGenres, ICollection<Tag> allTags, bool forceUpdate)
|
||||
{
|
||||
var isBook = series.Library.Type == LibraryType.Book;
|
||||
var firstVolume = series.Volumes.OrderBy(c => c.Number, new ChapterSortComparer()).FirstWithChapters(isBook);
|
||||
|
|
@ -233,10 +252,6 @@ public class MetadataService : IMetadataService
|
|||
if (firstFile == null || _cacheHelper.HasFileNotChangedSinceCreationOrLastScan(firstChapter, forceUpdate, firstFile)) return;
|
||||
if (Parser.Parser.IsPdf(firstFile.FilePath)) return;
|
||||
|
||||
var comicInfo = _readingItemService.GetComicInfo(firstFile.FilePath, firstFile.Format);
|
||||
if (comicInfo == null) return;
|
||||
|
||||
|
||||
foreach (var chapter in series.Volumes.SelectMany(volume => volume.Chapters))
|
||||
{
|
||||
PersonHelper.UpdatePeople(allPeople, chapter.People.Where(p => p.Role == PersonRole.Writer).Select(p => p.Name), PersonRole.Writer,
|
||||
|
|
@ -265,29 +280,40 @@ public class MetadataService : IMetadataService
|
|||
|
||||
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));
|
||||
}
|
||||
|
||||
var comicInfos = series.Volumes
|
||||
.SelectMany(volume => volume.Chapters)
|
||||
.OrderBy(c => double.Parse(c.Number), new ChapterSortComparer())
|
||||
.SelectMany(c => c.Files)
|
||||
.Select(file => _readingItemService.GetComicInfo(file.FilePath, file.Format))
|
||||
.Where(ci => ci != null)
|
||||
.ToList();
|
||||
|
||||
//var firstComicInfo = comicInfos.First(i => i.)
|
||||
// Summary Info
|
||||
if (!string.IsNullOrEmpty(comicInfo.Summary))
|
||||
var comicInfo = comicInfos.FirstOrDefault();
|
||||
if (!string.IsNullOrEmpty(comicInfo?.Summary))
|
||||
{
|
||||
// PERF: I can move this to the bottom as I have a comicInfo selection, save me an extra read
|
||||
series.Metadata.Summary = comicInfo.Summary;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(comicInfo?.LanguageISO))
|
||||
{
|
||||
series.Metadata.Language = comicInfo.LanguageISO;
|
||||
}
|
||||
|
||||
// Set the AgeRating as highest in all the comicInfos
|
||||
series.Metadata.AgeRating = comicInfos.Max(i => ComicInfo.ConvertAgeRatingToEnum(comicInfo.AgeRating));
|
||||
series.Metadata.AgeRating = comicInfos.Max(i => ComicInfo.ConvertAgeRatingToEnum(comicInfo?.AgeRating));
|
||||
series.Metadata.ReleaseYear = series.Volumes
|
||||
.SelectMany(volume => volume.Chapters).Min(c => c.ReleaseDate.Year);
|
||||
|
||||
var genres = comicInfos.SelectMany(i => i?.Genre.Split(",")).Distinct().ToList();
|
||||
var tags = comicInfos.SelectMany(i => i?.Tags.Split(",")).Distinct().ToList();
|
||||
var people = series.Volumes.SelectMany(volume => volume.Chapters).SelectMany(c => c.People).ToList();
|
||||
|
||||
|
||||
|
|
@ -304,7 +330,7 @@ public class MetadataService : IMetadataService
|
|||
/// </summary>
|
||||
/// <param name="series"></param>
|
||||
/// <param name="forceUpdate"></param>
|
||||
private void ProcessSeriesMetadataUpdate(Series series, ICollection<Person> allPeople, ICollection<Genre> allGenres, bool forceUpdate)
|
||||
private void ProcessSeriesMetadataUpdate(Series series, ICollection<Person> allPeople, ICollection<Genre> allGenres, ICollection<Tag> allTags, bool forceUpdate)
|
||||
{
|
||||
_logger.LogDebug("[MetadataService] Processing series {SeriesName}", series.OriginalName);
|
||||
try
|
||||
|
|
@ -316,14 +342,14 @@ public class MetadataService : IMetadataService
|
|||
foreach (var chapter in volume.Chapters)
|
||||
{
|
||||
chapterUpdated = UpdateChapterCoverImage(chapter, forceUpdate);
|
||||
UpdateChapterMetadata(chapter, allPeople, forceUpdate || chapterUpdated);
|
||||
UpdateChapterMetadata(chapter, allPeople, allTags, forceUpdate || chapterUpdated);
|
||||
}
|
||||
|
||||
volumeUpdated = UpdateVolumeCoverImage(volume, chapterUpdated || forceUpdate);
|
||||
}
|
||||
|
||||
UpdateSeriesCoverImage(series, volumeUpdated || forceUpdate);
|
||||
UpdateSeriesMetadata(series, allPeople, allGenres, forceUpdate);
|
||||
UpdateSeriesMetadata(series, allPeople, allGenres, allTags, forceUpdate);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -370,6 +396,7 @@ public class MetadataService : IMetadataService
|
|||
|
||||
var allPeople = await _unitOfWork.PersonRepository.GetAllPeople();
|
||||
var allGenres = await _unitOfWork.GenreRepository.GetAllGenresAsync();
|
||||
var allTags = await _unitOfWork.TagRepository.GetAllTagsAsync();
|
||||
|
||||
|
||||
var seriesIndex = 0;
|
||||
|
|
@ -377,7 +404,7 @@ public class MetadataService : IMetadataService
|
|||
{
|
||||
try
|
||||
{
|
||||
ProcessSeriesMetadataUpdate(series, allPeople, allGenres, forceUpdate);
|
||||
ProcessSeriesMetadataUpdate(series, allPeople, allGenres, allTags, forceUpdate);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -404,14 +431,19 @@ public class MetadataService : IMetadataService
|
|||
await _messageHub.Clients.All.SendAsync(SignalREvents.RefreshMetadataProgress,
|
||||
MessageFactory.RefreshMetadataProgressEvent(library.Id, 1F));
|
||||
|
||||
// TODO: Remove any leftover People from DB
|
||||
await _unitOfWork.PersonRepository.RemoveAllPeopleNoLongerAssociated();
|
||||
await _unitOfWork.GenreRepository.RemoveAllGenreNoLongerAssociated();
|
||||
await RemoveAbandonedMetadataKeys();
|
||||
|
||||
|
||||
_logger.LogInformation("[MetadataService] Updated metadata for {SeriesNumber} series in library {LibraryName} in {ElapsedMilliseconds} milliseconds total", chunkInfo.TotalSize, library.Name, totalTime);
|
||||
}
|
||||
|
||||
private async Task RemoveAbandonedMetadataKeys()
|
||||
{
|
||||
await _unitOfWork.TagRepository.RemoveAllTagNoLongerAssociated();
|
||||
await _unitOfWork.PersonRepository.RemoveAllPeopleNoLongerAssociated();
|
||||
await _unitOfWork.GenreRepository.RemoveAllGenreNoLongerAssociated();
|
||||
}
|
||||
|
||||
// TODO: I can probably refactor RefreshMetadata and RefreshMetadataForSeries to be the same by utilizing chunk size of 1, so most of the code can be the same.
|
||||
private async Task PerformScan(Library library, bool forceUpdate, Action<int, Chunk> action)
|
||||
{
|
||||
|
|
@ -490,8 +522,9 @@ public class MetadataService : IMetadataService
|
|||
|
||||
var allPeople = await _unitOfWork.PersonRepository.GetAllPeople();
|
||||
var allGenres = await _unitOfWork.GenreRepository.GetAllGenresAsync();
|
||||
var allTags = await _unitOfWork.TagRepository.GetAllTagsAsync();
|
||||
|
||||
ProcessSeriesMetadataUpdate(series, allPeople, allGenres, forceUpdate);
|
||||
ProcessSeriesMetadataUpdate(series, allPeople, allGenres, allTags, forceUpdate);
|
||||
|
||||
await _messageHub.Clients.All.SendAsync(SignalREvents.RefreshMetadataProgress,
|
||||
MessageFactory.RefreshMetadataProgressEvent(libraryId, 1F));
|
||||
|
|
@ -502,6 +535,8 @@ public class MetadataService : IMetadataService
|
|||
await _messageHub.Clients.All.SendAsync(SignalREvents.RefreshMetadata, MessageFactory.RefreshMetadataEvent(series.LibraryId, series.Id));
|
||||
}
|
||||
|
||||
await RemoveAbandonedMetadataKeys();
|
||||
|
||||
_logger.LogInformation("[MetadataService] Updated metadata for {SeriesName} in {ElapsedMilliseconds} milliseconds", series.Name, sw.ElapsedMilliseconds);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue