diff --git a/API/API.csproj b/API/API.csproj
index 997ca6817..fd4e7f9ac 100644
--- a/API/API.csproj
+++ b/API/API.csproj
@@ -215,4 +215,8 @@
<_ContentIncludedByDefault Remove="wwwroot\vendor.6b2a0912ae80e6fd297f.js.map" />
+
+
+
+
diff --git a/API/Controllers/BookController.cs b/API/Controllers/BookController.cs
index 0efa6c71e..f8dbaafe8 100644
--- a/API/Controllers/BookController.cs
+++ b/API/Controllers/BookController.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Threading.Tasks;
using API.DTOs;
@@ -18,14 +19,16 @@ namespace API.Controllers
private readonly ILogger _logger;
private readonly IBookService _bookService;
private readonly IUnitOfWork _unitOfWork;
+ private readonly ICacheService _cacheService;
private static readonly string BookApiUrl = "book-resources?file=";
- public BookController(ILogger logger, IBookService bookService, IUnitOfWork unitOfWork)
+ public BookController(ILogger logger, IBookService bookService, IUnitOfWork unitOfWork, ICacheService cacheService)
{
_logger = logger;
_bookService = bookService;
_unitOfWork = unitOfWork;
+ _cacheService = cacheService;
}
[HttpGet("{chapterId}/book-info")]
@@ -169,9 +172,11 @@ namespace API.Controllers
[HttpGet("{chapterId}/book-page")]
public async Task> GetBookPage(int chapterId, [FromQuery] int page)
{
- var chapter = await _unitOfWork.VolumeRepository.GetChapterAsync(chapterId);
+ var chapter = await _cacheService.Ensure(chapterId);
+ var path = _cacheService.GetCachedEpubFile(chapter.Id, chapter);
- using var book = await EpubReader.OpenBookAsync(chapter.Files.ElementAt(0).FilePath);
+
+ using var book = await EpubReader.OpenBookAsync(path);
var mappings = await _bookService.CreateKeyToPageMappingAsync(book);
var counter = 0;
@@ -196,12 +201,7 @@ namespace API.Controllers
{
if (doc.ParseErrors.Any())
{
- _logger.LogError("{FilePath} has an invalid html file (Page {PageName})", book.FilePath, contentFileRef.FileName);
- foreach (var error in doc.ParseErrors)
- {
- _logger.LogError("Line {LineNumber}, Reason: {Reason}", error.Line, error.Reason);
- }
-
+ LogBookErrors(book, contentFileRef, doc);
return BadRequest("The file is malformed! Cannot read.");
}
_logger.LogError("{FilePath} has no body tag! Generating one for support. Book may be skewed", book.FilePath);
@@ -322,5 +322,14 @@ namespace API.Controllers
return BadRequest("Could not find the appropriate html for that page");
}
+
+ private void LogBookErrors(EpubBookRef book, EpubTextContentFileRef contentFileRef, HtmlDocument doc)
+ {
+ _logger.LogError("{FilePath} has an invalid html file (Page {PageName})", book.FilePath, contentFileRef.FileName);
+ foreach (var error in doc.ParseErrors)
+ {
+ _logger.LogError("Line {LineNumber}, Reason: {Reason}", error.Line, error.Reason);
+ }
+ }
}
}
diff --git a/API/Interfaces/Services/ICacheService.cs b/API/Interfaces/Services/ICacheService.cs
index f2d6cfa71..8499702b1 100644
--- a/API/Interfaces/Services/ICacheService.cs
+++ b/API/Interfaces/Services/ICacheService.cs
@@ -35,5 +35,6 @@ namespace API.Interfaces.Services
Task<(string path, MangaFile file)> GetCachedPagePath(Chapter chapter, int page);
void EnsureCacheDirectory();
+ string GetCachedEpubFile(int chapterId, Chapter chapter);
}
}
diff --git a/API/Services/BookService.cs b/API/Services/BookService.cs
index ea8fb10aa..dbc3400dd 100644
--- a/API/Services/BookService.cs
+++ b/API/Services/BookService.cs
@@ -21,8 +21,6 @@ using HtmlAgilityPack;
using Microsoft.Extensions.Logging;
using Microsoft.IO;
using VersOne.Epub;
-using Image = NetVips.Image;
-using Point = System.Drawing.Point;
namespace API.Services
{
@@ -409,7 +407,7 @@ namespace API.Services
if (!createThumbnail) return coverImageContent.ReadContent();
using var stream = StreamManager.GetStream("BookService.GetCoverImage", coverImageContent.ReadContent());
- using var thumbnail = Image.ThumbnailStream(stream, MetadataService.ThumbnailWidth);
+ using var thumbnail = NetVips.Image.ThumbnailStream(stream, MetadataService.ThumbnailWidth);
return thumbnail.WriteToBuffer(".jpg");
}
@@ -433,7 +431,7 @@ namespace API.Services
if (!createThumbnail) return stream.ToArray();
- using var thumbnail = Image.ThumbnailStream(stream, MetadataService.ThumbnailWidth);
+ using var thumbnail = NetVips.Image.ThumbnailStream(stream, MetadataService.ThumbnailWidth);
return thumbnail.WriteToBuffer(".png");
}
diff --git a/API/Services/CacheService.cs b/API/Services/CacheService.cs
index 3d557ed7f..89d1cf395 100644
--- a/API/Services/CacheService.cs
+++ b/API/Services/CacheService.cs
@@ -9,6 +9,7 @@ using API.Entities.Enums;
using API.Extensions;
using API.Interfaces;
using API.Interfaces.Services;
+using Kavita.Common;
using Microsoft.Extensions.Logging;
namespace API.Services
@@ -42,6 +43,23 @@ namespace API.Services
}
}
+ ///
+ /// Returns the full path to the cached epub file. If the file does not exist, will fallback to the original.
+ ///
+ ///
+ ///
+ ///
+ public string GetCachedEpubFile(int chapterId, Chapter chapter)
+ {
+ var extractPath = GetCachePath(chapterId);
+ var path = Path.Join(extractPath, Path.GetFileName(chapter.Files.First().FilePath));
+ if (!(new FileInfo(path).Exists))
+ {
+ path = chapter.Files.First().FilePath;
+ }
+ return path;
+ }
+
public async Task Ensure(int chapterId)
{
EnsureCacheDirectory();
@@ -50,6 +68,7 @@ namespace API.Services
var fileCount = files.Count;
var extractPath = GetCachePath(chapterId);
var extraPath = "";
+ var removeNonImages = true;
if (Directory.Exists(extractPath))
{
@@ -83,15 +102,24 @@ namespace API.Services
if (file.Format == MangaFormat.Archive)
{
- _archiveService.ExtractArchive(file.FilePath, Path.Join(extractPath, extraPath));
+ _archiveService.ExtractArchive(file.FilePath, Path.Join(extractPath, extraPath));
} else if (file.Format == MangaFormat.Pdf)
{
_bookService.ExtractPdfImages(file.FilePath, Path.Join(extractPath, extraPath));
+ } else if (file.Format == MangaFormat.Epub)
+ {
+ removeNonImages = false;
+ DirectoryService.ExistOrCreate(extractPath);
+ _directoryService.CopyFileToDirectory(files[0].FilePath, extractPath);
}
}
extractDi.Flatten();
- extractDi.RemoveNonImages();
+ if (removeNonImages)
+ {
+ extractDi.RemoveNonImages();
+ }
+
return chapter;
}
diff --git a/API/Services/DirectoryService.cs b/API/Services/DirectoryService.cs
index 8345b32d1..d0e5cce84 100644
--- a/API/Services/DirectoryService.cs
+++ b/API/Services/DirectoryService.cs
@@ -106,11 +106,18 @@ namespace API.Services
public void CopyFileToDirectory(string fullFilePath, string targetDirectory)
{
- var fileInfo = new FileInfo(fullFilePath);
- if (fileInfo.Exists)
- {
- fileInfo.CopyTo(Path.Join(targetDirectory, fileInfo.Name));
- }
+ try
+ {
+ var fileInfo = new FileInfo(fullFilePath);
+ if (fileInfo.Exists)
+ {
+ fileInfo.CopyTo(Path.Join(targetDirectory, fileInfo.Name), true);
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "There was a critical error when copying {File} to {Directory}", fullFilePath, targetDirectory);
+ }
}
///
diff --git a/Kavita.Common/Kavita.Common.csproj b/Kavita.Common/Kavita.Common.csproj
index 7e67cccbb..dd2a776da 100644
--- a/Kavita.Common/Kavita.Common.csproj
+++ b/Kavita.Common/Kavita.Common.csproj
@@ -4,7 +4,7 @@
net5.0
kavitareader.com
Kavita
- 0.4.3.2
+ 0.4.3.3
en