summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Controllers/MediaController.cs40
-rw-r--r--Server.csproj4
-rw-r--r--Services/ConfigService.cs14
-rw-r--r--Services/MediaService.cs55
4 files changed, 92 insertions, 21 deletions
diff --git a/Controllers/MediaController.cs b/Controllers/MediaController.cs
index 2c015f7..cd6916f 100644
--- a/Controllers/MediaController.cs
+++ b/Controllers/MediaController.cs
@@ -8,18 +8,26 @@ namespace HyperBooru.Controllers;
[ApiController]
[Route("/media")]
public class MediaController : Controller {
- private IMediaService mediaService;
- private IConfigService config;
- private HBContext db;
+ 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.mediaService = mediaService;
- this.config = config;
- this.db = db;
+ this.httpContextAccessor = httpContextAccessor;
+ this.mediaService = mediaService;
+ this.config = config;
+ this.db = db;
}
[HttpGet("{mediaId}")]
@@ -30,9 +38,25 @@ public class MediaController : Controller {
if(media is null)
return NotFound();
- var fs = System.IO.File.OpenRead(mediaService.GetPath(media));
+ // 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";
- return new FileStreamResult(fs, media.CurrentUploadedFile!.MimeType);
+ 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}")]
diff --git a/Server.csproj b/Server.csproj
index 8e85fd2..1a48326 100644
--- a/Server.csproj
+++ b/Server.csproj
@@ -6,9 +6,9 @@
<ImplicitUsings>enable</ImplicitUsings>
<AssemblyName>HyperBooru</AssemblyName>
<RootNamespace>HyperBooru</RootNamespace>
- <AssemblyVersion>0.4.0.0</AssemblyVersion>
+ <AssemblyVersion>0.5.0.0</AssemblyVersion>
<FileVersion>$(AssemblyVersion)</FileVersion>
- <Version>0.4-alpha</Version>
+ <Version>0.5-alpha</Version>
<UserSecretsId>2907567f-4640-4581-8f4d-0977952d26bd</UserSecretsId>
</PropertyGroup>
diff --git a/Services/ConfigService.cs b/Services/ConfigService.cs
index 8460fd0..d2d1a06 100644
--- a/Services/ConfigService.cs
+++ b/Services/ConfigService.cs
@@ -1,11 +1,12 @@
namespace HyperBooru.Services;
public interface IConfigService {
- public string DataPath { get; }
- public string DbConnectionString { get; }
- public string MediaBasePath { get; }
- public string ThumbnailBasePath { get; }
- public bool EnableOcr { get; }
+ public string DataPath { get; }
+ public string DbConnectionString { get; }
+ public string MediaBasePath { get; }
+ public string ThumbnailBasePath { get; }
+ public string ConvertedMediaBasePath { get; }
+ public bool EnableOcr { get; }
}
public class ConfigService : IConfigService {
@@ -47,6 +48,9 @@ public class ConfigService : IConfigService {
public string ThumbnailBasePath =>
Path.Join(DataPath, "thumb");
+ public string ConvertedMediaBasePath =>
+ Path.Join(DataPath, "converted");
+
public bool EnableOcr =>
bool.TryParse(config["DisableOcr"], out bool x) ? !x : true;
diff --git a/Services/MediaService.cs b/Services/MediaService.cs
index a5803f9..2f7eac6 100644
--- a/Services/MediaService.cs
+++ b/Services/MediaService.cs
@@ -1,4 +1,5 @@
using ImageMagick;
+using ImageMagick.Formats;
using Microsoft.EntityFrameworkCore;
using MimeDetective;
using MimeDetective.Definitions;
@@ -31,14 +32,21 @@ public interface IMediaService {
public void DeleteThumbnails(Media media);
public Stream GetThumbnail(Guid media, int? width, int? height);
public Stream GetThumbnail(Media media, int? width, int? height);
+ public Stream GetConverted(Guid mediaId, string mimeType = "image/png");
+ public Stream GetConverted(Media media, string mimeType = "image/png");
public string GetPath(Guid media);
- public string GetPath(Guid media, int? width, int? height);
public string GetPath(Media media);
- public string GetPath(Media media, int? width, int? height);
}
public class MediaService : IMediaService {
+ private readonly Dictionary<string,MagickFormat> FormatMap = new() {
+ ["image/jpeg"] = MagickFormat.Jpeg,
+ ["image/jpg"] = MagickFormat.Jpg,
+ ["image/png"] = MagickFormat.Png,
+ ["image/webp"] = MagickFormat.WebP
+ };
+
private IDbContextFactory<HBContext> dbFactory;
private IConfigService config;
@@ -267,7 +275,7 @@ public class MediaService : IMediaService {
"Both width and height cannot be null!",
mediaId);
- var thumbPath = GetPath(mediaId, width, height);
+ var thumbPath = GetThumbnailPath(mediaId, width, height);
if(File.Exists(thumbPath))
return System.IO.File.OpenRead(thumbPath);
@@ -288,9 +296,30 @@ public class MediaService : IMediaService {
return System.IO.File.OpenRead(thumbPath);
}
+ public Stream GetConverted(Guid mediaId, string mimeType) {
+ if(!FormatMap.TryGetValue(mimeType, out var format))
+ throw new MediaException($"Cannot convert to unknown format ({mimeType})", mediaId);
+
+ var convertedPath = GetConvertedPath(mediaId, mimeType);
+
+ if(File.Exists(convertedPath))
+ return System.IO.File.OpenRead(convertedPath);
+
+ if(!File.Exists(GetPath(mediaId)))
+ throw new ObjectNotFoundException(mediaId);
+
+ using var image = new MagickImage(GetPath(mediaId));
+ image.Write(convertedPath, format);
+
+ return System.IO.File.OpenRead(convertedPath);
+ }
+
public Stream GetThumbnail(Media media, int? width, int? height) =>
GetThumbnail(media.Guid, width, height);
+ public Stream GetConverted(Media media, string mimeType) =>
+ GetConverted(media.Guid, mimeType);
+
public string GetPath(Guid mediaId) {
var fileInfo = new FileInfo(
Path.Join(
@@ -303,7 +332,7 @@ public class MediaService : IMediaService {
return fileInfo.FullName;
}
- public string GetPath(Guid mediaId, int? width, int? height) {
+ public string GetThumbnailPath(Guid mediaId, int? width, int? height) {
if(width is null && height is null)
throw new ThumbnailException(
"Both width and height cannot be null!",
@@ -319,11 +348,25 @@ public class MediaService : IMediaService {
return fileInfo.FullName;
}
+ public string GetConvertedPath(Guid mediaId, string mimeType) {
+ var fileInfo = new FileInfo(Path.Join(
+ config.ConvertedMediaBasePath,
+ mediaId.ToString().Substring(0, 2),
+ mediaId.ToString().Substring(2, 2),
+ $"{mediaId.ToString()}-{mimeType.Split('/')[1]}"));
+
+ Directory.CreateDirectory(fileInfo.Directory!.FullName);
+ return fileInfo.FullName;
+ }
+
public string GetPath(Media media) =>
GetPath(media.Guid);
- public string GetPath(Media media, int? width, int? height) =>
- GetPath(media.Guid, width, height);
+ public string GetThumbnailPath(Media media, int? width, int? height) =>
+ GetThumbnailPath(media.Guid, width, height);
+
+ public string GetConvertedPath(Media media, string mimeType) =>
+ GetConvertedPath(media.Guid, mimeType);
private int GetUploadedFileHash(UploadedFile uf) => (
uf.CreateTime,