mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-05-24 00:52:23 -04:00
Performance/cache epub (#438)
Changed: (Performance) Added the ability for epubs to cache, allowing faster page load for users with network mounted storage. (Fixes Investigate caching epubs (benefit for network mounted users) #433 )
This commit is contained in:
parent
3dbe7eec1f
commit
3c9f73ce2c
@ -215,4 +215,8 @@
|
||||
<_ContentIncludedByDefault Remove="wwwroot\vendor.6b2a0912ae80e6fd297f.js.map" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="System.Drawing.Common" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -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<BookController> _logger;
|
||||
private readonly IBookService _bookService;
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
private readonly ICacheService _cacheService;
|
||||
private static readonly string BookApiUrl = "book-resources?file=";
|
||||
|
||||
|
||||
public BookController(ILogger<BookController> logger, IBookService bookService, IUnitOfWork unitOfWork)
|
||||
public BookController(ILogger<BookController> 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<ActionResult<string>> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the full path to the cached epub file. If the file does not exist, will fallback to the original.
|
||||
/// </summary>
|
||||
/// <param name="chapterId"></param>
|
||||
/// <param name="chapter"></param>
|
||||
/// <returns></returns>
|
||||
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<Chapter> 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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -4,7 +4,7 @@
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<Company>kavitareader.com</Company>
|
||||
<Product>Kavita</Product>
|
||||
<AssemblyVersion>0.4.3.2</AssemblyVersion>
|
||||
<AssemblyVersion>0.4.3.3</AssemblyVersion>
|
||||
<NeutralLanguage>en</NeutralLanguage>
|
||||
</PropertyGroup>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user