354 lines
11 KiB
TypeScript
354 lines
11 KiB
TypeScript
export const ENHANCE_PRO_KREA_SETTING_KEY = "enhance_pro_krea_config";
|
|
export const ENHANCE_PRO_KREA_DEFAULT_BASE_URL = "https://api.krea.ai";
|
|
export const ENHANCE_PRO_KREA_DEFAULT_PROVIDER_NAME = "Krea Topaz Generative";
|
|
export const ENHANCE_PRO_KREA_DEFAULT_MODEL = "Redefine";
|
|
export const ENHANCE_PRO_KREA_DEFAULT_OUTPUT_FORMAT = "jpg";
|
|
const TRAILING_SLASHES_REGEX = /\/+$/;
|
|
|
|
export type EnhanceProKreaModel =
|
|
| "Redefine"
|
|
| "Recovery"
|
|
| "Recovery V2"
|
|
| "Reimagine";
|
|
|
|
export type EnhanceProOutputFormat = "png" | "jpg" | "webp";
|
|
|
|
export type EnhanceProSubjectDetection = "All" | "Foreground" | "Background";
|
|
|
|
export type EnhanceProPreset = {
|
|
id: string;
|
|
name: string;
|
|
description: string;
|
|
isDefault: boolean;
|
|
prompt: string;
|
|
batchSize: number;
|
|
seed?: number;
|
|
outputFormat: EnhanceProOutputFormat;
|
|
subjectDetection: EnhanceProSubjectDetection;
|
|
faceEnhancement: boolean;
|
|
faceEnhancementCreativity: number;
|
|
faceEnhancementStrength: number;
|
|
cropToFill: boolean;
|
|
model: EnhanceProKreaModel;
|
|
upscalingActivated: boolean;
|
|
imageScalingFactor: number;
|
|
creativity: number;
|
|
texture: number;
|
|
sharpen: number;
|
|
denoise: number;
|
|
detail: number;
|
|
};
|
|
|
|
export type EnhanceProKreaConfig = {
|
|
enabled: boolean;
|
|
providerName: string;
|
|
baseUrl: string;
|
|
apiKey: string;
|
|
defaultModel: EnhanceProKreaModel;
|
|
defaultOutputFormat: EnhanceProOutputFormat;
|
|
allowBatchSize: boolean;
|
|
maxBatchSize: number;
|
|
maxOutputEdge: number;
|
|
enableAdvancedControls: boolean;
|
|
presets: EnhanceProPreset[];
|
|
};
|
|
|
|
function clampInteger(
|
|
value: unknown,
|
|
min: number,
|
|
max: number,
|
|
fallback: number,
|
|
) {
|
|
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
if (!Number.isFinite(parsed)) {
|
|
return fallback;
|
|
}
|
|
return Math.min(max, Math.max(min, parsed));
|
|
}
|
|
|
|
function clampFloat(
|
|
value: unknown,
|
|
min: number,
|
|
max: number,
|
|
fallback: number,
|
|
) {
|
|
const parsed = Number.parseFloat(String(value ?? ""));
|
|
if (!Number.isFinite(parsed)) {
|
|
return fallback;
|
|
}
|
|
return Math.min(max, Math.max(min, parsed));
|
|
}
|
|
|
|
function createEnhanceProPresetId(name: string, index: number) {
|
|
const normalized = String(name || "")
|
|
.trim()
|
|
.toLowerCase()
|
|
.replace(/[^a-z0-9]+/g, "-")
|
|
.replace(/^-+|-+$/g, "");
|
|
return normalized || `enhance-pro-preset-${index + 1}`;
|
|
}
|
|
|
|
export function defaultEnhanceProPresets(): EnhanceProPreset[] {
|
|
return [
|
|
{
|
|
id: "fidelity-recovery",
|
|
name: "保真修复",
|
|
description: "优先保留原图结构和内容,适合老图修复、压缩损伤恢复、细节补全。",
|
|
isDefault: true,
|
|
prompt:
|
|
"保持原图主体、构图和光影关系基本不变,忠实修复细节,减少压缩噪点和模糊感,让整体画质更干净自然。",
|
|
batchSize: 1,
|
|
seed: 1337,
|
|
outputFormat: "jpg",
|
|
subjectDetection: "All",
|
|
faceEnhancement: false,
|
|
faceEnhancementCreativity: 0.35,
|
|
faceEnhancementStrength: 0.45,
|
|
cropToFill: false,
|
|
model: "Recovery V2",
|
|
upscalingActivated: true,
|
|
imageScalingFactor: 2,
|
|
creativity: 1,
|
|
texture: 2,
|
|
sharpen: 0.35,
|
|
denoise: 0.45,
|
|
detail: 0.65,
|
|
},
|
|
{
|
|
id: "portrait-repair",
|
|
name: "人像修复",
|
|
description: "针对人物和面部细节优化,适合证件照、半身照、商品模特图。",
|
|
isDefault: false,
|
|
prompt:
|
|
"重点增强人物面部和发丝细节,保持肤色自然真实,避免过度磨皮和塑料感,让五官更清晰但不过度重绘。",
|
|
batchSize: 1,
|
|
seed: 1337,
|
|
outputFormat: "jpg",
|
|
subjectDetection: "Foreground",
|
|
faceEnhancement: true,
|
|
faceEnhancementCreativity: 0.35,
|
|
faceEnhancementStrength: 0.75,
|
|
cropToFill: false,
|
|
model: "Recovery V2",
|
|
upscalingActivated: true,
|
|
imageScalingFactor: 2,
|
|
creativity: 2,
|
|
texture: 3,
|
|
sharpen: 0.4,
|
|
denoise: 0.4,
|
|
detail: 0.7,
|
|
},
|
|
{
|
|
id: "creative-reimagine",
|
|
name: "创意重建",
|
|
description: "允许模型更积极地重建高频细节,适合模糊图、概念图、二次质感提升。",
|
|
isDefault: false,
|
|
prompt:
|
|
"在保持主体可识别的基础上大胆重建高频细节与纹理,提升画面层次、材质感和视觉冲击力,让结果更有高级感。",
|
|
batchSize: 1,
|
|
seed: 1337,
|
|
outputFormat: "jpg",
|
|
subjectDetection: "All",
|
|
faceEnhancement: false,
|
|
faceEnhancementCreativity: 0.6,
|
|
faceEnhancementStrength: 0.55,
|
|
cropToFill: false,
|
|
model: "Reimagine",
|
|
upscalingActivated: true,
|
|
imageScalingFactor: 2,
|
|
creativity: 5,
|
|
texture: 4,
|
|
sharpen: 0.55,
|
|
denoise: 0.25,
|
|
detail: 0.8,
|
|
},
|
|
];
|
|
}
|
|
|
|
function normalizeEnhanceProPreset(
|
|
value: unknown,
|
|
index: number,
|
|
configDefaults: Pick<
|
|
EnhanceProKreaConfig,
|
|
"defaultModel" | "defaultOutputFormat" | "maxBatchSize"
|
|
>,
|
|
): EnhanceProPreset {
|
|
const defaults = defaultEnhanceProPresets()[index] || defaultEnhanceProPresets()[0];
|
|
const raw =
|
|
value && typeof value === "object" && !Array.isArray(value)
|
|
? (value as Record<string, unknown>)
|
|
: {};
|
|
|
|
const model = String(raw.model || "").trim();
|
|
const outputFormat = String(
|
|
raw.outputFormat || raw.output_format || "",
|
|
).trim();
|
|
const subjectDetection = String(
|
|
raw.subjectDetection || raw.subject_detection || "",
|
|
).trim();
|
|
|
|
return {
|
|
id:
|
|
String(raw.id || "").trim() ||
|
|
createEnhanceProPresetId(String(raw.name || defaults.name), index),
|
|
name: String(raw.name || "").trim() || defaults.name,
|
|
description: String(raw.description || "").trim() || defaults.description,
|
|
isDefault: raw.isDefault === true,
|
|
prompt: String(raw.prompt || "").trim() || defaults.prompt,
|
|
batchSize: clampInteger(
|
|
raw.batchSize,
|
|
1,
|
|
configDefaults.maxBatchSize,
|
|
defaults.batchSize,
|
|
),
|
|
seed: Number.isFinite(Number(raw.seed))
|
|
? Math.round(Number(raw.seed))
|
|
: defaults.seed,
|
|
outputFormat:
|
|
outputFormat === "png" ||
|
|
outputFormat === "webp" ||
|
|
outputFormat === "jpg"
|
|
? outputFormat
|
|
: defaults.outputFormat || configDefaults.defaultOutputFormat,
|
|
subjectDetection:
|
|
subjectDetection === "Foreground" || subjectDetection === "Background"
|
|
? subjectDetection
|
|
: defaults.subjectDetection,
|
|
faceEnhancement:
|
|
typeof raw.faceEnhancement === "boolean"
|
|
? raw.faceEnhancement
|
|
: defaults.faceEnhancement,
|
|
faceEnhancementCreativity: clampFloat(
|
|
raw.faceEnhancementCreativity,
|
|
0,
|
|
1,
|
|
defaults.faceEnhancementCreativity,
|
|
),
|
|
faceEnhancementStrength: clampFloat(
|
|
raw.faceEnhancementStrength,
|
|
0,
|
|
1,
|
|
defaults.faceEnhancementStrength,
|
|
),
|
|
cropToFill:
|
|
typeof raw.cropToFill === "boolean" ? raw.cropToFill : defaults.cropToFill,
|
|
model:
|
|
model === "Recovery" ||
|
|
model === "Recovery V2" ||
|
|
model === "Reimagine" ||
|
|
model === "Redefine"
|
|
? model
|
|
: defaults.model || configDefaults.defaultModel,
|
|
upscalingActivated:
|
|
typeof raw.upscalingActivated === "boolean"
|
|
? raw.upscalingActivated
|
|
: defaults.upscalingActivated,
|
|
imageScalingFactor: clampFloat(
|
|
raw.imageScalingFactor,
|
|
1,
|
|
32,
|
|
defaults.imageScalingFactor,
|
|
),
|
|
creativity: clampInteger(raw.creativity, 1, 6, defaults.creativity),
|
|
texture: clampInteger(raw.texture, 1, 5, defaults.texture),
|
|
sharpen: clampFloat(raw.sharpen, 0, 1, defaults.sharpen),
|
|
denoise: clampFloat(raw.denoise, 0, 1, defaults.denoise),
|
|
detail: clampFloat(raw.detail, 0, 1, defaults.detail),
|
|
};
|
|
}
|
|
|
|
export function defaultEnhanceProKreaConfig(): EnhanceProKreaConfig {
|
|
return {
|
|
enabled: false,
|
|
providerName: ENHANCE_PRO_KREA_DEFAULT_PROVIDER_NAME,
|
|
baseUrl: ENHANCE_PRO_KREA_DEFAULT_BASE_URL,
|
|
apiKey: "",
|
|
defaultModel: ENHANCE_PRO_KREA_DEFAULT_MODEL,
|
|
defaultOutputFormat: ENHANCE_PRO_KREA_DEFAULT_OUTPUT_FORMAT,
|
|
allowBatchSize: true,
|
|
maxBatchSize: 4,
|
|
maxOutputEdge: 4096,
|
|
enableAdvancedControls: true,
|
|
presets: defaultEnhanceProPresets(),
|
|
};
|
|
}
|
|
|
|
export function normalizeEnhanceProKreaConfig(
|
|
value: unknown,
|
|
apiKeyFallback = ""
|
|
): EnhanceProKreaConfig {
|
|
const defaults = defaultEnhanceProKreaConfig();
|
|
const raw =
|
|
value && typeof value === "object" && !Array.isArray(value)
|
|
? (value as Record<string, unknown>)
|
|
: {};
|
|
|
|
const defaultModel = String(raw.defaultModel || "").trim();
|
|
const defaultOutputFormat = String(raw.defaultOutputFormat || "").trim();
|
|
const normalizedPresets = Array.isArray(raw.presets)
|
|
? raw.presets.map((item, index) =>
|
|
normalizeEnhanceProPreset(item, index, {
|
|
defaultModel: defaults.defaultModel,
|
|
defaultOutputFormat: defaults.defaultOutputFormat,
|
|
maxBatchSize: clampInteger(raw.maxBatchSize, 1, 4, defaults.maxBatchSize),
|
|
}),
|
|
)
|
|
: defaultEnhanceProPresets();
|
|
const presetWithDefault = normalizedPresets.some((preset) => preset.isDefault)
|
|
? normalizedPresets
|
|
: normalizedPresets.map((preset, index) => ({
|
|
...preset,
|
|
isDefault: index === 0,
|
|
}));
|
|
|
|
return {
|
|
enabled: raw.enabled === true,
|
|
providerName:
|
|
String(raw.providerName || "").trim() || defaults.providerName,
|
|
baseUrl:
|
|
String(raw.baseUrl || "")
|
|
.trim()
|
|
.replace(TRAILING_SLASHES_REGEX, "") || defaults.baseUrl,
|
|
apiKey: String(raw.apiKey || "").trim() || apiKeyFallback,
|
|
defaultModel:
|
|
defaultModel === "Recovery" ||
|
|
defaultModel === "Recovery V2" ||
|
|
defaultModel === "Reimagine"
|
|
? defaultModel
|
|
: defaults.defaultModel,
|
|
defaultOutputFormat:
|
|
defaultOutputFormat === "png" ||
|
|
defaultOutputFormat === "webp" ||
|
|
defaultOutputFormat === "jpg"
|
|
? defaultOutputFormat
|
|
: defaults.defaultOutputFormat,
|
|
allowBatchSize: raw.allowBatchSize !== false,
|
|
maxBatchSize: clampInteger(raw.maxBatchSize, 1, 4, defaults.maxBatchSize),
|
|
maxOutputEdge: clampInteger(
|
|
raw.maxOutputEdge,
|
|
512,
|
|
16_384,
|
|
defaults.maxOutputEdge,
|
|
),
|
|
enableAdvancedControls: raw.enableAdvancedControls !== false,
|
|
presets: presetWithDefault,
|
|
};
|
|
}
|
|
|
|
export function sanitizeEnhanceProKreaConfigForClient(
|
|
config: EnhanceProKreaConfig,
|
|
) {
|
|
return {
|
|
enabled: config.enabled,
|
|
providerName: config.providerName,
|
|
baseUrl: config.baseUrl,
|
|
defaultModel: config.defaultModel,
|
|
defaultOutputFormat: config.defaultOutputFormat,
|
|
allowBatchSize: config.allowBatchSize,
|
|
maxBatchSize: config.maxBatchSize,
|
|
maxOutputEdge: config.maxOutputEdge,
|
|
enableAdvancedControls: config.enableAdvancedControls,
|
|
presets: config.presets,
|
|
};
|
|
}
|