From de681e7b9f608eb7971fbd0639f97256465f91c4 Mon Sep 17 00:00:00 2001 From: kaj <40004347+KAJdev@users.noreply.github.com> Date: Tue, 23 May 2023 14:26:18 -0800 Subject: [PATCH] refactor --- .../stablestudio-plugin-webui/src/index.ts | 792 ++++++++---------- .../stablestudio-plugin-webui/src/utils.ts | 141 ++++ 2 files changed, 471 insertions(+), 462 deletions(-) create mode 100644 packages/stablestudio-plugin-webui/src/utils.ts diff --git a/packages/stablestudio-plugin-webui/src/index.ts b/packages/stablestudio-plugin-webui/src/index.ts index a7c80e3..7dba257 100644 --- a/packages/stablestudio-plugin-webui/src/index.ts +++ b/packages/stablestudio-plugin-webui/src/index.ts @@ -1,504 +1,372 @@ import * as StableStudio from "@stability/stablestudio-plugin"; +import { StableDiffusionImage } from "@stability/stablestudio-plugin"; -function base64ToBlob(base64: string, contentType = '', sliceSize = 512): Promise { - return fetch(`data:${contentType};base64,${base64}`).then(res => res.blob()); -} +import { + base64ToBlob, + constructPayload, + fetchOptions, + setOptions, + testForHistoryPlugin, +} from "./utils"; -function blobToBase64(blob: Blob): Promise { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - - reader.onloadend = () => resolve(reader.result as string); - reader.onerror = reject; - - reader.readAsDataURL(blob); - }); -} +const manifest = { + name: "Stable Diffusion Webui", + author: "Terry Jia", + link: "https://github.com/jtydhr88", + icon: `${window.location.origin}/DummyImage.png`, + version: "0.0.0", + license: "MIT", + description: "Stable Diffusion Webui Plugin", +}; const webuiUpscalers = [ - { - label: "None", - value: "None" - }, - { - label: "Lanczos", - value: "Lanczos" - }, - { - label: "Nearest", - value: "Nearest" - }, - { - label: "ESRGAN_4x", - value: "ESRGAN_4x" - }, - { - label: "LDSR", - value: "LDSR" - }, - { - label: "R-ESRGAN 4x+", - value: "R-ESRGAN 4x+" - }, - { - label: "R-ESRGAN 4x+ Anime6B", - value: "R-ESRGAN 4x+ Anime6B" - }, - { - label: "ScuNET GAN", - value: "ScuNET GAN" - }, - { - label: "ScuNET PSNR", - value: "ScuNET PSNR" - }, - { - label: "SwinIR_4x", - value: "SwinIR_4x" - } + { + label: "None", + value: "None", + }, + { + label: "Lanczos", + value: "Lanczos", + }, + { + label: "Nearest", + value: "Nearest", + }, + { + label: "ESRGAN_4x", + value: "ESRGAN_4x", + }, + { + label: "LDSR", + value: "LDSR", + }, + { + label: "R-ESRGAN 4x+", + value: "R-ESRGAN 4x+", + }, + { + label: "R-ESRGAN 4x+ Anime6B", + value: "R-ESRGAN 4x+ Anime6B", + }, + { + label: "ScuNET GAN", + value: "ScuNET GAN", + }, + { + label: "ScuNET PSNR", + value: "ScuNET PSNR", + }, + { + label: "SwinIR_4x", + value: "SwinIR_4x", + }, ]; const getNumber = (strValue: string | null, defaultValue: number) => { - let retValue = defaultValue; + let retValue = defaultValue; - if (strValue) { - retValue = Number(strValue); - } + if (strValue) { + retValue = Number(strValue); + } - return retValue; -} + return retValue; +}; const getStableDiffusionDefaultCount = () => 4; export const createPlugin = StableStudio.createPlugin<{ - imagesGeneratedSoFar: number; - settings: { - webuiHostUrl: StableStudio.PluginSettingString; - historyImagesCount: StableStudio.PluginSettingNumber; - upscaler1: StableStudio.PluginSettingString; - }; -}>(({set, get}) => { - const webuiLoad = (webuiHostUrl?: string): Pick< - StableStudio.Plugin, - | "createStableDiffusionImages" - | "getStatus" - | "getStableDiffusionModels" - | "getStableDiffusionSamplers" - | "getStableDiffusionDefaultCount" - | "getStableDiffusionDefaultInput" - | "getStableDiffusionExistingImages" - > => { - if (webuiHostUrl === "") { - webuiHostUrl = "http://127.0.0.1:7861"; + settings: { + baseUrl: StableStudio.PluginSettingString; + upscaler: StableStudio.PluginSettingString; + historyImagesCount: StableStudio.PluginSettingNumber; + }; +}>(({ set, get }) => { + const webuiLoad = ( + webuiHostUrl?: string + ): Pick< + StableStudio.Plugin, + | "createStableDiffusionImages" + | "getStatus" + | "getStableDiffusionModels" + | "getStableDiffusionSamplers" + | "getStableDiffusionDefaultCount" + | "getStableDiffusionDefaultInput" + | "getStableDiffusionExistingImages" + > => { + webuiHostUrl = webuiHostUrl ?? "http://127.0.0.1:7861"; + + return { + createStableDiffusionImages: async (options) => { + if (!options) { + throw new Error("options is required"); + } + + // fetch the current webui options (model/sampler/etc) + const webUIOptions = await fetchOptions(webuiHostUrl); + + const { model, sampler, initialImage } = options?.input ?? {}; + options.count = options?.count ?? getStableDiffusionDefaultCount(); + + // quickly save the sampler and model name to local storage + if (sampler?.name) { + localStorage.setItem("webui-saved-sampler", sampler.name); + } + + if (model) { + localStorage.setItem("webui-saved-model", model); + } + + // little hacky until StableStudio is better with upscaling + const isUpscale = + options?.input?.initialImage?.weight === 1 && + model === "esrgan-v1-x2plus"; + + // WebUI doesn't have the right model loaded, switch the model + if (model && model !== webUIOptions.sd_model_checkpoint && !isUpscale) { + localStorage.setItem("webui-saved-model", model); + const modelResponse = await setOptions(webuiHostUrl, { + sd_model_checkpoint: model, + }); + + if (modelResponse.ok) { + console.log("applied model"); + } + } + + // Construct payload for webui + const data = await constructPayload( + options, + isUpscale, + get().settings.upscaler.value + ); + + // Send payload to webui + const response = await fetch( + initialImage + ? isUpscale + ? `${webuiHostUrl}/sdapi/v1/extra-single-image` + : `${webuiHostUrl}/sdapi/v1/img2img` + : `${webuiHostUrl}/sdapi/v1/txt2img`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), + } + ); + + const responseData = await response.json(); + + const images = []; + const createdAt = new Date(); + + if (isUpscale) { + // Upscaling only returns one image + const blob = await base64ToBlob(responseData.image, "image/jpeg"); + + const image = { + id: `${Math.random() * 10000000}`, + createdAt: createdAt, + blob: blob, + input: { + model: model ?? "", + }, + }; + + images.push(image); + } else { + // Image generation returns an array of images + const startIndex = + responseData.images.length > data.batch_size ? 1 : 0; + + for (let i = startIndex; i < responseData.images.length; i++) { + const blob = await base64ToBlob( + responseData.images[i], + "image/jpeg" + ); + + const image: StableDiffusionImage = { + id: `${Math.random() * 10000000}`, + createdAt, + blob, + input: { + prompts: options?.input?.prompts ?? [], + steps: options?.input?.steps ?? 0, + seed: responseData.images[i].seed, + model: model ?? "", + width: options?.input?.width ?? 512, + height: options?.input?.height ?? 512, + cfgScale: options?.input?.cfgScale ?? 7, + sampler: sampler ?? { id: "", name: "" }, + }, + }; + + images.push(image); + } } return { - createStableDiffusionImages: async (options) => { - console.log(options); + id: `${Math.random() * 10000000}`, + images: images, + }; + }, - const optionsUrl = webuiHostUrl + '/sdapi/v1/options'; + getStableDiffusionModels: async () => { + const response = await fetch(`${webuiHostUrl}/sdapi/v1/sd-models`); + const responseData = await response.json(); - const optionsResponse = await fetch(optionsUrl, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - } - }); + return responseData.map((model: any) => ({ + id: model.title, + name: model.model_name, + })); + }, - const webUIOptions = await optionsResponse.json(); + getStatus: async () => { + const optionsResponse = await fetch(`${webuiHostUrl}/sdapi/v1/options`); + const hasWebuiHistoryPlugin = await testForHistoryPlugin( + `${webuiHostUrl}` + ); - const model = options?.input?.model; + return optionsResponse.ok + ? { + indicator: hasWebuiHistoryPlugin ? "success" : "info", + text: `Ready ${ + hasWebuiHistoryPlugin ? "with" : "without" + } history plugin`, + } + : { + indicator: "error", + text: "unable to connect webui on " + webuiHostUrl, + }; + }, + }; + }; - let url = webuiHostUrl + '/sdapi/v1/txt2img'; + const webuiHostUrl = + localStorage.getItem("webui-host-url") ?? "http://127.0.0.1:7861"; - let isUpscale = false; + return { + ...webuiLoad(webuiHostUrl), - if (options?.input?.initialImage) { - url = webuiHostUrl + '/sdapi/v1/img2img'; + getStableDiffusionDefaultCount: () => 4, - // use this condition to assume the user wants to upscale - if ((options?.input?.initialImage?.weight === 1) && (model === "esrgan-v1-x2plus")) { - isUpscale = true; + getStableDiffusionDefaultInput: () => { + return { + steps: 20, + sampler: { + id: localStorage.getItem("webui-saved-sampler") ?? "", + name: localStorage.getItem("webui-saved-sampler") ?? "", + }, + model: localStorage.getItem("webui-saved-model") ?? "", + }; + }, - url = webuiHostUrl + '/sdapi/v1/extra-single-image'; - } - } + getStableDiffusionSamplers: async () => { + const response = await fetch(`${webuiHostUrl}/sdapi/v1/samplers`); + const responseData = await response.json(); - if (model && model !== webUIOptions["sd_model_checkpoint"] && !isUpscale) { - console.log("applying model"); + return responseData.map((sampler: any) => ({ + id: sampler.name, + name: sampler.name, + })); + }, - localStorage.setItem("model", model) - - const modelData = { - "sd_model_checkpoint": model - } - - const modelResponse = await fetch(optionsUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(modelData), - }); - - if (modelResponse.ok) { - console.log("applied model"); - } - } - - const sampler = options?.input?.sampler?.name; - - if (sampler) { - localStorage.setItem("sampler", sampler) - } - - const input = { - ...options?.input, - }; - - const prompts = input.prompts; - - const prompt = prompts?.at(0)?.text ?? ""; - const negativePrompt = prompts?.at(1)?.text ?? ""; - let seed = options?.input?.seed ?? -1; - - if (seed === 0) { - seed = -1; - } - - interface dataObject { - [key: string]: any; - } - - let data: dataObject = {}; - - - if (!isUpscale) { - data["seed"] = seed; - data["sampler_name"] = options?.input?.sampler?.name ?? ""; - data["sampler_index"] = options?.input?.sampler?.name ?? ""; - data["prompt"] = prompt; - data["negative_prompt"] = negativePrompt; - data["steps"] = options?.input?.steps ?? 20; - data["batch_size"] = options?.count ?? getStableDiffusionDefaultCount(); - - data["width"] = options?.input?.width ?? 512; - - localStorage.setItem("width", data["width"]); - - data["height"] = options?.input?.height ?? 512; - - localStorage.setItem("height", data["height"]); - - data["save_images"] = true; - } else { - data["upscaling_resize_w"] = options?.input?.width ?? 512; - - data["upscaling_resize_h"] = options?.input?.height ?? 512; - - data["upscaler_1"] = get().settings.upscaler1.value; - } - - if (options?.input?.initialImage?.weight && !isUpscale) { - data["denoising_strength"] = 1 - options.input.initialImage.weight; - } - - if (options?.input?.cfgScale) { - data["cfg_scale"] = options?.input?.cfgScale; - - localStorage.setItem("cfgScale", data["cfg_scale"]) - } - - if (options?.input?.initialImage?.blob) { - const initImgB64 = await blobToBase64(options?.input?.initialImage?.blob); - - if (isUpscale) { - data["image"] = initImgB64.split(",")[1]; - } else { - data["init_images"] = [initImgB64.split(",")[1]]; - } - } - - if (options?.input?.maskImage?.blob) { - const maskImgB64 = await blobToBase64(options?.input?.maskImage?.blob); - - data["mask"] = maskImgB64.split(",")[1]; - - data["inpainting_mask_invert"] = 1 // Mask mode - data["inpainting_fill"] = 1 // Masked content - data["inpaint_full_res"] = false // Inpaint area - } - - console.log(data); - - const response = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(data), - }); - - const responseData = await response.json(); - - console.log(responseData) - - const images = []; - const createdAt = new Date(); - - if (isUpscale) { - const blob = await base64ToBlob(responseData.image, 'image/jpeg'); - - const image = { - id: `${Math.random() * 10000000}`, - createdAt: createdAt, - blob: blob, - input: { - model: localStorage.getItem("model") - } - } - - images.push(image); - } else { - const generatedImagesLength = responseData.images.length; - - let startIndex = 0; - - if (generatedImagesLength > data["batch_size"]) { - startIndex = 1; - } - - for (let i = startIndex; i < responseData.images.length; i++) { - const blob = await base64ToBlob(responseData.images[i], 'image/jpeg'); - - const image = { - id: `${Math.random() * 10000000}`, - createdAt, - blob - } - - images.push(image) - } - - set(({imagesGeneratedSoFar}) => ({ - imagesGeneratedSoFar: imagesGeneratedSoFar + data["batch_size"], - })); - } - - return { - id: `${Math.random() * 10000000}`, - images: images - }; - }, - - getStableDiffusionModels: async () => { - const url = webuiHostUrl + '/sdapi/v1/sd-models'; - - const response = await fetch(url, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - }); - - const responseData = await response.json(); - - const models = []; - - for (let i = 0; i < responseData.length; i++) { - const model = { - id: responseData[i].title, - name: responseData[i].model_name - }; - - models.push(model); - } - - return models; - }, - - getStatus: async () => { - const optionsUrl = webuiHostUrl + '/sdapi/v1/options'; - - const optionsResponse = await fetch(optionsUrl, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - } - }); - - return optionsResponse.ok ? { - indicator: "success", - text: "Ready", - } : { - indicator: "error", - text: "unable to connect webui on " + webuiHostUrl - }; - }, + getStableDiffusionExistingImages: async () => { + const existingImagesResponse = await fetch( + `${webuiHostUrl}/StableStudio/get-generated-images`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + limit: get().settings.historyImagesCount.value, + }), } - } + ); - let webuiHostUrl = localStorage.getItem("webui-host-url"); + if (!existingImagesResponse.ok) { + console.warn("unable to get existing data from webui"); + } - if (webuiHostUrl === "" || !webuiHostUrl) { - webuiHostUrl = "http://127.0.0.1:7861"; - } + const responseData = await existingImagesResponse.json(); - return { - ...webuiLoad(webuiHostUrl), + return [ + { + id: `${Math.random() * 10000000}`, + images: responseData.map(async (image: any) => { + const blob = await base64ToBlob(image.content, "image/jpeg"); - getStableDiffusionDefaultCount: () => 4, + const timestampInSeconds = image.create_date; + const timestampInMilliseconds = timestampInSeconds * 1000; + const createdAt = new Date(timestampInMilliseconds); - getStableDiffusionDefaultInput: () => { return { - steps: 20, - sampler: { - id: localStorage.getItem("sampler") ?? "", - name: localStorage.getItem("sampler") ?? "" - }, - model: localStorage.getItem("model") ?? "" - } + id: image.image_name, + createdAt, + blob, + input: { + prompts: [], + style: "", + steps: -1, + seed: image.seed ?? -1, + model: "", + width: image.width, + height: image.height, + }, + }; + }), }, + ]; + }, - getStableDiffusionSamplers: async () => { - const url = webuiHostUrl + '/sdapi/v1/samplers'; - - const response = await fetch(url, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - }); - - const responseData = await response.json(); - - const samplers = []; - - for (let i = 0; i < responseData.length; i++) { - const sampler = { - id: responseData[i].name, - name: responseData[i].name - }; - - samplers.push(sampler); - } - - return samplers; - }, - - getStableDiffusionExistingImages: async () => { - const existingImagesUrl = webuiHostUrl + '/StableStudio/get-generated-images'; - - const data = { - limit: get().settings.historyImagesCount.value - } - - const existingImagesResponse = await fetch(existingImagesUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(data), - }); - - if (!existingImagesResponse.ok) { - console.warn("unable to get existing data from webui"); - } - - const responseData = await existingImagesResponse.json(); - - const images = []; - - for (let i = 0; i < responseData.length; i++) { - const blob = await base64ToBlob(responseData[i].content, 'image/jpeg'); - - let timestampInSeconds = responseData[i].create_date; - let timestampInMilliseconds = timestampInSeconds * 1000; - let createdAt = new Date(timestampInMilliseconds); - - const stableDiffusionImage = { - id: responseData[i].image_name, - createdAt: createdAt, - blob: blob, - input: { - prompts: [], - style: "", - steps: -1, - seed: responseData[i].seed, - model: "", - width: responseData[i].width, - height: responseData[i].height - } - } - - images.push(stableDiffusionImage) - } - - const stableDiffusionImages = { - id: `${Math.random() * 10000000}`, - images: images - } - - console.log(responseData); - - return [stableDiffusionImages] - }, - - imagesGeneratedSoFar: 0, + settings: { + baseUrl: { + type: "string", + title: "WebUI Host URL", + description: + "The URL of the WebUI host. This is usually http://127.0.0.1:7861", + placeholder: "http://127.0.0.1:7861", + value: localStorage.getItem("webui-host-url") ?? "", + }, + upscaler: { + type: "string", + title: "Upscaler 1", + options: webuiUpscalers, + value: localStorage.getItem("upscaler1") ?? webuiUpscalers[0].value, + description: + "Select the upscaler that is used when downloading images at more than 1x size.", + }, + historyImagesCount: { + type: "number", + title: "History image count", + description: "How many images do you get from webui locally?", + min: 0, + max: 50, + step: 1, + variant: "slider", + value: getNumber(localStorage.getItem("historyImagesCount"), 20), + }, + }, + setSetting: (key, value) => { + set(({ settings }) => ({ settings: { - webuiHostUrl: { - type: "string", - title: "Webui Host URL", - description: - "put your webui api url here, the default value is http://127.0.0.1:7861", - placeholder: "http://127.0.0.1:7861", - value: localStorage.getItem("webui-host-url") ?? "", - }, - historyImagesCount: { - type: "number", - title: "History image count", - description: - "How many images do you get from webui locally?", - min: 0, - max: 50, - step: 1, - variant: "slider", - value: getNumber(localStorage.getItem("historyImagesCount"), 20), - }, - upscaler1: { - type: "string", - title: "Upscaler 1", - options: webuiUpscalers, - value: localStorage.getItem("upscaler1") ?? "None", - } + ...settings, + [key]: { ...settings[key], value: value as string }, }, + })); - setSetting: (key, value) => { - set(({settings}) => ({ - settings: { - ...settings, - [key]: {...settings[key], value: value as string}, - }, - })); + if (key === "baseUrl" && typeof value === "string") { + localStorage.setItem("webui-host-url", value); + set((plugin) => ({ ...plugin, ...webuiLoad(value) })); + } else if (key === "upscaler" && typeof value === "string") { + localStorage.setItem("upscaler1", value); + } else if (key === "historyImagesCount" && typeof value === "number") { + localStorage.setItem("historyImagesCount", value.toString()); + } + }, - if (key === "webuiHostUrl" && typeof value === "string") { - localStorage.setItem("webui-host-url", value); - set((plugin) => ({...plugin, ...webuiLoad(value)})); - } else if (key === "upscaler1" && typeof value === "string") { - localStorage.setItem("upscaler1", value); - } else if (key === "historyImagesCount" && typeof value === "number") { - localStorage.setItem("historyImagesCount", value.toString()); - } - }, - - manifest: { - name: "Stable Diffusion webui Plugin", - author: "Terry Jia", - link: "https://github.com/jtydhr88", - icon: `${window.location.origin}/DummyImage.png`, - version: "0.0.1", - license: "MIT", - description: "An plugin for Stable Diffusion webui", - }, - } + manifest, + }; }); diff --git a/packages/stablestudio-plugin-webui/src/utils.ts b/packages/stablestudio-plugin-webui/src/utils.ts new file mode 100644 index 0000000..b95ca91 --- /dev/null +++ b/packages/stablestudio-plugin-webui/src/utils.ts @@ -0,0 +1,141 @@ +import { StableDiffusionInput } from "@stability/stablestudio-plugin"; + +export function base64ToBlob(base64: string, contentType = ""): Promise { + return fetch(`data:${contentType};base64,${base64}`).then((res) => + res.blob() + ); +} + +export function blobToBase64(blob: Blob): Promise { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + + reader.onloadend = () => resolve(reader.result as string); + reader.onerror = reject; + + reader.readAsDataURL(blob); + }); +} + +export async function fetchOptions(baseUrl: string | undefined) { + const optionsResponse = await fetch(`${baseUrl}/sdapi/v1/options`, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + + return await optionsResponse.json(); +} + +export async function setOptions(baseUrl: string | undefined, options: any) { + const optionsResponse = await fetch(`${baseUrl}/sdapi/v1/options`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(options), + }); + + return await optionsResponse.json(); +} + +export async function testForHistoryPlugin(webuiHostUrl: string) { + // timeout after 3 seconds + const finished = Promise.race([ + fetch(`${webuiHostUrl}/StableStudio/get-generated-images`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + limit: 1, + }), + }), + new Promise((_, reject) => + setTimeout(() => reject(new Error("Request timed out")), 1000) + ), + ]); + + try { + await finished; + return (finished as any).ok; + } catch (error) { + return false; + } +} + +export async function constructPayload( + options: { + input?: StableDiffusionInput | undefined; + count?: number | undefined; + }, + isUpscale = false, + upscaler: string | undefined +) { + const { sampler, prompts, initialImage, maskImage, width, height, steps } = + options?.input ?? {}; + + // Construct payload + const data: any = { + seed: options?.input?.seed === 0 ? -1 : options?.input?.seed, + cfgScale: options?.input?.cfgScale ?? 7, + }; + + if (isUpscale) { + /* + Upscaling values + */ + + data.upscaling_resize_w = width ?? 512; + data.upscaling_resize_h = height ?? 512; + data.upscaler_1 = upscaler; + } else { + /* + regular image generation values + */ + + data.width = width ?? 512; + data.height = height ?? 512; + + data.sampler_name = sampler?.name ?? ""; + data.sampler_index = sampler?.name ?? ""; + + data.prompt = + prompts?.find((p) => (p.text && (p.weight ?? 0) > 0) ?? 0 > 0)?.text ?? + ""; + data.negative_prompt = + prompts?.find((p) => (p.text && (p.weight ?? 0) < 0) ?? 0 < 0)?.text ?? + ""; + + data.steps = steps ?? 20; + data.batch_size = options?.count; + data.save_images = true; + } + + if (initialImage?.weight && !isUpscale) { + data.denoising_strength = 1 - initialImage.weight; + } + + if (initialImage?.blob) { + const initImgB64 = await blobToBase64(initialImage?.blob); + + if (isUpscale) { + data.image = initImgB64.split(",")[1]; + } else { + data.init_images = [initImgB64.split(",")[1]]; + } + } + + if (maskImage?.blob) { + const maskImgB64 = await blobToBase64(maskImage?.blob); + + data.mask = maskImgB64.split(",")[1]; + + data.inpainting_mask_invert = 1; // Mask mode + data.inpainting_fill = 1; // Masked content + data.inpaint_full_res = false; // Inpaint area + } + + return data; +}