From 12eaa5814ef20b0910e8d64a753378b6f6797989 Mon Sep 17 00:00:00 2001 From: Jake Mannens Date: Fri, 22 May 2026 00:52:16 +1000 Subject: Initial commit --- Server/Controllers/MediaController.cs | 154 ++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 Server/Controllers/MediaController.cs (limited to 'Server/Controllers/MediaController.cs') diff --git a/Server/Controllers/MediaController.cs b/Server/Controllers/MediaController.cs new file mode 100644 index 0000000..27c3cbd --- /dev/null +++ b/Server/Controllers/MediaController.cs @@ -0,0 +1,154 @@ +using HyperBooru.ApiModels; +using HyperBooru.Services; +using HyperBooru.Util; +using Microsoft.AspNetCore.Authorization; +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); + + // Parse tag IDs from headers + Guid[]? tagIds = formFile.Headers["X-HyperBooru-Tags"] + .ElementAtOrDefault(0)? + .Split(',') + .Select(t => Guid.Parse(t)) + .ToArray(); + + media = mediaService.Create( + formFile.OpenReadStream(), + formFile.FileName, + formFile.Headers["X-HyperBooru-Checksum"] + .ElementAtOrDefault(0), + lastAccessTime, + lastWriteTime, + createTime, + path, + (PathType?) pathType, + tagIds); + + // Return the GUID of the new media object if requested + bool returnMetadataParsed = bool.TryParse( + formFile.Headers["X-HyperBooru-ReturnMediaId"], out var returnMetadata); + + if(returnMetadataParsed && returnMetadata) + return Content(media.Guid.ToString()); + } catch(MediaCreateException e) { + return BadRequest(e.Message); + } + } + + if(Request.Form.Files.Count == 1) + return Redirect($"/ViewMedia?m={media.Guid}"); + else + return Redirect($"/Gallery"); + } +} \ No newline at end of file -- cgit v1.3