using System; using System.IO; using System.Linq; using API.Comparators; using API.Entities; using Microsoft.Extensions.Logging; using NetVips; namespace API.Services; public interface IImageService { void ExtractImages(string fileFilePath, string targetDirectory, int fileCount); string GetCoverImage(string path, string fileName); string GetCoverFile(MangaFile file); /// /// Creates a Thumbnail version of an image /// /// Path to the image file /// File name with extension of the file. This will always write to //string CreateThumbnail(string path, string fileName); /// /// Creates a Thumbnail version of a base64 image /// /// base64 encoded image /// File name with extension of the file. This will always write to string CreateThumbnailFromBase64(string encodedImage, string fileName); string WriteCoverThumbnail(Stream stream, string fileName); } public class ImageService : IImageService { private readonly ILogger _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+"; /// /// Width of the Thumbnail generation /// private const int ThumbnailWidth = 320; public ImageService(ILogger logger, IDirectoryService directoryService) { _logger = logger; _directoryService = directoryService; } public void ExtractImages(string fileFilePath, string targetDirectory, int fileCount = 1) { _directoryService.ExistOrCreate(targetDirectory); if (fileCount == 1) { _directoryService.CopyFileToDirectory(fileFilePath, targetDirectory); } else { _directoryService.CopyDirectoryToDirectory(Path.GetDirectoryName(fileFilePath), targetDirectory, Parser.Parser.ImageFileExtensions); } } /// /// Finds the first image in the directory of the first file. Does not check for "cover/folder".ext files to override. /// /// /// 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); using var thumbnail = Image.Thumbnail(path, ThumbnailWidth); var filename = fileName + ".png"; thumbnail.WriteToFile(_directoryService.FileSystem.Path.Join(_directoryService.CoverImageDirectory, 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; } /// // public string CreateThumbnail(string path, string fileName) // { // try // { // using var thumbnail = Image.Thumbnail(path, ThumbnailWidth); // var filename = fileName + ".png"; // thumbnail.WriteToFile(_directoryService.FileSystem.Path.Join(_directoryService.CoverImageDirectory, filename)); // return filename; // } // catch (Exception e) // { // _logger.LogError(e, "Error creating thumbnail from url"); // } // // return string.Empty; // } /// /// Creates a thumbnail out of a memory stream and saves to with the passed /// fileName and .png extension. /// /// Stream to write to disk. Ensure this is rewinded. /// filename to save as without extension /// File name with extension of the file. This will always write to public string WriteCoverThumbnail(Stream stream, string fileName) { using var thumbnail = Image.ThumbnailStream(stream, ThumbnailWidth); var filename = fileName + ".png"; _directoryService.ExistOrCreate(_directoryService.CoverImageDirectory); thumbnail.WriteToFile(_directoryService.FileSystem.Path.Join(_directoryService.CoverImageDirectory, filename)); return filename; } /// 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; } /// /// Returns the name format for a chapter cover image /// /// /// /// public static string GetChapterFormat(int chapterId, int volumeId) { return $"v{volumeId}_c{chapterId}"; } /// /// Returns the name format for a series cover image /// /// /// public static string GetSeriesFormat(int seriesId) { return $"series{seriesId}"; } /// /// Returns the name format for a collection tag cover image /// /// /// public static string GetCollectionTagFormat(int tagId) { return $"tag{tagId}"; } }