Foundational Cover Image Rework (#584)
* Updating wording on card item when total pages is 0, to be just "Cannot Read" since it could be a non-archive file * Refactored cover images to be stored on disk. This first commit has the extraction to disk and the metadata service to handle updating when applicable. * Refactored code to have the actual save to cover image directory done by ImageService. * Implemented the ability to override cover images. * Some cleanup on Image service * Implemented the ability to cleanup old covers nightly * Added a migration to migrate existing covers to new cover image format (files). * Refactored all CoverImages to just be the filename, leaving the Join with Cover directory to higher level code. * Ensure when getting user progress, we pick the first. * Added cleanup cover images for deleted tags. Don't pull any cover images that are blank. * After series update, clear out cover image. No change on UI, but just keeps things clear before metadata refresh hits * Refactored image formats for covers to ImageService. * Fixed an issue where after refactoring how images were stored, the cleanup service was deleting them after each scan. * Changed how ShouldUpdateCoverImage works to check if file exists or not even if cover image is locked. * Fixed unit tests * Added caching back to cover images. * Caching on series as well * Code Cleanup items * Ensure when checking if a file exists in MetadataService, that we join for cover image directory. After we scan library, do one last filter to delete any series that have 0 pages total. * Catch exceptions so we don't run cover migration if this is first time run. * After a scan, only clear out the cache directory and not do a deep clean. * Implemented the ability to backup custom locked covers only. * Fixed unit tests * Trying to figure out why GA crashes when running MetadataServiceTests.cs * Some debugging on GA tests not running * Commented out tests that were causing issues in GA. * Fixed an issue where series cover images wouldn't migrate * Fixed the updating of links to actually do all series and not just locked
This commit is contained in:
parent
fd6925b126
commit
82b5b599e0
50 changed files with 1928 additions and 234 deletions
|
|
@ -2,6 +2,7 @@
|
|||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using API.Archive;
|
||||
using API.Interfaces.Services;
|
||||
using API.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NSubstitute;
|
||||
|
|
@ -17,11 +18,12 @@ namespace API.Tests.Services
|
|||
private readonly ArchiveService _archiveService;
|
||||
private readonly ILogger<ArchiveService> _logger = Substitute.For<ILogger<ArchiveService>>();
|
||||
private readonly ILogger<DirectoryService> _directoryServiceLogger = Substitute.For<ILogger<DirectoryService>>();
|
||||
private readonly IDirectoryService _directoryService = new DirectoryService(Substitute.For<ILogger<DirectoryService>>());
|
||||
|
||||
public ArchiveServiceTests(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
_testOutputHelper = testOutputHelper;
|
||||
_archiveService = new ArchiveService(_logger, new DirectoryService(_directoryServiceLogger));
|
||||
_archiveService = new ArchiveService(_logger, _directoryService);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -50,7 +52,7 @@ namespace API.Tests.Services
|
|||
var testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/ArchiveService/Archives");
|
||||
Assert.Equal(expected, _archiveService.IsValidArchive(Path.Join(testDirectory, archivePath)));
|
||||
}
|
||||
|
||||
|
||||
[Theory]
|
||||
[InlineData("non existent file.zip", 0)]
|
||||
[InlineData("winrar.rar", 0)]
|
||||
|
|
@ -69,7 +71,7 @@ namespace API.Tests.Services
|
|||
Assert.Equal(expected, _archiveService.GetNumberOfPagesFromArchive(Path.Join(testDirectory, archivePath)));
|
||||
_testOutputHelper.WriteLine($"Processed Original in {sw.ElapsedMilliseconds} ms");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
[Theory]
|
||||
|
|
@ -84,12 +86,12 @@ namespace API.Tests.Services
|
|||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
var testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/ArchiveService/Archives");
|
||||
|
||||
|
||||
Assert.Equal(expected, _archiveService.CanOpen(Path.Join(testDirectory, archivePath)));
|
||||
_testOutputHelper.WriteLine($"Processed Original in {sw.ElapsedMilliseconds} ms");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
[Theory]
|
||||
[InlineData("non existent file.zip", 0)]
|
||||
[InlineData("winrar.rar", 0)]
|
||||
|
|
@ -100,18 +102,18 @@ namespace API.Tests.Services
|
|||
[InlineData("file in folder_alt.zip", 1)]
|
||||
public void CanExtractArchive(string archivePath, int expectedFileCount)
|
||||
{
|
||||
|
||||
|
||||
var testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/ArchiveService/Archives");
|
||||
var extractDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/ArchiveService/Archives/Extraction");
|
||||
|
||||
DirectoryService.ClearAndDeleteDirectory(extractDirectory);
|
||||
|
||||
|
||||
Stopwatch sw = Stopwatch.StartNew();
|
||||
_archiveService.ExtractArchive(Path.Join(testDirectory, archivePath), extractDirectory);
|
||||
var di1 = new DirectoryInfo(extractDirectory);
|
||||
Assert.Equal(expectedFileCount, di1.Exists ? di1.GetFiles().Length : 0);
|
||||
_testOutputHelper.WriteLine($"Processed in {sw.ElapsedMilliseconds} ms");
|
||||
|
||||
|
||||
DirectoryService.ClearAndDeleteDirectory(extractDirectory);
|
||||
}
|
||||
|
||||
|
|
@ -142,14 +144,14 @@ namespace API.Tests.Services
|
|||
var foundFile = _archiveService.FirstFileEntry(files);
|
||||
Assert.Equal(expected, string.IsNullOrEmpty(foundFile) ? "" : foundFile);
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Theory]
|
||||
|
||||
|
||||
|
||||
// TODO: This is broken on GA due to DirectoryService.CoverImageDirectory
|
||||
//[Theory]
|
||||
[InlineData("v10.cbz", "v10.expected.jpg")]
|
||||
[InlineData("v10 - with folder.cbz", "v10 - with folder.expected.jpg")]
|
||||
[InlineData("v10 - nested folder.cbz", "v10 - nested folder.expected.jpg")]
|
||||
//[InlineData("png.zip", "png.PNG")]
|
||||
[InlineData("macos_native.zip", "macos_native.jpg")]
|
||||
[InlineData("v10 - duplicate covers.cbz", "v10 - duplicate covers.expected.jpg")]
|
||||
[InlineData("sorting.zip", "sorting.expected.jpg")]
|
||||
|
|
@ -159,17 +161,29 @@ namespace API.Tests.Services
|
|||
var testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/ArchiveService/CoverImages");
|
||||
var expectedBytes = File.ReadAllBytes(Path.Join(testDirectory, expectedOutputFile));
|
||||
archiveService.Configure().CanOpen(Path.Join(testDirectory, inputFile)).Returns(ArchiveLibrary.Default);
|
||||
Stopwatch sw = Stopwatch.StartNew();
|
||||
Assert.Equal(expectedBytes, archiveService.GetCoverImage(Path.Join(testDirectory, inputFile)));
|
||||
var sw = Stopwatch.StartNew();
|
||||
|
||||
var outputDir = Path.Join(testDirectory, "output");
|
||||
DirectoryService.ClearAndDeleteDirectory(outputDir);
|
||||
DirectoryService.ExistOrCreate(outputDir);
|
||||
|
||||
|
||||
var coverImagePath = archiveService.GetCoverImage(Path.Join(testDirectory, inputFile),
|
||||
Path.GetFileNameWithoutExtension(inputFile) + "_output");
|
||||
var actual = File.ReadAllBytes(coverImagePath);
|
||||
|
||||
|
||||
Assert.Equal(expectedBytes, actual);
|
||||
_testOutputHelper.WriteLine($"Processed in {sw.ElapsedMilliseconds} ms");
|
||||
DirectoryService.ClearAndDeleteDirectory(outputDir);
|
||||
}
|
||||
|
||||
|
||||
[Theory]
|
||||
|
||||
|
||||
// TODO: This is broken on GA due to DirectoryService.CoverImageDirectory
|
||||
//[Theory]
|
||||
[InlineData("v10.cbz", "v10.expected.jpg")]
|
||||
[InlineData("v10 - with folder.cbz", "v10 - with folder.expected.jpg")]
|
||||
[InlineData("v10 - nested folder.cbz", "v10 - nested folder.expected.jpg")]
|
||||
//[InlineData("png.zip", "png.PNG")]
|
||||
[InlineData("macos_native.zip", "macos_native.jpg")]
|
||||
[InlineData("v10 - duplicate covers.cbz", "v10 - duplicate covers.expected.jpg")]
|
||||
[InlineData("sorting.zip", "sorting.expected.jpg")]
|
||||
|
|
@ -178,20 +192,21 @@ namespace API.Tests.Services
|
|||
var archiveService = Substitute.For<ArchiveService>(_logger, new DirectoryService(_directoryServiceLogger));
|
||||
var testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/ArchiveService/CoverImages");
|
||||
var expectedBytes = File.ReadAllBytes(Path.Join(testDirectory, expectedOutputFile));
|
||||
|
||||
|
||||
archiveService.Configure().CanOpen(Path.Join(testDirectory, inputFile)).Returns(ArchiveLibrary.SharpCompress);
|
||||
Stopwatch sw = Stopwatch.StartNew();
|
||||
Assert.Equal(expectedBytes, archiveService.GetCoverImage(Path.Join(testDirectory, inputFile)));
|
||||
Assert.Equal(expectedBytes, File.ReadAllBytes(archiveService.GetCoverImage(Path.Join(testDirectory, inputFile), Path.GetFileNameWithoutExtension(inputFile) + "_output")));
|
||||
_testOutputHelper.WriteLine($"Processed in {sw.ElapsedMilliseconds} ms");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
// TODO: This is broken on GA due to DirectoryService.CoverImageDirectory
|
||||
//[Theory]
|
||||
[InlineData("Archives/macos_native.zip")]
|
||||
[InlineData("Formats/One File with DB_Supported.zip")]
|
||||
public void CanParseCoverImage(string inputFile)
|
||||
{
|
||||
var testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/ArchiveService/");
|
||||
Assert.NotEmpty(_archiveService.GetCoverImage(Path.Join(testDirectory, inputFile)));
|
||||
Assert.NotEmpty(File.ReadAllBytes(_archiveService.GetCoverImage(Path.Join(testDirectory, inputFile), Path.GetFileNameWithoutExtension(inputFile) + "_output")));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -200,9 +215,9 @@ namespace API.Tests.Services
|
|||
var testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/ArchiveService/ComicInfos");
|
||||
var archive = Path.Join(testDirectory, "file in folder.zip");
|
||||
var summaryInfo = "By all counts, Ryouta Sakamoto is a loser when he's not holed up in his room, bombing things into oblivion in his favorite online action RPG. But his very own uneventful life is blown to pieces when he's abducted and taken to an uninhabited island, where he soon learns the hard way that he's being pitted against others just like him in a explosives-riddled death match! How could this be happening? Who's putting them up to this? And why!? The name, not to mention the objective, of this very real survival game is eerily familiar to Ryouta, who has mastered its virtual counterpart-BTOOOM! Can Ryouta still come out on top when he's playing for his life!?";
|
||||
|
||||
|
||||
Assert.Equal(summaryInfo, _archiveService.GetSummaryInfo(archive));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue