Serve assets.

This commit is contained in:
2025-11-15 14:11:48 +00:00
parent 80791a8cc9
commit e2f86b76db
4 changed files with 102 additions and 69 deletions

View File

@@ -36,11 +36,11 @@ public class Photos
{
public delegate Task ConversionFinished(bool result);
public static void EnsureCreated(string webRootPath, bool scan, string item)
public static void EnsureCreated(string assetRootPath, bool scan, string item)
{
List<string> paths = [];
string photosRoot = Path.Combine(webRootPath, "assets", scan ? "scan" : "photos");
string photosRoot = Path.Combine(assetRootPath, scan ? "scan" : "photos");
string itemPhotosRoot = Path.Combine(photosRoot, item);
string itemThumbsRoot = Path.Combine(itemPhotosRoot, "thumbs");
string itemOriginalPhotosRoot = Path.Combine(itemPhotosRoot, "originals");
@@ -88,14 +88,14 @@ public class Photos
foreach(string path in paths.Where(path => !Directory.Exists(path))) Directory.CreateDirectory(path);
}
public static bool Convert(string webRootPath, Guid id, string originalPath, string sourceFormat,
public static bool Convert(string assetRootPath, Guid id, string originalPath, string sourceFormat,
string outputFormat, string resolution, bool thumbnail, bool scan, string item)
{
outputFormat = outputFormat.ToLowerInvariant();
resolution = resolution.ToLowerInvariant();
sourceFormat = sourceFormat.ToLowerInvariant();
string outputPath = Path.Combine(webRootPath, "assets", scan ? "scans" : "photos", item);
string outputPath = Path.Combine(assetRootPath, scan ? "scans" : "photos", item);
int width, height;
if(thumbnail) outputPath = Path.Combine(outputPath, "thumbs");
@@ -268,12 +268,12 @@ public class Photos
}
}
public void ConversionWorker(string webRootPath, Guid id, string originalFilePath, string sourceFormat, bool scan,
public void ConversionWorker(string assetRootPath, Guid id, string originalFilePath, string sourceFormat, bool scan,
string item)
{
List<Task> pool =
[
new(() => FinishedRenderingJpeg4kThumbnail?.Invoke(Convert(webRootPath,
new(() => FinishedRenderingJpeg4kThumbnail?.Invoke(Convert(assetRootPath,
id,
originalFilePath,
sourceFormat,
@@ -282,7 +282,7 @@ public class Photos
true,
scan,
item))),
new(() => FinishedRenderingJpeg1440Thumbnail?.Invoke(Convert(webRootPath,
new(() => FinishedRenderingJpeg1440Thumbnail?.Invoke(Convert(assetRootPath,
id,
originalFilePath,
sourceFormat,
@@ -291,7 +291,7 @@ public class Photos
true,
scan,
item))),
new(() => FinishedRenderingJpegHdThumbnail?.Invoke(Convert(webRootPath,
new(() => FinishedRenderingJpegHdThumbnail?.Invoke(Convert(assetRootPath,
id,
originalFilePath,
sourceFormat,
@@ -300,7 +300,7 @@ public class Photos
true,
scan,
item))),
new(() => FinishedRenderingJpeg4K?.Invoke(Convert(webRootPath,
new(() => FinishedRenderingJpeg4K?.Invoke(Convert(assetRootPath,
id,
originalFilePath,
sourceFormat,
@@ -309,7 +309,7 @@ public class Photos
false,
scan,
item))),
new(() => FinishedRenderingJpeg1440?.Invoke(Convert(webRootPath,
new(() => FinishedRenderingJpeg1440?.Invoke(Convert(assetRootPath,
id,
originalFilePath,
sourceFormat,
@@ -318,7 +318,7 @@ public class Photos
false,
scan,
item))),
new(() => FinishedRenderingJpegHd?.Invoke(Convert(webRootPath,
new(() => FinishedRenderingJpegHd?.Invoke(Convert(assetRootPath,
id,
originalFilePath,
sourceFormat,
@@ -327,7 +327,7 @@ public class Photos
false,
scan,
item))),
new(() => FinishedRenderingJp2k4kThumbnail?.Invoke(Convert(webRootPath,
new(() => FinishedRenderingJp2k4kThumbnail?.Invoke(Convert(assetRootPath,
id,
originalFilePath,
sourceFormat,
@@ -336,7 +336,7 @@ public class Photos
true,
scan,
item))),
new(() => FinishedRenderingJp2k1440Thumbnail?.Invoke(Convert(webRootPath,
new(() => FinishedRenderingJp2k1440Thumbnail?.Invoke(Convert(assetRootPath,
id,
originalFilePath,
sourceFormat,
@@ -345,7 +345,7 @@ public class Photos
true,
scan,
item))),
new(() => FinishedRenderingJp2kHdThumbnail?.Invoke(Convert(webRootPath,
new(() => FinishedRenderingJp2kHdThumbnail?.Invoke(Convert(assetRootPath,
id,
originalFilePath,
sourceFormat,
@@ -354,7 +354,7 @@ public class Photos
true,
scan,
item))),
new(() => FinishedRenderingJp2k4k?.Invoke(Convert(webRootPath,
new(() => FinishedRenderingJp2k4k?.Invoke(Convert(assetRootPath,
id,
originalFilePath,
sourceFormat,
@@ -363,7 +363,7 @@ public class Photos
false,
scan,
item))),
new(() => FinishedRenderingJp2k1440?.Invoke(Convert(webRootPath,
new(() => FinishedRenderingJp2k1440?.Invoke(Convert(assetRootPath,
id,
originalFilePath,
sourceFormat,
@@ -372,7 +372,7 @@ public class Photos
false,
scan,
item))),
new(() => FinishedRenderingJp2kHd?.Invoke(Convert(webRootPath,
new(() => FinishedRenderingJp2kHd?.Invoke(Convert(assetRootPath,
id,
originalFilePath,
sourceFormat,
@@ -381,7 +381,7 @@ public class Photos
false,
scan,
item))),
new(() => FinishedRenderingWebp4kThumbnail?.Invoke(Convert(webRootPath,
new(() => FinishedRenderingWebp4kThumbnail?.Invoke(Convert(assetRootPath,
id,
originalFilePath,
sourceFormat,
@@ -390,7 +390,7 @@ public class Photos
true,
scan,
item))),
new(() => FinishedRenderingWebp1440Thumbnail?.Invoke(Convert(webRootPath,
new(() => FinishedRenderingWebp1440Thumbnail?.Invoke(Convert(assetRootPath,
id,
originalFilePath,
sourceFormat,
@@ -399,7 +399,7 @@ public class Photos
true,
scan,
item))),
new(() => FinishedRenderingWebpHdThumbnail?.Invoke(Convert(webRootPath,
new(() => FinishedRenderingWebpHdThumbnail?.Invoke(Convert(assetRootPath,
id,
originalFilePath,
sourceFormat,
@@ -408,7 +408,7 @@ public class Photos
true,
scan,
item))),
new(() => FinishedRenderingWebp4k?.Invoke(Convert(webRootPath,
new(() => FinishedRenderingWebp4k?.Invoke(Convert(assetRootPath,
id,
originalFilePath,
sourceFormat,
@@ -417,7 +417,7 @@ public class Photos
false,
scan,
item))),
new(() => FinishedRenderingWebp1440?.Invoke(Convert(webRootPath,
new(() => FinishedRenderingWebp1440?.Invoke(Convert(assetRootPath,
id,
originalFilePath,
sourceFormat,
@@ -426,7 +426,7 @@ public class Photos
false,
scan,
item))),
new(() => FinishedRenderingWebpHd?.Invoke(Convert(webRootPath,
new(() => FinishedRenderingWebpHd?.Invoke(Convert(assetRootPath,
id,
originalFilePath,
sourceFormat,
@@ -435,7 +435,7 @@ public class Photos
false,
scan,
item))),
new(() => FinishedRenderingHeif4kThumbnail?.Invoke(Convert(webRootPath,
new(() => FinishedRenderingHeif4kThumbnail?.Invoke(Convert(assetRootPath,
id,
originalFilePath,
sourceFormat,
@@ -444,7 +444,7 @@ public class Photos
true,
scan,
item))),
new(() => FinishedRenderingHeif1440Thumbnail?.Invoke(Convert(webRootPath,
new(() => FinishedRenderingHeif1440Thumbnail?.Invoke(Convert(assetRootPath,
id,
originalFilePath,
sourceFormat,
@@ -453,7 +453,7 @@ public class Photos
true,
scan,
item))),
new(() => FinishedRenderingHeifHdThumbnail?.Invoke(Convert(webRootPath,
new(() => FinishedRenderingHeifHdThumbnail?.Invoke(Convert(assetRootPath,
id,
originalFilePath,
sourceFormat,
@@ -462,7 +462,7 @@ public class Photos
true,
scan,
item))),
new(() => FinishedRenderingHeif4K?.Invoke(Convert(webRootPath,
new(() => FinishedRenderingHeif4K?.Invoke(Convert(assetRootPath,
id,
originalFilePath,
sourceFormat,
@@ -471,7 +471,7 @@ public class Photos
false,
scan,
item))),
new(() => FinishedRenderingHeif1440?.Invoke(Convert(webRootPath,
new(() => FinishedRenderingHeif1440?.Invoke(Convert(assetRootPath,
id,
originalFilePath,
sourceFormat,
@@ -480,7 +480,7 @@ public class Photos
false,
scan,
item))),
new(() => FinishedRenderingHeifHd?.Invoke(Convert(webRootPath,
new(() => FinishedRenderingHeifHd?.Invoke(Convert(assetRootPath,
id,
originalFilePath,
sourceFormat,
@@ -489,7 +489,7 @@ public class Photos
false,
scan,
item))),
new(() => FinishedRenderingAvif4kThumbnail?.Invoke(Convert(webRootPath,
new(() => FinishedRenderingAvif4kThumbnail?.Invoke(Convert(assetRootPath,
id,
originalFilePath,
sourceFormat,
@@ -498,7 +498,7 @@ public class Photos
true,
scan,
item))),
new(() => FinishedRenderingAvif1440Thumbnail?.Invoke(Convert(webRootPath,
new(() => FinishedRenderingAvif1440Thumbnail?.Invoke(Convert(assetRootPath,
id,
originalFilePath,
sourceFormat,
@@ -507,7 +507,7 @@ public class Photos
true,
scan,
item))),
new(() => FinishedRenderingAvifHdThumbnail?.Invoke(Convert(webRootPath,
new(() => FinishedRenderingAvifHdThumbnail?.Invoke(Convert(assetRootPath,
id,
originalFilePath,
sourceFormat,
@@ -516,7 +516,7 @@ public class Photos
true,
scan,
item))),
new(() => FinishedRenderingAvif4K?.Invoke(Convert(webRootPath,
new(() => FinishedRenderingAvif4K?.Invoke(Convert(assetRootPath,
id,
originalFilePath,
sourceFormat,
@@ -525,7 +525,7 @@ public class Photos
false,
scan,
item))),
new(() => FinishedRenderingAvif1440?.Invoke(Convert(webRootPath,
new(() => FinishedRenderingAvif1440?.Invoke(Convert(assetRootPath,
id,
originalFilePath,
sourceFormat,
@@ -534,7 +534,7 @@ public class Photos
false,
scan,
item))),
new(() => FinishedRenderingAvifHd?.Invoke(Convert(webRootPath,
new(() => FinishedRenderingAvifHd?.Invoke(Convert(assetRootPath,
id,
originalFilePath,
sourceFormat,

View File

@@ -33,11 +33,11 @@ namespace Marechai.Server.Helpers;
public static class SvgRender
{
public static void RenderCountries()
public static void RenderCountries(string assetRootPath)
{
if(!Directory.Exists("wwwroot/assets/flags/countries")) return;
if(!Directory.Exists($"{assetRootPath}/flags/countries")) return;
foreach(string file in Directory.GetFiles("wwwroot/assets/flags/countries/",
foreach(string file in Directory.GetFiles($"{assetRootPath}/flags/countries/",
"*.svg",
SearchOption.TopDirectoryOnly))
{
@@ -50,8 +50,8 @@ public static class SvgRender
"png", "webp"
})
{
if(!Directory.Exists(Path.Combine("wwwroot/assets/flags/countries", format)))
Directory.CreateDirectory(Path.Combine("wwwroot/assets/flags/countries", format));
if(!Directory.Exists(Path.Combine($"{assetRootPath}/flags/countries", format)))
Directory.CreateDirectory(Path.Combine($"{assetRootPath}/flags/countries", format));
SKEncodedImageFormat skFormat;
@@ -72,14 +72,14 @@ public static class SvgRender
1, 2, 3
})
{
if(!Directory.Exists(Path.Combine("wwwroot/assets/flags/countries", format, $"{multiplier}x")))
if(!Directory.Exists(Path.Combine($"{assetRootPath}/flags/countries", format, $"{multiplier}x")))
{
Directory.CreateDirectory(Path.Combine("wwwroot/assets/flags/countries",
Directory.CreateDirectory(Path.Combine($"{assetRootPath}/flags/countries",
format,
$"{multiplier}x"));
}
string rendered = Path.Combine("wwwroot/assets/flags/countries",
string rendered = Path.Combine($"{assetRootPath}/flags/countries",
format,
$"{multiplier}x",
flagName + $".{format}");
@@ -102,11 +102,11 @@ public static class SvgRender
}
}
public static void ImportCompanyLogos(MarechaiContext context)
public static void ImportCompanyLogos(string assetRootPath, MarechaiContext context)
{
if(!Directory.Exists("wwwroot/assets/incoming")) return;
if(!Directory.Exists($"{assetRootPath}/incoming")) return;
foreach(string file in Directory.GetFiles("wwwroot/assets/incoming",
foreach(string file in Directory.GetFiles($"{assetRootPath}/incoming",
"company_*.svg",
SearchOption.TopDirectoryOnly))
{
@@ -153,8 +153,8 @@ public static class SvgRender
})
{
string outDir = minSize == 32
? Path.Combine("wwwroot/assets/logos/thumbs", format)
: Path.Combine("wwwroot/assets/logos", format);
? Path.Combine($"{assetRootPath}/logos/thumbs", format)
: Path.Combine($"{assetRootPath}/logos", format);
if(!Directory.Exists(outDir)) Directory.CreateDirectory(outDir);
@@ -200,7 +200,7 @@ public static class SvgRender
}
}
File.Move(file, $"wwwroot/assets/logos/{guid}.svg");
File.Move(file, $"{assetRootPath}/logos/{guid}.svg");
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
@@ -12,9 +13,11 @@ using Markdig;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Tokens;
using Version = Marechai.Server.Interop.Version;
@@ -140,14 +143,36 @@ file class Program
}
}
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
string assetRootPath = builder.Configuration["AssetRootPath"];
if(string.IsNullOrEmpty(assetRootPath))
{
Console.WriteLine("\e[31;1mAsset root path not set, cannot continue, check configuration...\e[0m");
return;
}
Directory.CreateDirectory(assetRootPath);
DateTime start = DateTime.Now;
Console.WriteLine("\e[31;1mRendering new country flags...\e[0m");
SvgRender.RenderCountries();
SvgRender.RenderCountries(assetRootPath);
DateTime end = DateTime.Now;
Console.WriteLine("\e[31;1mTook \e[32;1m{0} seconds\e[31;1m...\e[0m", (end - start).TotalSeconds);
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
start = DateTime.Now;
Console.WriteLine("\e[31;1mEnsuring photo folders exist...\e[0m");
Photos.EnsureCreated(assetRootPath, false, "machines");
Console.WriteLine("\e[31;1mEnsuring scan folders exist...\e[0m");
Photos.EnsureCreated(assetRootPath, true, "books");
Photos.EnsureCreated(assetRootPath, true, "documents");
Photos.EnsureCreated(assetRootPath, true, "magazines");
end = DateTime.Now;
Console.WriteLine("\e[31;1mTook \e[32;1m{0} seconds\e[31;1m...\e[0m", (end - start).TotalSeconds);
// Add services to the container.
builder.Services.AddControllers()
@@ -210,8 +235,7 @@ file class Program
builder.Services.AddScoped<TokenService, TokenService>();
// Read allowed CORS origins from configuration
string[] allowedOrigins = builder.Configuration.GetSection("CORS:AllowedOrigins").Get<string[]>() ??
Array.Empty<string>();
string[] allowedOrigins = builder.Configuration.GetSection("CORS:AllowedOrigins").Get<string[]>() ?? [];
builder.Services.AddCors(options =>
{
@@ -241,6 +265,28 @@ file class Program
app.MapControllers();
// Set up custom content types - associating file extension to MIME type
var provider = new FileExtensionContentTypeProvider
{
Mappings =
{
// Add new mappings
[".avif"] = "image/avif", // AVIF image format
[".heic"] = "image/heic", // HEIC image format
[".heif"] = "image/heif", // HEIF image format
[".jxl"] = "image/jxl", // JPEG-XL image format
[".webp"] = "image/webp", // WebP image format
[".svg"] = "image/svg+xml" // SVG image format
}
};
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(assetRootPath),
RequestPath = "/assets",
ContentTypeProvider = provider
});
using(IServiceScope scope = app.Services.CreateScope())
{
IServiceProvider services = scope.ServiceProvider;
@@ -257,7 +303,7 @@ file class Program
start = DateTime.Now;
Console.WriteLine("\e[31;1mImporting company logos...\e[0m");
SvgRender.ImportCompanyLogos(context);
SvgRender.ImportCompanyLogos(assetRootPath, context);
end = DateTime.Now;
Console.WriteLine("\e[31;1mTook \e[32;1m{0} seconds\e[31;1m...\e[0m", (end - start).TotalSeconds);
@@ -278,20 +324,6 @@ file class Program
end = DateTime.Now;
Console.WriteLine("\e[31;1mTook \e[32;1m{0} seconds\e[31;1m...\e[0m", (end - start).TotalSeconds);
start = DateTime.Now;
Console.WriteLine("\e[31;1mEnsuring photo folders exist...\e[0m");
Photos.EnsureCreated("wwwroot", false, "machines");
end = DateTime.Now;
start = DateTime.Now;
Console.WriteLine("\e[31;1mEnsuring scan folders exist...\e[0m");
Photos.EnsureCreated("wwwroot", true, "books");
Photos.EnsureCreated("wwwroot", true, "documents");
Photos.EnsureCreated("wwwroot", true, "magazines");
end = DateTime.Now;
Console.WriteLine("\e[31;1mTook \e[32;1m{0} seconds\e[31;1m...\e[0m", (end - start).TotalSeconds);
}
catch(Exception ex)
{

View File

@@ -64,5 +64,6 @@
"Name": "NormalUser",
"Description": "A normal user role."
}
]
],
"AssetRootPath": "/var/marechai/assets"
}