mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-06-03 13:44:31 -04:00
* Some performance refactoring around getting Library and avoid a byte[] copy for getting cover images for epubs. * Initial commit. Rewrote the main series scan loop to use chunks of data at a time. Not fully shaken out. * Hooked in the ability for the UI to react to series being added or removed from the DB. * Cleaned up the messaging in the scan loop to be more clear. * Metadata scan and scan work as expected and populate data to the UI. There is a slow down in speed for overall operation. Scan series and refresh series metadata does not work fully. * Fixed a bug where MangaFiles were not having LastModified Updated correctly, meaning they were opening archives every scan. * Modified the code to be more realistic to the underlying file * Updated ScanService to properly handle deleted files and not result in a higher-level scan. * Shuffled around volume related repo apis to the volume repo rather than being in series. * Rewrote scan series to be much cleaner and more concise on the flow. Fixed an issue in UpdateVolumes such that the debug code to log out removed volumes could throw an exception and actually break updating volumes. * Refactored the code to set MangaFile last modified timestamp into the MangaFile entity. * Added Series Name to ScanSeries event * Added additional checks in ScanSeries to ensure we never go outside the library folder. Added extra debug messages for when a metadata refresh doesn't actually make changes and for when we regen cover images. * More logging statements saying where they originate from. Fixed a critical bug which caused only 1 chunk to ever be processed. * Fixed a concurrency issue with natural sorter which could cause issues in ArchiveService.cs. * Log cleanups * Fixed an issue with logging out total time of a scan. * Only show added toastrs for admins. When kicking off a refresh metadata for series, make sure we regenerate all cover images. * Code smells on benchmark despite it being ignored
155 lines
4.7 KiB
C#
155 lines
4.7 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using API.Comparators;
|
|
using API.Entities;
|
|
using API.Interfaces.Services;
|
|
using Microsoft.Extensions.Logging;
|
|
using NetVips;
|
|
|
|
namespace API.Services
|
|
{
|
|
|
|
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 = @"seres\d+";
|
|
public const string CollectionTagCoverImageRegex = @"tag\d+";
|
|
|
|
|
|
/// <summary>
|
|
/// Width of the Thumbnail generation
|
|
/// </summary>
|
|
private const int ThumbnailWidth = 320;
|
|
|
|
public ImageService(ILogger<ImageService> logger, IDirectoryService directoryService)
|
|
{
|
|
_logger = logger;
|
|
_directoryService = directoryService;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds the first image in the directory of the first file. Does not check for "cover/folder".ext files to override.
|
|
/// </summary>
|
|
/// <param name="file"></param>
|
|
/// <returns></returns>
|
|
public string GetCoverFile(MangaFile file)
|
|
{
|
|
var directory = Path.GetDirectoryName(file.FilePath);
|
|
if (string.IsNullOrEmpty(directory))
|
|
{
|
|
_logger.LogError("Could not find Directory for {File}", file.FilePath);
|
|
return null;
|
|
}
|
|
|
|
var firstImage = _directoryService.GetFilesWithExtension(directory, Parser.Parser.ImageFileExtensions)
|
|
.OrderBy(f => f, new NaturalSortComparer()).FirstOrDefault();
|
|
|
|
|
|
|
|
return firstImage;
|
|
}
|
|
|
|
public string GetCoverImage(string path, string fileName)
|
|
{
|
|
if (string.IsNullOrEmpty(path)) return string.Empty;
|
|
|
|
try
|
|
{
|
|
return CreateThumbnail(path, 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;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public string CreateThumbnail(string path, string fileName)
|
|
{
|
|
try
|
|
{
|
|
using var thumbnail = Image.Thumbnail(path, ThumbnailWidth);
|
|
var filename = fileName + ".png";
|
|
thumbnail.WriteToFile(Path.Join(DirectoryService.CoverImageDirectory, filename));
|
|
return filename;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
_logger.LogError(e, "Error creating thumbnail from url");
|
|
}
|
|
|
|
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 static string WriteCoverThumbnail(Stream stream, string fileName)
|
|
{
|
|
using var thumbnail = NetVips.Image.ThumbnailStream(stream, ThumbnailWidth);
|
|
var filename = fileName + ".png";
|
|
thumbnail.WriteToFile(Path.Join(DirectoryService.CoverImageDirectory, fileName + ".png"));
|
|
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(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}";
|
|
}
|
|
}
|
|
}
|