From 35a47f5d8810cb8f53df0248bdcb919355fbfdc9 Mon Sep 17 00:00:00 2001 From: Joseph Milazzo Date: Thu, 29 Apr 2021 17:52:34 -0500 Subject: [PATCH] Book Feedback and small bugs (#183) * Remove automatic retry for scanLibraries as if something fails, it wont pass magically. Catch exceptions when opening books for parsing and swallow to ignore the file. * Delete extra attempts * Switched to using FirstOrDefault for finding existing series. This will help avoid pointless crashes. * Updated message when duplicate series are found (not sure how this happens) * Fixed a negation for deleting volumes where files still exist. * Implemented the ability to automatically scale the manga reader based on screen size. --- API/Controllers/AccountController.cs | 2 +- API/Controllers/SeriesController.cs | 2 ++ API/DTOs/SeriesDto.cs | 1 + API/Data/SeriesRepository.cs | 4 +-- API/Entities/Enums/ScalingOption.cs | 3 +- API/Interfaces/IBookService.cs | 2 ++ API/Services/BookService.cs | 43 +++++++++++++++++++--------- API/Services/Tasks/ScannerService.cs | 31 ++++++++++++++++---- 8 files changed, 65 insertions(+), 23 deletions(-) diff --git a/API/Controllers/AccountController.cs b/API/Controllers/AccountController.cs index f788f931f..82dbe4e19 100644 --- a/API/Controllers/AccountController.cs +++ b/API/Controllers/AccountController.cs @@ -89,7 +89,7 @@ namespace API.Controllers var user = _mapper.Map(registerDto); user.UserPreferences ??= new AppUserPreferences(); - + var result = await _userManager.CreateAsync(user, registerDto.Password); if (!result.Succeeded) return BadRequest(result.Errors); diff --git a/API/Controllers/SeriesController.cs b/API/Controllers/SeriesController.cs index a2d350ebd..94a5b024a 100644 --- a/API/Controllers/SeriesController.cs +++ b/API/Controllers/SeriesController.cs @@ -32,6 +32,8 @@ namespace API.Controllers await _unitOfWork.SeriesRepository.GetSeriesDtoForLibraryIdAsync(libraryId, user.Id, userParams); // Apply progress/rating information (I can't work out how to do this in initial query) + if (series == null) return BadRequest("Could not get series for library"); + await _unitOfWork.SeriesRepository.AddSeriesModifiers(user.Id, series); Response.AddPaginationHeader(series.CurrentPage, series.PageSize, series.TotalCount, series.TotalPages); diff --git a/API/DTOs/SeriesDto.cs b/API/DTOs/SeriesDto.cs index b3057baac..3cb7f120b 100644 --- a/API/DTOs/SeriesDto.cs +++ b/API/DTOs/SeriesDto.cs @@ -23,5 +23,6 @@ public string UserReview { get; set; } public int LibraryId { get; set; } + public string LibraryName { get; set; } } } \ No newline at end of file diff --git a/API/Data/SeriesRepository.cs b/API/Data/SeriesRepository.cs index f111911c3..de35a8321 100644 --- a/API/Data/SeriesRepository.cs +++ b/API/Data/SeriesRepository.cs @@ -307,7 +307,7 @@ namespace API.Data } /// - /// Returns Series that the user + /// Returns Series that the user has some partial progress on /// /// /// @@ -327,8 +327,8 @@ namespace API.Data && s.PagesRead > 0 && s.PagesRead < s.Series.Pages && (libraryId <= 0 || s.Series.LibraryId == libraryId)) - .OrderByDescending(s => s.LastModified) .Take(limit) + .OrderByDescending(s => s.LastModified) .Select(s => s.Series) .ProjectTo(_mapper.ConfigurationProvider) .AsNoTracking() diff --git a/API/Entities/Enums/ScalingOption.cs b/API/Entities/Enums/ScalingOption.cs index 7e144100c..2da3b79f7 100644 --- a/API/Entities/Enums/ScalingOption.cs +++ b/API/Entities/Enums/ScalingOption.cs @@ -4,6 +4,7 @@ { FitToHeight = 0, FitToWidth = 1, - Original = 2 + Original = 2, + Automatic = 3 } } \ No newline at end of file diff --git a/API/Interfaces/IBookService.cs b/API/Interfaces/IBookService.cs index 43c3cd479..f0b5a8826 100644 --- a/API/Interfaces/IBookService.cs +++ b/API/Interfaces/IBookService.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Threading.Tasks; +using API.Parser; using VersOne.Epub; namespace API.Interfaces @@ -17,5 +18,6 @@ namespace API.Interfaces /// Task ScopeStyles(string stylesheetHtml, string apiBase); string GetSummaryInfo(string filePath); + ParserInfo ParseInfo(string filePath); } } \ No newline at end of file diff --git a/API/Services/BookService.cs b/API/Services/BookService.cs index 046dbcb40..e09485645 100644 --- a/API/Services/BookService.cs +++ b/API/Services/BookService.cs @@ -187,22 +187,37 @@ namespace API.Services return dict; } - public static ParserInfo ParseInfo(string filePath) + /// + /// Parses out Title from book. Chapters and Volumes will always be "0". If there is any exception reading book (malformed books) + /// then null is returned. + /// + /// + /// + public ParserInfo ParseInfo(string filePath) { - var epubBook = EpubReader.OpenBook(filePath); - - return new ParserInfo() + try { - Chapters = "0", - Edition = "", - Format = MangaFormat.Book, - Filename = Path.GetFileName(filePath), - Title = epubBook.Title, - FullFilePath = filePath, - IsSpecial = false, - Series = epubBook.Title, - Volumes = "0" - }; + var epubBook = EpubReader.OpenBook(filePath); + + return new ParserInfo() + { + Chapters = "0", + Edition = "", + Format = MangaFormat.Book, + Filename = Path.GetFileName(filePath), + Title = epubBook.Title, + FullFilePath = filePath, + IsSpecial = false, + Series = epubBook.Title, + Volumes = "0" + }; + } + catch (Exception ex) + { + _logger.LogError(ex, "There was an exception when opening epub book: {FileName}", filePath); + } + + return null; } public byte[] GetCoverImage(string fileFilePath, bool createThumbnail = true) diff --git a/API/Services/Tasks/ScannerService.cs b/API/Services/Tasks/ScannerService.cs index b6ca2c2be..f558c3a3a 100644 --- a/API/Services/Tasks/ScannerService.cs +++ b/API/Services/Tasks/ScannerService.cs @@ -42,12 +42,12 @@ namespace API.Services.Tasks [DisableConcurrentExecution(timeoutInSeconds: 360)] + [AutomaticRetry(Attempts = 0, OnAttemptsExceeded = AttemptsExceededAction.Delete)] public void ScanLibraries() { var libraries = Task.Run(() => _unitOfWork.LibraryRepository.GetLibrariesAsync()).Result.ToList(); foreach (var lib in libraries) { - // BUG?: I think we need to keep _scannedSeries within the ScanLibrary instance since this is multithreaded. ScanLibrary(lib.Id, false); } } @@ -68,6 +68,7 @@ namespace API.Services.Tasks } [DisableConcurrentExecution(360)] + [AutomaticRetry(Attempts = 0, OnAttemptsExceeded = AttemptsExceededAction.Delete)] public void ScanLibrary(int libraryId, bool forceUpdate) { var sw = Stopwatch.StartNew(); @@ -203,7 +204,27 @@ namespace API.Services.Tasks foreach (var (key, infos) in parsedSeries) { // Key is normalized already - var existingSeries = library.Series.SingleOrDefault(s => s.NormalizedName == key || Parser.Parser.Normalize(s.OriginalName) == key); + Series existingSeries = null; + try + { + existingSeries = library.Series.SingleOrDefault(s => s.NormalizedName == key || Parser.Parser.Normalize(s.OriginalName) == key); + } + catch (Exception e) + { + _logger.LogCritical(e, "There are multiple series that map to normalized key {Key}. You can manually delete the entity via UI and rescan to fix it", key); + var duplicateSeries = library.Series.Where(s => s.NormalizedName == key || Parser.Parser.Normalize(s.OriginalName) == key).ToList(); + //var firstSeries = duplicateSeries.First(); + //duplicateSeries. + foreach (var series in duplicateSeries) + { + _logger.LogCritical("{Key} maps with {Series}", key, series.OriginalName); + + } + // Merge them together? + //_unitOfWork.AppUserProgressRepository.MapSeriesProgressFromTo(firstSeries.Id, ); + + continue; + } if (existingSeries == null) { existingSeries = DbFactory.Series(infos[0].Series); @@ -292,7 +313,7 @@ namespace API.Services.Tasks foreach (var volume in deletedVolumes) { var file = volume.Chapters.FirstOrDefault()?.Files.FirstOrDefault()?.FilePath ?? "no files"; - if (!new FileInfo(file).Exists) + if (new FileInfo(file).Exists) { _logger.LogError("Volume cleanup code was trying to remove a volume with a file still existing on disk. File: {File}", file); } @@ -435,7 +456,7 @@ namespace API.Services.Tasks if (type == LibraryType.Book && Parser.Parser.IsEpub(path)) { - info = BookService.ParseInfo(path); + info = _bookService.ParseInfo(path); } else { @@ -451,7 +472,7 @@ namespace API.Services.Tasks if (type == LibraryType.Book && Parser.Parser.IsEpub(path) && Parser.Parser.ParseVolume(info.Series) != "0") { info = Parser.Parser.Parse(path, rootPath, type); - var info2 = BookService.ParseInfo(path); + var info2 = _bookService.ParseInfo(path); info.Merge(info2); }