using HyperBooru.Services; using HyperBooru.Util; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace HyperBooru.Controllers; [ApiController] [Route("/media")] public class MediaController : Controller { private IHttpContextAccessor httpContextAccessor; private IMediaService mediaService; private IConfigService config; private HBContext db; private readonly string[] FormatPriority = [ "image/webp", "image/png" ]; public MediaController( IHttpContextAccessor httpContextAccessor, IMediaService mediaService, IConfigService config, HBContext db) { this.httpContextAccessor = httpContextAccessor; this.mediaService = mediaService; this.config = config; this.db = db; } [HttpGet("{mediaId}")] public IActionResult Fetch([FromRoute] Guid mediaId) { var media = db.Media .Include(m => m.CurrentUploadedFile) .First(m => m.Guid == mediaId); if(media is null) return NotFound(); // Check if the requested media item is a HEIC image and if it is, convert it // otherwise, return the original file content, unaltered if(media.CurrentUploadedFile!.MimeType == "image/heic") { // If the media needs to be converted, check the HTTP request for allowed // media formats, and convert to the best available format or WebP otherwise var allowedTypes = httpContextAccessor .HttpContext? .Request .GetTypedHeaders().Accept.Select(h => h.MediaType.ToString()) ?? Array.Empty(); var format = FormatPriority.FirstOrDefault(f => allowedTypes.Contains(f)) ?? "image/webp"; var fs = mediaService.GetConverted(media, format); return new FileStreamResult(fs, format); } else { var fs = System.IO.File.OpenRead(mediaService.GetPath(media)); return new FileStreamResult(fs, media.CurrentUploadedFile!.MimeType); } } [HttpGet("thumb/{mediaId}")] public IActionResult Thumbnail( [FromRoute] Guid mediaId, [FromQuery(Name = "w")] int? width, [FromQuery(Name = "h")] int? height) { try { var thumb = mediaService.GetThumbnail(mediaId, width, height); return new FileStreamResult(thumb, "image/jpeg"); } catch(ThumbnailException e) { return BadRequest(e.Message); } catch(ObjectNotFoundException e) { return NotFound(e.Message); } } [HttpDelete("{mediaId}")] public void Delete([FromRoute] Guid mediaId) { mediaService.Delete(mediaId); } [HttpPost] public IActionResult Upload() { if(Request.Form.Files.Count == 0) return BadRequest("No files"); Media media = new(); foreach(var formFile in Request.Form.Files) { try { // Parse timestamps from headers DateTime? lastAccessTime = formFile.Headers["X-HyperBooru-LastAccessTime"] .ElementAtOrDefault(0)? .TryParseDateTimeUtc(); DateTime? lastWriteTime = formFile.Headers["X-HyperBooru-LastWriteTime"] .ElementAtOrDefault(0)? .TryParseDateTimeUtc(); DateTime? createTime = formFile.Headers["X-HyperBooru-CreateTime"] .ElementAtOrDefault(0)? .TryParseDateTimeUtc(); // Parse original path from headers string? path = formFile.Headers["X-HyperBooru-Path"] .ElementAtOrDefault(0); object? pathType = null; string? pathTypeString = formFile.Headers["X-HyperBooru-PathType"] .ElementAtOrDefault(0); Enum.TryParse(typeof(PathType), pathTypeString, true, out pathType); media = mediaService.Create( formFile.OpenReadStream(), formFile.FileName, formFile.Headers["X-HyperBooru-Checksum"] .ElementAtOrDefault(0), lastAccessTime, lastWriteTime, createTime, path, (PathType?) pathType); } catch(MediaCreateException e) { return BadRequest(e.Message); } } if(Request.Form.Files.Count == 1) return Redirect($"/ViewMedia?m={media.Guid}"); else return Redirect($"/Gallery"); } }