stable-diffusion-api
Version:
API translation for Automatic1111 Stable Diffusion WebUI
507 lines (506 loc) • 26.9 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.StableDiffusionApi = void 0;
const axios_1 = __importDefault(require("axios"));
const string_similarity_1 = __importDefault(require("string-similarity"));
const StableDiffusionResult_1 = __importDefault(require("./StableDiffusionResult"));
const ControlNetApi_1 = require("./ControlNetApi");
const base64_1 = require("../utils/base64");
const createScriptsWithCnUnits = (initScripts, controlNetUnit) => __awaiter(void 0, void 0, void 0, function* () {
const promises = controlNetUnit.map((unit) => __awaiter(void 0, void 0, void 0, function* () { return yield unit.toJson(); }));
const args = yield Promise.all(promises);
const ControlNet = { args };
const scripts = Object.assign(Object.assign({}, initScripts), { ControlNet });
return scripts;
});
/**
* @class StableDiffusionApi
* @classdesc Stable Diffusion API, a translation layer for [Automatic1111's Stable Diffusion API](https://github.com/AUTOMATIC1111/stable-diffusion-webui)
* @param {StableDiffusionApiConfig} config - Configuration object
* @property {StableDiffusionApiConfig} config - Configuration object
* @property {axios.AxiosInstance} api - Axios instance
* @property {ControlNetApi} controlNet - ControlNet API
* @example
* const api = new StableDiffusionApi()
* const result = await api.txt2img({
* prompt: "A computer that has more brain power than a human being",
* batch_size: 2,
* })
*
* // Save the first image
* result.image.toFile("result.png")
*
* // Save all images
* result.images.forEach((image, i) => {
* image.toFile(`result_${i}.png`)
* })
*/
class StableDiffusionApi {
constructor({ host = "127.0.0.1", port = 7860, protocol = "http", timeout = 30000, baseUrl = null, defaultSampler = "Euler a", defaultStepCount = 20, } = {}) {
this.ControlNet = new ControlNetApi_1.ControlNetApi(this);
const baseURL = baseUrl || `${protocol}://${host}${port ? `:${port}` : ""}`;
this.config = {
host,
port,
protocol,
timeout,
baseUrl,
defaultSampler,
defaultStepCount,
};
this.api = axios_1.default.create({
baseURL,
timeout,
headers: {
"Content-Type": "application/json",
},
});
}
/**
* Set the authentication for the axios API
* @param {string} username
* @param {string} password
* @returns {StableDiffusionApi} this StableDiffusionApi instance
*/
setAuth(username, password) {
this.api.defaults.auth = {
username,
password,
};
return this;
}
/**
* Stable Diffusion txt2img call
* @param {Txt2ImgOptions} options
* @returns {Promise<StableDiffusionResult>} ApiResult containing the generated image(s)
* @memberof StableDiffusionApi
* @async
* @example
* const api = new StableDiffusionApi();
* const result = await api.txt2img({
* prompt: "An angry artist that claims that the Stable Diffusion model contains an exact copy of their artwork",
* });
*/
txt2img(options) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16;
return __awaiter(this, void 0, void 0, function* () {
const alwayson_scripts = yield createScriptsWithCnUnits(options.alwayson_scripts, (_a = options.controlnet_units) !== null && _a !== void 0 ? _a : []);
const response = yield this.api.post("/sdapi/v1/txt2img", {
enable_hr: (_b = options.enable_hr) !== null && _b !== void 0 ? _b : false,
hr_scale: (_c = options.hr_scale) !== null && _c !== void 0 ? _c : 2,
hr_upscaler: (_d = options.hr_upscaler) !== null && _d !== void 0 ? _d : "Latent",
hr_second_pass_steps: (_e = options.hr_second_pass_steps) !== null && _e !== void 0 ? _e : 0,
hr_resize_x: (_f = options.hr_resize_x) !== null && _f !== void 0 ? _f : 0,
hr_resize_y: (_g = options.hr_resize_y) !== null && _g !== void 0 ? _g : 0,
denoising_strength: (_h = options.denoising_strength) !== null && _h !== void 0 ? _h : 0.7,
firstphase_width: (_j = options.firstphase_width) !== null && _j !== void 0 ? _j : 0,
firstphase_height: (_k = options.firstphase_height) !== null && _k !== void 0 ? _k : 0,
prompt: (_l = options.prompt) !== null && _l !== void 0 ? _l : "",
styles: (_m = options.styles) !== null && _m !== void 0 ? _m : [],
seed: (_o = options.seed) !== null && _o !== void 0 ? _o : -1,
subseed: (_p = options.subseed) !== null && _p !== void 0 ? _p : -1,
subseed_strength: (_q = options.subseed_strength) !== null && _q !== void 0 ? _q : 0.0,
seed_resize_from_h: (_r = options.seed_resize_from_h) !== null && _r !== void 0 ? _r : 0,
seed_resize_from_w: (_s = options.seed_resize_from_w) !== null && _s !== void 0 ? _s : 0,
batch_size: (_t = options.batch_size) !== null && _t !== void 0 ? _t : 1,
n_iter: (_u = options.n_iter) !== null && _u !== void 0 ? _u : 1,
steps: (_v = options.steps) !== null && _v !== void 0 ? _v : this.config.defaultStepCount,
cfg_scale: (_w = options.cfg_scale) !== null && _w !== void 0 ? _w : 7.0,
width: (_x = options.width) !== null && _x !== void 0 ? _x : 512,
height: (_y = options.height) !== null && _y !== void 0 ? _y : 512,
restore_faces: (_z = options.restore_faces) !== null && _z !== void 0 ? _z : false,
tiling: (_0 = options.tiling) !== null && _0 !== void 0 ? _0 : false,
do_not_save_samples: (_1 = options.do_not_save_samples) !== null && _1 !== void 0 ? _1 : false,
do_not_save_grid: (_2 = options.do_not_save_grid) !== null && _2 !== void 0 ? _2 : false,
negative_prompt: (_3 = options.negative_prompt) !== null && _3 !== void 0 ? _3 : "",
eta: (_4 = options.eta) !== null && _4 !== void 0 ? _4 : 1.0,
s_churn: (_5 = options.s_churn) !== null && _5 !== void 0 ? _5 : 0,
s_tmax: (_6 = options.s_tmax) !== null && _6 !== void 0 ? _6 : 0,
s_tmin: (_7 = options.s_tmin) !== null && _7 !== void 0 ? _7 : 0,
s_noise: (_8 = options.s_noise) !== null && _8 !== void 0 ? _8 : 1,
override_settings: (_9 = options.override_settings) !== null && _9 !== void 0 ? _9 : {},
override_settings_restore_afterwards: (_10 = options.override_settings_restore_afterwards) !== null && _10 !== void 0 ? _10 : true,
script_args: (_11 = options.script_args) !== null && _11 !== void 0 ? _11 : [],
script_name: (_12 = options.script_name) !== null && _12 !== void 0 ? _12 : null,
send_images: (_13 = options.send_images) !== null && _13 !== void 0 ? _13 : true,
save_images: (_14 = options.save_images) !== null && _14 !== void 0 ? _14 : false,
alwayson_scripts,
sampler_name: (_15 = options.sampler_name) !== null && _15 !== void 0 ? _15 : this.config.defaultSampler,
use_deprecated_controlnet: (_16 = options.use_deprecated_controlnet) !== null && _16 !== void 0 ? _16 : false,
});
return new StableDiffusionResult_1.default(response);
});
}
/**
* Stable Diffusion img2img call
* @param {Img2ImgOptions} options Options for the img2img call
* @returns {Promise<StableDiffusionResult>} ApiResult containing the generated image(s)
* @memberof StableDiffusionApi
* @async
* @example
* const api = new StableDiffusionApi();
* const init_image = sharp("dog.png");
* const result = await api.img2img({
* init_images: [init_image],
* prompt: "Just a funky disco dog",
* });
*/
img2img(options) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17;
return __awaiter(this, void 0, void 0, function* () {
const init_images = yield Promise.all(options.init_images.map((image) => __awaiter(this, void 0, void 0, function* () { return yield (0, base64_1.toBase64)(image); })));
const mask = options.mask_image ? yield (0, base64_1.toBase64)(options.mask_image) : null;
const alwayson_scripts = yield createScriptsWithCnUnits(options.alwayson_scripts, (_a = options.controlnet_units) !== null && _a !== void 0 ? _a : []);
const response = yield this.api.post("/sdapi/v1/img2img", {
init_images,
resize_mode: (_b = options.resize_mode) !== null && _b !== void 0 ? _b : 0,
denoising_strength: (_c = options.denoising_strength) !== null && _c !== void 0 ? _c : 0.75,
image_cfg_scale: (_d = options.image_cfg_scale) !== null && _d !== void 0 ? _d : 1.5,
mask,
mask_blur: (_e = options.mask_blur) !== null && _e !== void 0 ? _e : 4,
inpainting_fill: (_f = options.inpainting_fill) !== null && _f !== void 0 ? _f : 0,
inpaint_full_res: (_g = options.inpaint_full_res) !== null && _g !== void 0 ? _g : true,
inpaint_full_res_padding: (_h = options.inpaint_full_res_padding) !== null && _h !== void 0 ? _h : 0,
inpainting_mask_invert: (_j = options.inpainting_mask_invert) !== null && _j !== void 0 ? _j : 0,
initial_noise_multiplier: (_k = options.initial_noise_multiplier) !== null && _k !== void 0 ? _k : 1,
prompt: (_l = options.prompt) !== null && _l !== void 0 ? _l : "",
styles: (_m = options.styles) !== null && _m !== void 0 ? _m : [],
seed: (_o = options.seed) !== null && _o !== void 0 ? _o : -1,
subseed: (_p = options.subseed) !== null && _p !== void 0 ? _p : -1,
subseed_strength: (_q = options.subseed_strength) !== null && _q !== void 0 ? _q : 0,
seed_resize_from_h: (_r = options.seed_resize_from_h) !== null && _r !== void 0 ? _r : 0,
seed_resize_from_w: (_s = options.seed_resize_from_w) !== null && _s !== void 0 ? _s : 0,
sampler_name: (_t = options.sampler_name) !== null && _t !== void 0 ? _t : this.config.defaultSampler,
batch_size: (_u = options.batch_size) !== null && _u !== void 0 ? _u : 1,
n_iter: (_v = options.n_iter) !== null && _v !== void 0 ? _v : 1,
steps: (_w = options.steps) !== null && _w !== void 0 ? _w : this.config.defaultStepCount,
cfg_scale: (_x = options.cfg_scale) !== null && _x !== void 0 ? _x : 7.0,
width: (_y = options.width) !== null && _y !== void 0 ? _y : 512,
height: (_z = options.height) !== null && _z !== void 0 ? _z : 512,
restore_faces: (_0 = options.restore_faces) !== null && _0 !== void 0 ? _0 : false,
tiling: (_1 = options.tiling) !== null && _1 !== void 0 ? _1 : false,
do_not_save_samples: (_2 = options.do_not_save_samples) !== null && _2 !== void 0 ? _2 : false,
do_not_save_grid: (_3 = options.do_not_save_grid) !== null && _3 !== void 0 ? _3 : false,
negative_prompt: (_4 = options.negative_prompt) !== null && _4 !== void 0 ? _4 : "",
eta: (_5 = options.eta) !== null && _5 !== void 0 ? _5 : 1.0,
s_churn: (_6 = options.s_churn) !== null && _6 !== void 0 ? _6 : 0,
s_tmax: (_7 = options.s_tmax) !== null && _7 !== void 0 ? _7 : 0,
s_tmin: (_8 = options.s_tmin) !== null && _8 !== void 0 ? _8 : 0,
s_noise: (_9 = options.s_noise) !== null && _9 !== void 0 ? _9 : 1,
override_settings: (_10 = options.override_settings) !== null && _10 !== void 0 ? _10 : {},
override_settings_restore_afterwards: (_11 = options.override_settings_restore_afterwards) !== null && _11 !== void 0 ? _11 : true,
script_args: (_12 = options.script_args) !== null && _12 !== void 0 ? _12 : [],
include_init_images: (_13 = options.include_init_images) !== null && _13 !== void 0 ? _13 : false,
script_name: (_14 = options.script_name) !== null && _14 !== void 0 ? _14 : null,
send_images: (_15 = options.send_images) !== null && _15 !== void 0 ? _15 : true,
save_images: (_16 = options.save_images) !== null && _16 !== void 0 ? _16 : false,
alwayson_scripts,
use_deprecated_controlnet: (_17 = options.use_deprecated_controlnet) !== null && _17 !== void 0 ? _17 : false,
});
return new StableDiffusionResult_1.default(response);
});
}
/**
* Stable Diffusion extra's call for single images
* @param {ExtraSingleOptions} options Options for the extra's call
* @returns {Promise<StableDiffusionResult>} ApiResult containing the generated image(s)
* @memberof StableDiffusionApi
* @async
* @example
* const api = new StableDiffusionApi();
* const image = sharp("dog.png");
* const result = await api.extraSingle({
* image,
* upscaler_1: "Lanczos",
* upscaling_resize: 2,
* });
*/
extraSingle(options) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
return __awaiter(this, void 0, void 0, function* () {
const image = yield (0, base64_1.toBase64)(options.image);
const response = yield this.api.post("/sdapi/v1/extra-single-image", {
image,
resize_mode: (_a = options.resize_mode) !== null && _a !== void 0 ? _a : 0,
show_extras_results: (_b = options.show_extras_results) !== null && _b !== void 0 ? _b : true,
gfpgan_visibility: (_c = options.gfpgan_visibility) !== null && _c !== void 0 ? _c : 0,
codeformer_weight: (_d = options.codeformer_weight) !== null && _d !== void 0 ? _d : 0,
upscaling_resize: (_e = options.upscaling_resize) !== null && _e !== void 0 ? _e : 2,
upscaling_resize_w: (_f = options.upscaling_resize_w) !== null && _f !== void 0 ? _f : 512,
upscaling_resize_h: (_g = options.upscaling_resize_h) !== null && _g !== void 0 ? _g : 512,
upscaling_resize_crop: (_h = options.upscaling_resize_crop) !== null && _h !== void 0 ? _h : true,
upscaler_1: (_j = options.upscaler_1) !== null && _j !== void 0 ? _j : "None",
upscaler_2: (_k = options.upscaler_2) !== null && _k !== void 0 ? _k : "None",
extras_upscaler_2_visibility: (_l = options.extras_upscaler_2_visibility) !== null && _l !== void 0 ? _l : 0,
upscale_first: (_m = options.upscale_first) !== null && _m !== void 0 ? _m : false,
});
return new StableDiffusionResult_1.default(response);
});
}
/**
* Stable Diffusion extra's call for batch images
* @param {ExtraBatchOptions} batchOptions Options for the extra's call
* @returns {Promise<StableDiffusionResult>} ApiResult containing the generated image(s)
* @memberof StableDiffusionApi
* @async
* @example
* const api = new StableDiffusionApi();
* const image1 = sharp("dog.png");
* const image2 = sharp("cat.png");
* const result = await api.extraBatch({
* images: [image1, image2],
* name_list: ["dog", "cat"],
* upscaler_1: "Lanczos",
* upscaling_resize: 2,
* });
*/
extraBatch(options) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
return __awaiter(this, void 0, void 0, function* () {
if (options.images.length !== options.name_list.length) {
throw new Error("The number of images and names must be the same in extraBatch");
}
const images = yield Promise.all(options.images.map((image) => __awaiter(this, void 0, void 0, function* () { return yield (0, base64_1.toBase64)(image); })));
const image_list = images.map((image, index) => {
return {
image,
name: options.name_list[index],
};
});
const response = yield this.api.post("/sdapi/v1/extra-batch-images", {
image_list,
resize_mode: (_a = options.resize_mode) !== null && _a !== void 0 ? _a : 0,
show_extras_results: (_b = options.show_extras_results) !== null && _b !== void 0 ? _b : true,
gfpgan_visibility: (_c = options.gfpgan_visibility) !== null && _c !== void 0 ? _c : 0,
codeformer_weight: (_d = options.codeformer_weight) !== null && _d !== void 0 ? _d : 0,
upscaling_resize: (_e = options.upscaling_resize) !== null && _e !== void 0 ? _e : 2,
upscaling_resize_w: (_f = options.upscaling_resize_w) !== null && _f !== void 0 ? _f : 512,
upscaling_resize_h: (_g = options.upscaling_resize_h) !== null && _g !== void 0 ? _g : 512,
upscaling_resize_crop: (_h = options.upscaling_resize_crop) !== null && _h !== void 0 ? _h : true,
upscaler_1: (_j = options.upscaler_1) !== null && _j !== void 0 ? _j : "None",
upscaler_2: (_k = options.upscaler_2) !== null && _k !== void 0 ? _k : "None",
extras_upscaler_2_visibility: (_l = options.extras_upscaler_2_visibility) !== null && _l !== void 0 ? _l : 0,
upscale_first: (_m = options.upscale_first) !== null && _m !== void 0 ? _m : false,
});
return new StableDiffusionResult_1.default(response);
});
}
/**
* Gets the info of a png image
* @param {Sharp} image Image to get info from
* @returns {Promise<StableDiffusionResult>} ApiResult containing the info
*/
pngInfo(image) {
return __awaiter(this, void 0, void 0, function* () {
const image_data = yield (0, base64_1.toBase64)(image);
const response = yield this.api.post("/sdapi/v1/png-info", {
image: image_data,
});
return new StableDiffusionResult_1.default(response);
});
}
/**
* Interrogates an image with an interrogation model
* @param {Sharp} image Image to interrogate
* @param model Model to use for interrogation
* @returns {Promise<StableDiffusionResult>} The result of the interrogation
*/
interrogate(image, model) {
return __awaiter(this, void 0, void 0, function* () {
const image_data = yield (0, base64_1.toBase64)(image);
const response = yield this.api.post("/sdapi/v1/interrogate", {
image: image_data,
});
return new StableDiffusionResult_1.default(response);
});
}
getOptions() {
return __awaiter(this, void 0, void 0, function* () {
const response = yield this.api.get("/sdapi/v1/options");
return response.data;
});
}
setOptions(options) {
return __awaiter(this, void 0, void 0, function* () {
const response = yield this.api.post("/sdapi/v1/options", options);
return response.data;
});
}
/**
* Gets the progress status of the current session
* @param {boolean} skipCurrentImage True to skip the current image, false to include it
* @returns {Promise<Progress>} The progress status of the current session
*/
getProgress(skipCurrentImage = false) {
return __awaiter(this, void 0, void 0, function* () {
const response = yield this.api.get(`/sdapi/v1/progress?skipCurrentImage=${skipCurrentImage}`);
return response.data;
});
}
/**
* Gets the list of command line flags that are available
* @returns {Promise<Record<string, unknown>>} The list of command line flags that are available
*/
getCmdFlags() {
return __awaiter(this, void 0, void 0, function* () {
const response = yield this.api.get("/sdapi/v1/cmd-flags");
return response.data;
});
}
/**
* Gets the list of samplers
* @returns {Promise<Sampler[]>} The list of samplers
*/
getSamplers() {
return __awaiter(this, void 0, void 0, function* () {
const response = yield this.api.get("/sdapi/v1/samplers");
return response.data;
});
}
/**
* Gets the list of upscalers
* @returns {Promise<Upscaler[]>} The list of upscalers
*/
getUpscalers() {
return __awaiter(this, void 0, void 0, function* () {
const response = yield this.api.get("/sdapi/v1/upscalers");
return response.data;
});
}
/**
* Gets the list of Stable Diffusion models
* @returns {Promise<StableDiffusionModel[]>} The list of Stable Diffusion models
*/
getSdModels() {
return __awaiter(this, void 0, void 0, function* () {
const response = yield this.api.get("/sdapi/v1/sd-models");
return response.data;
});
}
/**
* Gets the list of hypernetworks
* @returns {Promise<HyperNetwork[]>} The list of hypernetworks
*/
getHypernetworks() {
return __awaiter(this, void 0, void 0, function* () {
const response = yield this.api.get("/sdapi/v1/hypernetworks");
return response.data;
});
}
/**
* Gets the list of face restorers
* @returns {Promise<FaceRestorer[]>} The list of face restorers
*/
getFaceRestorers() {
return __awaiter(this, void 0, void 0, function* () {
const response = yield this.api.get("/sdapi/v1/face-restorers");
return response.data;
});
}
/**
* Gets the list of Real-ESRGAN models
* @returns {Promise<RealESRGanModel[]>} The list of Real-ESRGAN models
*/
getRealesrganModels() {
return __awaiter(this, void 0, void 0, function* () {
const response = yield this.api.get("/sdapi/v1/realesrgan-models");
return response.data;
});
}
/**
* Gets the list of Stable Diffusion prompt styles
* @returns {Promise<PromptStyle[]>} The list of prompt styles
*/
getPromptStyles() {
return __awaiter(this, void 0, void 0, function* () {
const response = yield this.api.get("/sdapi/v1/prompt-styles");
return response.data;
});
}
/**
* Refreshes the list of Stable Diffusion checkpoints
* @returns {Promise<void>}
*/
refreshCheckpoints() {
return __awaiter(this, void 0, void 0, function* () {
yield this.api.post("/sdapi/v1/refresh-checkpoints");
});
}
/**
* Gets the name of the current Stable Diffusion checkpoint being used
* @returns {Promise<string>} The name of the current Stable Diffusion checkpoint being used
*/
getCurrentModel() {
return __awaiter(this, void 0, void 0, function* () {
const options = yield this.getOptions();
return options.sd_model_checkpoint;
});
}
/**
* Sets the Stable Diffusion checkpoint to use
* @param name Name of the model to set.
* @param findClosest If true, will try to find the closest model name if the exact name is not found
* @returns {Promise<void>}
*/
setModel(name, findClosest = true) {
return __awaiter(this, void 0, void 0, function* () {
const models = yield this.getSdModels();
const modelNames = models.map((model) => model.name);
let foundModel = null;
if (modelNames.includes(name)) {
foundModel = name;
}
else if (findClosest) {
const bestMatch = string_similarity_1.default.findBestMatch(name, modelNames);
if (bestMatch.bestMatch.rating > 0.5) {
foundModel = bestMatch.bestMatch.target;
}
}
if (foundModel) {
const options = {
sd_model_checkpoint: foundModel,
};
yield this.setOptions(options);
}
else {
throw new Error("Model not found");
}
});
}
/**
* Waits for the Stable Diffusion server to be ready to accept new requests
* @param checkInterval Interval in seconds to check progress
* @returns {Promise<boolean>} Only resolves when progress is 0.0 and job_count is 0
*/
waitForReady(checkInterval = 5.0) {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve, _reject) => {
const interval = setInterval(() => __awaiter(this, void 0, void 0, function* () {
const result = yield this.getProgress();
const progress = result.progress;
const jobCount = result.state.job_count;
if (progress === 0.0 && jobCount === 0) {
clearInterval(interval);
resolve(true);
}
else {
console.log(`[WAIT]: progress = ${progress.toFixed(4)}, job_count = ${jobCount}`);
}
}), checkInterval * 1000);
});
});
}
}
exports.StableDiffusionApi = StableDiffusionApi;