Initial commit
commit
89a7ebc894
|
|
@ -0,0 +1,2 @@
|
|||
.idea
|
||||
__pycache__/
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2023 Ilian Iliev
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<img alt="" src="https://img.shields.io/badge/JavaScript-323330?style=for-the-badge&logo=javascript&logoColor=F7DF1E" />
|
||||
<img alt="" src="https://img.shields.io/badge/Python-FFD43B?style=for-the-badge&logo=python&logoColor=blue" />
|
||||
|
||||
# stable-diffusion-webui-state
|
||||
|
||||
This extension is for AUTOMATIC1111's [Stable Diffusion web UI](https://github.com/AUTOMATIC1111/stable-diffusion-webui)
|
||||
|
||||
### Capabilities
|
||||
|
||||
* Preserve web UI parameters (inputs, sliders, checkboxes etc.) after page reload.
|
||||
* It can be extended to preserve basically everything in the UI.
|
||||
|
||||
### Install
|
||||
|
||||
Use **Install from URL** option with this repo url.
|
||||
|
||||
### Requirements
|
||||
|
||||
None at all.
|
||||
|
||||
### Usage
|
||||
|
||||
Go to **Settings->State** and check all parameters that you want to be preserved after page reload.
|
||||
|
||||
### Contributing
|
||||
|
||||
Feel free to submit PRs to develop!
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
onUiLoaded(StateController.init);
|
||||
});
|
||||
|
||||
|
||||
const StateController = (function () {
|
||||
|
||||
const LS_PREFIX = 'state-';
|
||||
const TABS = ['txt2img', 'img2img'];
|
||||
const ELEMENTS = {
|
||||
'prompt': 'prompt',
|
||||
'negative_prompt': 'neg_prompt',
|
||||
'sampling': 'sampling',
|
||||
'sampling_steps': 'steps',
|
||||
'restore_faces': 'restore_faces',
|
||||
'tiling': 'tiling',
|
||||
'hires_fix': 'enable_hr',
|
||||
'hires_upscaler': 'hr_upscaler',
|
||||
'hires_steps': 'hires_steps',
|
||||
'hires_scale': 'hr_scale',
|
||||
'hires_resize_x': 'hr_resize_x',
|
||||
'hires_resize_y': 'hr_resize_y',
|
||||
'hires_denoising_strength': 'denoising_strength',
|
||||
'width': 'width',
|
||||
'height': 'height',
|
||||
'batch_count': 'batch_count',
|
||||
'batch_size': 'batch_size',
|
||||
'cfg_scale': 'cfg_scale',
|
||||
'denoising_strength': 'denoising_strength',
|
||||
'seed': 'seed'
|
||||
};
|
||||
|
||||
const ELEMENTS_WITHOUT_PREFIX = {
|
||||
'resize_mode': 'resize_mode',
|
||||
};
|
||||
|
||||
function triggerEvent(element, event) {
|
||||
if (! element) {
|
||||
return;
|
||||
}
|
||||
element.dispatchEvent(new Event(event.trim()));
|
||||
return element;
|
||||
}
|
||||
|
||||
function hasSetting(id, tab) {
|
||||
const suffix = tab ? `_${tab}` : '';
|
||||
return this[`state${suffix}`] && this[`state${suffix}`].indexOf(id) > -1;
|
||||
}
|
||||
|
||||
function init() {
|
||||
fetch('/state/config.json?_=' + (+new Date()))
|
||||
.then(response => response.json())
|
||||
.then(config => {
|
||||
try {
|
||||
config.hasSetting = hasSetting
|
||||
load(config);
|
||||
} catch (error) {
|
||||
console.error('[state]: Error:', error);
|
||||
}
|
||||
})
|
||||
.catch(error => console.error('[state]: Error getting JSON file:', error));
|
||||
}
|
||||
|
||||
function load(config) {
|
||||
|
||||
restoreTabs(config);
|
||||
|
||||
for (const [settingId, element] of Object.entries(ELEMENTS)) {
|
||||
TABS.forEach(tab => {
|
||||
if (config.hasSetting(settingId, tab)) {
|
||||
handleSavedInput(`${tab}_${element}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (const [settingId, element] of Object.entries(ELEMENTS_WITHOUT_PREFIX)) {
|
||||
TABS.forEach(tab => {
|
||||
if (config.hasSetting(settingId, tab)) {
|
||||
handleSavedInput(`${element}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function restoreTabs(config) {
|
||||
|
||||
if (! config.hasSetting('tabs')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tabs = gradioApp().querySelectorAll('#tabs > div:first-child button');
|
||||
|
||||
tabs.forEach(tab => {
|
||||
tab.addEventListener('click', function () {
|
||||
localStorage.setItem(LS_PREFIX + 'tab', this.textContent);
|
||||
});
|
||||
});
|
||||
|
||||
const value = localStorage.getItem(LS_PREFIX + 'tab');
|
||||
|
||||
if (value) {
|
||||
for (var i = 0; i < tabs.length; i++) {
|
||||
if (tabs[i].textContent === value) {
|
||||
triggerEvent(tabs[i], 'click');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleSavedInput(id) {
|
||||
|
||||
const elements = gradioApp().querySelectorAll(`#${id} textarea, #${id} select, #${id} input`);
|
||||
const events = ['change', 'input'];
|
||||
|
||||
if (! elements || ! elements.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
let forEach = function (action) {
|
||||
events.forEach(function(event) {
|
||||
elements.forEach(function (element) {
|
||||
action.call(element, event);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
forEach(function (event) {
|
||||
this.addEventListener(event, function () {
|
||||
let value = this.value;
|
||||
if (this.type && this.type === 'checkbox') {
|
||||
value = this.checked;
|
||||
}
|
||||
localStorage.setItem(LS_PREFIX + id, value);
|
||||
});
|
||||
});
|
||||
|
||||
TABS.forEach(tab => {
|
||||
const seedInput = gradioApp().querySelector(`#${tab}_seed input`);
|
||||
['random_seed', 'reuse_seed'].forEach(id => {
|
||||
const btn = gradioApp().querySelector(`#${tab}_${id}`);
|
||||
btn.addEventListener('click', () => {
|
||||
setTimeout(() => {
|
||||
triggerEvent(seedInput, 'change');
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
let value = localStorage.getItem(LS_PREFIX + id);
|
||||
|
||||
if (! value) {
|
||||
return;
|
||||
}
|
||||
|
||||
forEach(function (event) {
|
||||
switch (this.type) {
|
||||
case 'checkbox':
|
||||
this.checked = value === 'true';
|
||||
triggerEvent(this, event);
|
||||
break;
|
||||
case 'radio':
|
||||
if (this.value === value) {
|
||||
this.checked = true;
|
||||
triggerEvent(this, event);
|
||||
} else {
|
||||
this.checked = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
this.value = value;
|
||||
triggerEvent(this, event);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return { init };
|
||||
}());
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
from fastapi import FastAPI, Body, HTTPException, Request, Response
|
||||
from fastapi.responses import FileResponse
|
||||
|
||||
import gradio as gr
|
||||
import modules.script_callbacks as script_callbacks
|
||||
|
||||
|
||||
class StateApi():
|
||||
|
||||
BASE_PATH = '/state'
|
||||
|
||||
def get_path(self, path):
|
||||
return f"{self.BASE_PATH}{path}"
|
||||
|
||||
def add_api_route(self, path: str, endpoint, **kwargs):
|
||||
return self.app.add_api_route(self.get_path(path), endpoint, **kwargs)
|
||||
|
||||
def start(self, _: gr.Blocks, app: FastAPI):
|
||||
self.app = app
|
||||
self.add_api_route('/config.json', self.get_config, methods=['GET'])
|
||||
|
||||
def get_config(self):
|
||||
return FileResponse('config.json')
|
||||
|
||||
|
||||
try:
|
||||
api = StateApi()
|
||||
script_callbacks.on_app_started(api.start)
|
||||
except:
|
||||
pass
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
import gradio as gr
|
||||
import modules.shared as shared
|
||||
from modules import scripts
|
||||
|
||||
|
||||
def on_ui_settings():
|
||||
|
||||
section = ("state", "State")
|
||||
|
||||
shared.opts.add_option("state", shared.OptionInfo([], "Saved main elements", gr.CheckboxGroup, lambda: {
|
||||
"choices": [
|
||||
"tabs"
|
||||
]
|
||||
}, section=section))
|
||||
|
||||
shared.opts.add_option("state_txt2img", shared.OptionInfo([], "Saved elements from txt2img", gr.CheckboxGroup, lambda: {
|
||||
"choices": [
|
||||
"prompt",
|
||||
"negative_prompt",
|
||||
"sampling",
|
||||
"sampling_steps",
|
||||
"width",
|
||||
"height",
|
||||
"batch_count",
|
||||
"batch_size",
|
||||
"cfg_scale",
|
||||
"seed",
|
||||
"restore_faces",
|
||||
"tiling",
|
||||
"hires_fix",
|
||||
"hires_upscaler",
|
||||
"hires_steps",
|
||||
"hires_scale",
|
||||
"hires_resize_x",
|
||||
"hires_resize_y",
|
||||
"hires_denoising_strength",
|
||||
]
|
||||
}, section=section))
|
||||
|
||||
shared.opts.add_option("state_img2img", shared.OptionInfo([], "Saved elements from img2img", gr.CheckboxGroup, lambda: {
|
||||
"choices": [
|
||||
"prompt",
|
||||
"negative_prompt",
|
||||
"sampling",
|
||||
"resize_mode",
|
||||
"sampling_steps",
|
||||
"restore_faces",
|
||||
"tiling",
|
||||
"width",
|
||||
"height",
|
||||
"batch_count",
|
||||
"batch_size",
|
||||
"cfg_scale",
|
||||
"denoising_strength",
|
||||
"seed"
|
||||
]
|
||||
}, section=section))
|
||||
|
||||
|
||||
scripts.script_callbacks.on_ui_settings(on_ui_settings)
|
||||
Loading…
Reference in New Issue