using System; using System.Collections.Generic; using AIImages.Settings; namespace AIImages.Validation { /// /// Валидатор для настроек мода AI Images /// public static class SettingsValidator { /// /// Валидирует все настройки и возвращает список ошибок /// 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}"); } } } /// /// Результат валидации настроек /// public class ValidationResult { private readonly List _errors = new List(); private readonly List _warnings = new List(); public bool IsValid => _errors.Count == 0; public bool HasWarnings => _warnings.Count > 0; public IReadOnlyList Errors => _errors; public IReadOnlyList 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(); 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); } } }