Bugs, Enhancements, and Performance (#580)
* Added parser case for "The Duke of Death and His Black Maid - Ch. 177 - The Ball (3).cbz" * Removed a file that is created and modified every test run. * Fixed a bad parser case for "Batman Beyond 02 (of 6) (1999)" which was consuming too many characters * Removed a lot of "Volume" parsing for Comics that don't make sense. This is prep work for the upcoming Comic Rework release. * Reworked a lot of parsing cases for comics based on naming conventions observed from releases found online. * Added a way for external scripts to use a user api key to authenticate * Fixed an issue if the manga only had one page, the bottom menu would be missing page and chapter controls. * Fixed a bug where on small phones, nav bar could overflow due to scroll to top * Tweaked a lot of regex for manga parsing to handle some cases where poorly named files, like "Vol. 03 Ch. 21" would end up parsing as Series "Vol. 03". * Even more handling of parser cases. Manga parser should be as it was but more robust to handle bad naming. * Fixed: Don't force metadata refresh on Scan Series, only on refresh metadata * Implemented the ability to automatically refresh after a series scan based on when server finishes. Remove a duplicate API call from series detail. * Removed another API call for series metadata that isn't needed. * Refactored Message creation to a factory, hardcoded strings are centralized, and RefreshSeriesMetadata sends an event and is refactored to be async. * Fixed a bug when really poorly named files are within a folder that contains the series name, fallback couldn't occur due to it being taken as root folder. Now we detect said condition and will go one level higher, resulting in potentially more I/O, but the series will not be deleted. * Added the Read in Incognito context item for Chapter cards * Skip an additional check for series summary for series that aren't EPUB or Archive formats. * Fixed an issue where cover image generation could occur due to a bad check on LastWriteTime on the underlying file. * Added some extra comic parser tests * Added a ScanLibrary event (not hooked up in UI) * Performance improvement on metadata service. Now when we scan for cover image changes, we emit when a change occurs and only then do we update parent entities (array copy). * Removed an hr from series detail and ensure we update the cover image for series when scan series finishes. * Updated the infinite scroller to use a Flags pattern for the debug mode. Updated a few logical conditions for mobile. * Removed the concurrency check on row progress as if too many calls hit the DB, it will throw, but it doesn't matter. Fixed a bad logic code which could cause scrolling after hitting the bottom of the chapter. * Ensure prefetching uses totalPages + 1 since we pass in totalPages as - 1 from manga reader * Fixed issue where last page of webtoon wouldn't be prefetched due to a < instead of <= on prefetching code * Implemented ability to send images from archives to the UI without incurring any extra memory pressure. * Dropdown menus now have a darker background * Webtoon reader now works on mobile. * Fixed how keyboard presses for up/down/left/right work with MANGA_UD reading mode. See issue #579 * Fixed cont reader for webtoons on mobile * Fixed a small issue where top spacer would too quickly switch to prev chapter * Updated user preferences to use same slider style. Removed some css that is not used. * Added comic parser case for "Saga 001 (2012) (Digital) (Empire-Zone)" * Added accessibility toggle to reading list order and aligned sliders to all use the same style. * Removed a todo for checking on new image serving code. It works great. * Fixed a missing await * Auth guard will now check if an existing toast is present giving same message before poping the toast. * Fixed alignment on phones for reading lists * Moved sorters so they aren't resused between multiple threads. Slightly higher memory footprint. * Fixed a broken unit test * Code smells * More unit test fixing
This commit is contained in:
parent
b62d581491
commit
cf4fd2cb9c
52 changed files with 685 additions and 336 deletions
|
@ -30,4 +30,8 @@
|
|||
<Folder Include="Services\Test Data\ScannerService\Manga" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Extensions\Test Data\modified on run.txt" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
This file should be modified by the unit test08/20/2021 10:26:03
|
||||
08/20/2021 10:26:29
|
||||
08/22/2021 12:39:58
|
|
@ -23,27 +23,34 @@ namespace API.Tests.Parser
|
|||
[InlineData("Amazing Man Comics chapter 25", "Amazing Man Comics")]
|
||||
[InlineData("Amazing Man Comics issue #25", "Amazing Man Comics")]
|
||||
[InlineData("Teen Titans v1 038 (1972) (c2c).cbr", "Teen Titans")]
|
||||
[InlineData("Batman Beyond 02 (of 6) (1999)", "Batman Beyond")]
|
||||
[InlineData("Batman Beyond - Return of the Joker (2001)", "Batman Beyond - Return of the Joker")]
|
||||
[InlineData("Invincible 033.5 - Marvel Team-Up 14 (2006) (digital) (Minutemen-Slayer)", "Invincible")]
|
||||
[InlineData("Batman Wayne Family Adventures - Ep. 001 - Moving In", "Batman Wayne Family Adventures")]
|
||||
[InlineData("Saga 001 (2012) (Digital) (Empire-Zone).cbr", "Saga")]
|
||||
public void ParseComicSeriesTest(string filename, string expected)
|
||||
{
|
||||
Assert.Equal(expected, API.Parser.Parser.ParseComicSeries(filename));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("01 Spider-Man & Wolverine 01.cbr", "1")]
|
||||
[InlineData("04 - Asterix the Gladiator (1964) (Digital-Empire) (WebP by Doc MaKS)", "4")]
|
||||
[InlineData("01 Spider-Man & Wolverine 01.cbr", "0")]
|
||||
[InlineData("04 - Asterix the Gladiator (1964) (Digital-Empire) (WebP by Doc MaKS)", "0")]
|
||||
[InlineData("The First Asterix Frieze (WebP by Doc MaKS)", "0")]
|
||||
[InlineData("Batman & Catwoman - Trail of the Gun 01", "1")]
|
||||
[InlineData("Batman & Catwoman - Trail of the Gun 01", "0")]
|
||||
[InlineData("Batman & Daredevil - King of New York", "0")]
|
||||
[InlineData("Batman & Grendel (1996) 01 - Devil's Bones", "1")]
|
||||
[InlineData("Batman & Grendel (1996) 01 - Devil's Bones", "0")]
|
||||
[InlineData("Batman & Robin the Teen Wonder #0", "0")]
|
||||
[InlineData("Batman & Wildcat (1 of 3)", "0")]
|
||||
[InlineData("Batman And Superman World's Finest #01", "1")]
|
||||
[InlineData("Babe 01", "1")]
|
||||
[InlineData("Scott Pilgrim 01 - Scott Pilgrim's Precious Little Life (2004)", "1")]
|
||||
[InlineData("Batman And Superman World's Finest #01", "0")]
|
||||
[InlineData("Babe 01", "0")]
|
||||
[InlineData("Scott Pilgrim 01 - Scott Pilgrim's Precious Little Life (2004)", "0")]
|
||||
[InlineData("Teen Titans v1 001 (1966-02) (digital) (OkC.O.M.P.U.T.O.-Novus)", "1")]
|
||||
[InlineData("Scott Pilgrim 02 - Scott Pilgrim vs. The World (2005)", "2")]
|
||||
[InlineData("Scott Pilgrim 02 - Scott Pilgrim vs. The World (2005)", "0")]
|
||||
[InlineData("Superman v1 024 (09-10 1943)", "1")]
|
||||
[InlineData("Amazing Man Comics chapter 25", "0")]
|
||||
[InlineData("Invincible 033.5 - Marvel Team-Up 14 (2006) (digital) (Minutemen-Slayer)", "0")]
|
||||
[InlineData("Cyberpunk 2077 - Trauma Team 04.cbz", "0")]
|
||||
public void ParseComicVolumeTest(string filename, string expected)
|
||||
{
|
||||
Assert.Equal(expected, API.Parser.Parser.ParseComicVolume(filename));
|
||||
|
@ -66,6 +73,9 @@ namespace API.Tests.Parser
|
|||
[InlineData("Superman v1 024 (09-10 1943)", "24")]
|
||||
[InlineData("Invincible 070.5 - Invincible Returns 1 (2010) (digital) (Minutemen-InnerDemons).cbr", "70.5")]
|
||||
[InlineData("Amazing Man Comics chapter 25", "25")]
|
||||
[InlineData("Invincible 033.5 - Marvel Team-Up 14 (2006) (digital) (Minutemen-Slayer)", "33.5")]
|
||||
[InlineData("Batman Wayne Family Adventures - Ep. 014 - Moving In", "14")]
|
||||
[InlineData("Saga 001 (2012) (Digital) (Empire-Zone)", "1")]
|
||||
public void ParseComicChapterTest(string filename, string expected)
|
||||
{
|
||||
Assert.Equal(expected, API.Parser.Parser.ParseComicChapter(filename));
|
||||
|
|
|
@ -159,7 +159,13 @@ namespace API.Tests.Parser
|
|||
[InlineData("Hentai Ouji to Warawanai Neko. - Vol. 06 Ch. 034.5", "Hentai Ouji to Warawanai Neko.")]
|
||||
[InlineData("The 100 Girlfriends Who Really, Really, Really, Really, Really Love You - Vol. 03 Ch. 023.5 - Volume 3 Extras.cbz", "The 100 Girlfriends Who Really, Really, Really, Really, Really Love You")]
|
||||
[InlineData("Kimi no Koto ga Daidaidaidaidaisuki na 100-nin no Kanojo Chapter 1-10", "Kimi no Koto ga Daidaidaidaidaisuki na 100-nin no Kanojo")]
|
||||
[InlineData("The Duke of Death and His Black Maid - Ch. 177 - The Ball (3).cbz", "The Duke of Death and His Black Maid")]
|
||||
[InlineData("A Compendium of Ghosts - 031 - The Third Story_ Part 12 (Digital) (Cobalt001)", "A Compendium of Ghosts")]
|
||||
[InlineData("The Duke of Death and His Black Maid - Vol. 04 Ch. 054.5 - V4 Omake", "The Duke of Death and His Black Maid")]
|
||||
[InlineData("Vol. 04 Ch. 054.5", "")]
|
||||
[InlineData("Great_Teacher_Onizuka_v16[TheSpectrum]", "Great Teacher Onizuka")]
|
||||
[InlineData("[Renzokusei]_Kimi_wa_Midara_na_Boku_no_Joou_Ch5_Final_Chapter", "Kimi wa Midara na Boku no Joou")]
|
||||
[InlineData("Battle Royale, v01 (2000) [TokyoPop] [Manga-Sketchbook]", "Battle Royale")]
|
||||
public void ParseSeriesTest(string filename, string expected)
|
||||
{
|
||||
Assert.Equal(expected, API.Parser.Parser.ParseSeries(filename));
|
||||
|
@ -412,6 +418,22 @@ namespace API.Tests.Parser
|
|||
FullFilePath = filepath, IsSpecial = false
|
||||
});
|
||||
|
||||
filepath = @"E:\Manga\Kono Subarashii Sekai ni Bakuen wo!\Vol. 00 Ch. 000.cbz";
|
||||
expected.Add(filepath, new ParserInfo
|
||||
{
|
||||
Series = "Kono Subarashii Sekai ni Bakuen wo!", Volumes = "0", Edition = "",
|
||||
Chapters = "0", Filename = "Vol. 00 Ch. 000.cbz", Format = MangaFormat.Archive,
|
||||
FullFilePath = filepath, IsSpecial = false
|
||||
});
|
||||
|
||||
filepath = @"E:\Manga\Toukyou Akazukin\Vol. 01 Ch. 001.cbz";
|
||||
expected.Add(filepath, new ParserInfo
|
||||
{
|
||||
Series = "Toukyou Akazukin", Volumes = "1", Edition = "",
|
||||
Chapters = "1", Filename = "Vol. 01 Ch. 001.cbz", Format = MangaFormat.Archive,
|
||||
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);
|
||||
|
|
|
@ -89,6 +89,15 @@ namespace API.Tests.Services
|
|||
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(new string[] {"C:/Manga/"}, new string[] {"C:/Manga/Love Hina/Vol. 01.cbz"}, "C:/Manga/Love Hina")]
|
||||
public void FindHighestDirectoriesFromFilesTest(string[] rootDirectories, string[] folders, string expectedDirectory)
|
||||
{
|
||||
var actual = DirectoryService.FindHighestDirectoriesFromFiles(rootDirectories, folders);
|
||||
var expected = new Dictionary<string, string> {{expectedDirectory, ""}};
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("C:/Manga/", "C:/Manga/Love Hina/Specials/Omake/", "Omake,Specials,Love Hina")]
|
||||
[InlineData("C:/Manga/", "C:/Manga/Love Hina/Specials/Omake", "Omake,Specials,Love Hina")]
|
||||
|
@ -102,6 +111,7 @@ namespace API.Tests.Services
|
|||
[InlineData(@"C:/", @"C://Btooom!/Vol.1 Chapter 2/1.cbz", "Vol.1 Chapter 2,Btooom!")]
|
||||
[InlineData(@"C:\\", @"C://Btooom!/Vol.1 Chapter 2/1.cbz", "Vol.1 Chapter 2,Btooom!")]
|
||||
[InlineData(@"C://mount/gdrive/Library/Test Library/Comics", @"C://mount/gdrive/Library/Test Library/Comics/Dragon Age/Test", "Test,Dragon Age")]
|
||||
[InlineData(@"M:\", @"M:\Toukyou Akazukin\Vol. 01 Ch. 005.cbz", @"Toukyou Akazukin")]
|
||||
public void GetFoldersTillRoot_Test(string rootPath, string fullpath, string expectedArray)
|
||||
{
|
||||
var expected = expectedArray.Split(",");
|
||||
|
|
|
@ -4,6 +4,8 @@ using API.Entities;
|
|||
using API.Interfaces;
|
||||
using API.Interfaces.Services;
|
||||
using API.Services;
|
||||
using API.SignalR;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
@ -19,10 +21,11 @@ namespace API.Tests.Services
|
|||
private readonly IBookService _bookService = Substitute.For<IBookService>();
|
||||
private readonly IArchiveService _archiveService = Substitute.For<IArchiveService>();
|
||||
private readonly ILogger<MetadataService> _logger = Substitute.For<ILogger<MetadataService>>();
|
||||
private readonly IHubContext<MessageHub> _messageHub = Substitute.For<IHubContext<MessageHub>>();
|
||||
|
||||
public MetadataServiceTests()
|
||||
{
|
||||
_metadataService = new MetadataService(_unitOfWork, _logger, _archiveService, _bookService, _imageService);
|
||||
_metadataService = new MetadataService(_unitOfWork, _logger, _archiveService, _bookService, _imageService, _messageHub);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -108,5 +111,16 @@ namespace API.Tests.Services
|
|||
LastModified = new FileInfo(Path.Join(_testDirectory, "file in folder.zip")).LastWriteTime
|
||||
}, false, false));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
public void ShouldUpdateCoverImage_OnSecondRun_HasCoverImage_NoForceUpdate_NoLock()
|
||||
{
|
||||
Assert.False(MetadataService.ShouldUpdateCoverImage(new byte[] {1}, new MangaFile()
|
||||
{
|
||||
FilePath = Path.Join(_testDirectory, "file in folder.zip"),
|
||||
LastModified = DateTime.Now
|
||||
}, false, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,8 +14,10 @@ using API.Parser;
|
|||
using API.Services;
|
||||
using API.Services.Tasks;
|
||||
using API.Services.Tasks.Scanner;
|
||||
using API.SignalR;
|
||||
using API.Tests.Helpers;
|
||||
using AutoMapper;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
|
@ -34,6 +36,7 @@ namespace API.Tests.Services
|
|||
private readonly IImageService _imageService = Substitute.For<IImageService>();
|
||||
private readonly ILogger<MetadataService> _metadataLogger = Substitute.For<ILogger<MetadataService>>();
|
||||
private readonly ICacheService _cacheService = Substitute.For<ICacheService>();
|
||||
private readonly IHubContext<MessageHub> _messageHub = Substitute.For<IHubContext<MessageHub>>();
|
||||
|
||||
private readonly DbConnection _connection;
|
||||
private readonly DataContext _context;
|
||||
|
@ -52,8 +55,8 @@ namespace API.Tests.Services
|
|||
IUnitOfWork unitOfWork = new UnitOfWork(_context, Substitute.For<IMapper>(), null);
|
||||
|
||||
|
||||
IMetadataService metadataService = Substitute.For<MetadataService>(unitOfWork, _metadataLogger, _archiveService, _bookService, _imageService);
|
||||
_scannerService = new ScannerService(unitOfWork, _logger, _archiveService, metadataService, _bookService, _cacheService);
|
||||
IMetadataService metadataService = Substitute.For<MetadataService>(unitOfWork, _metadataLogger, _archiveService, _bookService, _imageService, _messageHub);
|
||||
_scannerService = new ScannerService(unitOfWork, _logger, _archiveService, metadataService, _bookService, _cacheService, _messageHub);
|
||||
}
|
||||
|
||||
private async Task<bool> SeedDb()
|
||||
|
@ -111,6 +114,7 @@ namespace API.Tests.Services
|
|||
Assert.Empty(_scannerService.FindSeriesNotOnDisk(existingSeries, infos));
|
||||
}
|
||||
|
||||
|
||||
// TODO: Figure out how to do this with ParseScannedFiles
|
||||
// [Theory]
|
||||
// [InlineData(new [] {"Darker than Black"}, "Darker than Black", "Darker than Black")]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue