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); /// /// 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, string outputDirectory); } 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+"; public const string ReadingListCoverImageRegex = @"readinglist\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) { _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; } /// /// 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, 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; } /// 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}"; } /// /// Returns the name format for a reading list cover image /// /// /// public static string GetReadingListFormat(int readingListId) { return $"readinglist{readingListId}"; } }