mirror of
https://github.com/anapnoe/stable-diffusion-webui-ux.git
synced 2026-09-20 01:31:44 +02:00
Merge branch 'dev' of https://github.com/anapnoe/stable-diffusion-webui-ux into dev
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
## Upcoming version:
|
||||
### Features:
|
||||
* switch to torch 2.0.0 (except for AMD GPUs)
|
||||
* visual improvements to custom code scripts
|
||||
* add filename patterns: [clip_skip], [hasprompt<>], [batch_number], [generation_number]
|
||||
* add support for saving init images in img2img, and record their hashes in infotext for reproducability
|
||||
* automatically select current word when adjusting weight with ctrl+up/down
|
||||
* add dropdowns for X/Y/Z plot
|
||||
* setting: Stable Diffusion/Random number generator source: makes it possible to make images generated from a given manual seed consistent across different GPUs
|
||||
* support Gradio's theme API
|
||||
* use TCMalloc on Linux by default; possible fix for memory leaks
|
||||
* (optimization) option to remove negative conditioning at low sigma values #9177
|
||||
* embed model merge metadata in .safetensors file
|
||||
* extension settings backup/restore feature #9169
|
||||
* add "resize by" and "resize to" tabs to img2img
|
||||
* add option "keep original size" to textual inversion images preprocess
|
||||
* image viewer scrolling via analog stick
|
||||
* button to restore the progress from session lost / tab reload
|
||||
|
||||
### Minor:
|
||||
* gradio bumped to 3.28.1
|
||||
* in extra tab, change extras "scale to" to sliders
|
||||
* add labels to tool buttons to make it possible to hide them
|
||||
* add tiled inference support for ScuNET
|
||||
* add branch support for extension installation
|
||||
* change linux installation script to insall into current directory rather than /home/username
|
||||
* sort textual inversion embeddings by name (case insensitive)
|
||||
* allow styles.csv to be symlinked or mounted in docker
|
||||
* remove the "do not add watermark to images" option
|
||||
* make selected tab configurable with UI config
|
||||
* extra networks UI in now fixed height and scrollable
|
||||
* add disable_tls_verify arg for use with self-signed certs
|
||||
|
||||
### Extensions:
|
||||
* Add reload callback
|
||||
* add is_hr_pass field for processing
|
||||
|
||||
### Bug Fixes:
|
||||
* fix broken batch image processing on 'Extras/Batch Process' tab
|
||||
* add "None" option to extra networks dropdowns
|
||||
* fix FileExistsError for CLIP Interrogator
|
||||
* fix /sdapi/v1/txt2img endpoint not working on Linux #9319
|
||||
* fix disappearing live previews and progressbar during slow tasks
|
||||
* fix fullscreen image view not working properly in some cases
|
||||
* prevent alwayson_scripts args param resizing script_arg list when they are inserted in it
|
||||
* fix prompt schedule for second order samplers
|
||||
* fix image mask/composite for weird resolutions #9628
|
||||
* use correct images for previews when using AND (see #9491)
|
||||
* one broken image in img2img batch won't stop all processing
|
||||
* fix image orientation bug in train/preprocess
|
||||
* fix Ngrok recreating tunnels every reload
|
||||
* fix --realesrgan-models-path and --ldsr-models-path not working
|
||||
* fix --skip-install not working
|
||||
* outpainting Mk2 & Poorman should use the SAMPLE file format to save images, not GRID file format
|
||||
* do not fail all Loras if some have failed to load when making a picture
|
||||
|
||||
## Before versions:
|
||||
* everything
|
||||
@@ -22,10 +22,12 @@ titles = {
|
||||
"\u{1f4cb}": "Apply selected styles to current prompt",
|
||||
"\u{1f4d2}": "Paste available values into the field",
|
||||
"\u{1f3b4}": "Show/hide extra networks",
|
||||
|
||||
"\u{1f5e8}": "Interogate Clip",
|
||||
"\u{1f5ea}": "Interogate Deepbooru",
|
||||
"\u{1F300}": "Restore progress",
|
||||
|
||||
|
||||
"Inpaint a part of image": "Draw a mask over an image, and the script will regenerate the masked area with content according to prompt",
|
||||
"SD upscale": "Upscale image normally, split result into tiles, improve each tile using img2img, merge whole image back",
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ function randomId(){
|
||||
// starts sending progress requests to "/internal/progress" uri, creating progressbar above progressbarContainer element and
|
||||
// preview inside gallery element. Cleans up all created stuff when the task is over and calls atEnd.
|
||||
// calls onProgress every time there is a progress update
|
||||
function requestProgress(id_task, progressbarContainer, gallery, atEnd, onProgress){
|
||||
function requestProgress(id_task, progressbarContainer, gallery, atEnd, onProgress, inactivityTimeout=40){
|
||||
var dateStart = new Date()
|
||||
var wasEverActive = false
|
||||
var parentProgressbar = progressbarContainer.parentNode
|
||||
@@ -155,7 +155,7 @@ function requestProgress(id_task, progressbarContainer, gallery, atEnd, onProgre
|
||||
return
|
||||
}
|
||||
|
||||
if(elapsedFromStart > 40 && !res.queued && !res.active){
|
||||
if(elapsedFromStart > inactivityTimeout && !res.queued && !res.active){
|
||||
removeProgressBar()
|
||||
return
|
||||
}
|
||||
|
||||
+47
-26
@@ -159,13 +159,24 @@ function showSubmitButtons(tabname, show){
|
||||
gradioApp().getElementById(tabname+'_skip').style.display = show ? "none" : "block"
|
||||
}
|
||||
|
||||
function showRestoreProgressButton(tabname, show){
|
||||
button = gradioApp().getElementById(tabname + "_restore_progress")
|
||||
if(! button) return
|
||||
|
||||
button.style.display = show ? "flex" : "none"
|
||||
}
|
||||
|
||||
function submit(){
|
||||
rememberGallerySelection('txt2img_gallery')
|
||||
showSubmitButtons('txt2img', false)
|
||||
|
||||
var id = randomId()
|
||||
localStorage.setItem("txt2img_task_id", id);
|
||||
|
||||
requestProgress(id, gradioApp().getElementById('txt2img_gallery_container'), gradioApp().getElementById('txt2img_gallery'), function(){
|
||||
showSubmitButtons('txt2img', true)
|
||||
localStorage.removeItem("txt2img_task_id")
|
||||
showRestoreProgressButton('txt2img', false)
|
||||
})
|
||||
|
||||
var res = create_submit_args(arguments)
|
||||
@@ -180,8 +191,12 @@ function submit_img2img(){
|
||||
showSubmitButtons('img2img', false)
|
||||
|
||||
var id = randomId()
|
||||
localStorage.setItem("img2img_task_id", id);
|
||||
|
||||
requestProgress(id, gradioApp().getElementById('img2img_gallery_container'), gradioApp().getElementById('img2img_gallery'), function(){
|
||||
showSubmitButtons('img2img', true)
|
||||
localStorage.removeItem("img2img_task_id")
|
||||
showRestoreProgressButton('img2img', false)
|
||||
})
|
||||
|
||||
var res = create_submit_args(arguments)
|
||||
@@ -192,6 +207,36 @@ function submit_img2img(){
|
||||
return res
|
||||
}
|
||||
|
||||
function restoreProgressTxt2img(x){
|
||||
id = localStorage.getItem("txt2img_task_id")
|
||||
|
||||
if(id) {
|
||||
requestProgress(id, gradioApp().getElementById('txt2img_gallery_container'), gradioApp().getElementById('txt2img_gallery'), function(){
|
||||
showSubmitButtons('txt2img', true)
|
||||
}, null, 0)
|
||||
}
|
||||
|
||||
return [id]
|
||||
}
|
||||
function restoreProgressImg2img(x){
|
||||
id = localStorage.getItem("img2img_task_id")
|
||||
|
||||
if(id) {
|
||||
requestProgress(id, gradioApp().getElementById('img2img_gallery_container'), gradioApp().getElementById('img2img_gallery'), function(){
|
||||
showSubmitButtons('img2img', true)
|
||||
}, null, 0)
|
||||
}
|
||||
|
||||
return [id]
|
||||
}
|
||||
|
||||
|
||||
onUiLoaded(function () {
|
||||
showRestoreProgressButton('txt2img', localStorage.getItem("txt2img_task_id"))
|
||||
showRestoreProgressButton('img2img', localStorage.getItem("img2img_task_id"))
|
||||
});
|
||||
|
||||
|
||||
function modelmerger(){
|
||||
var id = randomId()
|
||||
requestProgress(id, gradioApp().getElementById('modelmerger_results_panel'), null, function(){})
|
||||
@@ -1546,32 +1591,6 @@ function selectCheckpoint(name){
|
||||
gradioApp().getElementById('change_checkpoint').click()
|
||||
}
|
||||
|
||||
function restoreProgress (task_tag) {
|
||||
|
||||
if (task_tag) {
|
||||
let successHandler = ({ current_task }) => {
|
||||
if (current_task) {
|
||||
showSubmitButtons(task_tag, false)
|
||||
requestProgress(current_task, gradioApp().getElementById(`${task_tag}_gallery_container`), gradioApp().getElementById(`${task_tag}_gallery`), function(){
|
||||
showSubmitButtons(task_tag, true)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let errorHandler = e => window.alert(`invalid internal api respsonse. message: ${e}`)
|
||||
|
||||
fetch("./internal/current_task")
|
||||
.then(res => res.json())
|
||||
.then(successHandler)
|
||||
.catch(errorHandler)
|
||||
}
|
||||
|
||||
var res = create_submit_args(arguments)
|
||||
res[0] = 0
|
||||
return res
|
||||
|
||||
}
|
||||
|
||||
function currentImg2imgSourceResolution(_, _, scaleBy){
|
||||
var img = gradioApp().querySelector('#mode_img2img > div[style="display: block;"] img')
|
||||
return img ? [img.naturalWidth, img.naturalHeight, scaleBy] : [0, 0, scaleBy]
|
||||
@@ -1603,3 +1622,5 @@ window.onload = function() {
|
||||
setTimeout(function(){document.body.style.display = "block";},1000)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -222,7 +222,7 @@ def run_extensions_installers(settings_file):
|
||||
|
||||
|
||||
def prepare_environment():
|
||||
torch_command = os.environ.get('TORCH_COMMAND', "pip install torch==2.0.0 torchvision==0.15.1 --index-url https://download.pytorch.org/whl/cu118")
|
||||
torch_command = os.environ.get('TORCH_COMMAND', "pip install torch==2.0.0 torchvision==0.15.1 --extra-index-url https://download.pytorch.org/whl/cu118")
|
||||
requirements_file = os.environ.get('REQS_FILE', "requirements_versions.txt")
|
||||
|
||||
xformers_package = os.environ.get('XFORMERS_PACKAGE', 'xformers==0.0.17')
|
||||
|
||||
+6
-13
@@ -4,16 +4,10 @@ import threading
|
||||
import traceback
|
||||
import time
|
||||
|
||||
import gradio as gr
|
||||
from modules import shared, progress
|
||||
|
||||
queue_lock = threading.Lock()
|
||||
queue_lock_condition = threading.Condition(lock=queue_lock)
|
||||
|
||||
def wrap_session_call(func):
|
||||
def f(request: gr.Request, *args, **kwargs):
|
||||
return func(request, *args, **kwargs)
|
||||
return f
|
||||
|
||||
def wrap_queued_call(func):
|
||||
def f(*args, **kwargs):
|
||||
@@ -26,31 +20,30 @@ def wrap_queued_call(func):
|
||||
|
||||
|
||||
def wrap_gradio_gpu_call(func, extra_outputs=None):
|
||||
def f(request: gr.Request, *args, **kwargs):
|
||||
user = request.username
|
||||
def f(*args, **kwargs):
|
||||
|
||||
# if the first argument is a string that says "task(...)", it is treated as a job id
|
||||
if len(args) > 0 and type(args[0]) == str and args[0][0:5] == "task(" and args[0][-1] == ")":
|
||||
id_task = args[0]
|
||||
progress.add_task_to_queue(user, id_task)
|
||||
progress.add_task_to_queue(id_task)
|
||||
else:
|
||||
id_task = None
|
||||
|
||||
with queue_lock:
|
||||
shared.state.begin()
|
||||
progress.start_task(user, id_task)
|
||||
progress.start_task(id_task)
|
||||
|
||||
try:
|
||||
res = func(*args, **kwargs)
|
||||
progress.record_results(id_task, res)
|
||||
finally:
|
||||
progress.finish_task(user, id_task)
|
||||
progress.set_last_task_result(user, id_task, res)
|
||||
progress.finish_task(id_task)
|
||||
|
||||
shared.state.end()
|
||||
|
||||
return res
|
||||
|
||||
return wrap_session_call(wrap_gradio_call(f, extra_outputs=extra_outputs, add_stats=True))
|
||||
return wrap_gradio_call(f, extra_outputs=extra_outputs, add_stats=True)
|
||||
|
||||
|
||||
def wrap_gradio_call(func, extra_outputs=None, add_stats=False):
|
||||
|
||||
+24
-66
@@ -4,84 +4,46 @@ import time
|
||||
|
||||
import gradio as gr
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional
|
||||
from fastapi import Depends, Security
|
||||
from fastapi.security import APIKeyCookie
|
||||
|
||||
from modules import call_queue
|
||||
from modules.shared import opts
|
||||
|
||||
import modules.shared as shared
|
||||
|
||||
|
||||
current_task_user = None
|
||||
current_task = None
|
||||
pending_tasks = {}
|
||||
finished_tasks = []
|
||||
recorded_results = []
|
||||
recorded_results_limit = 2
|
||||
|
||||
|
||||
def start_task(user, id_task):
|
||||
def start_task(id_task):
|
||||
global current_task
|
||||
global current_task_user
|
||||
|
||||
current_task_user = user
|
||||
current_task = id_task
|
||||
pending_tasks.pop((user, id_task), None)
|
||||
pending_tasks.pop(id_task, None)
|
||||
|
||||
|
||||
def finish_task(user, id_task):
|
||||
def finish_task(id_task):
|
||||
global current_task
|
||||
global current_task_user
|
||||
|
||||
if current_task == id_task:
|
||||
current_task = None
|
||||
|
||||
if current_task_user == user:
|
||||
current_task_user = None
|
||||
|
||||
finished_tasks.append((user, id_task))
|
||||
finished_tasks.append(id_task)
|
||||
if len(finished_tasks) > 16:
|
||||
finished_tasks.pop(0)
|
||||
|
||||
|
||||
def add_task_to_queue(user, id_job):
|
||||
pending_tasks[(user, id_job)] = time.time()
|
||||
|
||||
last_task_id = None
|
||||
last_task_result = None
|
||||
last_task_user = None
|
||||
|
||||
def set_last_task_result(user, id_job, result):
|
||||
|
||||
global last_task_id
|
||||
global last_task_result
|
||||
global last_task_user
|
||||
|
||||
last_task_id = id_job
|
||||
last_task_result = result
|
||||
last_task_user = user
|
||||
def record_results(id_task, res):
|
||||
recorded_results.append((id_task, res))
|
||||
if len(recorded_results) > recorded_results_limit:
|
||||
recorded_results.pop(0)
|
||||
|
||||
|
||||
def restore_progress_call(request: gr.Request):
|
||||
if current_task is None:
|
||||
def add_task_to_queue(id_job):
|
||||
pending_tasks[id_job] = time.time()
|
||||
|
||||
# image, generation_info, html_info, html_log
|
||||
return tuple(list([None, None, None, None]))
|
||||
|
||||
else:
|
||||
user = request.username
|
||||
|
||||
if current_task_user == user:
|
||||
t_task = current_task
|
||||
with call_queue.queue_lock_condition:
|
||||
call_queue.queue_lock_condition.wait_for(lambda: t_task == last_task_id)
|
||||
|
||||
return last_task_result
|
||||
|
||||
return tuple(list([None, None, None, None]))
|
||||
|
||||
class CurrentTaskResponse(BaseModel):
|
||||
current_task: str = Field(default=None, title="Task ID", description="id of the current progress task")
|
||||
|
||||
class ProgressRequest(BaseModel):
|
||||
id_task: str = Field(default=None, title="Task ID", description="id of the task to get progress for")
|
||||
@@ -102,21 +64,6 @@ class ProgressResponse(BaseModel):
|
||||
def setup_progress_api(app):
|
||||
return app.add_api_route("/internal/progress", progressapi, methods=["POST"], response_model=ProgressResponse)
|
||||
|
||||
def setup_current_task_api(app):
|
||||
|
||||
def get_current_user(token: Optional[str] = Security(APIKeyCookie(name="access-token", auto_error=False))):
|
||||
return None if token is None else app.tokens.get(token)
|
||||
|
||||
def current_task_api(current_user: str = Depends(get_current_user)):
|
||||
|
||||
if app.auth is None or current_task_user == current_user:
|
||||
current_user_task = current_task
|
||||
else:
|
||||
current_user_task = None
|
||||
|
||||
return CurrentTaskResponse(current_task=current_user_task)
|
||||
|
||||
return app.add_api_route("/internal/current_task", current_task_api, methods=["GET"], response_model=CurrentTaskResponse)
|
||||
|
||||
def progressapi(req: ProgressRequest):
|
||||
active = req.id_task == current_task
|
||||
@@ -156,4 +103,15 @@ def progressapi(req: ProgressRequest):
|
||||
else:
|
||||
live_preview = None
|
||||
|
||||
return ProgressResponse(active=active, queued=queued, completed=completed, progress=progress, eta=eta, live_preview=live_preview, id_live_preview=id_live_preview, textinfo=shared.state.textinfo)
|
||||
return ProgressResponse(active=active, queued=queued, completed=completed, progress=progress, eta=eta, live_preview=live_preview, id_live_preview=id_live_preview, textinfo=shared.state.textinfo)
|
||||
|
||||
|
||||
def restore_progress(id_task):
|
||||
while id_task == current_task or id_task in pending_tasks:
|
||||
time.sleep(0.1)
|
||||
|
||||
res = next(iter([x[1] for x in recorded_results if id_task == x[0]]), None)
|
||||
if res is not None:
|
||||
return res
|
||||
|
||||
return gr.update(), gr.update(), gr.update(), f"Couldn't restore progress for {id_task}: results either have been discarded or never were obtained"
|
||||
|
||||
+25
-22
@@ -19,7 +19,7 @@ import numpy as np
|
||||
from PIL import Image, PngImagePlugin
|
||||
from modules.call_queue import wrap_gradio_gpu_call, wrap_queued_call, wrap_gradio_call
|
||||
|
||||
from modules import sd_hijack, sd_models, localization, script_callbacks, ui_extensions, deepbooru, sd_vae, extra_networks, postprocessing, ui_components, ui_common, ui_postprocessing
|
||||
from modules import sd_hijack, sd_models, localization, script_callbacks, ui_extensions, deepbooru, sd_vae, extra_networks, postprocessing, ui_components, ui_common, ui_postprocessing, progress
|
||||
from modules.ui_components import FormRow, FormColumn, FormGroup, ToolButton, FormHTML
|
||||
from modules.paths import script_path, data_path
|
||||
|
||||
@@ -41,7 +41,6 @@ from modules.textual_inversion import textual_inversion
|
||||
import modules.hypernetworks.ui
|
||||
from modules.generation_parameters_copypaste import image_from_url_text
|
||||
import modules.extras
|
||||
from modules.progress import restore_progress_call
|
||||
|
||||
warnings.filterwarnings("default" if opts.show_warnings else "ignore", category=UserWarning)
|
||||
|
||||
@@ -86,6 +85,7 @@ extra_networks_symbol = '\U0001F3B4' # 🎴
|
||||
switch_values_symbol = '\u2B80' # ⮀
|
||||
restore_progress_symbol = '\U0001F300' # 🌀
|
||||
|
||||
|
||||
interogate_bubble_symbol = '\U0001F5E8' # 🗨
|
||||
interogate_2bubble_symbol = '\U0001F5EA' # 🗪
|
||||
def plaintext_to_html(text):
|
||||
@@ -356,7 +356,8 @@ def create_toprow(is_img2img):
|
||||
if is_img2img:
|
||||
button_interrogate = ToolButton(value=interogate_bubble_symbol, elem_id="interrogate")
|
||||
button_deepbooru = ToolButton(value=interogate_2bubble_symbol, elem_id="deepbooru")
|
||||
restore_progress_button = ToolButton(value=restore_progress_symbol, elem_id=f"{id_part}_restore_progress")
|
||||
|
||||
restore_progress_button = ToolButton(value=restore_progress_symbol, elem_id=f"{id_part}_restore_progress", visible=False)
|
||||
|
||||
token_button = gr.Button(visible=False, elem_id=f"{id_part}_token_button")
|
||||
negative_token_button = gr.Button(visible=False, elem_id=f"{id_part}_negative_token_button")
|
||||
@@ -637,15 +638,16 @@ def create_ui():
|
||||
res_switch_btn.click(lambda w, h: (h, w), inputs=[width, height], outputs=[width, height], show_progress=False)
|
||||
|
||||
restore_progress_button.click(
|
||||
fn=restore_progress_call,
|
||||
_js="() => restoreProgress('txt2img')",
|
||||
inputs=[],
|
||||
outputs=[
|
||||
txt2img_gallery,
|
||||
generation_info,
|
||||
html_info,
|
||||
html_log,
|
||||
]
|
||||
fn=progress.restore_progress,
|
||||
_js="restoreProgressTxt2img",
|
||||
inputs=[dummy_component],
|
||||
outputs=[
|
||||
txt2img_gallery,
|
||||
generation_info,
|
||||
html_info,
|
||||
html_log,
|
||||
],
|
||||
show_progress=False,
|
||||
)
|
||||
|
||||
txt_prompt_img.change(
|
||||
@@ -1052,15 +1054,16 @@ def create_ui():
|
||||
res_switch_btn.click(lambda w, h: (h, w), inputs=[width, height], outputs=[width, height], show_progress=False)
|
||||
|
||||
restore_progress_button.click(
|
||||
fn=restore_progress_call,
|
||||
_js="() => restoreProgress('img2img')",
|
||||
inputs=[],
|
||||
outputs=[
|
||||
img2img_gallery,
|
||||
generation_info,
|
||||
html_info,
|
||||
html_log,
|
||||
]
|
||||
fn=progress.restore_progress,
|
||||
_js="restoreProgressImg2img",
|
||||
inputs=[dummy_component],
|
||||
outputs=[
|
||||
img2img_gallery,
|
||||
generation_info,
|
||||
html_info,
|
||||
html_log,
|
||||
],
|
||||
show_progress=False,
|
||||
)
|
||||
|
||||
img2img_interrogate.click(
|
||||
@@ -1700,7 +1703,7 @@ def create_ui():
|
||||
gr.HTML(shared.html("licenses.html"), elem_id="licenses")
|
||||
|
||||
gr.Button(value="Show all pages", elem_id="settings_show_all_pages")
|
||||
|
||||
|
||||
|
||||
def unload_sd_weights():
|
||||
modules.sd_models.unload_model_weights()
|
||||
|
||||
@@ -3867,6 +3867,7 @@ div[data-testid="image"] canvas {
|
||||
[id^="spotlight"].menu .spl-prev {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
[id^="spotlight"].menu .spl-next {
|
||||
transform: translateX(0) scaleX(-1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user