using OF_DL.Models.Config; namespace OF_DL.Gui.Services; internal static class ConfigValidationService { public static IReadOnlyDictionary Validate(Config config) { Dictionary errors = new(StringComparer.Ordinal); ValidatePath(config.DownloadPath, nameof(Config.DownloadPath), errors, requireExistingFile: false); ValidatePath(config.FFmpegPath, nameof(Config.FFmpegPath), errors, requireExistingFile: true); ValidatePath(config.FFprobePath, nameof(Config.FFprobePath), errors, requireExistingFile: true); if (config.Timeout.HasValue && config.Timeout.Value <= 0 && config.Timeout.Value != -1) { errors[nameof(Config.Timeout)] = "Timeout must be -1 or greater than 0."; } if (config.LimitDownloadRate && config.DownloadLimitInMbPerSec <= 0) { errors[nameof(Config.DownloadLimitInMbPerSec)] = "DownloadLimitInMbPerSec must be greater than 0 when LimitDownloadRate is enabled."; } if (config.DownloadOnlySpecificDates && !config.CustomDate.HasValue) { errors[nameof(Config.CustomDate)] = "CustomDate is required when DownloadOnlySpecificDates is enabled."; } ValidateFileNameFormat(config.PaidPostFileNameFormat, nameof(Config.PaidPostFileNameFormat), errors); ValidateFileNameFormat(config.PostFileNameFormat, nameof(Config.PostFileNameFormat), errors); ValidateFileNameFormat(config.PaidMessageFileNameFormat, nameof(Config.PaidMessageFileNameFormat), errors); ValidateFileNameFormat(config.MessageFileNameFormat, nameof(Config.MessageFileNameFormat), errors); foreach (KeyValuePair creatorConfig in config.CreatorConfigs) { ValidateFileNameFormat(creatorConfig.Value.PaidPostFileNameFormat, $"{nameof(Config.CreatorConfigs)}.{creatorConfig.Key}.PaidPostFileNameFormat", errors); ValidateFileNameFormat(creatorConfig.Value.PostFileNameFormat, $"{nameof(Config.CreatorConfigs)}.{creatorConfig.Key}.PostFileNameFormat", errors); ValidateFileNameFormat(creatorConfig.Value.PaidMessageFileNameFormat, $"{nameof(Config.CreatorConfigs)}.{creatorConfig.Key}.PaidMessageFileNameFormat", errors); ValidateFileNameFormat(creatorConfig.Value.MessageFileNameFormat, $"{nameof(Config.CreatorConfigs)}.{creatorConfig.Key}.MessageFileNameFormat", errors); } return errors; } private static void ValidatePath(string? path, string fieldName, IDictionary errors, bool requireExistingFile) { if (string.IsNullOrWhiteSpace(path)) { return; } if (path.Any(character => Path.GetInvalidPathChars().Contains(character))) { errors[fieldName] = "Path contains invalid characters."; return; } if (requireExistingFile && !File.Exists(path)) { errors[fieldName] = "Path must point to an existing file."; } } private static void ValidateFileNameFormat(string? format, string fieldName, IDictionary errors) { if (string.IsNullOrWhiteSpace(format)) { return; } bool hasUniqueToken = format.Contains("{mediaId}", StringComparison.OrdinalIgnoreCase) || format.Contains("{filename}", StringComparison.OrdinalIgnoreCase); if (!hasUniqueToken) { errors[fieldName] = "Format must include {mediaId} or {filename} to avoid file collisions."; } } }