using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.EntityFrameworkCore;
using Sakayaki.Models;
using Sakayaki.Services;
namespace Sakayaki.Pages;
public class FolderModel(AppDbContext dbContext, ThumbnailService thumbnailService, IConfiguration configuration) : PageModel
{
private static readonly HashSet<string> ImageExtensions = new(StringComparer.OrdinalIgnoreCase)
{
".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp"
};
private readonly AppDbContext _dbContext = dbContext;
private readonly ThumbnailService _thumbnailService = thumbnailService;
private readonly string? _rootPath = configuration["Fanbox:RootPath"];
public sealed record ImageItem(string FileName, string Url, string ThumbnailUrl);
public FanboxFolder? Folder { get; private set; }
public IReadOnlyList<string> Keywords { get; private set; } = Array.Empty<string>();
public IReadOnlyList<ImageItem> Images { get; private set; } = Array.Empty<ImageItem>();
public string? Keyword { get; private set; }
public int? ReturnPage { get; private set; }
public string BackUrl =>
Url.Page("Index", null, new { keyword = Keyword, p = ReturnPage }) ?? Url.Page("Index") ?? "/";
public async Task OnGetAsync(Guid id, string? keyword, int? p)
{
Keyword = string.IsNullOrWhiteSpace(keyword) ? null : keyword.Trim();
ReturnPage = p is > 1 ? p : null;
Folder = await _dbContext.FanboxFolders.AsNoTracking()
.FirstOrDefaultAsync(x => x.Id == id);
if (Folder is null)
{
return;
}
if (!string.IsNullOrWhiteSpace(Folder.Keywords))
{
Keywords = Folder.Keywords
.Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
.ToArray();
}
if (string.IsNullOrWhiteSpace(_rootPath))
{
return;
}
var folderPath = Path.Combine(_rootPath, Folder.FolderName);
if (!Directory.Exists(folderPath))
{
return;
}
var images = Directory.GetFiles(folderPath)
.Where(path => ImageExtensions.Contains(Path.GetExtension(path)))
.OrderBy(path => path, StringComparer.OrdinalIgnoreCase)
.Select(path =>
{
var fileName = Path.GetFileName(path);
var url = Url.Page("Folder", "Image", new { id = Folder.Id, file = fileName }) ?? string.Empty;
var thumbnailUrl = Url.Page("Folder", "Thumbnail", new { id = Folder.Id, file = fileName }) ?? string.Empty;
return new ImageItem(fileName, url, thumbnailUrl);
})
.ToArray();
Images = images;
}
public async Task<IActionResult> OnGetImageAsync(Guid id, string file)
{
if (string.IsNullOrWhiteSpace(_rootPath) || string.IsNullOrWhiteSpace(file))
return NotFound();
var safeFile = Path.GetFileName(file);
if (string.IsNullOrWhiteSpace(safeFile))
return NotFound();
if (!ImageExtensions.Contains(Path.GetExtension(safeFile)))
return NotFound();
var folder = await _dbContext.FanboxFolders.AsNoTracking()
.FirstOrDefaultAsync(x => x.Id == id);
if (folder is null)
return NotFound();
var filePath = Path.Combine(_rootPath, folder.FolderName, safeFile);
if (!System.IO.File.Exists(filePath))
return NotFound();
var provider = new FileExtensionContentTypeProvider();
if (!provider.TryGetContentType(filePath, out var contentType))
contentType = "application/octet-stream";
return PhysicalFile(filePath, contentType);
}
public async Task<IActionResult> OnGetThumbnailAsync(Guid id, string file)
{
if (string.IsNullOrWhiteSpace(file))
return NotFound();
var safeFile = Path.GetFileName(file);
if (string.IsNullOrWhiteSpace(safeFile))
return NotFound();
if (!ImageExtensions.Contains(Path.GetExtension(safeFile)))
return NotFound();
var folder = await _dbContext.FanboxFolders.AsNoTracking()
.FirstOrDefaultAsync(x => x.Id == id);
if (folder is null)
return NotFound();
var thumbPath = _thumbnailService.GetThumbnailPath(folder.Id, safeFile);
if (string.IsNullOrWhiteSpace(thumbPath))
return NotFound();
if (!System.IO.File.Exists(thumbPath))
{
await _thumbnailService.EnsureThumbnailAsync(folder.Id, folder.FolderName, safeFile, HttpContext.RequestAborted);
}
if (!System.IO.File.Exists(thumbPath))
return NotFound();
return PhysicalFile(thumbPath, "image/jpeg");
}
}