mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-05-24 00:52:23 -04:00
* From previous fix, added the other locking conditions on the update series metadata. * Fixed a bug where custom series, collection tag, and reading list covers weren't being removed on cleanup. * Ensure reading list detail has a margin to align to the standard * Refactored some event stuff to use dedicated consts. Introduced a new event when users read something, which can update progress bars on cards. * Added recomended and library tags to the library detail page. This will eventually offer more custom analytics * Cleanup some code onc arousel * Adjusted scale to height/width css to better fit * Small css tweaks to better center images in the manga reader in both axis. This takes care of double page rendering as well. * When a special has a Title set in the metadata, on series detail page, show that on the card rather than filename. * Fixed a bug where when paging in manga reader, the scroll to top wasn't working due to changing where scrolling is done * More css goodness for rendering images in manga reader * Fixed a bug where clearing a typeahead externally wouldn't clear the x button * Fixed a bug where filering then using keyboard would select wrong option * Added a new sorting field for Last Chapter Added (new field) to get a similar on deck feel. * Tweaked recently updated to hit the NFR of 500ms (300ms fresh start) and still give a much better experience. * Refactored On deck to now go to all series and also sort by last updated. Recently Added Series now loads all series with sort by created. * Some tweaks on css for cover image chooser * Fixed a bug in pagination control where multiple pagination events could trigger on load and thus multiple requests for data on parent controller. * Updated edit series modal to show when the last chapter was added and when user last read it. * Implemented a highlight on the fitler button when a filter is active. * Refactored metadata filter screens to perserve the filters in the url and thus when navigating back and forth, it will retain. users should click side nav to reset the state. * Hide middle section on companion bar on phones * Cleaned up some prefilters and console.logs * Don't open drawer by default when a filter is active
160 lines
5.5 KiB
C#
160 lines
5.5 KiB
C#
using System;
|
|
using System.IO;
|
|
using Microsoft.Extensions.Logging;
|
|
using NetVips;
|
|
|
|
namespace API.Services;
|
|
|
|
public interface IImageService
|
|
{
|
|
void ExtractImages(string fileFilePath, string targetDirectory, int fileCount = 1);
|
|
string GetCoverImage(string path, string fileName, string outputDirectory);
|
|
|
|
/// <summary>
|
|
/// Creates a Thumbnail version of a base64 image
|
|
/// </summary>
|
|
/// <param name="encodedImage">base64 encoded image</param>
|
|
/// <param name="fileName"></param>
|
|
/// <returns>File name with extension of the file. This will always write to <see cref="DirectoryService.CoverImageDirectory"/></returns>
|
|
string CreateThumbnailFromBase64(string encodedImage, string fileName);
|
|
|
|
string WriteCoverThumbnail(Stream stream, string fileName, string outputDirectory);
|
|
}
|
|
|
|
public class ImageService : IImageService
|
|
{
|
|
private readonly ILogger<ImageService> _logger;
|
|
private readonly IDirectoryService _directoryService;
|
|
public const string ChapterCoverImageRegex = @"v\d+_c\d+";
|
|
public const string SeriesCoverImageRegex = @"series\d+";
|
|
public const string CollectionTagCoverImageRegex = @"tag\d+";
|
|
public const string ReadingListCoverImageRegex = @"readinglist\d+";
|
|
|
|
|
|
/// <summary>
|
|
/// Width of the Thumbnail generation
|
|
/// </summary>
|
|
private const int ThumbnailWidth = 320;
|
|
|
|
public ImageService(ILogger<ImageService> logger, IDirectoryService directoryService)
|
|
{
|
|
_logger = logger;
|
|
_directoryService = directoryService;
|
|
}
|
|
|
|
public void ExtractImages(string fileFilePath, string targetDirectory, int fileCount)
|
|
{
|
|
_directoryService.ExistOrCreate(targetDirectory);
|
|
if (fileCount == 1)
|
|
{
|
|
_directoryService.CopyFileToDirectory(fileFilePath, targetDirectory);
|
|
}
|
|
else
|
|
{
|
|
_directoryService.CopyDirectoryToDirectory(Path.GetDirectoryName(fileFilePath), targetDirectory,
|
|
Parser.Parser.ImageFileExtensions);
|
|
}
|
|
}
|
|
|
|
public string GetCoverImage(string path, string fileName, string outputDirectory)
|
|
{
|
|
if (string.IsNullOrEmpty(path)) return string.Empty;
|
|
|
|
try
|
|
{
|
|
using var thumbnail = Image.Thumbnail(path, ThumbnailWidth);
|
|
var filename = fileName + ".png";
|
|
thumbnail.WriteToFile(_directoryService.FileSystem.Path.Join(outputDirectory, filename));
|
|
return filename;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning(ex, "[GetCoverImage] There was an error and prevented thumbnail generation on {ImageFile}. Defaulting to no cover image", path);
|
|
}
|
|
|
|
return string.Empty;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a thumbnail out of a memory stream and saves to <see cref="DirectoryService.CoverImageDirectory"/> with the passed
|
|
/// fileName and .png extension.
|
|
/// </summary>
|
|
/// <param name="stream">Stream to write to disk. Ensure this is rewinded.</param>
|
|
/// <param name="fileName">filename to save as without extension</param>
|
|
/// <returns>File name with extension of the file. This will always write to <see cref="DirectoryService.CoverImageDirectory"/></returns>
|
|
public string WriteCoverThumbnail(Stream stream, string fileName, string outputDirectory)
|
|
{
|
|
using var thumbnail = Image.ThumbnailStream(stream, ThumbnailWidth);
|
|
var filename = fileName + ".png";
|
|
_directoryService.ExistOrCreate(outputDirectory);
|
|
try
|
|
{
|
|
_directoryService.FileSystem.File.Delete(_directoryService.FileSystem.Path.Join(outputDirectory, filename));
|
|
} catch (Exception) {/* Swallow exception */}
|
|
thumbnail.WriteToFile(_directoryService.FileSystem.Path.Join(outputDirectory, filename));
|
|
return filename;
|
|
}
|
|
|
|
|
|
/// <inheritdoc />
|
|
public string CreateThumbnailFromBase64(string encodedImage, string fileName)
|
|
{
|
|
try
|
|
{
|
|
using var thumbnail = Image.ThumbnailBuffer(Convert.FromBase64String(encodedImage), ThumbnailWidth);
|
|
var filename = fileName + ".png";
|
|
thumbnail.WriteToFile(_directoryService.FileSystem.Path.Join(_directoryService.CoverImageDirectory, fileName + ".png"));
|
|
return filename;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
_logger.LogError(e, "Error creating thumbnail from url");
|
|
}
|
|
|
|
return string.Empty;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the name format for a chapter cover image
|
|
/// </summary>
|
|
/// <param name="chapterId"></param>
|
|
/// <param name="volumeId"></param>
|
|
/// <returns></returns>
|
|
public static string GetChapterFormat(int chapterId, int volumeId)
|
|
{
|
|
return $"v{volumeId}_c{chapterId}";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the name format for a series cover image
|
|
/// </summary>
|
|
/// <param name="seriesId"></param>
|
|
/// <returns></returns>
|
|
public static string GetSeriesFormat(int seriesId)
|
|
{
|
|
return $"series{seriesId}";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the name format for a collection tag cover image
|
|
/// </summary>
|
|
/// <param name="tagId"></param>
|
|
/// <returns></returns>
|
|
public static string GetCollectionTagFormat(int tagId)
|
|
{
|
|
return $"tag{tagId}";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the name format for a reading list cover image
|
|
/// </summary>
|
|
/// <param name="readingListId"></param>
|
|
/// <returns></returns>
|
|
public static string GetReadingListFormat(int readingListId)
|
|
{
|
|
return $"readinglist{readingListId}";
|
|
}
|
|
|
|
|
|
}
|