Compare commits

...

59 Commits

Author SHA1 Message Date
w-e-w 60efbc4618 API /sdapi/v1/server-reload-script-bodies 2024-12-24 08:23:47 +09:00
w-e-w a56190cdb1 API /sdapi/v1/server-reload-ui 2024-12-24 08:23:47 +09:00
w-e-w 04903af798 Merge pull request #16604 from Haoming02/ext-updt-parallel
Check for Extension Updates in Parallel
2024-12-18 03:21:48 +09:00
w-e-w e8c3b1f2a0 Merge pull request #16718 from Haoming02/bracket-checker-order
[Bracket Checker] Also check for the order of brackets
2024-12-18 02:37:30 +09:00
Haoming 8bf30e3c42 revert IIFE 2024-12-18 01:02:40 +08:00
Haoming fbc51fa210 skip escaped 2024-12-16 09:47:38 +08:00
Haoming 7025a2c4a5 check-for-order 2024-12-12 16:08:15 +08:00
w-e-w 0120768f63 Merge pull request #16687 from Haoming02/dropdown4format
Use gr.Dropdown for Image Formats
2024-11-28 17:39:12 +09:00
w-e-w b425b97ad6 improve img fromat description 2024-11-28 17:14:03 +09:00
w-e-w 539ea3982d use DropdownEditable
use DropdownEditable so user can input other formats if they require it
make the default png the first on the list
2024-11-28 14:10:44 +09:00
Haoming 65bd61e87c format-dropdown 2024-11-27 10:42:50 +08:00
w-e-w 023454b49e fix passing of literal backslash (#16671) 2024-11-20 21:33:59 -05:00
w-e-w cd869bb7a3 fix prompt-bracket-checker miscounting of literal tokens (#16669) 2024-11-20 12:29:41 -05:00
w-e-w 957888a100 Merge pull request #16667 from AUTOMATIC1111/fix/safetensors-bump
Bump safetensors to v0.4.5
2024-11-19 14:41:37 +09:00
catboxanon d2c9efb1f3 Bump safetensors to v0.4.5
Resolves #16650
2024-11-18 20:48:36 -05:00
w-e-w 7799859f9f Merge pull request #16620 from AUTOMATIC1111/fix/api-webp-lossless
Honor lossless WebP compression option in API
2024-11-02 01:28:12 +09:00
catboxanon ca3bedbd02 Honor lossless webp compression option in API 2024-11-01 11:32:52 -04:00
w-e-w 1b16c62608 use shared.hf_endpoint (#16611) 2024-10-30 13:01:32 -04:00
w-e-w 91de919451 Warn if WebUI is installed under a dot directory (#16584) 2024-10-30 09:34:37 -04:00
w-e-w aa52408aab Merge pull request #16606 from AUTOMATIC1111/fix/vweighting
Fix config for SDXL v-pred
2024-10-30 09:51:41 +09:00
catboxanon e6f36d9cdc sd_xl_v.yaml: use_checkpoint = False
In accordance with https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/15803
2024-10-29 13:27:32 -04:00
w-e-w 28323cf8ee XYZ option to disable grid (#16416) 2024-10-29 13:18:38 -04:00
w-e-w 533c7b7b7f Fix Default system None filter logic (#16309) 2024-10-29 13:13:16 -04:00
catboxanon ac28cad998 Fix weighting config for SDXL v-pred
Fixes a small oversight I made.
2024-10-29 11:49:09 -04:00
w-e-w 5206b938ef InputAccordion duplicate elem_id handling (#16381) 2024-10-29 11:03:21 -04:00
w-e-w 59481435f4 addEventListener {passive: false} (#16575) 2024-10-29 10:59:04 -04:00
viking1304 f31faf6f14 pyenv-win compatibility - another approach (#16287) 2024-10-29 10:54:58 -04:00
w-e-w 14c6d6cb3a Disable Hires checkpoint if same as First pass checkpoint (#16269) 2024-10-29 10:45:45 -04:00
w-e-w 4ec10bcdc1 Fix postprocessing_enable_in_main_ui ScriptPostprocessing elem_id (#16373) 2024-10-29 10:38:55 -04:00
w-e-w 0bf36cf2ad extra_only / main_ui_only ScriptPostprocessing (#16374) 2024-10-29 10:35:46 -04:00
w-e-w 820fe8d2b5 Allow newline in Extra Network activation text (#16428) 2024-10-29 10:30:08 -04:00
w-e-w deb3803a3a image embedding data cache (#16556) 2024-10-29 10:28:21 -04:00
w-e-w 95686227bd limit number of simultaneous updates
shared.opts.concurrent_git_fetch_limit
2024-10-29 20:16:15 +09:00
Haoming df74c3c638 threading 2024-10-29 14:12:42 +08:00
w-e-w d88a3c15f7 Merge pull request #16588 from bluelovers/patch-3
chore(js): avoid lots of `Wake Lock is not supported.`
2024-10-27 10:28:23 +09:00
w-e-w 38c8043229 Merge pull request #16523 from changeworld/fix/typo
Fix typo: Github -> GitHub
2024-10-27 01:09:41 +09:00
bluelovers ee0ad5cceb chore(js): avoid lots of Wake Lock is not supported. 2024-10-25 09:59:45 +08:00
w-e-w 984b952eb3 Fix DAT models download (#16302) 2024-10-24 09:05:51 -04:00
w-e-w 5865da28d1 Merge pull request #16569 from AUTOMATIC1111/feat/ztsnr-auto
Automatically enable ztSNR based on existence of key in `state_dict`
2024-10-20 11:40:02 +09:00
w-e-w bb1f39196e clarify readme: weget ... chmod +x webui.sh (#16251) 2024-10-19 20:58:53 -04:00
w-e-w 6a59766313 Add Skip Early CFG to XYZ (#16282)
Co-authored-by: Yevhenii Hurin <evgeny.gurin@gmail.com>
2024-10-19 20:56:12 -04:00
w-e-w 65423d2b33 MIME type text/css (#16406) 2024-10-19 20:52:47 -04:00
w-e-w c2bc187ce7 fix modalImageViewer preview/result flicker (#16426) 2024-10-19 20:51:59 -04:00
w-e-w d0b27dc906 Merge pull request #16300 from hello2564/fix_NGMS_pr_typo
fix NGMS pr typo
2024-10-20 09:42:21 +09:00
catboxanon c2ce1d3b9c Automatically enable ztSNR based on existence of key in state_dict 2024-10-19 19:58:13 -04:00
w-e-w bb4cbaf0cf Merge pull request #16341 from gutris1/devv
add break-word for geninfo in pnginfo
2024-10-20 08:27:12 +09:00
catboxanon c462e5a575 Merge pull request #16460 from AUTOMATIC1111/sd-1.5-url 2024-10-19 10:51:16 -04:00
AUTOMATIC1111 8b19b75270 Merge pull request #16567 from AUTOMATIC1111/feat/sdxl-vpred
Support and automatically detect SDXL V-prediction models
2024-10-19 17:40:56 +03:00
AUTOMATIC1111 907bfb5ef0 add w-e-w and catboxanon to codeowners file 2024-10-19 17:33:58 +03:00
catboxanon 1ae073c052 Support SDXL v-pred models 2024-10-19 06:53:19 -04:00
missionfloyd c9a06d1093 Use stable-diffusion-v1-5 repo instead 2024-10-08 16:50:39 -06:00
Takashi Takebayashi d8ad364617 Fix typo
Github -> GitHub
2024-10-03 14:33:37 +09:00
missionfloyd f57ec2b53b Update stable diffusion 1.5 URL 2024-09-03 19:58:29 -06:00
gutris1 9677b09b7c add break-word for geninfo in pnginfo 2024-08-07 17:37:23 +07:00
hello2564 cbaaf0af0e fix NGMS pr typo 2024-07-31 14:55:30 +08:00
AUTOMATIC1111 48239090f1 Merge branch 'master' into dev 2024-07-27 15:50:26 +03:00
AUTOMATIC1111 850e14923e Merge pull request #16275 from AUTOMATIC1111/fix-image-upscale-on-cpu
fix image upscale on cpu
2024-07-27 15:47:49 +03:00
w-e-w 8e0881d9ab fix image upscale on cpu
for some reason upscale using cpu will fail with
RuntimeError: Inplace update to inference tensor outside InferenceMode
switch from no_grad to inference_mode seems to have fixed it
2024-07-27 21:28:10 +09:00
AUTOMATIC1111 834297b13d Merge branch 'master' into dev 2024-07-27 07:09:08 +03:00
43 changed files with 546 additions and 198 deletions
+1 -12
View File
@@ -1,12 +1 @@
* @AUTOMATIC1111 * @AUTOMATIC1111 @w-e-w @catboxanon
# if you were managing a localization and were removed from this file, this is because
# the intended way to do localizations now is via extensions. See:
# https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Developing-extensions
# Make a repo with your localization and since you are still listed as a collaborator
# you can add it to the wiki page yourself. This change is because some people complained
# the git commit log is cluttered with things unrelated to almost everyone and
# because I believe this is the best overall for the project to handle localizations almost
# entirely without my oversight.
+1
View File
@@ -148,6 +148,7 @@ python_cmd="python3.11"
2. Navigate to the directory you would like the webui to be installed and execute the following command: 2. Navigate to the directory you would like the webui to be installed and execute the following command:
```bash ```bash
wget -q https://raw.githubusercontent.com/AUTOMATIC1111/stable-diffusion-webui/master/webui.sh wget -q https://raw.githubusercontent.com/AUTOMATIC1111/stable-diffusion-webui/master/webui.sh
chmod +x webui.sh
``` ```
Or just clone the repo wherever you want: Or just clone the repo wherever you want:
```bash ```bash
+98
View File
@@ -0,0 +1,98 @@
model:
target: sgm.models.diffusion.DiffusionEngine
params:
scale_factor: 0.13025
disable_first_stage_autocast: True
denoiser_config:
target: sgm.modules.diffusionmodules.denoiser.DiscreteDenoiser
params:
num_idx: 1000
weighting_config:
target: sgm.modules.diffusionmodules.denoiser_weighting.VWeighting
scaling_config:
target: sgm.modules.diffusionmodules.denoiser_scaling.VScaling
discretization_config:
target: sgm.modules.diffusionmodules.discretizer.LegacyDDPMDiscretization
network_config:
target: sgm.modules.diffusionmodules.openaimodel.UNetModel
params:
adm_in_channels: 2816
num_classes: sequential
use_checkpoint: False
in_channels: 4
out_channels: 4
model_channels: 320
attention_resolutions: [4, 2]
num_res_blocks: 2
channel_mult: [1, 2, 4]
num_head_channels: 64
use_spatial_transformer: True
use_linear_in_transformer: True
transformer_depth: [1, 2, 10] # note: the first is unused (due to attn_res starting at 2) 32, 16, 8 --> 64, 32, 16
context_dim: 2048
spatial_transformer_attn_type: softmax-xformers
legacy: False
conditioner_config:
target: sgm.modules.GeneralConditioner
params:
emb_models:
# crossattn cond
- is_trainable: False
input_key: txt
target: sgm.modules.encoders.modules.FrozenCLIPEmbedder
params:
layer: hidden
layer_idx: 11
# crossattn and vector cond
- is_trainable: False
input_key: txt
target: sgm.modules.encoders.modules.FrozenOpenCLIPEmbedder2
params:
arch: ViT-bigG-14
version: laion2b_s39b_b160k
freeze: True
layer: penultimate
always_return_pooled: True
legacy: False
# vector cond
- is_trainable: False
input_key: original_size_as_tuple
target: sgm.modules.encoders.modules.ConcatTimestepEmbedderND
params:
outdim: 256 # multiplied by two
# vector cond
- is_trainable: False
input_key: crop_coords_top_left
target: sgm.modules.encoders.modules.ConcatTimestepEmbedderND
params:
outdim: 256 # multiplied by two
# vector cond
- is_trainable: False
input_key: target_size_as_tuple
target: sgm.modules.encoders.modules.ConcatTimestepEmbedderND
params:
outdim: 256 # multiplied by two
first_stage_config:
target: sgm.models.autoencoder.AutoencoderKLInferenceWrapper
params:
embed_dim: 4
monitor: val/rec_loss
ddconfig:
attn_type: vanilla-xformers
double_z: true
z_channels: 4
resolution: 256
in_channels: 3
out_ch: 3
ch: 128
ch_mult: [1, 2, 4, 4]
num_res_blocks: 2
attn_resolutions: []
dropout: 0.0
lossconfig:
target: torch.nn.Identity
@@ -816,7 +816,7 @@ onUiLoaded(async() => {
// Increase or decrease brush size based on scroll direction // Increase or decrease brush size based on scroll direction
adjustBrushSize(elemId, e.deltaY); adjustBrushSize(elemId, e.deltaY);
} }
}); }, {passive: false});
// Handle the move event for pan functionality. Updates the panX and panY variables and applies the new transform to the target element. // Handle the move event for pan functionality. Updates the panX and panY variables and applies the new transform to the target element.
function handleMoveKeyDown(e) { function handleMoveKeyDown(e) {
+1 -1
View File
@@ -1,7 +1,7 @@
""" """
Hypertile module for splitting attention layers in SD-1.5 U-Net and SD-1.5 VAE Hypertile module for splitting attention layers in SD-1.5 U-Net and SD-1.5 VAE
Warn: The patch works well only if the input image has a width and height that are multiples of 128 Warn: The patch works well only if the input image has a width and height that are multiples of 128
Original author: @tfernd Github: https://github.com/tfernd/HyperTile Original author: @tfernd GitHub: https://github.com/tfernd/HyperTile
""" """
from __future__ import annotations from __future__ import annotations
@@ -34,14 +34,14 @@ class ScriptPostprocessingAutosizedCrop(scripts_postprocessing.ScriptPostprocess
with ui_components.InputAccordion(False, label="Auto-sized crop") as enable: with ui_components.InputAccordion(False, label="Auto-sized crop") as enable:
gr.Markdown('Each image is center-cropped with an automatically chosen width and height.') gr.Markdown('Each image is center-cropped with an automatically chosen width and height.')
with gr.Row(): with gr.Row():
mindim = gr.Slider(minimum=64, maximum=2048, step=8, label="Dimension lower bound", value=384, elem_id="postprocess_multicrop_mindim") mindim = gr.Slider(minimum=64, maximum=2048, step=8, label="Dimension lower bound", value=384, elem_id=self.elem_id_suffix("postprocess_multicrop_mindim"))
maxdim = gr.Slider(minimum=64, maximum=2048, step=8, label="Dimension upper bound", value=768, elem_id="postprocess_multicrop_maxdim") maxdim = gr.Slider(minimum=64, maximum=2048, step=8, label="Dimension upper bound", value=768, elem_id=self.elem_id_suffix("postprocess_multicrop_maxdim"))
with gr.Row(): with gr.Row():
minarea = gr.Slider(minimum=64 * 64, maximum=2048 * 2048, step=1, label="Area lower bound", value=64 * 64, elem_id="postprocess_multicrop_minarea") minarea = gr.Slider(minimum=64 * 64, maximum=2048 * 2048, step=1, label="Area lower bound", value=64 * 64, elem_id=self.elem_id_suffix("postprocess_multicrop_minarea"))
maxarea = gr.Slider(minimum=64 * 64, maximum=2048 * 2048, step=1, label="Area upper bound", value=640 * 640, elem_id="postprocess_multicrop_maxarea") maxarea = gr.Slider(minimum=64 * 64, maximum=2048 * 2048, step=1, label="Area upper bound", value=640 * 640, elem_id=self.elem_id_suffix("postprocess_multicrop_maxarea"))
with gr.Row(): with gr.Row():
objective = gr.Radio(["Maximize area", "Minimize error"], value="Maximize area", label="Resizing objective", elem_id="postprocess_multicrop_objective") objective = gr.Radio(["Maximize area", "Minimize error"], value="Maximize area", label="Resizing objective", elem_id=self.elem_id_suffix("postprocess_multicrop_objective"))
threshold = gr.Slider(minimum=0, maximum=1, step=0.01, label="Error threshold", value=0.1, elem_id="postprocess_multicrop_threshold") threshold = gr.Slider(minimum=0, maximum=1, step=0.01, label="Error threshold", value=0.1, elem_id=self.elem_id_suffix("postprocess_multicrop_threshold"))
return { return {
"enable": enable, "enable": enable,
@@ -11,10 +11,10 @@ class ScriptPostprocessingFocalCrop(scripts_postprocessing.ScriptPostprocessing)
def ui(self): def ui(self):
with ui_components.InputAccordion(False, label="Auto focal point crop") as enable: with ui_components.InputAccordion(False, label="Auto focal point crop") as enable:
face_weight = gr.Slider(label='Focal point face weight', value=0.9, minimum=0.0, maximum=1.0, step=0.05, elem_id="postprocess_focal_crop_face_weight") face_weight = gr.Slider(label='Focal point face weight', value=0.9, minimum=0.0, maximum=1.0, step=0.05, elem_id=self.elem_id_suffix("postprocess_focal_crop_face_weight"))
entropy_weight = gr.Slider(label='Focal point entropy weight', value=0.15, minimum=0.0, maximum=1.0, step=0.05, elem_id="postprocess_focal_crop_entropy_weight") entropy_weight = gr.Slider(label='Focal point entropy weight', value=0.15, minimum=0.0, maximum=1.0, step=0.05, elem_id=self.elem_id_suffix("postprocess_focal_crop_entropy_weight"))
edges_weight = gr.Slider(label='Focal point edges weight', value=0.5, minimum=0.0, maximum=1.0, step=0.05, elem_id="postprocess_focal_crop_edges_weight") edges_weight = gr.Slider(label='Focal point edges weight', value=0.5, minimum=0.0, maximum=1.0, step=0.05, elem_id=self.elem_id_suffix("postprocess_focal_crop_edges_weight"))
debug = gr.Checkbox(label='Create debug image', elem_id="train_process_focal_crop_debug") debug = gr.Checkbox(label='Create debug image', elem_id=self.elem_id_suffix("train_process_focal_crop_debug"))
return { return {
"enable": enable, "enable": enable,
@@ -35,8 +35,8 @@ class ScriptPostprocessingSplitOversized(scripts_postprocessing.ScriptPostproces
def ui(self): def ui(self):
with ui_components.InputAccordion(False, label="Split oversized images") as enable: with ui_components.InputAccordion(False, label="Split oversized images") as enable:
with gr.Row(): with gr.Row():
split_threshold = gr.Slider(label='Threshold', value=0.5, minimum=0.0, maximum=1.0, step=0.05, elem_id="postprocess_split_threshold") split_threshold = gr.Slider(label='Threshold', value=0.5, minimum=0.0, maximum=1.0, step=0.05, elem_id=self.elem_id_suffix("postprocess_split_threshold"))
overlap_ratio = gr.Slider(label='Overlap ratio', value=0.2, minimum=0.0, maximum=0.9, step=0.05, elem_id="postprocess_overlap_ratio") overlap_ratio = gr.Slider(label='Overlap ratio', value=0.2, minimum=0.0, maximum=0.9, step=0.05, elem_id=self.elem_id_suffix("postprocess_overlap_ratio"))
return { return {
"enable": enable, "enable": enable,
@@ -1,36 +1,69 @@
// Stable Diffusion WebUI - Bracket checker // Stable Diffusion WebUI - Bracket Checker
// By Hingashi no Florin/Bwin4L & @akx // By @Bwin4L, @akx, @w-e-w, @Haoming02
// Counts open and closed brackets (round, square, curly) in the prompt and negative prompt text boxes in the txt2img and img2img tabs. // Counts open and closed brackets (round, square, curly) in the prompt and negative prompt text boxes in the txt2img and img2img tabs.
// If there's a mismatch, the keyword counter turns red and if you hover on it, a tooltip tells you what's wrong. // If there's a mismatch, the keyword counter turns red, and if you hover on it, a tooltip tells you what's wrong.
function checkBrackets(textArea, counterElt) { function checkBrackets(textArea, counterElem) {
var counts = {}; const pairs = [
(textArea.value.match(/[(){}[\]]/g) || []).forEach(bracket => { ['(', ')', 'round brackets'],
counts[bracket] = (counts[bracket] || 0) + 1; ['[', ']', 'square brackets'],
}); ['{', '}', 'curly brackets']
var errors = []; ];
function checkPair(open, close, kind) { const counts = {};
if (counts[open] !== counts[close]) { const errors = new Set();
errors.push( let i = 0;
`${open}...${close} - Detected ${counts[open] || 0} opening and ${counts[close] || 0} closing ${kind}.`
); while (i < textArea.value.length) {
let char = textArea.value[i];
let escaped = false;
while (char === '\\' && i + 1 < textArea.value.length) {
escaped = !escaped;
i++;
char = textArea.value[i];
}
if (escaped) {
i++;
continue;
}
for (const [open, close, label] of pairs) {
if (char === open) {
counts[label] = (counts[label] || 0) + 1;
} else if (char === close) {
counts[label] = (counts[label] || 0) - 1;
if (counts[label] < 0) {
errors.add(`Incorrect order of ${label}.`);
}
}
}
i++;
}
for (const [open, close, label] of pairs) {
if (counts[label] == undefined) {
continue;
}
if (counts[label] > 0) {
errors.add(`${open} ... ${close} - Detected ${counts[label]} more opening than closing ${label}.`);
} else if (counts[label] < 0) {
errors.add(`${open} ... ${close} - Detected ${-counts[label]} more closing than opening ${label}.`);
} }
} }
checkPair('(', ')', 'round brackets'); counterElem.title = [...errors].join('\n');
checkPair('[', ']', 'square brackets'); counterElem.classList.toggle('error', errors.size !== 0);
checkPair('{', '}', 'curly brackets');
counterElt.title = errors.join('\n');
counterElt.classList.toggle('error', errors.length !== 0);
} }
function setupBracketChecking(id_prompt, id_counter) { function setupBracketChecking(id_prompt, id_counter) {
var textarea = gradioApp().querySelector("#" + id_prompt + " > label > textarea"); const textarea = gradioApp().querySelector(`#${id_prompt} > label > textarea`);
var counter = gradioApp().getElementById(id_counter); const counter = gradioApp().getElementById(id_counter);
if (textarea && counter) { if (textarea && counter) {
textarea.addEventListener("input", () => checkBrackets(textarea, counter)); onEdit(`${id_prompt}_BracketChecking`, textarea, 400, () => checkBrackets(textarea, counter));
} }
} }
+1 -1
View File
@@ -1,7 +1,7 @@
<div> <div>
<a href="{api_docs}">API</a> <a href="{api_docs}">API</a>
 •   • 
<a href="https://github.com/AUTOMATIC1111/stable-diffusion-webui">Github</a> <a href="https://github.com/AUTOMATIC1111/stable-diffusion-webui">GitHub</a>
 •   • 
<a href="https://gradio.app">Gradio</a> <a href="https://gradio.app">Gradio</a>
 •   • 
+1 -1
View File
@@ -104,7 +104,7 @@ var contextMenuInit = function() {
e.preventDefault(); e.preventDefault();
} }
}); });
}); }, {passive: false});
}); });
eventListenerApplied = true; eventListenerApplied = true;
+1 -1
View File
@@ -201,7 +201,7 @@ function setupExtraNetworks() {
setupExtraNetworksForTab('img2img'); setupExtraNetworksForTab('img2img');
} }
var re_extranet = /<([^:^>]+:[^:]+):[\d.]+>(.*)/; var re_extranet = /<([^:^>]+:[^:]+):[\d.]+>(.*)/s;
var re_extranet_g = /<([^:^>]+:[^:]+):[\d.]+>/g; var re_extranet_g = /<([^:^>]+:[^:]+):[\d.]+>/g;
var re_extranet_neg = /\(([^:^>]+:[\d.]+)\)/; var re_extranet_neg = /\(([^:^>]+:[\d.]+)\)/;
+18 -12
View File
@@ -13,6 +13,7 @@ function showModal(event) {
if (modalImage.style.display === 'none') { if (modalImage.style.display === 'none') {
lb.style.setProperty('background-image', 'url(' + source.src + ')'); lb.style.setProperty('background-image', 'url(' + source.src + ')');
} }
updateModalImage();
lb.style.display = "flex"; lb.style.display = "flex";
lb.focus(); lb.focus();
@@ -31,21 +32,26 @@ function negmod(n, m) {
return ((n % m) + m) % m; return ((n % m) + m) % m;
} }
function updateModalImage() {
const modalImage = gradioApp().getElementById("modalImage");
let currentButton = selected_gallery_button();
let preview = gradioApp().querySelectorAll('.livePreview > img');
if (opts.js_live_preview_in_modal_lightbox && preview.length > 0) {
// show preview image if available
modalImage.src = preview[preview.length - 1].src;
} else if (currentButton?.children?.length > 0 && modalImage.src != currentButton.children[0].src) {
modalImage.src = currentButton.children[0].src;
if (modalImage.style.display === 'none') {
const modal = gradioApp().getElementById("lightboxModal");
modal.style.setProperty('background-image', `url(${modalImage.src})`);
}
}
}
function updateOnBackgroundChange() { function updateOnBackgroundChange() {
const modalImage = gradioApp().getElementById("modalImage"); const modalImage = gradioApp().getElementById("modalImage");
if (modalImage && modalImage.offsetParent) { if (modalImage && modalImage.offsetParent) {
let currentButton = selected_gallery_button(); updateModalImage();
let preview = gradioApp().querySelectorAll('.livePreview > img');
if (opts.js_live_preview_in_modal_lightbox && preview.length > 0) {
// show preview image if available
modalImage.src = preview[preview.length - 1].src;
} else if (currentButton?.children?.length > 0 && modalImage.src != currentButton.children[0].src) {
modalImage.src = currentButton.children[0].src;
if (modalImage.style.display === 'none') {
const modal = gradioApp().getElementById("lightboxModal");
modal.style.setProperty('background-image', `url(${modalImage.src})`);
}
}
} }
} }
+2 -1
View File
@@ -79,11 +79,12 @@ function requestProgress(id_task, progressbarContainer, gallery, atEnd, onProgre
var wakeLock = null; var wakeLock = null;
var requestWakeLock = async function() { var requestWakeLock = async function() {
if (!opts.prevent_screen_sleep_during_generation || wakeLock) return; if (!opts.prevent_screen_sleep_during_generation || wakeLock !== null) return;
try { try {
wakeLock = await navigator.wakeLock.request('screen'); wakeLock = await navigator.wakeLock.request('screen');
} catch (err) { } catch (err) {
console.error('Wake Lock is not supported.'); console.error('Wake Lock is not supported.');
wakeLock = false;
} }
}; };
+1 -1
View File
@@ -124,7 +124,7 @@
} else { } else {
R.screenX = evt.changedTouches[0].screenX; R.screenX = evt.changedTouches[0].screenX;
} }
}); }, {passive: false});
}); });
resizeHandle.addEventListener('dblclick', onDoubleClick); resizeHandle.addEventListener('dblclick', onDoubleClick);
+10 -1
View File
@@ -122,7 +122,7 @@ def encode_pil_to_base64(image):
if opts.samples_format.lower() in ("jpg", "jpeg"): if opts.samples_format.lower() in ("jpg", "jpeg"):
image.save(output_bytes, format="JPEG", exif = exif_bytes, quality=opts.jpeg_quality) image.save(output_bytes, format="JPEG", exif = exif_bytes, quality=opts.jpeg_quality)
else: else:
image.save(output_bytes, format="WEBP", exif = exif_bytes, quality=opts.jpeg_quality) image.save(output_bytes, format="WEBP", exif = exif_bytes, quality=opts.jpeg_quality, lossless=opts.webp_lossless)
else: else:
raise HTTPException(status_code=500, detail="Invalid image format") raise HTTPException(status_code=500, detail="Invalid image format")
@@ -249,6 +249,8 @@ class Api:
self.add_api_route("/sdapi/v1/server-kill", self.kill_webui, methods=["POST"]) self.add_api_route("/sdapi/v1/server-kill", self.kill_webui, methods=["POST"])
self.add_api_route("/sdapi/v1/server-restart", self.restart_webui, methods=["POST"]) self.add_api_route("/sdapi/v1/server-restart", self.restart_webui, methods=["POST"])
self.add_api_route("/sdapi/v1/server-stop", self.stop_webui, methods=["POST"]) self.add_api_route("/sdapi/v1/server-stop", self.stop_webui, methods=["POST"])
self.add_api_route("/sdapi/v1/server-reload-ui", self.reload_webui, methods=["POST"])
self.add_api_route("/sdapi/v1/server-reload-script-bodies", self.reload_script_bodies, methods=["POST"])
self.default_script_arg_txt2img = [] self.default_script_arg_txt2img = []
self.default_script_arg_img2img = [] self.default_script_arg_img2img = []
@@ -926,3 +928,10 @@ class Api:
shared.state.server_command = "stop" shared.state.server_command = "stop"
return Response("Stopping.") return Response("Stopping.")
def reload_webui(self):
shared.state.request_restart()
return Response("Reloading.")
def reload_script_bodies(self):
scripts.reload_script_body_only()
return Response("Reload script bodies.")
+18 -4
View File
@@ -1,7 +1,7 @@
import os import os
from modules import modelloader, errors from modules import modelloader, errors
from modules.shared import cmd_opts, opts from modules.shared import cmd_opts, opts, hf_endpoint
from modules.upscaler import Upscaler, UpscalerData from modules.upscaler import Upscaler, UpscalerData
from modules.upscaler_utils import upscale_with_model from modules.upscaler_utils import upscale_with_model
@@ -49,7 +49,18 @@ class UpscalerDAT(Upscaler):
scaler.local_data_path = modelloader.load_file_from_url( scaler.local_data_path = modelloader.load_file_from_url(
scaler.data_path, scaler.data_path,
model_dir=self.model_download_path, model_dir=self.model_download_path,
hash_prefix=scaler.sha256,
) )
if os.path.getsize(scaler.local_data_path) < 200:
# Re-download if the file is too small, probably an LFS pointer
scaler.local_data_path = modelloader.load_file_from_url(
scaler.data_path,
model_dir=self.model_download_path,
hash_prefix=scaler.sha256,
re_download=True,
)
if not os.path.exists(scaler.local_data_path): if not os.path.exists(scaler.local_data_path):
raise FileNotFoundError(f"DAT data missing: {scaler.local_data_path}") raise FileNotFoundError(f"DAT data missing: {scaler.local_data_path}")
return scaler return scaler
@@ -60,20 +71,23 @@ def get_dat_models(scaler):
return [ return [
UpscalerData( UpscalerData(
name="DAT x2", name="DAT x2",
path="https://github.com/n0kovo/dat_upscaler_models/raw/main/DAT/DAT_x2.pth", path=f"{hf_endpoint}/w-e-w/DAT/resolve/main/experiments/pretrained_models/DAT/DAT_x2.pth",
scale=2, scale=2,
upscaler=scaler, upscaler=scaler,
sha256='7760aa96e4ee77e29d4f89c3a4486200042e019461fdb8aa286f49aa00b89b51',
), ),
UpscalerData( UpscalerData(
name="DAT x3", name="DAT x3",
path="https://github.com/n0kovo/dat_upscaler_models/raw/main/DAT/DAT_x3.pth", path=f"{hf_endpoint}/w-e-w/DAT/resolve/main/experiments/pretrained_models/DAT/DAT_x3.pth",
scale=3, scale=3,
upscaler=scaler, upscaler=scaler,
sha256='581973e02c06f90d4eb90acf743ec9604f56f3c2c6f9e1e2c2b38ded1f80d197',
), ),
UpscalerData( UpscalerData(
name="DAT x4", name="DAT x4",
path="https://github.com/n0kovo/dat_upscaler_models/raw/main/DAT/DAT_x4.pth", path=f"{hf_endpoint}/w-e-w/DAT/resolve/main/experiments/pretrained_models/DAT/DAT_x4.pth",
scale=4, scale=4,
upscaler=scaler, upscaler=scaler,
sha256='391a6ce69899dff5ea3214557e9d585608254579217169faf3d4c353caff049e',
), ),
] ]
+1 -1
View File
@@ -23,7 +23,7 @@ def run_pnginfo(image):
info = '' info = ''
for key, text in items.items(): for key, text in items.items():
info += f""" info += f"""
<div> <div class="infotext">
<p><b>{plaintext_to_html(str(key))}</b></p> <p><b>{plaintext_to_html(str(key))}</b></p>
<p>{plaintext_to_html(str(text))}</p> <p>{plaintext_to_html(str(text))}</p>
</div> </div>
+1 -24
View File
@@ -10,6 +10,7 @@ import torch
from modules import shared from modules import shared
from modules.upscaler import Upscaler, UpscalerLanczos, UpscalerNearest, UpscalerNone from modules.upscaler import Upscaler, UpscalerLanczos, UpscalerNearest, UpscalerNone
from modules.util import load_file_from_url # noqa, backwards compatibility
if TYPE_CHECKING: if TYPE_CHECKING:
import spandrel import spandrel
@@ -17,30 +18,6 @@ if TYPE_CHECKING:
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def load_file_from_url(
url: str,
*,
model_dir: str,
progress: bool = True,
file_name: str | None = None,
hash_prefix: str | None = None,
) -> str:
"""Download a file from `url` into `model_dir`, using the file present if possible.
Returns the path to the downloaded file.
"""
os.makedirs(model_dir, exist_ok=True)
if not file_name:
parts = urlparse(url)
file_name = os.path.basename(parts.path)
cached_file = os.path.abspath(os.path.join(model_dir, file_name))
if not os.path.exists(cached_file):
print(f'Downloading: "{url}" to {cached_file}\n')
from torch.hub import download_url_to_file
download_url_to_file(url, cached_file, progress=progress, hash_prefix=hash_prefix)
return cached_file
def load_models(model_path: str, model_url: str = None, command_path: str = None, ext_filter=None, download_name=None, ext_blacklist=None, hash_prefix=None) -> list: def load_models(model_path: str, model_url: str = None, command_path: str = None, ext_filter=None, download_name=None, ext_blacklist=None, hash_prefix=None) -> list:
""" """
A one-and done loader to try finding the desired models in specified directories. A one-and done loader to try finding the desired models in specified directories.
+3 -3
View File
@@ -24,7 +24,7 @@ class SafetensorsMapping(typing.Mapping):
return self.file.get_tensor(key) return self.file.get_tensor(key)
CLIPL_URL = "https://huggingface.co/AUTOMATIC/stable-diffusion-3-medium-text-encoders/resolve/main/clip_l.safetensors" CLIPL_URL = f"{shared.hf_endpoint}/AUTOMATIC/stable-diffusion-3-medium-text-encoders/resolve/main/clip_l.safetensors"
CLIPL_CONFIG = { CLIPL_CONFIG = {
"hidden_act": "quick_gelu", "hidden_act": "quick_gelu",
"hidden_size": 768, "hidden_size": 768,
@@ -33,7 +33,7 @@ CLIPL_CONFIG = {
"num_hidden_layers": 12, "num_hidden_layers": 12,
} }
CLIPG_URL = "https://huggingface.co/AUTOMATIC/stable-diffusion-3-medium-text-encoders/resolve/main/clip_g.safetensors" CLIPG_URL = f"{shared.hf_endpoint}/AUTOMATIC/stable-diffusion-3-medium-text-encoders/resolve/main/clip_g.safetensors"
CLIPG_CONFIG = { CLIPG_CONFIG = {
"hidden_act": "gelu", "hidden_act": "gelu",
"hidden_size": 1280, "hidden_size": 1280,
@@ -43,7 +43,7 @@ CLIPG_CONFIG = {
"textual_inversion_key": "clip_g", "textual_inversion_key": "clip_g",
} }
T5_URL = "https://huggingface.co/AUTOMATIC/stable-diffusion-3-medium-text-encoders/resolve/main/t5xxl_fp16.safetensors" T5_URL = f"{shared.hf_endpoint}/AUTOMATIC/stable-diffusion-3-medium-text-encoders/resolve/main/t5xxl_fp16.safetensors"
T5_CONFIG = { T5_CONFIG = {
"d_ff": 10240, "d_ff": 10240,
"d_model": 4096, "d_model": 4096,
+4 -1
View File
@@ -1259,7 +1259,10 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing):
if self.hr_checkpoint_info is None: if self.hr_checkpoint_info is None:
raise Exception(f'Could not find checkpoint with name {self.hr_checkpoint_name}') raise Exception(f'Could not find checkpoint with name {self.hr_checkpoint_name}')
self.extra_generation_params["Hires checkpoint"] = self.hr_checkpoint_info.short_title if shared.sd_model.sd_checkpoint_info == self.hr_checkpoint_info:
self.hr_checkpoint_info = None
else:
self.extra_generation_params["Hires checkpoint"] = self.hr_checkpoint_info.short_title
if self.hr_sampler_name is not None and self.hr_sampler_name != self.sampler_name: if self.hr_sampler_name is not None and self.hr_sampler_name != self.sampler_name:
self.extra_generation_params["Hires sampler"] = self.hr_sampler_name self.extra_generation_params["Hires sampler"] = self.hr_sampler_name
+2 -1
View File
@@ -13,6 +13,7 @@ class ScriptPostprocessingForMainUI(scripts.Script):
return scripts.AlwaysVisible return scripts.AlwaysVisible
def ui(self, is_img2img): def ui(self, is_img2img):
self.script.tab_name = '_img2img' if is_img2img else '_txt2img'
self.postprocessing_controls = self.script.ui() self.postprocessing_controls = self.script.ui()
return self.postprocessing_controls.values() return self.postprocessing_controls.values()
@@ -33,7 +34,7 @@ def create_auto_preprocessing_script_data():
for name in shared.opts.postprocessing_enable_in_main_ui: for name in shared.opts.postprocessing_enable_in_main_ui:
script = next(iter([x for x in scripts.postprocessing_scripts_data if x.script_class.name == name]), None) script = next(iter([x for x in scripts.postprocessing_scripts_data if x.script_class.name == name]), None)
if script is None: if script is None or script.script_class.extra_only:
continue continue
constructor = lambda s=script: ScriptPostprocessingForMainUI(s.script_class()) constructor = lambda s=script: ScriptPostprocessingForMainUI(s.script_class())
+31 -5
View File
@@ -1,3 +1,4 @@
import re
import dataclasses import dataclasses
import os import os
import gradio as gr import gradio as gr
@@ -59,6 +60,10 @@ class ScriptPostprocessing:
args_from = None args_from = None
args_to = None args_to = None
# define if the script should be used only in extras or main UI
extra_only = None
main_ui_only = None
order = 1000 order = 1000
"""scripts will be ordred by this value in postprocessing UI""" """scripts will be ordred by this value in postprocessing UI"""
@@ -97,6 +102,31 @@ class ScriptPostprocessing:
def image_changed(self): def image_changed(self):
pass pass
tab_name = '' # used by ScriptPostprocessingForMainUI
replace_pattern = re.compile(r'\s')
rm_pattern = re.compile(r'[^a-z_0-9]')
def elem_id(self, item_id):
"""
Helper function to generate id for a HTML element
constructs final id out of script name and user-supplied item_id
'script_extras_{self.name.lower()}_{item_id}'
{tab_name} will append to the end of the id if set
tab_name will be set to '_img2img' or '_txt2img' if use by ScriptPostprocessingForMainUI
Extensions should use this function to generate element IDs
"""
return self.elem_id_suffix(f'extras_{self.name.lower()}_{item_id}')
def elem_id_suffix(self, base_id):
"""
Append tab_name to the base_id
Extensions that already have specific there element IDs and wish to keep their IDs the same when possible should use this function
"""
base_id = self.rm_pattern.sub('', self.replace_pattern.sub('_', base_id))
return f'{base_id}{self.tab_name}'
def wrap_call(func, filename, funcname, *args, default=None, **kwargs): def wrap_call(func, filename, funcname, *args, default=None, **kwargs):
try: try:
@@ -119,10 +149,6 @@ class ScriptPostprocessingRunner:
for script_data in scripts_data: for script_data in scripts_data:
script: ScriptPostprocessing = script_data.script_class() script: ScriptPostprocessing = script_data.script_class()
script.filename = script_data.path script.filename = script_data.path
if script.name == "Simple Upscale":
continue
self.scripts.append(script) self.scripts.append(script)
def create_script_ui(self, script, inputs): def create_script_ui(self, script, inputs):
@@ -152,7 +178,7 @@ class ScriptPostprocessingRunner:
return len(self.scripts) return len(self.scripts)
filtered_scripts = [script for script in self.scripts if script.name not in scripts_filter_out] filtered_scripts = [script for script in self.scripts if script.name not in scripts_filter_out and not script.main_ui_only]
script_scores = {script.name: (script_score(script.name), script.order, script.name, original_index) for original_index, script in enumerate(filtered_scripts)} script_scores = {script.name: (script_score(script.name), script.order, script.name, original_index) for original_index, script in enumerate(filtered_scripts)}
return sorted(filtered_scripts, key=lambda x: script_scores[x.name]) return sorted(filtered_scripts, key=lambda x: script_scores[x.name])
+1 -1
View File
@@ -76,7 +76,7 @@ class DisableInitialization(ReplaceHelper):
def transformers_utils_hub_get_file_from_cache(original, url, *args, **kwargs): def transformers_utils_hub_get_file_from_cache(original, url, *args, **kwargs):
# this file is always 404, prevent making request # this file is always 404, prevent making request
if url == 'https://huggingface.co/openai/clip-vit-large-patch14/resolve/main/added_tokens.json' or url == 'openai/clip-vit-large-patch14' and args[0] == 'added_tokens.json': if url == f'{shared.hf_endpoint}/openai/clip-vit-large-patch14/resolve/main/added_tokens.json' or url == 'openai/clip-vit-large-patch14' and args[0] == 'added_tokens.json':
return None return None
try: try:
+10 -5
View File
@@ -159,7 +159,7 @@ def list_models():
model_url = None model_url = None
expected_sha256 = None expected_sha256 = None
else: else:
model_url = f"{shared.hf_endpoint}/runwayml/stable-diffusion-v1-5/resolve/main/v1-5-pruned-emaonly.safetensors" model_url = f"{shared.hf_endpoint}/stable-diffusion-v1-5/stable-diffusion-v1-5/resolve/main/v1-5-pruned-emaonly.safetensors"
expected_sha256 = '6ce0161689b3853acaa03779ec93eafe75a02f4ced659bee03f50797806fa2fa' expected_sha256 = '6ce0161689b3853acaa03779ec93eafe75a02f4ced659bee03f50797806fa2fa'
model_list = modelloader.load_models(model_path=model_path, model_url=model_url, command_path=shared.cmd_opts.ckpt_dir, ext_filter=[".ckpt", ".safetensors"], download_name="v1-5-pruned-emaonly.safetensors", ext_blacklist=[".vae.ckpt", ".vae.safetensors"], hash_prefix=expected_sha256) model_list = modelloader.load_models(model_path=model_path, model_url=model_url, command_path=shared.cmd_opts.ckpt_dir, ext_filter=[".ckpt", ".safetensors"], download_name="v1-5-pruned-emaonly.safetensors", ext_blacklist=[".vae.ckpt", ".vae.safetensors"], hash_prefix=expected_sha256)
@@ -423,6 +423,10 @@ def load_model_weights(model, checkpoint_info: CheckpointInfo, state_dict, timer
set_model_type(model, state_dict) set_model_type(model, state_dict)
set_model_fields(model) set_model_fields(model)
if 'ztsnr' in state_dict:
model.ztsnr = True
else:
model.ztsnr = False
if model.is_sdxl: if model.is_sdxl:
sd_models_xl.extend_sdxl(model) sd_models_xl.extend_sdxl(model)
@@ -661,7 +665,7 @@ def apply_alpha_schedule_override(sd_model, p=None):
p.extra_generation_params['Downcast alphas_cumprod'] = opts.use_downcasted_alpha_bar p.extra_generation_params['Downcast alphas_cumprod'] = opts.use_downcasted_alpha_bar
sd_model.alphas_cumprod = sd_model.alphas_cumprod.half().to(shared.device) sd_model.alphas_cumprod = sd_model.alphas_cumprod.half().to(shared.device)
if opts.sd_noise_schedule == "Zero Terminal SNR": if opts.sd_noise_schedule == "Zero Terminal SNR" or (hasattr(sd_model, 'ztsnr') and sd_model.ztsnr):
if p is not None: if p is not None:
p.extra_generation_params['Noise Schedule'] = opts.sd_noise_schedule p.extra_generation_params['Noise Schedule'] = opts.sd_noise_schedule
sd_model.alphas_cumprod = rescale_zero_terminal_snr_abar(sd_model.alphas_cumprod).to(shared.device) sd_model.alphas_cumprod = rescale_zero_terminal_snr_abar(sd_model.alphas_cumprod).to(shared.device)
@@ -783,7 +787,7 @@ def get_obj_from_str(string, reload=False):
return getattr(importlib.import_module(module, package=None), cls) return getattr(importlib.import_module(module, package=None), cls)
def load_model(checkpoint_info=None, already_loaded_state_dict=None): def load_model(checkpoint_info=None, already_loaded_state_dict=None, checkpoint_config=None):
from modules import sd_hijack from modules import sd_hijack
checkpoint_info = checkpoint_info or select_checkpoint() checkpoint_info = checkpoint_info or select_checkpoint()
@@ -801,7 +805,8 @@ def load_model(checkpoint_info=None, already_loaded_state_dict=None):
else: else:
state_dict = get_checkpoint_state_dict(checkpoint_info, timer) state_dict = get_checkpoint_state_dict(checkpoint_info, timer)
checkpoint_config = sd_models_config.find_checkpoint_config(state_dict, checkpoint_info) if not checkpoint_config:
checkpoint_config = sd_models_config.find_checkpoint_config(state_dict, checkpoint_info)
clip_is_included_into_sd = any(x for x in [sd1_clip_weight, sd2_clip_weight, sdxl_clip_weight, sdxl_refiner_clip_weight] if x in state_dict) clip_is_included_into_sd = any(x for x in [sd1_clip_weight, sd2_clip_weight, sdxl_clip_weight, sdxl_refiner_clip_weight] if x in state_dict)
timer.record("find config") timer.record("find config")
@@ -974,7 +979,7 @@ def reload_model_weights(sd_model=None, info=None, forced_reload=False):
if sd_model is not None: if sd_model is not None:
send_model_to_trash(sd_model) send_model_to_trash(sd_model)
load_model(checkpoint_info, already_loaded_state_dict=state_dict) load_model(checkpoint_info, already_loaded_state_dict=state_dict, checkpoint_config=checkpoint_config)
return model_data.sd_model return model_data.sd_model
try: try:
+4
View File
@@ -14,6 +14,7 @@ config_sd2 = os.path.join(sd_repo_configs_path, "v2-inference.yaml")
config_sd2v = os.path.join(sd_repo_configs_path, "v2-inference-v.yaml") config_sd2v = os.path.join(sd_repo_configs_path, "v2-inference-v.yaml")
config_sd2_inpainting = os.path.join(sd_repo_configs_path, "v2-inpainting-inference.yaml") config_sd2_inpainting = os.path.join(sd_repo_configs_path, "v2-inpainting-inference.yaml")
config_sdxl = os.path.join(sd_xl_repo_configs_path, "sd_xl_base.yaml") config_sdxl = os.path.join(sd_xl_repo_configs_path, "sd_xl_base.yaml")
config_sdxlv = os.path.join(sd_configs_path, "sd_xl_v.yaml")
config_sdxl_refiner = os.path.join(sd_xl_repo_configs_path, "sd_xl_refiner.yaml") config_sdxl_refiner = os.path.join(sd_xl_repo_configs_path, "sd_xl_refiner.yaml")
config_sdxl_inpainting = os.path.join(sd_configs_path, "sd_xl_inpaint.yaml") config_sdxl_inpainting = os.path.join(sd_configs_path, "sd_xl_inpaint.yaml")
config_depth_model = os.path.join(sd_repo_configs_path, "v2-midas-inference.yaml") config_depth_model = os.path.join(sd_repo_configs_path, "v2-midas-inference.yaml")
@@ -81,6 +82,9 @@ def guess_model_config_from_state_dict(sd, filename):
if diffusion_model_input.shape[1] == 9: if diffusion_model_input.shape[1] == 9:
return config_sdxl_inpainting return config_sdxl_inpainting
else: else:
if ('v_pred' in sd):
del sd['v_pred']
return config_sdxlv
return config_sdxl return config_sdxl
if sd.get('conditioner.embedders.0.model.ln_final.weight', None) is not None: if sd.get('conditioner.embedders.0.model.ln_final.weight', None) is not None:
+5 -3
View File
@@ -16,10 +16,12 @@ def dat_models_names():
return [x.name for x in modules.dat_model.get_dat_models(None)] return [x.name for x in modules.dat_model.get_dat_models(None)]
def postprocessing_scripts(): def postprocessing_scripts(filter_out_extra_only=False, filter_out_main_ui_only=False):
import modules.scripts import modules.scripts
return list(filter(
return modules.scripts.scripts_postproc.scripts lambda s: (not filter_out_extra_only or not s.extra_only) and (not filter_out_main_ui_only or not s.main_ui_only),
modules.scripts.scripts_postproc.scripts,
))
def sd_vae_items(): def sd_vae_items():
+9 -7
View File
@@ -33,12 +33,12 @@ categories.register_category("training", "Training")
options_templates.update(options_section(('saving-images', "Saving images/grids", "saving"), { options_templates.update(options_section(('saving-images', "Saving images/grids", "saving"), {
"samples_save": OptionInfo(True, "Always save all generated images"), "samples_save": OptionInfo(True, "Always save all generated images"),
"samples_format": OptionInfo('png', 'File format for images'), "samples_format": OptionInfo('png', 'File format for images', ui_components.DropdownEditable, {"choices": ("png", "jpg", "jpeg", "webp", "avif")}).info("manual input of <a href='https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html' target='_blank'>other formats</a> is possible, but compatibility is not guaranteed"),
"samples_filename_pattern": OptionInfo("", "Images filename pattern", component_args=hide_dirs).link("wiki", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Custom-Images-Filename-Name-and-Subdirectory"), "samples_filename_pattern": OptionInfo("", "Images filename pattern", component_args=hide_dirs).link("wiki", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Custom-Images-Filename-Name-and-Subdirectory"),
"save_images_add_number": OptionInfo(True, "Add number to filename when saving", component_args=hide_dirs), "save_images_add_number": OptionInfo(True, "Add number to filename when saving", component_args=hide_dirs),
"save_images_replace_action": OptionInfo("Replace", "Saving the image to an existing file", gr.Radio, {"choices": ["Replace", "Add number suffix"], **hide_dirs}), "save_images_replace_action": OptionInfo("Replace", "Saving the image to an existing file", gr.Radio, {"choices": ["Replace", "Add number suffix"], **hide_dirs}),
"grid_save": OptionInfo(True, "Always save all generated image grids"), "grid_save": OptionInfo(True, "Always save all generated image grids"),
"grid_format": OptionInfo('png', 'File format for grids'), "grid_format": OptionInfo('png', 'File format for grids', ui_components.DropdownEditable, {"choices": ("png", "jpg", "jpeg", "webp", "avif")}).info("manual input of <a href='https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html' target='_blank'>other formats</a> is possible, but compatibility is not guaranteed"),
"grid_extended_filename": OptionInfo(False, "Add extended info (seed, prompt) to filename when saving grid"), "grid_extended_filename": OptionInfo(False, "Add extended info (seed, prompt) to filename when saving grid"),
"grid_only_if_multiple": OptionInfo(True, "Do not save grids consisting of one picture"), "grid_only_if_multiple": OptionInfo(True, "Do not save grids consisting of one picture"),
"grid_prevent_empty_spots": OptionInfo(False, "Prevent empty spots in grid (when set to autodetect)"), "grid_prevent_empty_spots": OptionInfo(False, "Prevent empty spots in grid (when set to autodetect)"),
@@ -128,6 +128,7 @@ options_templates.update(options_section(('system', "System", "system"), {
"disable_mmap_load_safetensors": OptionInfo(False, "Disable memmapping for loading .safetensors files.").info("fixes very slow loading speed in some cases"), "disable_mmap_load_safetensors": OptionInfo(False, "Disable memmapping for loading .safetensors files.").info("fixes very slow loading speed in some cases"),
"hide_ldm_prints": OptionInfo(True, "Prevent Stability-AI's ldm/sgm modules from printing noise to console."), "hide_ldm_prints": OptionInfo(True, "Prevent Stability-AI's ldm/sgm modules from printing noise to console."),
"dump_stacks_on_signal": OptionInfo(False, "Print stack traces before exiting the program with ctrl+c."), "dump_stacks_on_signal": OptionInfo(False, "Print stack traces before exiting the program with ctrl+c."),
"concurrent_git_fetch_limit": OptionInfo(16, "Number of simultaneous extension update checks ", gr.Slider, {"step": 1, "minimum": 1, "maximum": 100}).info("reduce extension update check time"),
})) }))
options_templates.update(options_section(('profiler', "Profiler", "system"), { options_templates.update(options_section(('profiler', "Profiler", "system"), {
@@ -231,7 +232,7 @@ options_templates.update(options_section(('img2img', "img2img", "sd"), {
options_templates.update(options_section(('optimizations', "Optimizations", "sd"), { options_templates.update(options_section(('optimizations', "Optimizations", "sd"), {
"cross_attention_optimization": OptionInfo("Automatic", "Cross attention optimization", gr.Dropdown, lambda: {"choices": shared_items.cross_attention_optimizations()}), "cross_attention_optimization": OptionInfo("Automatic", "Cross attention optimization", gr.Dropdown, lambda: {"choices": shared_items.cross_attention_optimizations()}),
"s_min_uncond": OptionInfo(0.0, "Negative Guidance minimum sigma", gr.Slider, {"minimum": 0.0, "maximum": 15.0, "step": 0.01}, infotext='NGMS').link("PR", "https://github.com/AUTOMATIC1111/stablediffusion-webui/pull/9177").info("skip negative prompt for some steps when the image is almost ready; 0=disable, higher=faster"), "s_min_uncond": OptionInfo(0.0, "Negative Guidance minimum sigma", gr.Slider, {"minimum": 0.0, "maximum": 15.0, "step": 0.01}, infotext='NGMS').link("PR", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/9177").info("skip negative prompt for some steps when the image is almost ready; 0=disable, higher=faster"),
"s_min_uncond_all": OptionInfo(False, "Negative Guidance minimum sigma all steps", infotext='NGMS all steps').info("By default, NGMS above skips every other step; this makes it skip all steps"), "s_min_uncond_all": OptionInfo(False, "Negative Guidance minimum sigma all steps", infotext='NGMS all steps').info("By default, NGMS above skips every other step; this makes it skip all steps"),
"token_merging_ratio": OptionInfo(0.0, "Token merging ratio", gr.Slider, {"minimum": 0.0, "maximum": 0.9, "step": 0.1}, infotext='Token merging ratio').link("PR", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/9256").info("0=disable, higher=faster"), "token_merging_ratio": OptionInfo(0.0, "Token merging ratio", gr.Slider, {"minimum": 0.0, "maximum": 0.9, "step": 0.1}, infotext='Token merging ratio').link("PR", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/9256").info("0=disable, higher=faster"),
"token_merging_ratio_img2img": OptionInfo(0.0, "Token merging ratio for img2img", gr.Slider, {"minimum": 0.0, "maximum": 0.9, "step": 0.1}).info("only applies if non-zero and overrides above"), "token_merging_ratio_img2img": OptionInfo(0.0, "Token merging ratio for img2img", gr.Slider, {"minimum": 0.0, "maximum": 0.9, "step": 0.1}).info("only applies if non-zero and overrides above"),
@@ -291,6 +292,7 @@ options_templates.update(options_section(('extra_networks', "Extra Networks", "s
"textual_inversion_print_at_load": OptionInfo(False, "Print a list of Textual Inversion embeddings when loading model"), "textual_inversion_print_at_load": OptionInfo(False, "Print a list of Textual Inversion embeddings when loading model"),
"textual_inversion_add_hashes_to_infotext": OptionInfo(True, "Add Textual Inversion hashes to infotext"), "textual_inversion_add_hashes_to_infotext": OptionInfo(True, "Add Textual Inversion hashes to infotext"),
"sd_hypernetwork": OptionInfo("None", "Add hypernetwork to prompt", gr.Dropdown, lambda: {"choices": ["None", *shared.hypernetworks]}, refresh=shared_items.reload_hypernetworks), "sd_hypernetwork": OptionInfo("None", "Add hypernetwork to prompt", gr.Dropdown, lambda: {"choices": ["None", *shared.hypernetworks]}, refresh=shared_items.reload_hypernetworks),
"textual_inversion_image_embedding_data_cache": OptionInfo(False, 'Cache the data of image embeddings').info('potentially increase TI load time at the cost some disk space'),
})) }))
options_templates.update(options_section(('ui_prompt_editing', "Prompt editing", "ui"), { options_templates.update(options_section(('ui_prompt_editing', "Prompt editing", "ui"), {
@@ -404,15 +406,15 @@ options_templates.update(options_section(('sampler-params', "Sampler parameters"
'uni_pc_order': OptionInfo(3, "UniPC order", gr.Slider, {"minimum": 1, "maximum": 50, "step": 1}, infotext='UniPC order').info("must be < sampling steps"), 'uni_pc_order': OptionInfo(3, "UniPC order", gr.Slider, {"minimum": 1, "maximum": 50, "step": 1}, infotext='UniPC order').info("must be < sampling steps"),
'uni_pc_lower_order_final': OptionInfo(True, "UniPC lower order final", infotext='UniPC lower order final'), 'uni_pc_lower_order_final': OptionInfo(True, "UniPC lower order final", infotext='UniPC lower order final'),
'sd_noise_schedule': OptionInfo("Default", "Noise schedule for sampling", gr.Radio, {"choices": ["Default", "Zero Terminal SNR"]}, infotext="Noise Schedule").info("for use with zero terminal SNR trained models"), 'sd_noise_schedule': OptionInfo("Default", "Noise schedule for sampling", gr.Radio, {"choices": ["Default", "Zero Terminal SNR"]}, infotext="Noise Schedule").info("for use with zero terminal SNR trained models"),
'skip_early_cond': OptionInfo(0.0, "Ignore negative prompt during early sampling", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}, infotext="Skip Early CFG").info("disables CFG on a proportion of steps at the beginning of generation; 0=skip none; 1=skip all; can both improve sample diversity/quality and speed up sampling"), 'skip_early_cond': OptionInfo(0.0, "Ignore negative prompt during early sampling", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}, infotext="Skip Early CFG").info("disables CFG on a proportion of steps at the beginning of generation; 0=skip none; 1=skip all; can both improve sample diversity/quality and speed up sampling; XYZ plot: Skip Early CFG"),
'beta_dist_alpha': OptionInfo(0.6, "Beta scheduler - alpha", gr.Slider, {"minimum": 0.01, "maximum": 1.0, "step": 0.01}, infotext='Beta scheduler alpha').info('Default = 0.6; the alpha parameter of the beta distribution used in Beta sampling'), 'beta_dist_alpha': OptionInfo(0.6, "Beta scheduler - alpha", gr.Slider, {"minimum": 0.01, "maximum": 1.0, "step": 0.01}, infotext='Beta scheduler alpha').info('Default = 0.6; the alpha parameter of the beta distribution used in Beta sampling'),
'beta_dist_beta': OptionInfo(0.6, "Beta scheduler - beta", gr.Slider, {"minimum": 0.01, "maximum": 1.0, "step": 0.01}, infotext='Beta scheduler beta').info('Default = 0.6; the beta parameter of the beta distribution used in Beta sampling'), 'beta_dist_beta': OptionInfo(0.6, "Beta scheduler - beta", gr.Slider, {"minimum": 0.01, "maximum": 1.0, "step": 0.01}, infotext='Beta scheduler beta').info('Default = 0.6; the beta parameter of the beta distribution used in Beta sampling'),
})) }))
options_templates.update(options_section(('postprocessing', "Postprocessing", "postprocessing"), { options_templates.update(options_section(('postprocessing', "Postprocessing", "postprocessing"), {
'postprocessing_enable_in_main_ui': OptionInfo([], "Enable postprocessing operations in txt2img and img2img tabs", ui_components.DropdownMulti, lambda: {"choices": [x.name for x in shared_items.postprocessing_scripts()]}), 'postprocessing_enable_in_main_ui': OptionInfo([], "Enable postprocessing operations in txt2img and img2img tabs", ui_components.DropdownMulti, lambda: {"choices": [x.name for x in shared_items.postprocessing_scripts(filter_out_extra_only=True)]}),
'postprocessing_disable_in_extras': OptionInfo([], "Disable postprocessing operations in extras tab", ui_components.DropdownMulti, lambda: {"choices": [x.name for x in shared_items.postprocessing_scripts()]}), 'postprocessing_disable_in_extras': OptionInfo([], "Disable postprocessing operations in extras tab", ui_components.DropdownMulti, lambda: {"choices": [x.name for x in shared_items.postprocessing_scripts(filter_out_main_ui_only=True)]}),
'postprocessing_operation_order': OptionInfo([], "Postprocessing operation order", ui_components.DropdownMulti, lambda: {"choices": [x.name for x in shared_items.postprocessing_scripts()]}), 'postprocessing_operation_order': OptionInfo([], "Postprocessing operation order", ui_components.DropdownMulti, lambda: {"choices": [x.name for x in shared_items.postprocessing_scripts(filter_out_main_ui_only=True)]}),
'upscaling_max_images_in_cache': OptionInfo(5, "Maximum number of images in upscaling cache", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}), 'upscaling_max_images_in_cache': OptionInfo(5, "Maximum number of images in upscaling cache", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}),
'postprocessing_existing_caption_action': OptionInfo("Ignore", "Action for existing captions", gr.Radio, {"choices": ["Ignore", "Keep", "Prepend", "Append"]}).info("when generating captions using postprocessing; Ignore = use generated; Keep = use original; Prepend/Append = combine both"), 'postprocessing_existing_caption_action': OptionInfo("Ignore", "Action for existing captions", gr.Radio, {"choices": ["Ignore", "Keep", "Prepend", "Append"]}).info("when generating captions using postprocessing; Ignore = use generated; Keep = use original; Prepend/Append = combine both"),
})) }))
+31 -13
View File
@@ -12,7 +12,7 @@ import safetensors.torch
import numpy as np import numpy as np
from PIL import Image, PngImagePlugin from PIL import Image, PngImagePlugin
from modules import shared, devices, sd_hijack, sd_models, images, sd_samplers, sd_hijack_checkpoint, errors, hashes from modules import shared, devices, sd_hijack, sd_models, images, sd_samplers, sd_hijack_checkpoint, errors, hashes, cache
import modules.textual_inversion.dataset import modules.textual_inversion.dataset
from modules.textual_inversion.learn_schedule import LearnRateScheduler from modules.textual_inversion.learn_schedule import LearnRateScheduler
@@ -116,6 +116,7 @@ class EmbeddingDatabase:
self.expected_shape = -1 self.expected_shape = -1
self.embedding_dirs = {} self.embedding_dirs = {}
self.previously_displayed_embeddings = () self.previously_displayed_embeddings = ()
self.image_embedding_cache = cache.cache('image-embedding')
def add_embedding_dir(self, path): def add_embedding_dir(self, path):
self.embedding_dirs[path] = DirWithTextualInversionEmbeddings(path) self.embedding_dirs[path] = DirWithTextualInversionEmbeddings(path)
@@ -154,6 +155,31 @@ class EmbeddingDatabase:
vec = shared.sd_model.cond_stage_model.encode_embedding_init_text(",", 1) vec = shared.sd_model.cond_stage_model.encode_embedding_init_text(",", 1)
return vec.shape[1] return vec.shape[1]
def read_embedding_from_image(self, path, name):
try:
ondisk_mtime = os.path.getmtime(path)
if (cache_embedding := self.image_embedding_cache.get(path)) and ondisk_mtime == cache_embedding.get('mtime', 0):
# cache will only be used if the file has not been modified time matches
return cache_embedding.get('data', None), cache_embedding.get('name', None)
embed_image = Image.open(path)
if hasattr(embed_image, 'text') and 'sd-ti-embedding' in embed_image.text:
data = embedding_from_b64(embed_image.text['sd-ti-embedding'])
name = data.get('name', name)
elif data := extract_image_data_embed(embed_image):
name = data.get('name', name)
if data is None or shared.opts.textual_inversion_image_embedding_data_cache:
# data of image embeddings only will be cached if the option textual_inversion_image_embedding_data_cache is enabled
# results of images that are not embeddings will allways be cached to reduce unnecessary future disk reads
self.image_embedding_cache[path] = {'data': data, 'name': None if data is None else name, 'mtime': ondisk_mtime}
return data, name
except Exception:
errors.report(f"Error loading embedding {path}", exc_info=True)
return None, None
def load_from_file(self, path, filename): def load_from_file(self, path, filename):
name, ext = os.path.splitext(filename) name, ext = os.path.splitext(filename)
ext = ext.upper() ext = ext.upper()
@@ -163,17 +189,10 @@ class EmbeddingDatabase:
if second_ext.upper() == '.PREVIEW': if second_ext.upper() == '.PREVIEW':
return return
embed_image = Image.open(path) data, name = self.read_embedding_from_image(path, name)
if hasattr(embed_image, 'text') and 'sd-ti-embedding' in embed_image.text: if data is None:
data = embedding_from_b64(embed_image.text['sd-ti-embedding']) return
name = data.get('name', name)
else:
data = extract_image_data_embed(embed_image)
if data:
name = data.get('name', name)
else:
# if data is None, means this is not an embedding, just a preview image
return
elif ext in ['.BIN', '.PT']: elif ext in ['.BIN', '.PT']:
data = torch.load(path, map_location="cpu") data = torch.load(path, map_location="cpu")
elif ext in ['.SAFETENSORS']: elif ext in ['.SAFETENSORS']:
@@ -191,7 +210,6 @@ class EmbeddingDatabase:
else: else:
print(f"Unable to load Textual inversion embedding due to data issue: '{name}'.") print(f"Unable to load Textual inversion embedding due to data issue: '{name}'.")
def load_from_dir(self, embdir): def load_from_dir(self, embdir):
if not os.path.isdir(embdir.path): if not os.path.isdir(embdir.path):
return return
+3
View File
@@ -44,6 +44,9 @@ mimetypes.add_type('application/javascript', '.mjs')
mimetypes.add_type('image/webp', '.webp') mimetypes.add_type('image/webp', '.webp')
mimetypes.add_type('image/avif', '.avif') mimetypes.add_type('image/avif', '.avif')
# override potentially incorrect mimetypes
mimetypes.add_type('text/css', '.css')
if not cmd_opts.share and not cmd_opts.listen: if not cmd_opts.share and not cmd_opts.listen:
# fix gradio phoning home # fix gradio phoning home
gradio.utils.version_check = lambda: None gradio.utils.version_check = lambda: None
+17
View File
@@ -91,6 +91,7 @@ class InputAccordion(gr.Checkbox):
Actually just a hidden checkbox, but creates an accordion that follows and is followed by the state of the checkbox. Actually just a hidden checkbox, but creates an accordion that follows and is followed by the state of the checkbox.
""" """
accordion_id_set = set()
global_index = 0 global_index = 0
def __init__(self, value, **kwargs): def __init__(self, value, **kwargs):
@@ -99,6 +100,18 @@ class InputAccordion(gr.Checkbox):
self.accordion_id = f"input-accordion-{InputAccordion.global_index}" self.accordion_id = f"input-accordion-{InputAccordion.global_index}"
InputAccordion.global_index += 1 InputAccordion.global_index += 1
if not InputAccordion.accordion_id_set:
from modules import script_callbacks
script_callbacks.on_script_unloaded(InputAccordion.reset)
if self.accordion_id in InputAccordion.accordion_id_set:
count = 1
while (unique_id := f'{self.accordion_id}-{count}') in InputAccordion.accordion_id_set:
count += 1
self.accordion_id = unique_id
InputAccordion.accordion_id_set.add(self.accordion_id)
kwargs_checkbox = { kwargs_checkbox = {
**kwargs, **kwargs,
"elem_id": f"{self.accordion_id}-checkbox", "elem_id": f"{self.accordion_id}-checkbox",
@@ -143,3 +156,7 @@ class InputAccordion(gr.Checkbox):
def get_block_name(self): def get_block_name(self):
return "checkbox" return "checkbox"
@classmethod
def reset(cls):
cls.global_index = 0
cls.accordion_id_set.clear()
+11 -4
View File
@@ -1,5 +1,6 @@
import json import json
import os import os
from concurrent.futures import ThreadPoolExecutor
import threading import threading
import time import time
from datetime import datetime, timezone from datetime import datetime, timezone
@@ -106,18 +107,24 @@ def check_updates(id_task, disable_list):
exts = [ext for ext in extensions.extensions if ext.remote is not None and ext.name not in disabled] exts = [ext for ext in extensions.extensions if ext.remote is not None and ext.name not in disabled]
shared.state.job_count = len(exts) shared.state.job_count = len(exts)
for ext in exts: lock = threading.Lock()
shared.state.textinfo = ext.name
def _check_update(ext):
try: try:
ext.check_updates() ext.check_updates()
except FileNotFoundError as e: except FileNotFoundError as e:
if 'FETCH_HEAD' not in str(e): if 'FETCH_HEAD' not in str(e):
raise raise
except Exception: except Exception:
errors.report(f"Error checking updates for {ext.name}", exc_info=True) with lock:
errors.report(f"Error checking updates for {ext.name}", exc_info=True)
with lock:
shared.state.textinfo = ext.name
shared.state.nextjob()
shared.state.nextjob() with ThreadPoolExecutor(max_workers=max(1, int(shared.opts.concurrent_git_fetch_limit))) as executor:
for ext in exts:
executor.submit(_check_update, ext)
return extension_table(), "" return extension_table(), ""
+2 -4
View File
@@ -177,10 +177,8 @@ def add_pages_to_demo(app):
app.add_api_route("/sd_extra_networks/get-single-card", get_single_card, methods=["GET"]) app.add_api_route("/sd_extra_networks/get-single-card", get_single_card, methods=["GET"])
def quote_js(s): def quote_js(s: str):
s = s.replace('\\', '\\\\') return json.dumps(s, ensure_ascii=False)
s = s.replace('"', '\\"')
return f'"{s}"'
class ExtraNetworksPage: class ExtraNetworksPage:
+1 -1
View File
@@ -176,7 +176,7 @@ class UiLoadsave:
if new_value == old_value: if new_value == old_value:
continue continue
if old_value is None and new_value == '' or new_value == []: if old_value is None and (new_value == '' or new_value == []):
continue continue
yield path, old_value, new_value yield path, old_value, new_value
+2 -1
View File
@@ -93,13 +93,14 @@ class UpscalerData:
scaler: Upscaler = None scaler: Upscaler = None
model: None model: None
def __init__(self, name: str, path: str, upscaler: Upscaler = None, scale: int = 4, model=None): def __init__(self, name: str, path: str, upscaler: Upscaler = None, scale: int = 4, model=None, sha256: str = None):
self.name = name self.name = name
self.data_path = path self.data_path = path
self.local_data_path = path self.local_data_path = path
self.scaler = upscaler self.scaler = upscaler
self.scale = scale self.scale = scale
self.model = model self.model = model
self.sha256 = sha256
def __repr__(self): def __repr__(self):
return f"<UpscalerData name={self.name} path={self.data_path} scale={self.scale}>" return f"<UpscalerData name={self.name} path={self.data_path} scale={self.scale}>"
+77
View File
@@ -211,3 +211,80 @@ Requested path was: {path}
subprocess.Popen(["explorer.exe", subprocess.check_output(["wslpath", "-w", path])]) subprocess.Popen(["explorer.exe", subprocess.check_output(["wslpath", "-w", path])])
else: else:
subprocess.Popen(["xdg-open", path]) subprocess.Popen(["xdg-open", path])
def load_file_from_url(
url: str,
*,
model_dir: str,
progress: bool = True,
file_name: str | None = None,
hash_prefix: str | None = None,
re_download: bool = False,
) -> str:
"""Download a file from `url` into `model_dir`, using the file present if possible.
Returns the path to the downloaded file.
file_name: if specified, it will be used as the filename, otherwise the filename will be extracted from the url.
file is downloaded to {file_name}.tmp then moved to the final location after download is complete.
hash_prefix: sha256 hex string, if provided, the hash of the downloaded file will be checked against this prefix.
if the hash does not match, the temporary file is deleted and a ValueError is raised.
re_download: forcibly re-download the file even if it already exists.
"""
from urllib.parse import urlparse
import requests
try:
from tqdm import tqdm
except ImportError:
class tqdm:
def __init__(self, *args, **kwargs):
pass
def update(self, n=1, *args, **kwargs):
pass
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
pass
if not file_name:
parts = urlparse(url)
file_name = os.path.basename(parts.path)
cached_file = os.path.abspath(os.path.join(model_dir, file_name))
if re_download or not os.path.exists(cached_file):
os.makedirs(model_dir, exist_ok=True)
temp_file = os.path.join(model_dir, f"{file_name}.tmp")
print(f'\nDownloading: "{url}" to {cached_file}')
response = requests.get(url, stream=True)
response.raise_for_status()
total_size = int(response.headers.get('content-length', 0))
with tqdm(total=total_size, unit='B', unit_scale=True, desc=file_name, disable=not progress) as progress_bar:
with open(temp_file, 'wb') as file:
for chunk in response.iter_content(chunk_size=1024):
if chunk:
file.write(chunk)
progress_bar.update(len(chunk))
if hash_prefix and not compare_sha256(temp_file, hash_prefix):
print(f"Hash mismatch for {temp_file}. Deleting the temporary file.")
os.remove(temp_file)
raise ValueError(f"File hash does not match the expected hash prefix {hash_prefix}!")
os.rename(temp_file, cached_file)
return cached_file
def compare_sha256(file_path: str, hash_prefix: str) -> bool:
"""Check if the SHA256 hash of the file matches the given prefix."""
import hashlib
hash_sha256 = hashlib.sha256()
blksize = 1024 * 1024
with open(file_path, "rb") as f:
for chunk in iter(lambda: f.read(blksize), b""):
hash_sha256.update(chunk)
return hash_sha256.hexdigest().startswith(hash_prefix.strip().lower())
+1 -1
View File
@@ -22,7 +22,7 @@ protobuf==3.20.0
psutil==5.9.5 psutil==5.9.5
pytorch_lightning==1.9.4 pytorch_lightning==1.9.4
resize-right==0.0.2 resize-right==0.0.2
safetensors==0.4.2 safetensors==0.4.5
scikit-image==0.21.0 scikit-image==0.21.0
spandrel==0.3.4 spandrel==0.3.4
spandrel-extra-arches==0.1.1 spandrel-extra-arches==0.1.1
+2 -2
View File
@@ -12,8 +12,8 @@ class ScriptPostprocessingCodeFormer(scripts_postprocessing.ScriptPostprocessing
def ui(self): def ui(self):
with ui_components.InputAccordion(False, label="CodeFormer") as enable: with ui_components.InputAccordion(False, label="CodeFormer") as enable:
with gr.Row(): with gr.Row():
codeformer_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="Visibility", value=1.0, elem_id="extras_codeformer_visibility") codeformer_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="Visibility", value=1.0, elem_id=self.elem_id_suffix("extras_codeformer_visibility"))
codeformer_weight = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="Weight (0 = maximum effect, 1 = minimum effect)", value=0, elem_id="extras_codeformer_weight") codeformer_weight = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="Weight (0 = maximum effect, 1 = minimum effect)", value=0, elem_id=self.elem_id_suffix("extras_codeformer_weight"))
return { return {
"enable": enable, "enable": enable,
+1 -1
View File
@@ -11,7 +11,7 @@ class ScriptPostprocessingGfpGan(scripts_postprocessing.ScriptPostprocessing):
def ui(self): def ui(self):
with ui_components.InputAccordion(False, label="GFPGAN") as enable: with ui_components.InputAccordion(False, label="GFPGAN") as enable:
gfpgan_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="Visibility", value=1.0, elem_id="extras_gfpgan_visibility") gfpgan_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="Visibility", value=1.0, elem_id=self.elem_id_suffix("extras_gfpgan_visibility"))
return { return {
"enable": enable, "enable": enable,
+16 -15
View File
@@ -30,31 +30,31 @@ class ScriptPostprocessingUpscale(scripts_postprocessing.ScriptPostprocessing):
def ui(self): def ui(self):
selected_tab = gr.Number(value=0, visible=False) selected_tab = gr.Number(value=0, visible=False)
with InputAccordion(True, label="Upscale", elem_id="extras_upscale") as upscale_enabled: with InputAccordion(True, label="Upscale", elem_id=self.elem_id_suffix("extras_upscale")) as upscale_enabled:
with FormRow(): with FormRow():
extras_upscaler_1 = gr.Dropdown(label='Upscaler 1', elem_id="extras_upscaler_1", choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name) extras_upscaler_1 = gr.Dropdown(label='Upscaler 1', elem_id=self.elem_id_suffix("extras_upscaler_1"), choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name)
with FormRow(): with FormRow():
extras_upscaler_2 = gr.Dropdown(label='Upscaler 2', elem_id="extras_upscaler_2", choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name) extras_upscaler_2 = gr.Dropdown(label='Upscaler 2', elem_id=self.elem_id_suffix("extras_upscaler_2"), choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name)
extras_upscaler_2_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="Upscaler 2 visibility", value=0.0, elem_id="extras_upscaler_2_visibility") extras_upscaler_2_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="Upscaler 2 visibility", value=0.0, elem_id=self.elem_id_suffix("extras_upscaler_2_visibility"))
with FormRow(): with FormRow():
with gr.Tabs(elem_id="extras_resize_mode"): with gr.Tabs(elem_id=self.elem_id_suffix("extras_resize_mode")):
with gr.TabItem('Scale by', elem_id="extras_scale_by_tab") as tab_scale_by: with gr.TabItem('Scale by', elem_id=self.elem_id_suffix("extras_scale_by_tab")) as tab_scale_by:
with gr.Row(): with gr.Row():
with gr.Column(scale=4): with gr.Column(scale=4):
upscaling_resize = gr.Slider(minimum=1.0, maximum=8.0, step=0.05, label="Resize", value=4, elem_id="extras_upscaling_resize") upscaling_resize = gr.Slider(minimum=1.0, maximum=8.0, step=0.05, label="Resize", value=4, elem_id=self.elem_id_suffix("extras_upscaling_resize"))
with gr.Column(scale=1, min_width=160): with gr.Column(scale=1, min_width=160):
max_side_length = gr.Number(label="Max side length", value=0, elem_id="extras_upscale_max_side_length", tooltip="If any of two sides of the image ends up larger than specified, will downscale it to fit. 0 = no limit.", min_width=160, step=8, minimum=0) max_side_length = gr.Number(label="Max side length", value=0, elem_id=self.elem_id_suffix("extras_upscale_max_side_length"), tooltip="If any of two sides of the image ends up larger than specified, will downscale it to fit. 0 = no limit.", min_width=160, step=8, minimum=0)
with gr.TabItem('Scale to', elem_id="extras_scale_to_tab") as tab_scale_to: with gr.TabItem('Scale to', elem_id=self.elem_id_suffix("extras_scale_to_tab")) as tab_scale_to:
with FormRow(): with FormRow():
with gr.Column(elem_id="upscaling_column_size", scale=4): with gr.Column(elem_id=self.elem_id_suffix("upscaling_column_size"), scale=4):
upscaling_resize_w = gr.Slider(minimum=64, maximum=8192, step=8, label="Width", value=512, elem_id="extras_upscaling_resize_w") upscaling_resize_w = gr.Slider(minimum=64, maximum=8192, step=8, label="Width", value=512, elem_id=self.elem_id_suffix("extras_upscaling_resize_w"))
upscaling_resize_h = gr.Slider(minimum=64, maximum=8192, step=8, label="Height", value=512, elem_id="extras_upscaling_resize_h") upscaling_resize_h = gr.Slider(minimum=64, maximum=8192, step=8, label="Height", value=512, elem_id=self.elem_id_suffix("extras_upscaling_resize_h"))
with gr.Column(elem_id="upscaling_dimensions_row", scale=1, elem_classes="dimensions-tools"): with gr.Column(elem_id=self.elem_id_suffix("upscaling_dimensions_row"), scale=1, elem_classes="dimensions-tools"):
upscaling_res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="upscaling_res_switch_btn", tooltip="Switch width/height") upscaling_res_switch_btn = ToolButton(value=switch_values_symbol, elem_id=self.elem_id_suffix("upscaling_res_switch_btn"), tooltip="Switch width/height")
upscaling_crop = gr.Checkbox(label='Crop to fit', value=True, elem_id="extras_upscaling_crop") upscaling_crop = gr.Checkbox(label='Crop to fit', value=True, elem_id=self.elem_id_suffix("extras_upscaling_crop"))
def on_selected_upscale_method(upscale_method): def on_selected_upscale_method(upscale_method):
if not shared.opts.set_scale_by_when_changing_upscaler: if not shared.opts.set_scale_by_when_changing_upscaler:
@@ -169,6 +169,7 @@ class ScriptPostprocessingUpscale(scripts_postprocessing.ScriptPostprocessing):
class ScriptPostprocessingUpscaleSimple(ScriptPostprocessingUpscale): class ScriptPostprocessingUpscaleSimple(ScriptPostprocessingUpscale):
name = "Simple Upscale" name = "Simple Upscale"
order = 900 order = 900
main_ui_only = True
def ui(self): def ui(self):
with FormRow(): with FormRow():
+40 -34
View File
@@ -20,7 +20,7 @@ import modules.sd_models
import modules.sd_vae import modules.sd_vae
import re import re
from modules.ui_components import ToolButton from modules.ui_components import ToolButton, InputAccordion
fill_values_symbol = "\U0001f4d2" # 📒 fill_values_symbol = "\U0001f4d2" # 📒
@@ -259,6 +259,7 @@ axis_options = [
AxisOption("Schedule min sigma", float, apply_override("sigma_min")), AxisOption("Schedule min sigma", float, apply_override("sigma_min")),
AxisOption("Schedule max sigma", float, apply_override("sigma_max")), AxisOption("Schedule max sigma", float, apply_override("sigma_max")),
AxisOption("Schedule rho", float, apply_override("rho")), AxisOption("Schedule rho", float, apply_override("rho")),
AxisOption("Skip Early CFG", float, apply_override('skip_early_cond')),
AxisOption("Beta schedule alpha", float, apply_override("beta_dist_alpha")), AxisOption("Beta schedule alpha", float, apply_override("beta_dist_alpha")),
AxisOption("Beta schedule beta", float, apply_override("beta_dist_beta")), AxisOption("Beta schedule beta", float, apply_override("beta_dist_beta")),
AxisOption("Eta", float, apply_field("eta")), AxisOption("Eta", float, apply_field("eta")),
@@ -284,7 +285,7 @@ axis_options = [
] ]
def draw_xyz_grid(p, xs, ys, zs, x_labels, y_labels, z_labels, cell, draw_legend, include_lone_images, include_sub_grids, first_axes_processed, second_axes_processed, margin_size): def draw_xyz_grid(p, xs, ys, zs, x_labels, y_labels, z_labels, cell, draw_legend, include_lone_images, include_sub_grids, first_axes_processed, second_axes_processed, margin_size, draw_grid):
hor_texts = [[images.GridAnnotation(x)] for x in x_labels] hor_texts = [[images.GridAnnotation(x)] for x in x_labels]
ver_texts = [[images.GridAnnotation(y)] for y in y_labels] ver_texts = [[images.GridAnnotation(y)] for y in y_labels]
title_texts = [[images.GridAnnotation(z)] for z in z_labels] title_texts = [[images.GridAnnotation(z)] for z in z_labels]
@@ -369,29 +370,30 @@ def draw_xyz_grid(p, xs, ys, zs, x_labels, y_labels, z_labels, cell, draw_legend
print("Unexpected error: draw_xyz_grid failed to return even a single processed image") print("Unexpected error: draw_xyz_grid failed to return even a single processed image")
return Processed(p, []) return Processed(p, [])
z_count = len(zs) if draw_grid:
z_count = len(zs)
for i in range(z_count): for i in range(z_count):
start_index = (i * len(xs) * len(ys)) + i start_index = (i * len(xs) * len(ys)) + i
end_index = start_index + len(xs) * len(ys) end_index = start_index + len(xs) * len(ys)
grid = images.image_grid(processed_result.images[start_index:end_index], rows=len(ys)) grid = images.image_grid(processed_result.images[start_index:end_index], rows=len(ys))
if draw_legend:
grid_max_w, grid_max_h = map(max, zip(*(img.size for img in processed_result.images[start_index:end_index])))
grid = images.draw_grid_annotations(grid, grid_max_w, grid_max_h, hor_texts, ver_texts, margin_size)
processed_result.images.insert(i, grid)
processed_result.all_prompts.insert(i, processed_result.all_prompts[start_index])
processed_result.all_seeds.insert(i, processed_result.all_seeds[start_index])
processed_result.infotexts.insert(i, processed_result.infotexts[start_index])
z_grid = images.image_grid(processed_result.images[:z_count], rows=1)
z_sub_grid_max_w, z_sub_grid_max_h = map(max, zip(*(img.size for img in processed_result.images[:z_count])))
if draw_legend: if draw_legend:
grid_max_w, grid_max_h = map(max, zip(*(img.size for img in processed_result.images[start_index:end_index]))) z_grid = images.draw_grid_annotations(z_grid, z_sub_grid_max_w, z_sub_grid_max_h, title_texts, [[images.GridAnnotation()]])
grid = images.draw_grid_annotations(grid, grid_max_w, grid_max_h, hor_texts, ver_texts, margin_size) processed_result.images.insert(0, z_grid)
processed_result.images.insert(i, grid) # TODO: Deeper aspects of the program rely on grid info being misaligned between metadata arrays, which is not ideal.
processed_result.all_prompts.insert(i, processed_result.all_prompts[start_index]) # processed_result.all_prompts.insert(0, processed_result.all_prompts[0])
processed_result.all_seeds.insert(i, processed_result.all_seeds[start_index]) # processed_result.all_seeds.insert(0, processed_result.all_seeds[0])
processed_result.infotexts.insert(i, processed_result.infotexts[start_index]) processed_result.infotexts.insert(0, processed_result.infotexts[0])
z_grid = images.image_grid(processed_result.images[:z_count], rows=1)
z_sub_grid_max_w, z_sub_grid_max_h = map(max, zip(*(img.size for img in processed_result.images[:z_count])))
if draw_legend:
z_grid = images.draw_grid_annotations(z_grid, z_sub_grid_max_w, z_sub_grid_max_h, title_texts, [[images.GridAnnotation()]])
processed_result.images.insert(0, z_grid)
# TODO: Deeper aspects of the program rely on grid info being misaligned between metadata arrays, which is not ideal.
# processed_result.all_prompts.insert(0, processed_result.all_prompts[0])
# processed_result.all_seeds.insert(0, processed_result.all_seeds[0])
processed_result.infotexts.insert(0, processed_result.infotexts[0])
return processed_result return processed_result
@@ -441,7 +443,6 @@ class Script(scripts.Script):
with gr.Row(variant="compact", elem_id="axis_options"): with gr.Row(variant="compact", elem_id="axis_options"):
with gr.Column(): with gr.Column():
draw_legend = gr.Checkbox(label='Draw legend', value=True, elem_id=self.elem_id("draw_legend"))
no_fixed_seeds = gr.Checkbox(label='Keep -1 for seeds', value=False, elem_id=self.elem_id("no_fixed_seeds")) no_fixed_seeds = gr.Checkbox(label='Keep -1 for seeds', value=False, elem_id=self.elem_id("no_fixed_seeds"))
with gr.Row(): with gr.Row():
vary_seeds_x = gr.Checkbox(label='Vary seeds for X', value=False, min_width=80, elem_id=self.elem_id("vary_seeds_x"), tooltip="Use different seeds for images along X axis.") vary_seeds_x = gr.Checkbox(label='Vary seeds for X', value=False, min_width=80, elem_id=self.elem_id("vary_seeds_x"), tooltip="Use different seeds for images along X axis.")
@@ -449,9 +450,12 @@ class Script(scripts.Script):
vary_seeds_z = gr.Checkbox(label='Vary seeds for Z', value=False, min_width=80, elem_id=self.elem_id("vary_seeds_z"), tooltip="Use different seeds for images along Z axis.") vary_seeds_z = gr.Checkbox(label='Vary seeds for Z', value=False, min_width=80, elem_id=self.elem_id("vary_seeds_z"), tooltip="Use different seeds for images along Z axis.")
with gr.Column(): with gr.Column():
include_lone_images = gr.Checkbox(label='Include Sub Images', value=False, elem_id=self.elem_id("include_lone_images")) include_lone_images = gr.Checkbox(label='Include Sub Images', value=False, elem_id=self.elem_id("include_lone_images"))
include_sub_grids = gr.Checkbox(label='Include Sub Grids', value=False, elem_id=self.elem_id("include_sub_grids"))
csv_mode = gr.Checkbox(label='Use text inputs instead of dropdowns', value=False, elem_id=self.elem_id("csv_mode")) csv_mode = gr.Checkbox(label='Use text inputs instead of dropdowns', value=False, elem_id=self.elem_id("csv_mode"))
with gr.Column():
with InputAccordion(True, label='Draw grid', elem_id=self.elem_id('draw_grid')) as draw_grid:
with gr.Row():
include_sub_grids = gr.Checkbox(label='Include Sub Grids', value=False, elem_id=self.elem_id("include_sub_grids"))
draw_legend = gr.Checkbox(label='Draw legend', value=True, elem_id=self.elem_id("draw_legend"))
margin_size = gr.Slider(label="Grid margins (px)", minimum=0, maximum=500, value=0, step=2, elem_id=self.elem_id("margin_size")) margin_size = gr.Slider(label="Grid margins (px)", minimum=0, maximum=500, value=0, step=2, elem_id=self.elem_id("margin_size"))
with gr.Row(variant="compact", elem_id="swap_axes"): with gr.Row(variant="compact", elem_id="swap_axes"):
@@ -533,9 +537,9 @@ class Script(scripts.Script):
(z_values_dropdown, lambda params: get_dropdown_update_from_params("Z", params)), (z_values_dropdown, lambda params: get_dropdown_update_from_params("Z", params)),
) )
return [x_type, x_values, x_values_dropdown, y_type, y_values, y_values_dropdown, z_type, z_values, z_values_dropdown, draw_legend, include_lone_images, include_sub_grids, no_fixed_seeds, vary_seeds_x, vary_seeds_y, vary_seeds_z, margin_size, csv_mode] return [x_type, x_values, x_values_dropdown, y_type, y_values, y_values_dropdown, z_type, z_values, z_values_dropdown, draw_legend, include_lone_images, include_sub_grids, no_fixed_seeds, vary_seeds_x, vary_seeds_y, vary_seeds_z, margin_size, csv_mode, draw_grid]
def run(self, p, x_type, x_values, x_values_dropdown, y_type, y_values, y_values_dropdown, z_type, z_values, z_values_dropdown, draw_legend, include_lone_images, include_sub_grids, no_fixed_seeds, vary_seeds_x, vary_seeds_y, vary_seeds_z, margin_size, csv_mode): def run(self, p, x_type, x_values, x_values_dropdown, y_type, y_values, y_values_dropdown, z_type, z_values, z_values_dropdown, draw_legend, include_lone_images, include_sub_grids, no_fixed_seeds, vary_seeds_x, vary_seeds_y, vary_seeds_z, margin_size, csv_mode, draw_grid):
x_type, y_type, z_type = x_type or 0, y_type or 0, z_type or 0 # if axle type is None set to 0 x_type, y_type, z_type = x_type or 0, y_type or 0, z_type or 0 # if axle type is None set to 0
if not no_fixed_seeds: if not no_fixed_seeds:
@@ -780,7 +784,8 @@ class Script(scripts.Script):
include_sub_grids=include_sub_grids, include_sub_grids=include_sub_grids,
first_axes_processed=first_axes_processed, first_axes_processed=first_axes_processed,
second_axes_processed=second_axes_processed, second_axes_processed=second_axes_processed,
margin_size=margin_size margin_size=margin_size,
draw_grid=draw_grid,
) )
if not processed.images: if not processed.images:
@@ -789,14 +794,15 @@ class Script(scripts.Script):
z_count = len(zs) z_count = len(zs)
# Set the grid infotexts to the real ones with extra_generation_params (1 main grid + z_count sub-grids) if draw_grid:
processed.infotexts[:1 + z_count] = grid_infotext[:1 + z_count] # Set the grid infotexts to the real ones with extra_generation_params (1 main grid + z_count sub-grids)
processed.infotexts[:1 + z_count] = grid_infotext[:1 + z_count]
if not include_lone_images: if not include_lone_images:
# Don't need sub-images anymore, drop from list: # Don't need sub-images anymore, drop from list:
processed.images = processed.images[:z_count + 1] processed.images = processed.images[:z_count + 1] if draw_grid else []
if opts.grid_save: if draw_grid and opts.grid_save:
# Auto-save main and sub-grids: # Auto-save main and sub-grids:
grid_count = z_count + 1 if z_count > 1 else 1 grid_count = z_count + 1 if z_count > 1 else 1
for g in range(grid_count): for g in range(grid_count):
@@ -806,7 +812,7 @@ class Script(scripts.Script):
if not include_sub_grids: # if not include_sub_grids then skip saving after the first grid if not include_sub_grids: # if not include_sub_grids then skip saving after the first grid
break break
if not include_sub_grids: if draw_grid and not include_sub_grids:
# Done with sub-grids, drop all related information: # Done with sub-grids, drop all related information:
for _ in range(z_count): for _ in range(z_count):
del processed.images[1] del processed.images[1]
+10 -1
View File
@@ -4,7 +4,16 @@ if exist webui.settings.bat (
call webui.settings.bat call webui.settings.bat
) )
if not defined PYTHON (set PYTHON=python) if not defined PYTHON (
for /f "delims=" %%A in ('where python ^| findstr /n . ^| findstr ^^1:') do (
if /i "%%~xA" == ".exe" (
set PYTHON=python
) else (
set PYTHON=call python
)
)
)
if defined GIT (set "GIT_PYTHON_GIT_EXECUTABLE=%GIT%") if defined GIT (set "GIT_PYTHON_GIT_EXECUTABLE=%GIT%")
if not defined VENV_DIR (set "VENV_DIR=%~dp0%venv") if not defined VENV_DIR (set "VENV_DIR=%~dp0%venv")
+40
View File
@@ -45,6 +45,44 @@ def api_only():
) )
def warning_if_invalid_install_dir():
"""
Shows a warning if the webui is installed under a path that contains a leading dot in any of its parent directories.
Gradio '/file=' route will block access to files that have a leading dot in the path segments.
We use this route to serve files such as JavaScript and CSS to the webpage,
if those files are blocked, the webpage will not function properly.
See https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/13292
This is a security feature was added to Gradio 3.32.0 and is removed in later versions,
this function replicates Gradio file access blocking logic.
This check should be removed when it's no longer applicable.
"""
from packaging.version import parse
from pathlib import Path
import gradio
if parse('3.32.0') <= parse(gradio.__version__) < parse('4'):
def abspath(path):
"""modified from Gradio 3.41.2 gradio.utils.abspath()"""
if path.is_absolute():
return path
is_symlink = path.is_symlink() or any(parent.is_symlink() for parent in path.parents)
return Path.cwd() / path if (is_symlink or path == path.resolve()) else path.resolve()
webui_root = Path(__file__).parent
if any(part.startswith(".") for part in abspath(webui_root).parts):
print(f'''{"!"*25} Warning {"!"*25}
WebUI is installed in a directory that has a leading dot (.) in one of its parent directories.
This will prevent WebUI from functioning properly.
Please move the installation to a different directory.
Current path: "{webui_root}"
For more information see: https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/13292
{"!"*25} Warning {"!"*25}''')
def webui(): def webui():
from modules.shared_cmd_options import cmd_opts from modules.shared_cmd_options import cmd_opts
@@ -53,6 +91,8 @@ def webui():
from modules import shared, ui_tempdir, script_callbacks, ui, progress, ui_extra_networks from modules import shared, ui_tempdir, script_callbacks, ui, progress, ui_extra_networks
warning_if_invalid_install_dir()
while 1: while 1:
if shared.opts.clean_temp_dir_at_start: if shared.opts.clean_temp_dir_at_start:
ui_tempdir.cleanup_tmpdr() ui_tempdir.cleanup_tmpdr()