diff --git a/API.Tests/Parser/ParserTest.cs b/API.Tests/Parser/ParserTest.cs index 6d75d564d..d24c03067 100644 --- a/API.Tests/Parser/ParserTest.cs +++ b/API.Tests/Parser/ParserTest.cs @@ -171,6 +171,8 @@ namespace API.Tests.Parser [InlineData("TEST/Love Hina - Special.jpg", false)] [InlineData("__macosx/Love Hina/", false)] [InlineData("MACOSX/Love Hina/", false)] + [InlineData("._Love Hina/Love Hina/", true)] + [InlineData("@Recently-Snapshot/Love Hina/", true)] public void HasBlacklistedFolderInPathTest(string inputPath, bool expected) { Assert.Equal(expected, HasBlacklistedFolderInPath(inputPath)); diff --git a/API/API.csproj b/API/API.csproj index 92671bce9..06a00518a 100644 --- a/API/API.csproj +++ b/API/API.csproj @@ -62,6 +62,7 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/API/Data/Metadata/ComicInfo.cs b/API/Data/Metadata/ComicInfo.cs index c34a305fd..7e53bb486 100644 --- a/API/Data/Metadata/ComicInfo.cs +++ b/API/Data/Metadata/ComicInfo.cs @@ -76,6 +76,7 @@ namespace API.Data.Metadata public string CoverArtist { get; set; } = string.Empty; public string Editor { get; set; } = string.Empty; public string Publisher { get; set; } = string.Empty; + public string Characters { get; set; } = string.Empty; public static AgeRating ConvertAgeRatingToEnum(string value) { diff --git a/API/Parser/Parser.cs b/API/Parser/Parser.cs index fb50cb880..410ec5c47 100644 --- a/API/Parser/Parser.cs +++ b/API/Parser/Parser.cs @@ -1097,7 +1097,7 @@ namespace API.Parser public static bool HasBlacklistedFolderInPath(string path) { - return path.Contains("__MACOSX"); + return path.Contains("__MACOSX") || path.StartsWith("@Recently-Snapshot"); } diff --git a/API/Services/ArchiveService.cs b/API/Services/ArchiveService.cs index fa0cb9068..ae8dc7e55 100644 --- a/API/Services/ArchiveService.cs +++ b/API/Services/ArchiveService.cs @@ -346,6 +346,7 @@ namespace API.Services info.Letterer = Parser.Parser.CleanAuthor(info.Letterer); info.Penciller = Parser.Parser.CleanAuthor(info.Penciller); info.Publisher = Parser.Parser.CleanAuthor(info.Publisher); + info.Characters = Parser.Parser.CleanAuthor(info.Characters); if (!string.IsNullOrEmpty(info.Web)) { diff --git a/API/Services/BookService.cs b/API/Services/BookService.cs index 6af4de9f9..d08229778 100644 --- a/API/Services/BookService.cs +++ b/API/Services/BookService.cs @@ -1,10 +1,7 @@ using System; using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; using System.IO; using System.Linq; -using System.Runtime.InteropServices; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; @@ -20,7 +17,10 @@ using ExCSS; using HtmlAgilityPack; using Microsoft.Extensions.Logging; using Microsoft.IO; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; using VersOne.Epub; +using Image = SixLabors.ImageSharp.Image; namespace API.Services { @@ -445,31 +445,25 @@ namespace API.Services return null; } - private static void AddBytesToBitmap(Bitmap bmp, byte[] rawBytes) - { - var rect = new Rectangle(0, 0, bmp.Width, bmp.Height); - - var bmpData = bmp.LockBits(rect, ImageLockMode.WriteOnly, bmp.PixelFormat); - var pNative = bmpData.Scan0; - - Marshal.Copy(rawBytes, 0, pNative, rawBytes.Length); - bmp.UnlockBits(bmpData); - } - + /// + /// Extracts a pdf into images to a target directory. Uses multi-threaded implementation since docnet is slow normally. + /// + /// + /// public void ExtractPdfImages(string fileFilePath, string targetDirectory) { _directoryService.ExistOrCreate(targetDirectory); using var docReader = DocLib.Instance.GetDocReader(fileFilePath, new PageDimensions(1080, 1920)); var pages = docReader.GetPageCount(); - using var stream = StreamManager.GetStream("BookService.GetPdfPage"); - for (var pageNumber = 0; pageNumber < pages; pageNumber++) + Parallel.For(0, pages, pageNumber => { + using var stream = StreamManager.GetStream("BookService.GetPdfPage"); GetPdfPage(docReader, pageNumber, stream); using var fileStream = File.Create(Path.Combine(targetDirectory, "Page-" + pageNumber + ".png")); stream.Seek(0, SeekOrigin.Begin); stream.CopyTo(fileStream); - } + }); } /// @@ -536,20 +530,14 @@ namespace API.Services private static void GetPdfPage(IDocReader docReader, int pageNumber, Stream stream) { - // TODO: BUG: Most of this Bitmap code is only supported on Windows. Refactor. using var pageReader = docReader.GetPageReader(pageNumber); var rawBytes = pageReader.GetImage(new NaiveTransparencyRemover()); var width = pageReader.GetPageWidth(); var height = pageReader.GetPageHeight(); - using var bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb); - AddBytesToBitmap(bmp, rawBytes); - // Removes 1px margin on left/right side after bitmap is copied out - for (var y = 0; y < bmp.Height; y++) - { - bmp.SetPixel(bmp.Width - 1, y, bmp.GetPixel(bmp.Width - 2, y)); - } + var image = Image.LoadPixelData(rawBytes, width, height); + stream.Seek(0, SeekOrigin.Begin); - bmp.Save(stream, ImageFormat.Jpeg); + image.SaveAsPng(stream); stream.Seek(0, SeekOrigin.Begin); } diff --git a/API/Services/MetadataService.cs b/API/Services/MetadataService.cs index 4a28b7998..0054ad62f 100644 --- a/API/Services/MetadataService.cs +++ b/API/Services/MetadataService.cs @@ -113,6 +113,14 @@ public class MetadataService : IMetadataService person => PersonHelper.AddPersonIfNotExists(chapter.People, person)); } + if (!string.IsNullOrEmpty(comicInfo.Characters)) + { + var people = comicInfo.Characters.Split(","); + PersonHelper.RemovePeople(chapter.People, people, PersonRole.Character); + PersonHelper.UpdatePeople(allPeople, people, PersonRole.Character, + person => PersonHelper.AddPersonIfNotExists(chapter.People, person)); + } + if (!string.IsNullOrEmpty(comicInfo.Translator)) { var people = comicInfo.Translator.Split(","); @@ -220,7 +228,6 @@ public class MetadataService : IMetadataService { if (series == null) return; - //var firstFile = series.Volumes.FirstWithChapters().Chapters.Fir if (!_cacheHelper.ShouldUpdateCoverImage(_directoryService.FileSystem.Path.Join(_directoryService.CoverImageDirectory, series.CoverImage), null, series.Created, forceUpdate, series.CoverImageLocked)) return;