You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
200 lines
6.1 KiB
200 lines
6.1 KiB
using System.Collections.Immutable;
|
|
using Exsersewo.Common.Extensions;
|
|
using Exsersewo.Common.Utilities;
|
|
using ImageMagick;
|
|
using Kynareth.Models;
|
|
using Kynareth.Extensions;
|
|
|
|
namespace Kynareth.Managers;
|
|
|
|
public static partial class ImageManager
|
|
{
|
|
private static List<ImageGenerationEndpoint> GenerationEndpoints;
|
|
private static IReadOnlyList<string> sourceFiles;
|
|
|
|
private static string GenerationTemplateBaseFolder;
|
|
|
|
static void ConfigureGeneration(IConfiguration configuration, IWebHostEnvironment _appEnvironment)
|
|
{
|
|
sourceFiles = Directory.EnumerateFiles(Path.Combine(_appEnvironment.WebRootPath, configuration.GetValue<string>("ShitpostBot:Folder"), "..", "random")).ToImmutableList();
|
|
GenerationTemplateBaseFolder = Path.Combine(_appEnvironment.WebRootPath, configuration.GetValue<string>("ShitpostBot:Folder"));
|
|
GenerationEndpoints = configuration.GetSection("ShitpostBot:Templates").Get<List<ImageGenerationEndpoint>>();
|
|
}
|
|
|
|
public static async Task<Object> GenerateImageAsync(CancellationToken cancellationToken)
|
|
{
|
|
ImageGenerationImage imageTemplate = null;
|
|
|
|
var endpoint = GenerationEndpoints.Random();
|
|
|
|
if (endpoint is null)
|
|
{
|
|
var ex = new ArgumentException($"Couldn't find endpoint");
|
|
|
|
return GetResult(StatusCodes.Status404NotFound, EventResult.FromFailureException(ex.Message, ex));
|
|
}
|
|
|
|
string variant = null;
|
|
if (endpoint.Variants != null && endpoint.Variants.Any() && TrueRandom.Next(0, 50) > 25)
|
|
{
|
|
variant = endpoint.Variants.Random().Name;
|
|
}
|
|
|
|
var sources = sourceFiles.GetRandomAmount(endpoint.SourcesRequired) as string[];
|
|
|
|
return await GenerateImageAsync(endpoint.Name, variant, sources, cancellationToken);
|
|
}
|
|
|
|
public static async Task<Object> GenerateImageAsync(string template, string variant, CancellationToken cancellationToken)
|
|
{
|
|
ImageGenerationImage imageTemplate = null;
|
|
|
|
var endpoint = GenerationEndpoints.FirstOrDefault(e => e.Name.ToLowerInvariant().Equals(template.ToLowerInvariant()));
|
|
|
|
if (endpoint is null)
|
|
{
|
|
var ex = new ArgumentException($"Couldn't find endpoint named \"{template}\"", nameof(template));
|
|
|
|
return GetResult(StatusCodes.Status404NotFound, EventResult.FromFailureException(ex.Message, ex));
|
|
}
|
|
|
|
var sources = sourceFiles.GetRandomAmount(endpoint.SourcesRequired) as string[];
|
|
|
|
return await GenerateImageAsync(template, variant, sources, cancellationToken);
|
|
}
|
|
|
|
public static async Task<object> GenerateImageAsync(string template, string variant, string[] sources, CancellationToken cancellationToken)
|
|
{
|
|
while (true)
|
|
{
|
|
if(cancellationToken.IsCancellationRequested)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
ImageGenerationImage imageTemplate = null;
|
|
|
|
var endpoint = GenerationEndpoints.FirstOrDefault(e => e.Name.ToLowerInvariant().Equals(template.ToLowerInvariant()));
|
|
|
|
if (endpoint is null)
|
|
{
|
|
var ex = new ArgumentException($"Couldn't find endpoint named \"{template}\"", nameof(template));
|
|
|
|
return GetResult(StatusCodes.Status404NotFound, EventResult.FromFailureException(ex.Message, ex));
|
|
}
|
|
|
|
if (endpoint.SourcesRequired > sources.Length)
|
|
{
|
|
var ex = new ArgumentException("Not enough sources provided", nameof(sources));
|
|
|
|
return GetResult(StatusCodes.Status400BadRequest, EventResult.FromFailureException(ex.Message, ex));
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(variant))
|
|
{
|
|
imageTemplate = endpoint.Variants?
|
|
.FirstOrDefault(v => v.Name.ToLowerInvariant().Equals(variant.ToLowerInvariant()));
|
|
|
|
if (imageTemplate is null)
|
|
{
|
|
var ex = new ArgumentException(
|
|
$"Invalid variant given \"{variant}\" for endpoint \"{template}\"",
|
|
nameof(variant)
|
|
);
|
|
|
|
return GetResult(StatusCodes.Status400BadRequest, EventResult.FromFailureException(ex.Message, ex));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
imageTemplate = endpoint;
|
|
}
|
|
|
|
string image = Path.Combine(GenerationTemplateBaseFolder, !string.IsNullOrWhiteSpace(endpoint.Folder) ? endpoint.Folder : "", imageTemplate.Image);
|
|
|
|
using MagickImage templateImage = new(System.IO.File.ReadAllBytes(image));
|
|
using MagickImage baseImage = new(MagickColors.Transparent, templateImage.Width, templateImage.Height);
|
|
|
|
baseImage.Format = MagickFormat.Png;
|
|
|
|
if (imageTemplate.PlaceUnder)
|
|
{
|
|
await AddSourcesToImage(baseImage, imageTemplate, sources, cancellationToken);
|
|
}
|
|
|
|
baseImage.Composite(templateImage, Gravity.Northwest, CompositeOperator.Over);
|
|
|
|
if (!imageTemplate.PlaceUnder)
|
|
{
|
|
await AddSourcesToImage(baseImage, imageTemplate, sources, cancellationToken);
|
|
}
|
|
|
|
return baseImage.ToByteArray();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
static async Task AddSourcesToImage(MagickImage image, ImageGenerationImage template, IEnumerable<string> sources, CancellationToken cancellationToken)
|
|
{
|
|
while (true)
|
|
{
|
|
if(cancellationToken.IsCancellationRequested)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int img = 0;
|
|
for(int x = 0; x < template.Positions.Count; x++)
|
|
{
|
|
var position = template.Positions.ElementAtOrDefault(x);
|
|
|
|
var src = sources.ElementAtOrDefault(img);
|
|
|
|
Stream imageData = null;
|
|
if (File.Exists(src))
|
|
{
|
|
imageData = new MemoryStream(await File.ReadAllBytesAsync(src));
|
|
}
|
|
else if (src.StartsWith("http"))
|
|
{
|
|
imageData = await HttpWebClient.GetStreamAsync(new Uri(src));
|
|
}
|
|
|
|
using MagickImage srcImg = new MagickImage(imageData);
|
|
|
|
srcImg.Resize(position.Width, position.Height);
|
|
|
|
if (position.Rotation != 0)
|
|
{
|
|
srcImg.Rotate(template.Rotate > 0 ? -position.Rotation : position.Rotation);
|
|
}
|
|
|
|
string background = !string.IsNullOrWhiteSpace(position.Background) ? $"#{position.Background}" : MagickColors.Transparent.ToHexString();
|
|
|
|
var tmp = new MagickImage($"xc:{background}", position.Width, position.Height);
|
|
|
|
tmp.Composite(srcImg, Gravity.Center, 0, 0, CompositeOperator.Over);
|
|
|
|
image.Composite(tmp, Gravity.Northwest, position.X, position.Y, CompositeOperator.Over);
|
|
|
|
if (img + 1 < sources.Count())
|
|
{
|
|
img++;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
public static IEnumerable<ImageGenerationEndpointRead> GetGenerationEndpoints()
|
|
{
|
|
return GenerationEndpoints.Select(e => new ImageGenerationEndpointRead
|
|
{
|
|
Name = e.Name,
|
|
SourcesRequired = e.SourcesRequired,
|
|
Variants = e.Variants.Select(v => v.Name)
|
|
});
|
|
}
|
|
} |