diff --git a/API.Tests/API.Tests.csproj b/API.Tests/API.Tests.csproj index e19d7abc9..ec5ee39dd 100644 --- a/API.Tests/API.Tests.csproj +++ b/API.Tests/API.Tests.csproj @@ -23,4 +23,8 @@ + + + + diff --git a/API.Tests/ParserTest.cs b/API.Tests/ParserTest.cs index 82c4a4892..320856c32 100644 --- a/API.Tests/ParserTest.cs +++ b/API.Tests/ParserTest.cs @@ -82,5 +82,15 @@ namespace API.Tests { Assert.Equal(expected, CleanTitle(input)); } + + [Theory] + [InlineData("test.cbz", true)] + [InlineData("test.cbr", true)] + [InlineData("test.zip", true)] + [InlineData("test.rar", true)] + public void IsArchiveTest(string input, bool expected) + { + Assert.Equal(expected, IsArchive(input)); + } } } \ No newline at end of file diff --git a/API.Tests/Services/ImageProviderTest.cs b/API.Tests/Services/ImageProviderTest.cs new file mode 100644 index 000000000..eae737cb7 --- /dev/null +++ b/API.Tests/Services/ImageProviderTest.cs @@ -0,0 +1,21 @@ +using System; +using System.IO; +using API.IO; +using Xunit; + +namespace API.Tests.Services +{ + public class ImageProviderTest + { + [Theory] + [InlineData("v10.cbz", "v10.expected.jpg")] + [InlineData("v10 - with folder.cbz", "v10 - with folder.expected.jpg")] + public void GetCoverImageTest(string inputFile, string expectedOutputFile) + { + var testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/ImageProvider"); + var expectedBytes = File.ReadAllBytes(Path.Join(testDirectory, expectedOutputFile)); + + Assert.Equal(expectedBytes, ImageProvider.GetCoverImage(Path.Join(testDirectory, inputFile))); + } + } +} \ No newline at end of file diff --git a/API.Tests/Services/Test Data/ImageProvider/v10 - nested folder.cbz b/API.Tests/Services/Test Data/ImageProvider/v10 - nested folder.cbz new file mode 100644 index 000000000..0d2886130 Binary files /dev/null and b/API.Tests/Services/Test Data/ImageProvider/v10 - nested folder.cbz differ diff --git a/API.Tests/Services/Test Data/ImageProvider/v10 - nested folder.expected.jpg b/API.Tests/Services/Test Data/ImageProvider/v10 - nested folder.expected.jpg new file mode 100644 index 000000000..51fd89ca0 Binary files /dev/null and b/API.Tests/Services/Test Data/ImageProvider/v10 - nested folder.expected.jpg differ diff --git a/API.Tests/Services/Test Data/ImageProvider/v10 - with folder.cbz b/API.Tests/Services/Test Data/ImageProvider/v10 - with folder.cbz new file mode 100644 index 000000000..013fddebc Binary files /dev/null and b/API.Tests/Services/Test Data/ImageProvider/v10 - with folder.cbz differ diff --git a/API.Tests/Services/Test Data/ImageProvider/v10 - with folder.expected.jpg b/API.Tests/Services/Test Data/ImageProvider/v10 - with folder.expected.jpg new file mode 100644 index 000000000..888eeb5ec Binary files /dev/null and b/API.Tests/Services/Test Data/ImageProvider/v10 - with folder.expected.jpg differ diff --git a/API.Tests/Services/Test Data/ImageProvider/v10.cbz b/API.Tests/Services/Test Data/ImageProvider/v10.cbz new file mode 100644 index 000000000..99fbfa400 Binary files /dev/null and b/API.Tests/Services/Test Data/ImageProvider/v10.cbz differ diff --git a/API.Tests/Services/Test Data/ImageProvider/v10.expected.jpg b/API.Tests/Services/Test Data/ImageProvider/v10.expected.jpg new file mode 100644 index 000000000..51fd89ca0 Binary files /dev/null and b/API.Tests/Services/Test Data/ImageProvider/v10.expected.jpg differ diff --git a/API/IO/ImageProvider.cs b/API/IO/ImageProvider.cs new file mode 100644 index 000000000..7a71c8813 --- /dev/null +++ b/API/IO/ImageProvider.cs @@ -0,0 +1,45 @@ +using System; +using System.IO; +using System.IO.Compression; +using System.Linq; + +namespace API.IO +{ + public static class ImageProvider + { + /// + /// Generates byte array of cover image. + /// Given a path to a compressed file (zip, rar, cbz, cbr, etc), will ensure the first image is returned unless + /// a folder.extension exists in the root directory of the compressed file. + /// + /// + /// + public static byte[] GetCoverImage(string filepath) + { + if (!File.Exists(filepath) || !Parser.Parser.IsArchive(filepath)) return Array.Empty(); + + using ZipArchive archive = ZipFile.OpenRead(filepath); + if (archive.Entries.Count <= 0) return Array.Empty(); + + var folder = archive.Entries.SingleOrDefault(x => Path.GetFileNameWithoutExtension(x.Name).ToLower() == "folder"); + var entry = archive.Entries[0]; + + if (folder != null) + { + entry = folder; + } + + return ExtractEntryToImage(entry); + } + + private static byte[] ExtractEntryToImage(ZipArchiveEntry entry) + { + var stream = entry.Open(); + using var ms = new MemoryStream(); + stream.CopyTo(ms); + var data = ms.ToArray(); + + return data; + } + } +} \ No newline at end of file diff --git a/API/Parser/Parser.cs b/API/Parser/Parser.cs index f0633c36e..b94f6f719 100644 --- a/API/Parser/Parser.cs +++ b/API/Parser/Parser.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Text.RegularExpressions; namespace API.Parser @@ -226,5 +227,12 @@ namespace API.Parser { return title.TrimStart(new[] { '0' }); } + + public static bool IsArchive(string filePath) + { + var fileInfo = new FileInfo(filePath); + + return MangaFileExtensions.Contains(fileInfo.Extension); + } } } \ No newline at end of file diff --git a/API/Services/DirectoryService.cs b/API/Services/DirectoryService.cs index 958b4641e..85344f40e 100644 --- a/API/Services/DirectoryService.cs +++ b/API/Services/DirectoryService.cs @@ -4,13 +4,13 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.IO; -using System.IO.Compression; using System.Linq; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using API.Entities; using API.Interfaces; +using API.IO; using API.Parser; using Microsoft.Extensions.Logging; @@ -183,7 +183,7 @@ namespace API.Services { Name = info.Volumes, Number = Int32.Parse(info.Volumes), - CoverImage = getCoverImage(info.FullFilePath), + CoverImage = ImageProvider.GetCoverImage(info.FullFilePath), Files = new List() { new MangaFile() @@ -352,31 +352,8 @@ namespace API.Services Console.WriteLine("Processed {0} files in {1} milliseconds", fileCount, sw.ElapsedMilliseconds); } - /// - /// Generates byte array of cover image. - /// Looks for first valid image file (folder.png, first jpg/png file) - /// - /// - /// - private byte[] getCoverImage(string filepath) - { - // TODO: Sort and file type - // if file not zip (folder or txt) - using (ZipArchive archive = ZipFile.OpenRead(filepath)) - { + - if (archive.Entries.Count <= 0) {return null;} - var stream = archive.Entries[0].Open(); - byte[] data; - using (var ms = new MemoryStream()) - { - stream.CopyTo(ms); - data = ms.ToArray(); - } - - return data; - } - - } + } } \ No newline at end of file