diff --git a/API.Tests/Parser/BookParserTests.cs b/API.Tests/Parser/BookParserTests.cs index 219f5d723..b33ef1f54 100644 --- a/API.Tests/Parser/BookParserTests.cs +++ b/API.Tests/Parser/BookParserTests.cs @@ -10,5 +10,12 @@ namespace API.Tests.Parser { Assert.Equal(expected, API.Parser.Parser.ParseSeries(filename)); } + + [Theory] + [InlineData("Harrison, Kim - Dates from Hell - Hollows Vol 2.5.epub", "2.5")] + public void ParseVolumeTest(string filename, string expected) + { + Assert.Equal(expected, API.Parser.Parser.ParseVolume(filename)); + } } } diff --git a/API.Tests/Parser/MangaParserTests.cs b/API.Tests/Parser/MangaParserTests.cs index 7fb204ca7..9cb9d560a 100644 --- a/API.Tests/Parser/MangaParserTests.cs +++ b/API.Tests/Parser/MangaParserTests.cs @@ -239,6 +239,7 @@ namespace API.Tests.Parser [InlineData("Kimi no Koto ga Daidaidaidaidaisuki na 100-nin no Kanojo Chapter 1-10", "1-10")] [InlineData("Deku_&_Bakugo_-_Rising_v1_c1.1.cbz", "1.1")] [InlineData("Chapter 63 - The Promise Made for 520 Cenz.cbr", "63")] + [InlineData("Harrison, Kim - The Good, The Bad, and the Undead - Hollows Vol 2.5.epub", "0")] public void ParseChaptersTest(string filename, string expected) { Assert.Equal(expected, API.Parser.Parser.ParseChapter(filename)); @@ -425,6 +426,14 @@ namespace API.Tests.Parser FullFilePath = filepath, IsSpecial = false }); + filepath = @"E:\Manga\Harrison, Kim - The Good, The Bad, and the Undead - Hollows Vol 2.5.epub"; + expected.Add(filepath, new ParserInfo + { + Series = "Harrison, Kim - The Good, The Bad, and the Undead - Hollows", Volumes = "2.5", Edition = "", + Chapters = "0", Filename = "Harrison, Kim - The Good, The Bad, and the Undead - Hollows Vol 2.5.epub", Format = MangaFormat.Epub, + FullFilePath = filepath, IsSpecial = false + }); + // If an image is cover exclusively, ignore it filepath = @"E:\Manga\Seraph of the End\cover.png"; expected.Add(filepath, null); diff --git a/API/Extensions/DirectoryInfoExtensions.cs b/API/Extensions/DirectoryInfoExtensions.cs index 892c690b3..b92901046 100644 --- a/API/Extensions/DirectoryInfoExtensions.cs +++ b/API/Extensions/DirectoryInfoExtensions.cs @@ -76,7 +76,8 @@ namespace API.Extensions directoryIndex++; } - foreach (var subDirectory in directory.EnumerateDirectories()) + var sort = new NaturalSortComparer(); + foreach (var subDirectory in directory.EnumerateDirectories().OrderBy(d => d.FullName, sort)) { FlattenDirectory(root, subDirectory, ref directoryIndex); } diff --git a/API/Parser/Parser.cs b/API/Parser/Parser.cs index 5c9c22f83..ef2d9ef5d 100644 --- a/API/Parser/Parser.cs +++ b/API/Parser/Parser.cs @@ -470,7 +470,7 @@ namespace API.Parser RegexTimeout), // Hinowa ga CRUSH! 018 (2019) (Digital) (LuCaZ).cbz, Hinowa ga CRUSH! 018.5 (2019) (Digital) (LuCaZ).cbz new Regex( - @"^(?!Vol)(?.+?)\s(?\d+(?:.\d+|-\d+)?)(?:\s\(\d{4}\))?(\b|_|-)", + @"^(?!Vol)(?.+?)(?\d+(?:.\d+|-\d+)?)(?:\s\(\d{4}\))?(\b|_|-)", MatchOptions, RegexTimeout), // Tower Of God S01 014 (CBT) (digital).cbz diff --git a/API/Services/Tasks/ScannerService.cs b/API/Services/Tasks/ScannerService.cs index 0701fe13f..f1ba9cc59 100644 --- a/API/Services/Tasks/ScannerService.cs +++ b/API/Services/Tasks/ScannerService.cs @@ -73,8 +73,17 @@ namespace API.Services.Tasks if (!anyFilesExist) { - _unitOfWork.SeriesRepository.Remove(series); - await CommitAndSend(libraryId, seriesId, totalFiles, parsedSeries, sw, scanElapsedTime, series, chapterIds, token); + try + { + _unitOfWork.SeriesRepository.Remove(series); + await CommitAndSend(libraryId, seriesId, totalFiles, parsedSeries, sw, scanElapsedTime, series, chapterIds); + } + catch (Exception ex) + { + _logger.LogCritical(ex, "There was an error during ScanSeries to delete the series"); + await _unitOfWork.RollbackAsync(); + } + } else { @@ -97,7 +106,7 @@ namespace API.Services.Tasks } } - _logger.LogInformation("{SeriesName} has bad naming convention, forcing rescan at a higher directory.", series.OriginalName); + _logger.LogInformation("{SeriesName} has bad naming convention, forcing rescan at a higher directory", series.OriginalName); scanner = new ParseScannedFiles(_bookService, _logger); parsedSeries = scanner.ScanLibrariesForSeries(library.Type, dirs.Keys, out var totalFiles2, out var scanElapsedTime2); totalFiles += totalFiles2; @@ -109,8 +118,19 @@ namespace API.Services.Tasks // At this point, parsedSeries will have at least one key and we can perform the update. If it still doesn't, just return and don't do anything if (parsedSeries.Count == 0) return; - UpdateSeries(series, parsedSeries); - await CommitAndSend(libraryId, seriesId, totalFiles, parsedSeries, sw, scanElapsedTime, series, chapterIds, token); + try + { + UpdateSeries(series, parsedSeries); + await CommitAndSend(libraryId, seriesId, totalFiles, parsedSeries, sw, scanElapsedTime, series, chapterIds); + } + catch (Exception ex) + { + _logger.LogCritical(ex, "There was an error during ScanSeries to update the series"); + await _unitOfWork.RollbackAsync(); + } + // Tell UI that this series is done + await _messageHub.Clients.All.SendAsync(SignalREvents.ScanSeries, MessageFactory.ScanSeriesEvent(seriesId, series.Name), + cancellationToken: token); } private static void RemoveParsedInfosNotForSeries(Dictionary> parsedSeries, Series series) @@ -124,10 +144,11 @@ namespace API.Services.Tasks } private async Task CommitAndSend(int libraryId, int seriesId, int totalFiles, - Dictionary> parsedSeries, Stopwatch sw, long scanElapsedTime, Series series, int[] chapterIds, CancellationToken token) + Dictionary> parsedSeries, Stopwatch sw, long scanElapsedTime, Series series, int[] chapterIds) { - if (await _unitOfWork.CommitAsync()) + if (_unitOfWork.HasChanges()) { + await _unitOfWork.CommitAsync(); _logger.LogInformation( "Processed {TotalFiles} files and {ParsedSeriesCount} series in {ElapsedScanTime} milliseconds for {SeriesName}", totalFiles, parsedSeries.Keys.Count, sw.ElapsedMilliseconds + scanElapsedTime, series.Name); @@ -135,15 +156,6 @@ namespace API.Services.Tasks await CleanupDbEntities(); BackgroundJob.Enqueue(() => _metadataService.RefreshMetadataForSeries(libraryId, seriesId, false)); BackgroundJob.Enqueue(() => _cacheService.CleanupChapters(chapterIds)); - // Tell UI that this series is done - await _messageHub.Clients.All.SendAsync(SignalREvents.ScanSeries, MessageFactory.ScanSeriesEvent(seriesId, series.Name), - cancellationToken: token); - } - else - { - _logger.LogCritical( - "There was a critical error that resulted in a failed scan. Please check logs and rescan"); - await _unitOfWork.RollbackAsync(); } } diff --git a/UI/Web/src/app/admin/_modals/reset-password-modal/reset-password-modal.component.html b/UI/Web/src/app/admin/_modals/reset-password-modal/reset-password-modal.component.html index c7f16ebef..323f4b35f 100644 --- a/UI/Web/src/app/admin/_modals/reset-password-modal/reset-password-modal.component.html +++ b/UI/Web/src/app/admin/_modals/reset-password-modal/reset-password-modal.component.html @@ -1,6 +1,6 @@
    -
  • +
  • {{library.name}}  @@ -13,8 +13,8 @@

    - - + +
    diff --git a/UI/Web/src/app/admin/manage-library/manage-library.component.ts b/UI/Web/src/app/admin/manage-library/manage-library.component.ts index 36d5db3f9..7ba0c87b8 100644 --- a/UI/Web/src/app/admin/manage-library/manage-library.component.ts +++ b/UI/Web/src/app/admin/manage-library/manage-library.component.ts @@ -25,6 +25,7 @@ export class ManageLibraryComponent implements OnInit, OnDestroy { */ deletionInProgress: boolean = false; scanInProgress: {[key: number]: boolean} = {}; + libraryTrackBy = (index: number, item: Library) => `${item.name}_${item.lastScanned}_${item.type}_${item.folders.length}`; private readonly onDestroy = new Subject(); diff --git a/UI/Web/src/app/admin/manage-users/manage-users.component.html b/UI/Web/src/app/admin/manage-users/manage-users.component.html index 2f54e76ef..5b6ce57b9 100644 --- a/UI/Web/src/app/admin/manage-users/manage-users.component.html +++ b/UI/Web/src/app/admin/manage-users/manage-users.component.html @@ -19,7 +19,7 @@
    Last Active: Never - {{member.lastActive | date: 'MM/dd/yyyy'}} + {{member.lastActive | date: 'short'}}
    Sharing: {{formatLibraries(member)}}
    diff --git a/UI/Web/src/app/cards/_modals/card-details-modal/card-details-modal.component.html b/UI/Web/src/app/cards/_modals/card-details-modal/card-details-modal.component.html index d18987f6b..85e17f125 100644 --- a/UI/Web/src/app/cards/_modals/card-details-modal/card-details-modal.component.html +++ b/UI/Web/src/app/cards/_modals/card-details-modal/card-details-modal.component.html @@ -19,7 +19,7 @@
    - Added: {{(data.created | date: 'MM/dd/yyyy') || '-'}} + Added: {{(data.created | date: 'short') || '-'}}
    Pages: {{data.pages}} diff --git a/UI/Web/src/app/cards/_modals/edit-series-modal/edit-series-modal.component.html b/UI/Web/src/app/cards/_modals/edit-series-modal/edit-series-modal.component.html index c354ac22d..f39ee04d1 100644 --- a/UI/Web/src/app/cards/_modals/edit-series-modal/edit-series-modal.component.html +++ b/UI/Web/src/app/cards/_modals/edit-series-modal/edit-series-modal.component.html @@ -95,7 +95,7 @@

    Information

    -
    Library: {{libraryName | titlecase}}
    +
    Library: {{libraryName | sentenceCase}}
    Format: {{utilityService.mangaFormat(series.format)}}

    Volumes

    @@ -110,10 +110,10 @@
    - Created: {{volume.created | date: 'MM/dd/yyyy'}} + Created: {{volume.created | date: 'short'}}
    - Last Modified: {{volume.lastModified | date: 'MM/dd/yyyy'}} + Last Modified: {{volume.lastModified | date: 'short'}}
    diff --git a/UI/Web/src/app/cards/card-item/card-item.component.html b/UI/Web/src/app/cards/card-item/card-item.component.html index 5f42ecf65..7fee96b23 100644 --- a/UI/Web/src/app/cards/card-item/card-item.component.html +++ b/UI/Web/src/app/cards/card-item/card-item.component.html @@ -38,6 +38,6 @@
    - {{libraryName}} + {{libraryName | sentenceCase}}
    \ No newline at end of file diff --git a/UI/Web/src/app/manga-reader/manga-reader.component.html b/UI/Web/src/app/manga-reader/manga-reader.component.html index 0a96cadd0..698596fb5 100644 --- a/UI/Web/src/app/manga-reader/manga-reader.component.html +++ b/UI/Web/src/app/manga-reader/manga-reader.component.html @@ -31,8 +31,8 @@
    -
    -
    +
    +
    diff --git a/UI/Web/src/app/nav-header/nav-header.component.html b/UI/Web/src/app/nav-header/nav-header.component.html index c371393d3..87bc92ec5 100644 --- a/UI/Web/src/app/nav-header/nav-header.component.html +++ b/UI/Web/src/app/nav-header/nav-header.component.html @@ -63,7 +63,7 @@