288 lines
9.1 KiB
C#
288 lines
9.1 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using AIImages.Settings;
|
||
|
||
namespace AIImages.Validation
|
||
{
|
||
/// <summary>
|
||
/// Валидатор для настроек мода AI Images
|
||
/// </summary>
|
||
public static class SettingsValidator
|
||
{
|
||
/// <summary>
|
||
/// Валидирует все настройки и возвращает список ошибок
|
||
/// </summary>
|
||
public static ValidationResult Validate(AIImagesModSettings settings)
|
||
{
|
||
var result = new ValidationResult();
|
||
|
||
if (settings == null)
|
||
{
|
||
result.AddError("Settings object is null");
|
||
return result;
|
||
}
|
||
|
||
// Валидация API endpoint
|
||
ValidateApiEndpoint(settings.apiEndpoint, result);
|
||
|
||
// Валидация размеров изображения
|
||
ValidateImageDimensions(settings.width, settings.height, result);
|
||
|
||
// Валидация steps
|
||
ValidateSteps(settings.steps, result);
|
||
|
||
// Валидация CFG scale
|
||
ValidateCfgScale(settings.cfgScale, result);
|
||
|
||
// Валидация sampler и scheduler
|
||
ValidateSamplerAndScheduler(
|
||
settings.selectedSampler,
|
||
settings.selectedScheduler,
|
||
result
|
||
);
|
||
|
||
// Валидация пути сохранения
|
||
ValidateSavePath(settings.savePath, result);
|
||
|
||
return result;
|
||
}
|
||
|
||
private static void ValidateApiEndpoint(string endpoint, ValidationResult result)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(endpoint))
|
||
{
|
||
result.AddError("API endpoint cannot be empty");
|
||
return;
|
||
}
|
||
|
||
if (!Uri.TryCreate(endpoint, UriKind.Absolute, out Uri uri))
|
||
{
|
||
result.AddError($"Invalid API endpoint format: {endpoint}");
|
||
return;
|
||
}
|
||
|
||
if (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps)
|
||
{
|
||
result.AddWarning($"API endpoint should use HTTP or HTTPS protocol: {endpoint}");
|
||
}
|
||
|
||
// Проверка на localhost/127.0.0.1
|
||
if (
|
||
uri.Host != "localhost"
|
||
&& uri.Host != "127.0.0.1"
|
||
&& !uri.Host.StartsWith("192.168.")
|
||
)
|
||
{
|
||
result.AddWarning(
|
||
"API endpoint is not pointing to a local address. Make sure the API is accessible."
|
||
);
|
||
}
|
||
}
|
||
|
||
private static void ValidateImageDimensions(int width, int height, ValidationResult result)
|
||
{
|
||
const int minDimension = 64;
|
||
const int maxDimension = 2048;
|
||
const int recommendedMin = 512;
|
||
const int recommendedMax = 1024;
|
||
|
||
if (width < minDimension || width > maxDimension)
|
||
{
|
||
result.AddError(
|
||
$"Width must be between {minDimension} and {maxDimension}. Current: {width}"
|
||
);
|
||
}
|
||
|
||
if (height < minDimension || height > maxDimension)
|
||
{
|
||
result.AddError(
|
||
$"Height must be between {minDimension} and {maxDimension}. Current: {height}"
|
||
);
|
||
}
|
||
|
||
// Проверка кратности 8 (рекомендация для Stable Diffusion)
|
||
if (width % 8 != 0)
|
||
{
|
||
result.AddWarning(
|
||
$"Width should be divisible by 8 for optimal results. Current: {width}"
|
||
);
|
||
}
|
||
|
||
if (height % 8 != 0)
|
||
{
|
||
result.AddWarning(
|
||
$"Height should be divisible by 8 for optimal results. Current: {height}"
|
||
);
|
||
}
|
||
|
||
// Предупреждения о производительности
|
||
if (width > recommendedMax || height > recommendedMax)
|
||
{
|
||
result.AddWarning(
|
||
$"Large image dimensions ({width}x{height}) may result in slow generation and high memory usage"
|
||
);
|
||
}
|
||
|
||
if (width < recommendedMin || height < recommendedMin)
|
||
{
|
||
result.AddWarning(
|
||
$"Small image dimensions ({width}x{height}) may result in lower quality"
|
||
);
|
||
}
|
||
}
|
||
|
||
private static void ValidateSteps(int steps, ValidationResult result)
|
||
{
|
||
const int minSteps = 1;
|
||
const int maxSteps = 150;
|
||
const int recommendedMin = 20;
|
||
const int recommendedMax = 50;
|
||
|
||
if (steps < minSteps || steps > maxSteps)
|
||
{
|
||
result.AddError(
|
||
$"Steps must be between {minSteps} and {maxSteps}. Current: {steps}"
|
||
);
|
||
}
|
||
|
||
if (steps < recommendedMin)
|
||
{
|
||
result.AddWarning(
|
||
$"Low steps value ({steps}) may result in lower quality. Recommended: {recommendedMin}-{recommendedMax}"
|
||
);
|
||
}
|
||
|
||
if (steps > recommendedMax)
|
||
{
|
||
result.AddWarning(
|
||
$"High steps value ({steps}) may result in slow generation with minimal quality improvement"
|
||
);
|
||
}
|
||
}
|
||
|
||
private static void ValidateCfgScale(float cfgScale, ValidationResult result)
|
||
{
|
||
const float minCfg = 1.0f;
|
||
const float maxCfg = 30.0f;
|
||
const float recommendedMin = 5.0f;
|
||
const float recommendedMax = 15.0f;
|
||
|
||
if (cfgScale < minCfg || cfgScale > maxCfg)
|
||
{
|
||
result.AddError(
|
||
$"CFG Scale must be between {minCfg} and {maxCfg}. Current: {cfgScale}"
|
||
);
|
||
}
|
||
|
||
if (cfgScale < recommendedMin)
|
||
{
|
||
result.AddWarning(
|
||
$"Low CFG scale ({cfgScale}) may ignore prompt. Recommended: {recommendedMin}-{recommendedMax}"
|
||
);
|
||
}
|
||
|
||
if (cfgScale > recommendedMax)
|
||
{
|
||
result.AddWarning(
|
||
$"High CFG scale ({cfgScale}) may result in over-saturated or distorted images"
|
||
);
|
||
}
|
||
}
|
||
|
||
private static void ValidateSamplerAndScheduler(
|
||
string sampler,
|
||
string scheduler,
|
||
ValidationResult result
|
||
)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(sampler))
|
||
{
|
||
result.AddWarning("Sampler is not selected. A default sampler will be used.");
|
||
}
|
||
|
||
if (string.IsNullOrWhiteSpace(scheduler))
|
||
{
|
||
result.AddWarning("Scheduler is not selected. A default scheduler will be used.");
|
||
}
|
||
}
|
||
|
||
private static void ValidateSavePath(string savePath, ValidationResult result)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(savePath))
|
||
{
|
||
result.AddError("Save path cannot be empty");
|
||
return;
|
||
}
|
||
|
||
// Проверка на недопустимые символы в пути
|
||
char[] invalidChars = System.IO.Path.GetInvalidPathChars();
|
||
if (savePath.IndexOfAny(invalidChars) >= 0)
|
||
{
|
||
result.AddError($"Save path contains invalid characters: {savePath}");
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Результат валидации настроек
|
||
/// </summary>
|
||
public class ValidationResult
|
||
{
|
||
private readonly List<string> _errors = new List<string>();
|
||
private readonly List<string> _warnings = new List<string>();
|
||
|
||
public bool IsValid => _errors.Count == 0;
|
||
public bool HasWarnings => _warnings.Count > 0;
|
||
public IReadOnlyList<string> Errors => _errors;
|
||
public IReadOnlyList<string> Warnings => _warnings;
|
||
|
||
public void AddError(string error)
|
||
{
|
||
if (!string.IsNullOrWhiteSpace(error))
|
||
{
|
||
_errors.Add(error);
|
||
}
|
||
}
|
||
|
||
public void AddWarning(string warning)
|
||
{
|
||
if (!string.IsNullOrWhiteSpace(warning))
|
||
{
|
||
_warnings.Add(warning);
|
||
}
|
||
}
|
||
|
||
public string GetErrorsAsString()
|
||
{
|
||
return string.Join("\n", _errors);
|
||
}
|
||
|
||
public string GetWarningsAsString()
|
||
{
|
||
return string.Join("\n", _warnings);
|
||
}
|
||
|
||
public string GetAllMessagesAsString()
|
||
{
|
||
var messages = new List<string>();
|
||
|
||
if (_errors.Count > 0)
|
||
{
|
||
messages.Add("Errors:");
|
||
messages.AddRange(_errors);
|
||
}
|
||
|
||
if (_warnings.Count > 0)
|
||
{
|
||
if (messages.Count > 0)
|
||
messages.Add("");
|
||
|
||
messages.Add("Warnings:");
|
||
messages.AddRange(_warnings);
|
||
}
|
||
|
||
return string.Join("\n", messages);
|
||
}
|
||
}
|
||
}
|