summaryrefslogtreecommitdiff
path: root/Controllers/MediaController.cs
blob: 6a9e1fc455660ad97698070bad5685e6c40ea8e2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
using HyperBooru.ApiModels;
using HyperBooru.Services;
using HyperBooru.Util;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace HyperBooru.Controllers;

[ApiController]
[Authorize]
[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<string>();

            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");
    }
}