I can't believe it's more fixes! (#863)

* Send stack trace to the UI on prod mode

* Pdfs will now generate cover images. I missed something a few releases ago.

* Ignore @Recently-Snapshot directories for QNAP.

* Refactored Bitmap code to use ImageSharp so it's truly cross platform.

* Updated pdf extraction to use a multi-threaded approach to greatly speed up pdf image extraction

* Hooked in Characters tag from ComicInfo.xml
This commit is contained in:
Joseph Milazzo 2021-12-20 11:50:47 -06:00 committed by GitHub
parent 6127f20bb2
commit bbdfe17247
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 28 additions and 28 deletions

View File

@ -171,6 +171,8 @@ namespace API.Tests.Parser
[InlineData("TEST/Love Hina - Special.jpg", false)] [InlineData("TEST/Love Hina - Special.jpg", false)]
[InlineData("__macosx/Love Hina/", false)] [InlineData("__macosx/Love Hina/", 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) public void HasBlacklistedFolderInPathTest(string inputPath, bool expected)
{ {
Assert.Equal(expected, HasBlacklistedFolderInPath(inputPath)); Assert.Equal(expected, HasBlacklistedFolderInPath(inputPath));

View File

@ -62,6 +62,7 @@
<PackageReference Include="NetVips.Native" Version="8.12.1" /> <PackageReference Include="NetVips.Native" Version="8.12.1" />
<PackageReference Include="NReco.Logging.File" Version="1.1.2" /> <PackageReference Include="NReco.Logging.File" Version="1.1.2" />
<PackageReference Include="SharpCompress" Version="0.30.1" /> <PackageReference Include="SharpCompress" Version="0.30.1" />
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.33.0.40503"> <PackageReference Include="SonarAnalyzer.CSharp" Version="8.33.0.40503">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@ -76,6 +76,7 @@ namespace API.Data.Metadata
public string CoverArtist { get; set; } = string.Empty; public string CoverArtist { get; set; } = string.Empty;
public string Editor { get; set; } = string.Empty; public string Editor { get; set; } = string.Empty;
public string Publisher { get; set; } = string.Empty; public string Publisher { get; set; } = string.Empty;
public string Characters { get; set; } = string.Empty;
public static AgeRating ConvertAgeRatingToEnum(string value) public static AgeRating ConvertAgeRatingToEnum(string value)
{ {

View File

@ -1097,7 +1097,7 @@ namespace API.Parser
public static bool HasBlacklistedFolderInPath(string path) public static bool HasBlacklistedFolderInPath(string path)
{ {
return path.Contains("__MACOSX"); return path.Contains("__MACOSX") || path.StartsWith("@Recently-Snapshot");
} }

View File

@ -346,6 +346,7 @@ namespace API.Services
info.Letterer = Parser.Parser.CleanAuthor(info.Letterer); info.Letterer = Parser.Parser.CleanAuthor(info.Letterer);
info.Penciller = Parser.Parser.CleanAuthor(info.Penciller); info.Penciller = Parser.Parser.CleanAuthor(info.Penciller);
info.Publisher = Parser.Parser.CleanAuthor(info.Publisher); info.Publisher = Parser.Parser.CleanAuthor(info.Publisher);
info.Characters = Parser.Parser.CleanAuthor(info.Characters);
if (!string.IsNullOrEmpty(info.Web)) if (!string.IsNullOrEmpty(info.Web))
{ {

View File

@ -1,10 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -20,7 +17,10 @@ using ExCSS;
using HtmlAgilityPack; using HtmlAgilityPack;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.IO; using Microsoft.IO;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using VersOne.Epub; using VersOne.Epub;
using Image = SixLabors.ImageSharp.Image;
namespace API.Services namespace API.Services
{ {
@ -445,31 +445,25 @@ namespace API.Services
return null; return null;
} }
private static void AddBytesToBitmap(Bitmap bmp, byte[] rawBytes) /// <summary>
{ /// Extracts a pdf into images to a target directory. Uses multi-threaded implementation since docnet is slow normally.
var rect = new Rectangle(0, 0, bmp.Width, bmp.Height); /// </summary>
/// <param name="fileFilePath"></param>
var bmpData = bmp.LockBits(rect, ImageLockMode.WriteOnly, bmp.PixelFormat); /// <param name="targetDirectory"></param>
var pNative = bmpData.Scan0;
Marshal.Copy(rawBytes, 0, pNative, rawBytes.Length);
bmp.UnlockBits(bmpData);
}
public void ExtractPdfImages(string fileFilePath, string targetDirectory) public void ExtractPdfImages(string fileFilePath, string targetDirectory)
{ {
_directoryService.ExistOrCreate(targetDirectory); _directoryService.ExistOrCreate(targetDirectory);
using var docReader = DocLib.Instance.GetDocReader(fileFilePath, new PageDimensions(1080, 1920)); using var docReader = DocLib.Instance.GetDocReader(fileFilePath, new PageDimensions(1080, 1920));
var pages = docReader.GetPageCount(); var pages = docReader.GetPageCount();
using var stream = StreamManager.GetStream("BookService.GetPdfPage"); Parallel.For(0, pages, pageNumber =>
for (var pageNumber = 0; pageNumber < pages; pageNumber++)
{ {
using var stream = StreamManager.GetStream("BookService.GetPdfPage");
GetPdfPage(docReader, pageNumber, stream); GetPdfPage(docReader, pageNumber, stream);
using var fileStream = File.Create(Path.Combine(targetDirectory, "Page-" + pageNumber + ".png")); using var fileStream = File.Create(Path.Combine(targetDirectory, "Page-" + pageNumber + ".png"));
stream.Seek(0, SeekOrigin.Begin); stream.Seek(0, SeekOrigin.Begin);
stream.CopyTo(fileStream); stream.CopyTo(fileStream);
} });
} }
/// <summary> /// <summary>
@ -536,20 +530,14 @@ namespace API.Services
private static void GetPdfPage(IDocReader docReader, int pageNumber, Stream stream) 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); using var pageReader = docReader.GetPageReader(pageNumber);
var rawBytes = pageReader.GetImage(new NaiveTransparencyRemover()); var rawBytes = pageReader.GetImage(new NaiveTransparencyRemover());
var width = pageReader.GetPageWidth(); var width = pageReader.GetPageWidth();
var height = pageReader.GetPageHeight(); var height = pageReader.GetPageHeight();
using var bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb); var image = Image.LoadPixelData<Bgra32>(rawBytes, width, height);
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));
}
stream.Seek(0, SeekOrigin.Begin); stream.Seek(0, SeekOrigin.Begin);
bmp.Save(stream, ImageFormat.Jpeg); image.SaveAsPng(stream);
stream.Seek(0, SeekOrigin.Begin); stream.Seek(0, SeekOrigin.Begin);
} }

View File

@ -113,6 +113,14 @@ public class MetadataService : IMetadataService
person => PersonHelper.AddPersonIfNotExists(chapter.People, person)); 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)) if (!string.IsNullOrEmpty(comicInfo.Translator))
{ {
var people = comicInfo.Translator.Split(","); var people = comicInfo.Translator.Split(",");
@ -220,7 +228,6 @@ public class MetadataService : IMetadataService
{ {
if (series == null) return; if (series == null) return;
//var firstFile = series.Volumes.FirstWithChapters().Chapters.Fir
if (!_cacheHelper.ShouldUpdateCoverImage(_directoryService.FileSystem.Path.Join(_directoryService.CoverImageDirectory, series.CoverImage), if (!_cacheHelper.ShouldUpdateCoverImage(_directoryService.FileSystem.Path.Join(_directoryService.CoverImageDirectory, series.CoverImage),
null, series.Created, forceUpdate, series.CoverImageLocked)) null, series.Created, forceUpdate, series.CoverImageLocked))
return; return;