summaryrefslogtreecommitdiff
path: root/MediaController.cs
diff options
context:
space:
mode:
authorJake Mannens <jake@asger.xyz>2025-08-17 22:10:27 +1000
committerJake Mannens <jake@asger.xyz>2025-08-18 10:59:28 +1000
commit6c53f3dc43f072dce4ffe4a1bd306074dd20ff39 (patch)
tree494335104504dca462a3837c3993e2add8e72bad /MediaController.cs
Initial commit
Diffstat (limited to 'MediaController.cs')
-rw-r--r--MediaController.cs194
1 files changed, 194 insertions, 0 deletions
diff --git a/MediaController.cs b/MediaController.cs
new file mode 100644
index 0000000..29a8f88
--- /dev/null
+++ b/MediaController.cs
@@ -0,0 +1,194 @@
+using HyperBooru.ApiRecords;
+using ImageMagick;
+using Microsoft.AspNetCore.Mvc;
+using MimeDetective;
+using System.Security.Cryptography;
+
+namespace HyperBooru;
+
+[ApiController]
+[Route("/media")]
+public class MediaController : Controller {
+ private IConfigService config;
+ private HyperBooruDbContext db;
+
+ private ContentInspector inspector;
+
+ public MediaController(IConfigService config, HyperBooruDbContext db) {
+ this.config = config;
+ this.db = db;
+
+ ContentInspectorBuilder inspectorBuilder = new() {
+ Definitions =
+ MimeDetective.Definitions.Default.FileTypes.Images.All()
+ .Union(MimeDetective.Definitions.Default.FileTypes.Video.All())
+ .ToList()
+ };
+
+ inspector = inspectorBuilder.Build();
+ }
+
+ [HttpGet("list")]
+ public IActionResult EnumerateMedia() =>
+ Ok(db.Media.Select(m => m.ObjectId).ToArray());
+
+ [HttpGet("{mediaId}")]
+ public IActionResult Fetch([FromRoute] Guid mediaId) {
+ var media = db.Media.First(m => m.Guid == mediaId);
+ if(media is null)
+ return NotFound();
+
+ var fs = System.IO.File.OpenRead(config.GetPath(media));
+
+ return new FileStreamResult(fs, media.MimeType);
+ }
+
+ [HttpGet("thumb/{mediaId}")]
+ public IActionResult Thumbnail(
+ [FromRoute] Guid mediaId,
+ [FromQuery] int? w,
+ [FromQuery] int? h) {
+
+ var media = db.Media.First(m => m.Guid == mediaId);
+ if(media is null)
+ return NotFound();
+
+ if(media.MimeType.Split("/")[0] != "image")
+ return BadRequest("Media object not an image");
+
+ using var image = new MagickImage(config.GetPath(media));
+
+ if(w is null && h is null)
+ return BadRequest("Both width and height cannot be null!");
+
+ if(w > image.Width || h > image.Height)
+ return BadRequest("Requested thumbnail size is larger than original media");
+
+ int width = (int) (w is not null ? w : image.Width * h / image.Height);
+ int height = (int) (h is not null ? h : image.Height * w / image.Width);
+
+ var thumbPath = config.GetPath(media, width, height);
+
+ if(!System.IO.File.Exists(thumbPath)) {
+ image.Resize(new MagickGeometry(width, height));
+ image.Write(thumbPath);
+ }
+
+ var fs = System.IO.File.OpenRead(thumbPath);
+
+ return new FileStreamResult(fs, "image/jpeg");
+ }
+
+ [HttpDelete("{mediaId}")]
+ public IActionResult Delete([FromRoute] Guid mediaId) {
+ var media = db.Media.First(m => m.Guid == mediaId);
+ if(media is null)
+ return NotFound();
+
+ System.IO.File.Delete(config.GetPath(media));
+
+ db.Media.Remove(media);
+ db.SaveChanges();
+ return Ok();
+ }
+
+ [HttpGet("{mediaId}/info")]
+ public IActionResult GetInfo([FromRoute] Guid mediaId) {
+ var media = db.Media.First(m => m.Guid == mediaId);
+ if(media is null)
+ return NotFound();
+
+ return Ok(new MediaInfo(media));
+ }
+
+ [HttpPatch("{mediaId}/info")]
+ public IActionResult UpdateInfo(
+ [FromRoute] Guid mediaId,
+ [FromBody] MediaUpdateInfo updateInfo) {
+
+ var media = db.Media.First(m => m.Guid == mediaId);
+ if(media is null)
+ return NotFound();
+
+ if(updateInfo.ShortDescription is not null)
+ media.ShortDescription = updateInfo.ShortDescription;
+ if(updateInfo.LongDescription is not null)
+ media.LongDescription = updateInfo.LongDescription;
+
+ db.Update(media);
+
+ db.SaveChanges();
+
+ return Ok();
+ }
+
+ [HttpPost]
+ public IActionResult Upload(
+ [FromForm] string? checksum,
+ [FromForm] DateTime? lastAccessTime,
+ [FromForm] DateTime? lastWriteTime,
+ [FromForm] DateTime? createTime) {
+
+ using var transaction = db.Database.BeginTransaction();
+
+ var formFile = Request.Form.Files[0];
+ if(formFile.Length < 1)
+ return BadRequest("Empty file");
+
+ var formStream = formFile.OpenReadStream();
+
+ // Calculate the checksum using the in-memory file contents
+ var hash = BitConverter
+ .ToString(MD5.Create().ComputeHash(formStream))
+ .Replace("-", "")
+ .ToLower();
+
+ if(checksum is not null && hash != checksum.ToLower())
+ return BadRequest("Checksum does not match");
+
+ var fileRecord = new DbUploadedFile() {
+ Filename = formFile.FileName,
+ OriginalChecksum = hash,
+ UploadTime = DateTime.Now,
+ LastAccessTime = lastAccessTime,
+ LastWriteTime = lastWriteTime,
+ CreateTime = createTime
+ };
+
+ formStream.Seek(0, SeekOrigin.Begin);
+ var defs = inspector.Inspect(formStream);
+
+ var mime = defs.ByMimeType().FirstOrDefault()?.MimeType;
+ if(mime is null)
+ return BadRequest("Unsupported file type");
+
+ var media = db.Media
+ .FirstOrDefault(m => m.Checksum == hash);
+
+ if(media is null) {
+ media = new() {
+ Checksum = hash,
+ MimeType = mime,
+ UploadedFiles = new() {
+ fileRecord
+ }
+ };
+
+ using var newFile = System.IO.File.Create(config.GetPath(media));
+
+ formStream.Seek(0, SeekOrigin.Begin);
+ formStream.CopyTo(newFile);
+ newFile.Flush();
+
+ db.Media.Add(media);
+ } else {
+ media.UploadedFiles.Add(fileRecord);
+ db.Update(media);
+ }
+
+ db.SaveChanges();
+ transaction.Commit();
+
+ return Ok(media.Guid);
+ }
+} \ No newline at end of file