using GozareshgirProgramManager.Application.Services.FileManagement; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using SixLabors.ImageSharp; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Formats.Jpeg; namespace GozareshgirProgramManager.Infrastructure.Services.FileManagement; /// /// سرویس تولید thumbnail با استفاده از ImageSharp /// public class ThumbnailGeneratorService : IThumbnailGeneratorService { private readonly string _thumbnailBasePath; private readonly string _baseUrl; public ThumbnailGeneratorService(IConfiguration configuration, IHttpContextAccessor httpContextAccessor) { _thumbnailBasePath = configuration["FileStorage:ThumbnailPath"] ?? Path.Combine(Directory.GetCurrentDirectory(), "storage", "Thumbnails"); var request = httpContextAccessor.HttpContext?.Request; if (request != null) { _baseUrl = $"{request.Scheme}://{request.Host}/storage"; } else { _baseUrl = configuration["FileStorage:BaseUrl"] ?? "http://localhost:5000/storage"; } if (!Directory.Exists(_thumbnailBasePath)) { Directory.CreateDirectory(_thumbnailBasePath); } } public async Task<(string ThumbnailPath, string ThumbnailUrl)?> GenerateImageThumbnailAsync( string imagePath, string category, int width = 200, int height = 200) { try { if (!File.Exists(imagePath)) { return null; } // بارگذاری تصویر using var image = await Image.LoadAsync(imagePath); // Resize با حفظ نسبت image.Mutate(x => x.Resize(new ResizeOptions { Size = new Size(width, height), Mode = ResizeMode.Max })); // نام فایل thumbnail var fileName = Path.GetFileNameWithoutExtension(imagePath); var extension = Path.GetExtension(imagePath); var thumbnailFileName = $"{fileName}_thumb{extension}"; // دریافت مسیر و URL توسط متد private var (thumbnailPath, thumbnailUrl) = GetThumbnailPathAndUrl(thumbnailFileName, category); // ذخیره thumbnail با کیفیت 80 await image.SaveAsync(thumbnailPath, new JpegEncoder { Quality = 80 }); return (thumbnailPath, thumbnailUrl); } catch (Exception) { // در صورت خطا null برمی‌گردانیم return null; } } public async Task<(string ThumbnailPath, string ThumbnailUrl)?> GenerateVideoThumbnailAsync(string videoPath, string category = "general") { // TODO: برای Video thumbnail باید از FFmpeg استفاده کنیم // فعلاً یک placeholder image برمی‌گردانیم await Task.CompletedTask; return null; } public Task DeleteThumbnailAsync(string thumbnailPath) { if (File.Exists(thumbnailPath)) { File.Delete(thumbnailPath); } return Task.CompletedTask; } public async Task<(int Width, int Height)?> GetImageDimensionsAsync(string imagePath) { try { if (!File.Exists(imagePath)) { return null; } var imageInfo = await Image.IdentifyAsync(imagePath); return (imageInfo.Width, imageInfo.Height); } catch (Exception) { return null; } } /// /// دریافت مسیر فیزیکی و URL برای thumbnail بر اساس category /// private (string ThumbnailPath, string ThumbnailUrl) GetThumbnailPathAndUrl(string thumbnailFileName, string category) { var categoryFolder = string.IsNullOrWhiteSpace(category) ? "general" : category; var categoryPath = Path.Combine(Directory.GetCurrentDirectory(), "storage", categoryFolder); if (!Directory.Exists(categoryPath)) { Directory.CreateDirectory(categoryPath); } var thumbnailSubPath = Path.Combine(categoryPath, "Thumbnails"); if (!Directory.Exists(thumbnailSubPath)) { Directory.CreateDirectory(thumbnailSubPath); } var thumbnailPath = Path.Combine(thumbnailSubPath, thumbnailFileName); var thumbnailUrl = $"{_baseUrl}/{categoryFolder}/thumbnails/{thumbnailFileName}"; return (thumbnailPath, thumbnailUrl); } }