Skip to content

Commit 552ddb4

Browse files
authored
Merge pull request #24 from jtydhr88/webui-plugin
Support local inference via the A1111 plugin
2 parents 9f35bf3 + 3dc0e66 commit 552ddb4

File tree

9 files changed

+674
-13
lines changed

9 files changed

+674
-13
lines changed

README.md

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,16 @@
44
<img src="./misc/GenerateScreenshot.png" style="width: 400px; max-width: 600px; flex-grow: 1;" />
55
<img src="./misc/EditScreenshot.png" style="width: 400px; max-width: 600px; flex-grow: 1;" />
66

7-
<h3>
8-
<span>👋 Welcome to StableStudio, the open-source version of <a href="https://dreamstudio.ai" target="_blank">DreamStudio</a></span>
7+
<h3>👋 Welcome to StableStudio, the open-source version of <a href="https://dreamstudio.ai" target="_blank">DreamStudio</a>!</h3>
8+
9+
**🗺 Contents – [🚀 Quick Start](#quick-start) · [ℹ️ About](#about) · [🙋 FAQ](#faq) · [🧑‍💻 Contributing](#contributing)**
10+
11+
**📚 Documentation – [🎨 UI](./packages/stablestudio-ui/README.md) · [🔌 Plugins](./packages/stablestudio-plugin/README.md) · <a href="https://platform.stability.ai" target="_blank">⚡️ platform.stability.ai</a>**
12+
13+
**🔗 Links – <a href="https://discord.com/channels/1002292111942635562/1108055793674227782" target="_blank">🎮 Discord</a> · <a href="https://dreamstudio.ai" target="_blank">🌈 DreamStudio</a> · <a href="https://github.com/Stability-AI/StableStudio/issues">🛟 Bugs & Support</a> · <a href="https://github.com/Stability-AI/StableStudio/discussions">💬 Discussion</a>**
14+
915
<br />
1016
<br />
11-
<span>[ <a href="./packages/stablestudio-ui/README.md">🎨 UI README</a> ]</span>
12-
<span>[ <a href="./packages/stablestudio-plugin/README.md" href="./packages/stablestudio-ui/README.md">🔌 Plugins README</a> ]</span>
13-
<span>[ <a href="https://discord.gg/stablediffusion" target="_blank">🎮 Discord</a> ]</span>
14-
<span>[ <a href="https://github.com/Stability-AI/StableStudio/issues">🛟 Bugs & Support</a> ]</span>
15-
<span>[ <a href="https://github.com/Stability-AI/StableStudio/discussions">💬 Discussion</a> ]</span>
16-
</h3>
17-
18-
<hr />
1917

2018
</div>
2119

misc/Electric1111.png

607 KB
Loading

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"stablestudio-plugin-webui": "yarn workspace @stability/stablestudio-plugin-webui",
1616
"stablestudio-ui": "yarn workspace @stability/stablestudio-ui",
1717
"dev:use-example-plugin": "cross-env VITE_USE_EXAMPLE_PLUGIN=true yarn dev",
18+
"dev:use-webui-plugin": "cross-env VITE_USE_WEBUI_PLUGIN=true yarn dev",
1819
"dev": "yarn workspaces foreach --all --interlaced --verbose --parallel --jobs unlimited run dev",
1920
"build": "yarn workspaces foreach --all --interlaced --verbose --jobs unlimited run build",
2021
"clean": "yarn workspaces foreach --all --interlaced --verbose --parallel --jobs unlimited run clean && rimraf node_modules"
Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,67 @@
1-
Soon™️
1+
<div align="center">
2+
3+
# 🔌 [`stable-diffusion-webui`](https://github.com/AUTOMATIC1111/stable-diffusion-webui) Plugin
4+
5+
**🗺 Contents – [ℹ️ About](#about) · [⚙️ Usage](#usage) · [⭐️ Features](#features)**
6+
7+
**[⬆️ Top-Level README](../../README.md)**
8+
9+
![Electric1111](../../misc/Electric1111.png)
10+
11+
</div>
12+
13+
# <a id="about" href="#about">ℹ️ About</a>
14+
15+
This plugin enables StableStudio to run using [`stable-diffusion-webui`](https://github.com/AUTOMATIC1111/stable-diffusion-webui), which means you can generate images entirely on your own machine!
16+
17+
Thanks goes to [Terry Jia](https://github.com/jtydhr88) for the original work on this plugin.
18+
19+
# <a id="usage" href="#usage">⚙️ Usage</a>
20+
21+
1. First, you'll need to configure your local installation of `stable-diffusion-webui` to run without the UI and with CORS enabled.
22+
23+
**Windows**
24+
25+
Edit the command line arguments within `webui-user.bat`:
26+
27+
```
28+
set COMMANDLINE_ARGS=--nowebui --cors-allow-origins=http://localhost:3000
29+
```
30+
31+
**Mac**
32+
33+
Edit the command line arguments within `webui-macos-env.sh`:
34+
35+
```
36+
export COMMANDLINE_ARGS="--nowebui --cors-allow-origins=http://localhost:3000"
37+
```
38+
39+
2. Start `stable-diffusion-webui` and look for `INFO: Uvicorn running on http://127.0.0.1:7861`.
40+
41+
You can make sure everything is running correctly by checking to see if [`http://127.0.0.1:7861/docs`](http://127.0.0.1:7861/docs) displays API documentation.
42+
43+
3. Within your installation of StableStudio, run `yarn dev:use-webui-plugin`.
44+
45+
_**That's it!**_ 🎉 You should now be able to generate images using your local machine.
46+
47+
## <a id="image-history" href="#image-history">💾 Image History</a>
48+
49+
To persist your image history, you'll need to install the [`sd-webui-StableStudio`](https://github.com/jtydhr88/sd-webui-StableStudio) extension for `stable-diffusion-webui`.
50+
51+
> 🛑 Be wary installing third-party extensions for `stable-diffusion-webui`, it's always a good idea to check before running untrusted code.
52+
53+
# <a id="features" href="#features">⭐️ Features</a>
54+
55+
Missing something? Please [let us know](https://github.com/Stability-AI/StableStudio/issues/new/choose)!
56+
57+
- [x] Text-to-image
58+
- [x] Image-to-image
59+
- [x] Basic features (prompt, negative prompt, steps, batch size, image size)
60+
- [x] Model selection
61+
- [x] Sampler selection
62+
- [x] Masking, in-painting, and out-painting
63+
- [x] Settings storage
64+
- [x] Accurate plugin status
65+
- [x] [Loading existing images]("#image-history)
66+
- [x] Upscaling
67+
- [ ] Lora support
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
import { StableDiffusionInput } from "@stability/stablestudio-plugin";
2+
3+
export function base64ToBlob(base64: string, contentType = ""): Promise<Blob> {
4+
return fetch(`data:${contentType};base64,${base64}`).then((res) =>
5+
res.blob()
6+
);
7+
}
8+
9+
export function blobToBase64(blob: Blob): Promise<string> {
10+
return new Promise((resolve, reject) => {
11+
const reader = new FileReader();
12+
13+
reader.onloadend = () => resolve(reader.result as string);
14+
reader.onerror = reject;
15+
16+
reader.readAsDataURL(blob);
17+
});
18+
}
19+
20+
export async function fetchOptions(baseUrl: string | undefined) {
21+
const optionsResponse = await fetch(`${baseUrl}/sdapi/v1/options`, {
22+
method: "GET",
23+
headers: {
24+
"Content-Type": "application/json",
25+
},
26+
});
27+
28+
return await optionsResponse.json();
29+
}
30+
31+
export async function setOptions(baseUrl: string | undefined, options: any) {
32+
const optionsResponse = await fetch(`${baseUrl}/sdapi/v1/options`, {
33+
method: "POST",
34+
headers: {
35+
"Content-Type": "application/json",
36+
},
37+
body: JSON.stringify(options),
38+
});
39+
40+
return await optionsResponse.json();
41+
}
42+
43+
export async function getImageInfo(
44+
baseUrl: string | undefined,
45+
base64image: any
46+
) {
47+
const imageInfoResponse = await fetch(`${baseUrl}/sdapi/v1/png-info`, {
48+
method: "POST",
49+
headers: {
50+
"Content-Type": "application/json",
51+
},
52+
body: JSON.stringify({ image: base64image }),
53+
});
54+
55+
const imageInfoJson = await imageInfoResponse.json();
56+
57+
const info = imageInfoJson.info.split("\n");
58+
59+
const data: any = {};
60+
61+
if (info.length === 0) {
62+
return data;
63+
}
64+
65+
data.prompt = info[0];
66+
67+
let detailIndex = 1;
68+
69+
if (info.length === 3) {
70+
data.nagtivePrompt = info[1].split(":")[1].trim();
71+
72+
detailIndex = 2;
73+
}
74+
75+
const details = info[detailIndex].split(",");
76+
77+
details.map((detail: any) => {
78+
const detailInfo = detail.trim().split(":");
79+
80+
data[detailInfo[0]] = detailInfo[1].trim();
81+
});
82+
83+
return data;
84+
}
85+
86+
export async function testForHistoryPlugin(webuiHostUrl: string) {
87+
// timeout after 1 second
88+
const finished = Promise.race([
89+
fetch(`${webuiHostUrl}/StableStudio/get-generated-images`, {
90+
method: "POST",
91+
headers: {
92+
"Content-Type": "application/json",
93+
},
94+
body: JSON.stringify({
95+
limit: 1,
96+
}),
97+
}),
98+
new Promise((_, reject) =>
99+
setTimeout(() => reject(new Error("Request timed out")), 1000)
100+
),
101+
]);
102+
103+
try {
104+
await finished;
105+
return (finished as any).ok;
106+
} catch (error) {
107+
return false;
108+
}
109+
}
110+
111+
export async function constructPayload(
112+
options: {
113+
input?: StableDiffusionInput | undefined;
114+
count?: number | undefined;
115+
},
116+
isUpscale = false,
117+
upscaler: string | undefined
118+
) {
119+
const { sampler, prompts, initialImage, maskImage, width, height, steps } =
120+
options?.input ?? {};
121+
122+
// Construct payload
123+
const data: any = {
124+
seed: options?.input?.seed === 0 ? -1 : options?.input?.seed,
125+
cfgScale: options?.input?.cfgScale ?? 7,
126+
};
127+
128+
if (isUpscale) {
129+
/*
130+
Upscaling values
131+
*/
132+
133+
data.upscaling_resize_w = width ?? 512;
134+
data.upscaling_resize_h = height ?? 512;
135+
data.upscaler_1 = upscaler;
136+
} else {
137+
/*
138+
regular image generation values
139+
*/
140+
141+
data.width = width ?? 512;
142+
data.height = height ?? 512;
143+
144+
data.sampler_name = sampler?.name ?? "";
145+
data.sampler_index = sampler?.name ?? "";
146+
147+
data.prompt =
148+
prompts?.find((p) => (p.text && (p.weight ?? 0) > 0) ?? 0 > 0)?.text ??
149+
"";
150+
data.negative_prompt =
151+
prompts?.find((p) => (p.text && (p.weight ?? 0) < 0) ?? 0 < 0)?.text ??
152+
"";
153+
154+
data.steps = steps ?? 20;
155+
data.batch_size = options?.count;
156+
data.save_images = true;
157+
}
158+
159+
if (initialImage?.weight && !isUpscale) {
160+
data.denoising_strength = 1 - initialImage.weight;
161+
}
162+
163+
if (initialImage?.blob) {
164+
const initImgB64 = await blobToBase64(initialImage?.blob);
165+
166+
if (isUpscale) {
167+
data.image = initImgB64.split(",")[1];
168+
} else {
169+
data.init_images = [initImgB64.split(",")[1]];
170+
}
171+
}
172+
173+
if (maskImage?.blob) {
174+
const maskImgB64 = await blobToBase64(maskImage?.blob);
175+
176+
data.mask = maskImgB64.split(",")[1];
177+
178+
data.inpainting_mask_invert = 1; // Mask mode
179+
data.inpainting_fill = 1; // Masked content
180+
data.inpaint_full_res = false; // Inpaint area
181+
}
182+
183+
return data;
184+
}

0 commit comments

Comments
 (0)