From ac91a15aa98ffa678c0645889a7aa9bfb0dc3fd5 Mon Sep 17 00:00:00 2001 From: "M.Hosoi" Date: Thu, 23 Mar 2023 09:20:10 +0900 Subject: [PATCH 001/291] fix: can't get file size if gnubin/stat is already installed in homebrew --- scripts/functions.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/functions.sh b/scripts/functions.sh index 5b1be7f4..495e9950 100644 --- a/scripts/functions.sh +++ b/scripts/functions.sh @@ -31,7 +31,7 @@ EOF filesize() { case "$(uname -s)" in Linux*) stat -c "%s" $1;; - Darwin*) stat -f "%z" $1;; + Darwin*) /usr/bin/stat -f "%z" $1;; *) echo "Unknown OS: $OS_NAME! This script runs only on Linux or Mac" && exit esac } From 20ad0d7f8cdcc12532290277ea6f7525fa46f5b4 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Thu, 23 Mar 2023 09:48:40 +0530 Subject: [PATCH 002/291] Merge pull request #1048 from JeLuF/base36 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🔥Hotfix: Avoid name clashes for autosave files --- ui/easydiffusion/utils/save_utils.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ui/easydiffusion/utils/save_utils.py b/ui/easydiffusion/utils/save_utils.py index b4a85538..6012bc44 100644 --- a/ui/easydiffusion/utils/save_utils.py +++ b/ui/easydiffusion/utils/save_utils.py @@ -1,11 +1,11 @@ import os import time -import base64 import re from easydiffusion.types import TaskData, GenerateImageRequest from sdkit.utils import save_images, save_dicts +from numpy import base_repr filename_regex = re.compile("[^a-zA-Z0-9._-]") @@ -121,8 +121,7 @@ def make_filename_callback(req: GenerateImageRequest, suffix=None, now=None): now = time.time() def make_filename(i): - img_id = base64.b64encode(int(now + i).to_bytes(8, "big")).decode() # Generate unique ID based on time. - img_id = img_id.translate({43: None, 47: None, 61: None})[-8:] # Remove + / = and keep last 8 chars. + img_id = base_repr(int(now * 10000), 36)[-7:] + base_repr(int(i),36) # Base 36 conversion, 0-9, A-Z prompt_flattened = filename_regex.sub("_", req.prompt)[:50] name = f"{prompt_flattened}_{img_id}" From 40598c28af8d20dfb775e4224e24c97505e29f59 Mon Sep 17 00:00:00 2001 From: JeLuF Date: Wed, 29 Mar 2023 00:46:13 +0200 Subject: [PATCH 003/291] Don't show lossless WEBP toggle for JPEG on first load https://discord.com/channels/1014774730907209781/1021695193499582494/1090402304173277266 --- ui/index.html | 2 +- ui/media/js/main.js | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ui/index.html b/ui/index.html index 0bf90733..f7dcf114 100644 --- a/ui/index.html +++ b/ui/index.html @@ -237,7 +237,7 @@ - + diff --git a/ui/media/js/main.js b/ui/media/js/main.js index e254956b..49707f59 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -1557,19 +1557,19 @@ updateOutputQuality() function updateOutputQualityVisibility() { if (outputFormatField.value === 'webp') { - outputLosslessContainer.style.display = 'unset' + outputLosslessContainer.classList.remove('displayNone') if (outputLosslessField.checked) { - outputQualityRow.style.display='none' + outputQualityRow.classList.add('displayNone') } else { - outputQualityRow.style.display='table-row' + outputQualityRow.classList.remove('displayNone') } } else if (outputFormatField.value === 'png') { - outputQualityRow.style.display='none' - outputLosslessContainer.style.display = 'none' + outputQualityRow.classList.add('displayNone') + outputLosslessContainer.classList.add('displayNone') } else { - outputQualityRow.style.display='table-row' - outputLosslessContainer.style.display = 'none' + outputQualityRow.classList.remove('displayNone') + outputLosslessContainer.classList.add('displayNone') } } From e5a47ac9648c2325827c094c5dca95d56b83f614 Mon Sep 17 00:00:00 2001 From: Olivia Godone-Maresca <8977984+ogmaresca@users.noreply.github.com> Date: Tue, 28 Mar 2023 23:49:05 -0400 Subject: [PATCH 004/291] Allow both embedding metadata and saving metadata files (#1058) * Allow both embedding metadata and saving metadata files * Use the correct variable * Descriptive variable --------- Co-authored-by: cmdr2 --- ui/easydiffusion/utils/save_utils.py | 18 ++++++++++-------- ui/media/js/parameters.js | 10 +++++++++- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/ui/easydiffusion/utils/save_utils.py b/ui/easydiffusion/utils/save_utils.py index 6affac78..950f04b0 100644 --- a/ui/easydiffusion/utils/save_utils.py +++ b/ui/easydiffusion/utils/save_utils.py @@ -47,14 +47,16 @@ def save_images_to_disk(images: list, filtered_images: list, req: GenerateImageR output_quality=task_data.output_quality, output_lossless=task_data.output_lossless, ) - if task_data.metadata_output_format.lower() in ["json", "txt", "embed"]: - save_dicts( - metadata_entries, - save_dir_path, - file_name=make_filename, - output_format=task_data.metadata_output_format, - file_format=task_data.output_format, - ) + if task_data.metadata_output_format: + for metadata_output_format in task_data.metadata_output_format.split(','): + if metadata_output_format.lower() in ["json", "txt", "embed"]: + save_dicts( + metadata_entries, + save_dir_path, + file_name=make_filename, + output_format=metadata_output_format, + file_format=task_data.output_format, + ) else: make_filter_filename = make_filename_callback(req, now=now, suffix="filtered") diff --git a/ui/media/js/parameters.js b/ui/media/js/parameters.js index cb0c7a4c..baa55469 100644 --- a/ui/media/js/parameters.js +++ b/ui/media/js/parameters.js @@ -76,7 +76,15 @@ var PARAMETERS = [ { value: "embed", label: "embed" - } + }, + { + value: "embed,txt", + label: "embed & txt", + }, + { + value: "embed,json", + label: "embed & json", + }, ], }, { From 4836abb5bd80d30869fe8f557094a70304180ef1 Mon Sep 17 00:00:00 2001 From: lucasmarcelli Date: Wed, 29 Mar 2023 13:52:41 -0400 Subject: [PATCH 005/291] reworked how task config is setup --- CHANGES.md | 1 + CONTRIBUTING.md | 2 -- ui/media/css/main.css | 5 +++ ui/media/js/main.js | 74 +++++++++++++++++++++++++++---------------- 4 files changed, 52 insertions(+), 30 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 4b225b32..752111f6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -21,6 +21,7 @@ Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. ### Detailed changelog +* 2.5.30 - 28 Mar 2023 - Refactor task entry config to use a generating method. Added ability for plugins to easily add to this. Removed confusing sentence from `contributing.md` * 2.5.30 - 28 Mar 2023 - Allow the user to undo the deletion of tasks or images, instead of showing a pop-up each time. The new `Undo` button will be present at the top of the UI. Thanks @JeLuf. * 2.5.30 - 28 Mar 2023 - Support saving lossless WEBP images. Thanks @ogmaresca. * 2.5.30 - 28 Mar 2023 - Lots of bug fixes for the UI (Read LoRA flag in metadata files, new prompt weight format with scrollwheel, fix overflow with lots of tabs, clear button in image editor, shorter filenames in download). Thanks @patriceac, @JeLuf and @ogmaresca. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 02ce6fc6..c01d489a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -42,8 +42,6 @@ or for Windows 10) Congrats, now any changes you make in your repo `ui` folder are linked to this running archive of the app and can be previewed in the browser. 11) Please update CHANGES.md in your pull requests. -Check the `ui/frontend/build/README.md` for instructions on running and building the React code. - ## Development environment for Installer changes Build the Windows installer using Windows, and the Linux installer using Linux. Don't mix the two, and don't use WSL. An Ubuntu VM is fine for building the Linux installer on a Windows host. diff --git a/ui/media/css/main.css b/ui/media/css/main.css index 88b8fc58..e6a95cda 100644 --- a/ui/media/css/main.css +++ b/ui/media/css/main.css @@ -572,6 +572,11 @@ div.img-preview img { margin-bottom: 5pt; margin-top: 5pt; } + +.taskConfigContainer { + display: inline; +} + .img-batch { display: inline; } diff --git a/ui/media/js/main.js b/ui/media/js/main.js index e254956b..313f4012 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -5,6 +5,28 @@ const MIN_GPUS_TO_SHOW_SELECTION = 2 const IMAGE_REGEX = new RegExp('data:image/[A-Za-z]+;base64') const htmlTaskMap = new WeakMap() +const taskConfigSetup = { + taskConfig: { + seed: { value: ({ seed }) => seed, label: 'Seed' }, + dimensions: { value: ({ reqBody }) => `${reqBody?.width}x${reqBody?.height}`, label: 'Dimensions' }, + sampler_name: 'Sampler', + num_inference_steps: 'Inference Steps', + guidance_scale: 'Guidance Scale', + use_stable_diffusion_model: 'Model', + use_vae_model: { label: 'VAE', visible: ({ reqBody }) => reqBody?.use_vae_model !== undefined && reqBody?.use_vae_model.trim() !== ''}, + negative_prompt: { label: 'Negative Prompt', visible: ({ reqBody }) => reqBody?.negative_prompt !== undefined && reqBody?.negative_prompt.trim() !== ''}, + prompt_strength: 'Prompt Strength', + use_face_correction: 'Fix Faces', + upscale: { value: ({ reqBody }) => `${reqBody?.use_upscale} (${reqBody?.upscale_amount || 4}x)`, label: 'Upscale', visible: ({ reqBody }) => !!reqBody?.use_upscale }, + use_hypernetwork_model: 'Hypernetwork', + hypernetwork_strength: { label: 'Hypernetwork Strength', visible: ({ reqBody }) => !!reqBody?.use_hypernetwork_model }, + use_lora_model: 'Lora Model', + preserve_init_image_color_profile: 'Preserve Color Profile', + }, + pluginTaskConfig: {}, + getCSSKey: (key) => key.split('_').map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join('') +} + let imageCounter = 0 let imageRequest = [] @@ -952,6 +974,29 @@ function onTaskEntryDragOver(event) { } } +function generateConfig({ label, value, visible, cssKey }) { + if (!visible) return null; + return `
${label}: ${value}` +} + +function getVisibleConfig(config, task) { + const mergedTaskConfig = { ...config.taskConfig, ...config.pluginTaskConfig } + return Object.keys(mergedTaskConfig) + .map((key) => { + const value = mergedTaskConfig?.[key]?.value?.(task) ?? task.reqBody[key] + const visible = mergedTaskConfig?.[key]?.visible?.(task) ?? value !== undefined ?? true + const label = mergedTaskConfig?.[key]?.label ?? mergedTaskConfig?.[key] + const cssKey = config.getCSSKey(key) + return { label, visible, value, cssKey } + }) + .map((obj) => generateConfig(obj)) + .filter(obj => obj) +} + +function createTaskConfig(task) { + return getVisibleConfig(taskConfigSetup, task).join('
') +} + function createTask(task) { let taskConfig = '' @@ -960,33 +1005,8 @@ function createTask(task) { let w = task.reqBody.width * h / task.reqBody.height >>0 taskConfig += `
` } - taskConfig += `Seed: ${task.seed}, Sampler: ${task.reqBody.sampler_name}, Inference Steps: ${task.reqBody.num_inference_steps}, Guidance Scale: ${task.reqBody.guidance_scale}, Model: ${task.reqBody.use_stable_diffusion_model}` - if (task.reqBody.use_vae_model.trim() !== '') { - taskConfig += `, VAE: ${task.reqBody.use_vae_model}` - } - if (task.reqBody.negative_prompt.trim() !== '') { - taskConfig += `, Negative Prompt: ${task.reqBody.negative_prompt}` - } - if (task.reqBody.init_image !== undefined) { - taskConfig += `, Prompt Strength: ${task.reqBody.prompt_strength}` - } - if (task.reqBody.use_face_correction) { - taskConfig += `, Fix Faces: ${task.reqBody.use_face_correction}` - } - if (task.reqBody.use_upscale) { - taskConfig += `, Upscale: ${task.reqBody.use_upscale} (${task.reqBody.upscale_amount || 4}x)` - } - if (task.reqBody.use_hypernetwork_model) { - taskConfig += `, Hypernetwork: ${task.reqBody.use_hypernetwork_model}` - taskConfig += `, Hypernetwork Strength: ${task.reqBody.hypernetwork_strength}` - } - if (task.reqBody.use_lora_model) { - taskConfig += `, LoRA: ${task.reqBody.use_lora_model}` - } - if (task.reqBody.preserve_init_image_color_profile) { - taskConfig += `, Preserve Color Profile: true` - } + taskConfig += `
${createTaskConfig(task)}
`; let taskEntry = document.createElement('div') taskEntry.id = `imageTaskContainer-${Date.now()}` @@ -1085,7 +1105,6 @@ function getCurrentUserRequest() { numOutputsTotal: numOutputsTotal, batchCount: Math.ceil(numOutputsTotal / numOutputsParallel), seed, - reqBody: { seed, used_random_seed: randomSeedField.checked, @@ -1117,7 +1136,6 @@ function getCurrentUserRequest() { if (IMAGE_REGEX.test(initImagePreview.src)) { newTask.reqBody.init_image = initImagePreview.src newTask.reqBody.prompt_strength = parseFloat(promptStrengthField.value) - // if (IMAGE_REGEX.test(maskImagePreview.src)) { // newTask.reqBody.mask = maskImagePreview.src // } From 7e53eb658c8105fa86fc285305556d994148e390 Mon Sep 17 00:00:00 2001 From: Olivia Godone-Maresca Date: Wed, 29 Mar 2023 20:56:24 -0400 Subject: [PATCH 006/291] Allow loading/saving app.config from plugins and support custom folder/filename formats from app.config --- ui/easydiffusion/server.py | 12 +- ui/easydiffusion/utils/save_utils.py | 148 +++++++++++++++++++++-- ui/media/js/parameters.js | 172 ++++++++++++++++++++++----- ui/media/js/utils.js | 26 +++- 4 files changed, 315 insertions(+), 43 deletions(-) diff --git a/ui/easydiffusion/server.py b/ui/easydiffusion/server.py index e27f9c5b..9642d735 100644 --- a/ui/easydiffusion/server.py +++ b/ui/easydiffusion/server.py @@ -10,7 +10,7 @@ from typing import List, Union from fastapi import FastAPI, HTTPException from fastapi.staticfiles import StaticFiles from starlette.responses import FileResponse, JSONResponse, StreamingResponse -from pydantic import BaseModel +from pydantic import BaseModel, Extra from easydiffusion import app, model_manager, task_manager from easydiffusion.types import TaskData, GenerateImageRequest, MergeRequest @@ -44,7 +44,7 @@ class NoCacheStaticFiles(StaticFiles): return super().is_not_modified(response_headers, request_headers) -class SetAppConfigRequest(BaseModel): +class SetAppConfigRequest(BaseModel, extra=Extra.allow): update_branch: str = None render_devices: Union[List[str], List[int], str, int] = None model_vae: str = None @@ -136,6 +136,14 @@ def set_app_config_internal(req: SetAppConfigRequest): config["test_diffusers"] = req.test_diffusers + for property, property_value in req.dict().items(): + log.info(f"set_app_config_internal {property} === {property_value}") + if property_value is not None and property in req.__fields__: + log.info(f"set_app_config_internal {property} IS DEFINED PROPERTY") + if property_value is not None and property not in req.__fields__: + log.info(f"set_app_config_internal {property} IS ADDITIONAL PROPERTY") + config[property] = property_value + try: app.setConfig(config) diff --git a/ui/easydiffusion/utils/save_utils.py b/ui/easydiffusion/utils/save_utils.py index 950f04b0..f936ad1b 100644 --- a/ui/easydiffusion/utils/save_utils.py +++ b/ui/easydiffusion/utils/save_utils.py @@ -1,13 +1,18 @@ import os import time +import base64 import re +from easydiffusion import app from easydiffusion.types import TaskData, GenerateImageRequest +from functools import reduce +from datetime import datetime from sdkit.utils import save_images, save_dicts from numpy import base_repr filename_regex = re.compile("[^a-zA-Z0-9._-]") +img_number_regex = re.compile("([0-9]{5,})") # keep in sync with `ui/media/js/dnd.js` TASK_TEXT_MAPPING = { @@ -31,12 +36,86 @@ TASK_TEXT_MAPPING = { # "lora_alpha": "LoRA Strength", } +time_placeholders = { + "$yyyy": "%Y", + "$MM": "%m", + "$dd": "%d", + "$HH": "%H", + "$mm": "%M", + "$ss": "%S", +} + +other_placeholders = { + "$id": lambda req, task_data: filename_regex.sub("_", task_data.session_id), + "$p": lambda req, task_data: filename_regex.sub("_", req.prompt)[:50], + "$s": lambda req, task_data: str(req.seed), +} + +class ImageNumber: + _factory = None + _evaluated = False + + def __init__(self, factory): + self._factory = factory + self._evaluated = None + def __call__(self) -> int: + if self._evaluated is None: + self._evaluated = self._factory() + return self._evaluated + +def format_placeholders(format: str, req: GenerateImageRequest, task_data: TaskData, now = None): + if now is None: + now = time.time() + + for placeholder, time_format in time_placeholders.items(): + if placeholder in format: + format = format.replace(placeholder, datetime.fromtimestamp(now).strftime(time_format)) + for placeholder, replace_func in other_placeholders.items(): + if placeholder in format: + format = format.replace(placeholder, replace_func(req, task_data)) + + return format + +def format_folder_name(format: str, req: GenerateImageRequest, task_data: TaskData): + format = format_placeholders(format, req, task_data) + return filename_regex.sub("_", format) + +def format_file_name( + format: str, + req: GenerateImageRequest, + task_data: TaskData, + now: float, + batch_file_number: int, + folder_img_number: ImageNumber, +): + format = format_placeholders(format, req, task_data, now) + + if "$n" in format: + format = format.replace("$n", f"{folder_img_number():05}") + + if "$tsb64" in format: + img_id = base_repr(int(now * 10000), 36)[-7:] + base_repr(int(batch_file_number), 36) # Base 36 conversion, 0-9, A-Z + format = format.replace("$tsb64", img_id) + + if "$ts" in format: + format = format.replace("$ts", str(int(now * 1000) + batch_file_number)) + + return filename_regex.sub("_", format) def save_images_to_disk(images: list, filtered_images: list, req: GenerateImageRequest, task_data: TaskData): now = time.time() - save_dir_path = os.path.join(task_data.save_to_disk_path, filename_regex.sub("_", task_data.session_id)) + app_config = app.getConfig() + folder_format = app_config.get("folder_format", "$id") + save_dir_path = os.path.join(task_data.save_to_disk_path, format_folder_name(folder_format, req, task_data)) metadata_entries = get_metadata_entries_for_request(req, task_data) - make_filename = make_filename_callback(req, now=now) + file_number = calculate_img_number(save_dir_path, task_data) + make_filename = make_filename_callback( + app_config.get("filename_format", "$p_$tsb64"), + req, + task_data, + file_number, + now=now, + ) if task_data.show_only_filtered_image or filtered_images is images: save_images( @@ -58,7 +137,7 @@ def save_images_to_disk(images: list, filtered_images: list, req: GenerateImageR file_format=task_data.output_format, ) else: - make_filter_filename = make_filename_callback(req, now=now, suffix="filtered") + make_filter_filename = make_filename_callback(req, task_data, file_number, now=now, suffix="filtered") save_images( images, @@ -105,9 +184,6 @@ def get_metadata_entries_for_request(req: GenerateImageRequest, task_data: TaskD if task_data.use_lora_model is None: if "lora_alpha" in metadata: del metadata["lora_alpha"] - - from easydiffusion import app - app_config = app.getConfig() if not app_config.get("test_diffusers", False) and "use_lora_model" in metadata: del metadata["use_lora_model"] @@ -133,16 +209,66 @@ def get_printable_request(req: GenerateImageRequest): return metadata -def make_filename_callback(req: GenerateImageRequest, suffix=None, now=None): +def make_filename_callback( + filename_format: str, + req: GenerateImageRequest, + task_data: TaskData, + folder_img_number: int, + suffix=None, + now=None, +): if now is None: now = time.time() def make_filename(i): - img_id = base_repr(int(now * 10000), 36)[-7:] + base_repr(int(i),36) # Base 36 conversion, 0-9, A-Z - - prompt_flattened = filename_regex.sub("_", req.prompt)[:50] - name = f"{prompt_flattened}_{img_id}" + name = format_file_name(filename_format, req, task_data, now, i, folder_img_number) name = name if suffix is None else f"{name}_{suffix}" + return name return make_filename + +def _calculate_img_number(save_dir_path: str, task_data: TaskData): + def get_highest_img_number(accumulator: int, file: os.DirEntry) -> int: + if not file.is_file: + return accumulator + + if len(list(filter(lambda e: file.name.endswith(e), app.IMAGE_EXTENSIONS))) == 0: + return accumulator + + get_highest_img_number.number_of_images = get_highest_img_number.number_of_images + 1 + + number_match = img_number_regex.match(file.name) + if not number_match: + return accumulator + + file_number = number_match.group().lstrip('0') + + # Handle 00000 + return int(file_number) if file_number else 0 + + get_highest_img_number.number_of_images = 0 + + highest_file_number = -1 + + if os.path.isdir(save_dir_path): + existing_files = list(os.scandir(save_dir_path)) + highest_file_number = reduce(get_highest_img_number, existing_files, -1) + + calculated_img_number = max(highest_file_number, get_highest_img_number.number_of_images - 1) + + if task_data.session_id in _calculate_img_number.session_img_numbers: + calculated_img_number = max( + _calculate_img_number.session_img_numbers[task_data.session_id], + calculated_img_number, + ) + + calculated_img_number = calculated_img_number + 1 + + _calculate_img_number.session_img_numbers[task_data.session_id] = calculated_img_number + return calculated_img_number + +_calculate_img_number.session_img_numbers = {} + +def calculate_img_number(save_dir_path: str, task_data: TaskData): + return ImageNumber(lambda: _calculate_img_number(save_dir_path, task_data)) diff --git a/ui/media/js/parameters.js b/ui/media/js/parameters.js index baa55469..0fc89359 100644 --- a/ui/media/js/parameters.js +++ b/ui/media/js/parameters.js @@ -15,10 +15,13 @@ * JSDoc style * @typedef {object} Parameter * @property {string} id - * @property {ParameterType} type - * @property {string} label - * @property {?string} note + * @property {keyof ParameterType} type + * @property {string | (parameter: Parameter) => (HTMLElement | string)} label + * @property {string | (parameter: Parameter) => (HTMLElement | string) | undefined} note + * @property {(parameter: Parameter) => (HTMLElement | string) | undefined} render + * @property {string | undefined} icon * @property {number|boolean|string} default + * @property {boolean?} saveInAppConfig */ @@ -118,6 +121,7 @@ var PARAMETERS = [ note: "starts the default browser on startup", icon: "fa-window-restore", default: true, + saveInAppConfig: true, }, { id: "vram_usage_level", @@ -179,6 +183,7 @@ var PARAMETERS = [ note: "Other devices on your network can access this web page", icon: "fa-network-wired", default: true, + saveInAppConfig: true, }, { id: "listen_port", @@ -188,7 +193,8 @@ var PARAMETERS = [ icon: "fa-anchor", render: (parameter) => { return `` - } + }, + saveInAppConfig: true, }, { id: "use_beta_channel", @@ -205,6 +211,7 @@ var PARAMETERS = [ note: "Experimental! Can have bugs! Use upcoming features (like LoRA) in our new engine. Please press Save, then restart the program after changing this.", icon: "fa-bolt", default: false, + saveInAppConfig: true, }, ]; @@ -228,6 +235,10 @@ function sliderUpdate(event) { } } +/** + * @param {Parameter} parameter + * @returns {string | HTMLElement} + */ function getParameterElement(parameter) { switch (parameter.type) { case ParameterType.checkbox: @@ -243,29 +254,74 @@ function getParameterElement(parameter) { case ParameterType.custom: return parameter.render(parameter) default: - console.error(`Invalid type for parameter ${parameter.id}`); + console.error(`Invalid type ${parameter.type} for parameter ${parameter.id}`); return "ERROR: Invalid Type" } } let parametersTable = document.querySelector("#system-settings .parameters-table") -/* fill in the system settings popup table */ -function initParameters() { - PARAMETERS.forEach(parameter => { - var element = getParameterElement(parameter) - var note = parameter.note ? `${parameter.note}` : ""; - var icon = parameter.icon ? `` : ""; - var newrow = document.createElement('div') - newrow.innerHTML = ` -
${icon}
-
${note}
-
${element}
` +/** + * fill in the system settings popup table + * @param {Array | undefined} parameters + * */ +function initParameters(parameters) { + parameters.forEach(parameter => { + const element = getParameterElement(parameter) + const elementWrapper = createElement('div') + if (element instanceof Node) { + elementWrapper.appendChild(element) + } else { + elementWrapper.innerHTML = element + } + + const note = typeof parameter.note === 'function' ? parameter.note(parameter) : parameter.note + const noteElements = [] + if (note) { + const noteElement = createElement('small') + if (note instanceof Node) { + noteElement.appendChild(note) + } else { + noteElement.innerHTML = note || '' + } + noteElements.push(noteElement) + } + + const icon = parameter.icon ? [createElement('i', undefined, ['fa', parameter.icon])] : [] + + const label = typeof parameter.label === 'function' ? parameter.label(parameter) : parameter.label + const labelElement = createElement('label', { for: parameter.id }) + if (label instanceof Node) { + labelElement.appendChild(label) + } else { + labelElement.innerHTML = label + } + + const newrow = createElement( + 'div', + { 'data-setting-id': parameter.id, 'data-save-in-app-config': parameter.saveInAppConfig }, + undefined, + [ + createElement('div', undefined, undefined, icon), + createElement('div', undefined, undefined, [labelElement, ...noteElements]), + elementWrapper, + ] + ) parametersTable.appendChild(newrow) parameter.settingsEntry = newrow }) } -initParameters() +initParameters(PARAMETERS) + +// listen to parameters from plugins +PARAMETERS.addEventListener('push', (...items) => { + initParameters(items) + + if (items.find(item => item.saveInAppConfig)) { + console.log('Reloading app config for new parameters', items.map(p => p.id)) + getAppConfig() + } +}) let vramUsageLevelField = document.querySelector('#vram_usage_level') let useCPUField = document.querySelector('#use_cpu') @@ -324,9 +380,44 @@ async function getAppConfig() { document.querySelector("#lora_model_container").style.display = (testDiffusers.checked ? '' : 'none') } + Array.from(parametersTable.children).forEach(parameterRow => { + if (parameterRow.dataset.settingId in config && parameterRow.dataset.saveInAppConfig === 'true') { + const configValue = config[parameterRow.dataset.settingId] + const parameterElement = document.getElementById(parameterRow.dataset.settingId) || + parameterRow.querySelector('input') || parameterRow.querySelector('select') + + switch (parameterElement?.tagName) { + case 'INPUT': + if (parameterElement.type === 'checkbox') { + parameterElement.checked = configValue + } else { + parameterElement.value = configValue + } + parameterElement.dispatchEvent(new Event('change')) + break + case 'SELECT': + if (Array.isArray(configValue)) { + Array.from(parameterElement.options).forEach(option => { + if (configValue.includes(option.value || option.text)) { + option.selected = true + } + }) + } else { + parameterElement.value = configValue + } + parameterElement.dispatchEvent(new Event('change')) + break + } + } + }) + console.log('get config status response', config) + + return config } catch (e) { console.log('get config status error', e) + + return {} } } @@ -486,16 +577,43 @@ saveSettingsBtn.addEventListener('click', function() { alert('The network port must be a number from 1 to 65535') return } - let updateBranch = (useBetaChannelField.checked ? 'beta' : 'main') - changeAppConfig({ + const updateBranch = (useBetaChannelField.checked ? 'beta' : 'main') + + const updateAppConfigRequest = { 'render_devices': getCurrentRenderDeviceSelection(), 'update_branch': updateBranch, - 'ui_open_browser_on_start': uiOpenBrowserOnStartField.checked, - 'listen_to_network': listenToNetworkField.checked, - 'listen_port': listenPortField.value, - 'test_diffusers': testDiffusers.checked - }) - saveSettingsBtn.classList.add('active') - asyncDelay(300).then(() => saveSettingsBtn.classList.remove('active')) -}) + } + Array.from(parametersTable.children).forEach(parameterRow => { + if (parameterRow.dataset.saveInAppConfig === 'true') { + const parameterElement = document.getElementById(parameterRow.dataset.settingId) || + parameterRow.querySelector('input') || parameterRow.querySelector('select') + + switch (parameterElement?.tagName) { + case 'INPUT': + if (parameterElement.type === 'checkbox') { + updateAppConfigRequest[parameterRow.dataset.settingId] = parameterElement.checked + } else { + updateAppConfigRequest[parameterRow.dataset.settingId] = parameterElement.value + } + break + case 'SELECT': + if (parameterElement.multiple) { + updateAppConfigRequest[parameterRow.dataset.settingId] = Array.from(parameterElement.options) + .filter(option => option.selected) + .map(option => option.value || option.text) + } else { + updateAppConfigRequest[parameterRow.dataset.settingId] = parameterElement.value + } + break + default: + console.error(`Setting parameter ${parameterRow.dataset.settingId} couldn't be saved to app.config - element #${parameter.id} is a <${parameterElement?.tagName} /> instead of a or a
+
diff --git a/ui/media/js/main.js b/ui/media/js/main.js index a03e1e2a..29f698d5 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -21,6 +21,7 @@ const taskConfigSetup = { use_hypernetwork_model: 'Hypernetwork', hypernetwork_strength: { label: 'Hypernetwork Strength', visible: ({ reqBody }) => !!reqBody?.use_hypernetwork_model }, use_lora_model: { label: 'Lora Model', visible: ({ reqBody }) => !!reqBody?.use_lora_model }, + lora_alpha: { label: 'Lora Strength', visible: ({ reqBody }) => !!reqBody?.use_lora_model }, preserve_init_image_color_profile: 'Preserve Color Profile', }, pluginTaskConfig: {}, @@ -1155,8 +1156,9 @@ function getCurrentUserRequest() { newTask.reqBody.use_hypernetwork_model = hypernetworkModelField.value newTask.reqBody.hypernetwork_strength = parseFloat(hypernetworkStrengthField.value) } - if (testDiffusers.checked) { + if (testDiffusers.checked && loraModelField.value) { newTask.reqBody.use_lora_model = loraModelField.value + newTask.reqBody.lora_alpha = parseFloat(loraAlphaField.value) } return newTask } @@ -1527,8 +1529,8 @@ function updateLoraAlpha() { function updateLoraAlphaSlider() { if (loraAlphaField.value < 0) { loraAlphaField.value = 0 - } else if (loraAlphaField.value > 0.99) { - loraAlphaField.value = 0.99 + } else if (loraAlphaField.value > 1) { + loraAlphaField.value = 1 } loraAlphaSlider.value = loraAlphaField.value * 100 @@ -1539,12 +1541,11 @@ loraAlphaSlider.addEventListener('input', updateLoraAlpha) loraAlphaField.addEventListener('input', updateLoraAlphaSlider) updateLoraAlpha() -// function updateLoraAlphaContainer() { -// document.querySelector("#lora_alpha_container").style.display = (loraModelField.value === "" ? 'none' : '') -// } -// loraModelField.addEventListener('change', updateLoraAlphaContainer) -// updateLoraAlphaContainer() -document.querySelector("#lora_alpha_container").style.display = 'none' +function updateLoraAlphaContainer() { + document.querySelector("#lora_alpha_container").style.display = (loraModelField.value === "" ? 'none' : '') +} +loraModelField.addEventListener('change', updateLoraAlphaContainer) +updateLoraAlphaContainer() /********************* JPEG/WEBP Quality **********************/ function updateOutputQuality() { From 02fdafc111483589c414d30d3d80391941870201 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Sat, 1 Apr 2023 16:08:53 +0530 Subject: [PATCH 020/291] changelog --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 752111f6..201a0249 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -21,6 +21,7 @@ Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. ### Detailed changelog +* 2.5.30 - 1 Apr 2023 - Slider to control the strength of the LoRA model. * 2.5.30 - 28 Mar 2023 - Refactor task entry config to use a generating method. Added ability for plugins to easily add to this. Removed confusing sentence from `contributing.md` * 2.5.30 - 28 Mar 2023 - Allow the user to undo the deletion of tasks or images, instead of showing a pop-up each time. The new `Undo` button will be present at the top of the UI. Thanks @JeLuf. * 2.5.30 - 28 Mar 2023 - Support saving lossless WEBP images. Thanks @ogmaresca. From 061e012cffd80105bfa55e6ed732070cabc492c7 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Sat, 1 Apr 2023 16:09:51 +0530 Subject: [PATCH 021/291] Revert "Remove Ctrl+Z for undoing task removals until the conflict with other listeners for Ctrl+Z can be figured out" This reverts commit 8f877a2cee559a97a03771039a89ad48c76327cc. --- ui/media/js/main.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ui/media/js/main.js b/ui/media/js/main.js index 29f698d5..e9f3f9d8 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -320,6 +320,12 @@ function undoRemove() { undoButton.addEventListener('click', () => { undoRemove() }) +document.addEventListener('keydown', function(e) { + if ((e.ctrlKey || e.metaKey) && e.key === 'z' && e.target == document.body) { + undoRemove() + } +}) + function showImages(reqBody, res, outputContainer, livePreview) { let imageItemElements = outputContainer.querySelectorAll('.imgItem') if(typeof res != 'object') return From fad36d9c0874be58e4d326ecf9f89d143c18382b Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Sat, 1 Apr 2023 16:42:06 +0530 Subject: [PATCH 022/291] Don't show or allow test_diffusers if not using the beta branch. This allows allow features to release to the main branch, while restricting diffusers to the beta branch --- ui/easydiffusion/renderer.py | 4 +++- ui/media/js/parameters.js | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ui/easydiffusion/renderer.py b/ui/easydiffusion/renderer.py index 8270d232..c8004fee 100644 --- a/ui/easydiffusion/renderer.py +++ b/ui/easydiffusion/renderer.py @@ -29,7 +29,9 @@ def init(device): from easydiffusion import app app_config = app.getConfig() - context.test_diffusers = app_config.get("test_diffusers", False) + context.test_diffusers = ( + app_config.get("test_diffusers", False) and app_config.get("update_branch", "main") != "main" + ) device_manager.device_init(context, device) diff --git a/ui/media/js/parameters.js b/ui/media/js/parameters.js index baa55469..eb79a2f0 100644 --- a/ui/media/js/parameters.js +++ b/ui/media/js/parameters.js @@ -309,6 +309,8 @@ async function getAppConfig() { if (config.update_branch === 'beta') { useBetaChannelField.checked = true document.querySelector("#updateBranchLabel").innerText = "(beta)" + } else { + getParameterSettingsEntry("test_diffusers").style.display = "none" } if (config.ui && config.ui.open_browser_on_start === false) { uiOpenBrowserOnStartField.checked = false @@ -320,7 +322,7 @@ async function getAppConfig() { listenPortField.value = config.net.listen_port } if (config.test_diffusers !== undefined) { - testDiffusers.checked = config.test_diffusers + testDiffusers.checked = config.test_diffusers && config.update_branch === 'beta' document.querySelector("#lora_model_container").style.display = (testDiffusers.checked ? '' : 'none') } From 7772b6901aa47452939f95760a42be6eee49d36b Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Sat, 1 Apr 2023 16:44:36 +0530 Subject: [PATCH 023/291] Hide lora slider if not enabled --- ui/media/js/parameters.js | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/media/js/parameters.js b/ui/media/js/parameters.js index eb79a2f0..93c7d725 100644 --- a/ui/media/js/parameters.js +++ b/ui/media/js/parameters.js @@ -324,6 +324,7 @@ async function getAppConfig() { if (config.test_diffusers !== undefined) { testDiffusers.checked = config.test_diffusers && config.update_branch === 'beta' document.querySelector("#lora_model_container").style.display = (testDiffusers.checked ? '' : 'none') + document.querySelector("#lora_alpha_container").style.display = (testDiffusers.checked ? '' : 'none') } console.log('get config status response', config) From 75445185c45935c41502f53f8b4eebfbf17069b7 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Sat, 1 Apr 2023 17:01:52 +0530 Subject: [PATCH 024/291] Hide the slider if lora is none --- ui/media/js/parameters.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/media/js/parameters.js b/ui/media/js/parameters.js index 93c7d725..cc12f32b 100644 --- a/ui/media/js/parameters.js +++ b/ui/media/js/parameters.js @@ -324,7 +324,7 @@ async function getAppConfig() { if (config.test_diffusers !== undefined) { testDiffusers.checked = config.test_diffusers && config.update_branch === 'beta' document.querySelector("#lora_model_container").style.display = (testDiffusers.checked ? '' : 'none') - document.querySelector("#lora_alpha_container").style.display = (testDiffusers.checked ? '' : 'none') + document.querySelector("#lora_alpha_container").style.display = (testDiffusers.checked && loraModelField.value !== "" ? '' : 'none') } console.log('get config status response', config) From 07b467e4bce8968db1b89b657ae4f42b00f56f47 Mon Sep 17 00:00:00 2001 From: Olivia Godone-Maresca Date: Sat, 1 Apr 2023 11:42:20 -0400 Subject: [PATCH 025/291] Add LoRA Strength to metadata files --- ui/easydiffusion/utils/save_utils.py | 2 +- ui/index.html | 2 +- ui/media/js/dnd.js | 8 ++++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/ui/easydiffusion/utils/save_utils.py b/ui/easydiffusion/utils/save_utils.py index 950f04b0..b80d0806 100644 --- a/ui/easydiffusion/utils/save_utils.py +++ b/ui/easydiffusion/utils/save_utils.py @@ -28,7 +28,7 @@ TASK_TEXT_MAPPING = { "use_hypernetwork_model": "Hypernetwork model", "hypernetwork_strength": "Hypernetwork Strength", "use_lora_model": "LoRA model", - # "lora_alpha": "LoRA Strength", + "lora_alpha": "LoRA Strength", } diff --git a/ui/index.html b/ui/index.html index fed2b3b9..faf7bfeb 100644 --- a/ui/index.html +++ b/ui/index.html @@ -221,7 +221,7 @@ - +
diff --git a/ui/media/js/dnd.js b/ui/media/js/dnd.js index 98c5e26a..88a8b0c7 100644 --- a/ui/media/js/dnd.js +++ b/ui/media/js/dnd.js @@ -245,6 +245,14 @@ const TASK_MAPPING = { readUI: () => loraModelField.value, parse: (val) => val }, + lora_alpha: { name: 'LoRA Strength', + setUI: (lora_alpha) => { + loraAlphaField.value = lora_alpha + updateLoraAlphaSlider() + }, + readUI: () => parseFloat(loraAlphaField.value), + parse: (val) => parseFloat(val) + }, use_hypernetwork_model: { name: 'Hypernetwork model', setUI: (use_hypernetwork_model) => { const oldVal = hypernetworkModelField.value From bc711414a8e628e6cd2d39bc67a26bb6a21f3c47 Mon Sep 17 00:00:00 2001 From: Olivia Godone-Maresca Date: Sat, 1 Apr 2023 21:26:32 -0400 Subject: [PATCH 026/291] Increase the random seed range --- ui/media/js/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/media/js/main.js b/ui/media/js/main.js index e9f3f9d8..61838f25 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -1099,7 +1099,7 @@ function createTask(task) { function getCurrentUserRequest() { const numOutputsTotal = parseInt(numOutputsTotalField.value) const numOutputsParallel = parseInt(numOutputsParallelField.value) - const seed = (randomSeedField.checked ? Math.floor(Math.random() * 10000000) : parseInt(seedField.value)) + const seed = (randomSeedField.checked ? Math.floor(Math.random() * (2**32 - 1)) : parseInt(seedField.value)) const newTask = { batchesDone: 0, From 6aa048e3ad825ae0ea25ffea55cbbf20423fdd9c Mon Sep 17 00:00:00 2001 From: patriceac <48073125+patriceac@users.noreply.github.com> Date: Sun, 2 Apr 2023 02:04:03 -0700 Subject: [PATCH 027/291] Reset the LoRA dropdown if not present in the task --- ui/media/js/dnd.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ui/media/js/dnd.js b/ui/media/js/dnd.js index 98c5e26a..6e78609c 100644 --- a/ui/media/js/dnd.js +++ b/ui/media/js/dnd.js @@ -340,7 +340,12 @@ function restoreTaskToUI(task, fieldsToSkip) { hypernetworkModelField.value = "" hypernetworkModelField.dispatchEvent(new Event("change")) } - + + if (!('use_lora_model' in task.reqBody)) { + loraModelField.value = "None" + loraModelField.dispatchEvent(new Event("change")) + } + // restore the original prompt if provided (e.g. use settings), fallback to prompt as needed (e.g. copy/paste or d&d) promptField.value = task.reqBody.original_prompt if (!('original_prompt' in task.reqBody)) { From ec0b08e4d02a088ad4526f7c4a7909cc54907d54 Mon Sep 17 00:00:00 2001 From: Diana <5275194+DianaNites@users.noreply.github.com> Date: Sun, 2 Apr 2023 09:15:15 -0700 Subject: [PATCH 028/291] user configuration --- scripts/on_env_start.bat | 4 ++++ scripts/on_env_start.sh | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/scripts/on_env_start.bat b/scripts/on_env_start.bat index 57dc5da0..9b461b78 100644 --- a/scripts/on_env_start.bat +++ b/scripts/on_env_start.bat @@ -8,6 +8,10 @@ if exist "scripts\config.bat" ( @call scripts\config.bat ) +if exist "scripts\user_config.bat" ( + @call scripts\user_config.bat +) + if "%update_branch%"=="" ( set update_branch=main ) diff --git a/scripts/on_env_start.sh b/scripts/on_env_start.sh index 7e180f02..39d3939e 100755 --- a/scripts/on_env_start.sh +++ b/scripts/on_env_start.sh @@ -8,6 +8,11 @@ if [ -f "scripts/config.sh" ]; then source scripts/config.sh fi +if [ -f "scripts/user_config.sh" ]; then + source scripts/user_config.sh +fi + + if [ "$update_branch" == "" ]; then export update_branch="main" fi From 69380e352767182a2a2238c382b3064a9693f51c Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Mon, 3 Apr 2023 15:57:52 +0530 Subject: [PATCH 029/291] sdkit 1.0.63 - bug fix - samplers wouldn't get created if an inpainting was the first to load --- scripts/on_sd_start.bat | 4 ++-- scripts/on_sd_start.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/on_sd_start.bat b/scripts/on_sd_start.bat index d04eb9bb..1b9ec588 100644 --- a/scripts/on_sd_start.bat +++ b/scripts/on_sd_start.bat @@ -95,7 +95,7 @@ if "%ERRORLEVEL%" EQU "0" ( set PYTHONNOUSERSITE=1 set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages - call python -m pip install --upgrade sdkit==1.0.62 -q || ( + call python -m pip install --upgrade sdkit==1.0.63 -q || ( echo "Error updating sdkit" ) ) @@ -106,7 +106,7 @@ if "%ERRORLEVEL%" EQU "0" ( set PYTHONNOUSERSITE=1 set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages - call python -m pip install sdkit==1.0.62 || ( + call python -m pip install sdkit==1.0.63 || ( echo "Error installing sdkit. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" pause exit /b diff --git a/scripts/on_sd_start.sh b/scripts/on_sd_start.sh index 80a61b8e..cf74349c 100755 --- a/scripts/on_sd_start.sh +++ b/scripts/on_sd_start.sh @@ -103,7 +103,7 @@ if python ../scripts/check_modules.py sdkit sdkit.models ldm transformers numpy export PYTHONNOUSERSITE=1 export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages" - python -m pip install --upgrade sdkit==1.0.62 -q + python -m pip install --upgrade sdkit==1.0.63 -q fi else echo "Installing sdkit: https://pypi.org/project/sdkit/" @@ -111,7 +111,7 @@ else export PYTHONNOUSERSITE=1 export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages" - if python -m pip install sdkit==1.0.62 ; then + if python -m pip install sdkit==1.0.63 ; then echo "Installed." else fail "sdkit install failed" From 3fc93e2c57f023838edc8e521f0a671ba23b02ea Mon Sep 17 00:00:00 2001 From: Olivia Godone-Maresca Date: Mon, 3 Apr 2023 17:39:34 -0400 Subject: [PATCH 030/291] Add a utility function to create tabs with lazy loading functionality --- ui/media/js/utils.js | 136 +++++++- ui/plugins/ui/merge.plugin.js | 464 ++++++++++++-------------- ui/plugins/ui/release-notes.plugin.js | 71 ++-- 3 files changed, 384 insertions(+), 287 deletions(-) diff --git a/ui/media/js/utils.js b/ui/media/js/utils.js index 6eb0d643..f38cb5ac 100644 --- a/ui/media/js/utils.js +++ b/ui/media/js/utils.js @@ -683,7 +683,7 @@ class ServiceContainer { * @param {string} tag * @param {object} attributes * @param {string | Array} classes - * @param {string | HTMLElement | Array} + * @param {string | Node | Array} * @returns {HTMLElement} */ function createElement(tagName, attributes, classes, textOrElements) { @@ -699,7 +699,7 @@ function createElement(tagName, attributes, classes, textOrElements) { if (textOrElements) { const children = Array.isArray(textOrElements) ? textOrElements : [textOrElements] children.forEach(textOrElem => { - if (textOrElem instanceof HTMLElement) { + if (textOrElem instanceof Node) { element.appendChild(textOrElem) } else { element.appendChild(document.createTextNode(textOrElem)) @@ -708,3 +708,135 @@ function createElement(tagName, attributes, classes, textOrElements) { } return element } + +/** + * @typedef {object} TabOpenDetails + * @property {HTMLElement} contentElement + * @property {HTMLElement} labelElement + * @property {number} timesOpened + * @property {boolean} firstOpen + */ + +/** + * @typedef {object} CreateTabRequest + * @property {string} id + * @property {string | Node | (() => (string | Node))} label + * Label text or an HTML element + * @property {string} icon + * @property {string | Node | Promise | (() => (string | Node | Promise)) | undefined} content + * HTML string or HTML element + * @property {((TabOpenDetails, Event) => (undefined | string | Node | Promise)) | undefined} onOpen + * If an HTML string or HTML element is returned, then that will replace the tab content + * @property {string | undefined} css + */ + +/** + * @param {CreateTabRequest} request + */ + function createTab(request) { + if (!request?.id) { + console.error('createTab() error - id is required', Error().stack) + return + } + + if (!request.label) { + console.error('createTab() error - label is required', Error().stack) + return + } + + if (!request.icon) { + console.error('createTab() error - icon is required', Error().stack) + return + } + + if (!request.content && !request.onOpen) { + console.error('createTab() error - content or onOpen required', Error().stack) + return + } + + const tabsContainer = document.querySelector('.tab-container') + if (!tabsContainer) { + return + } + + const tabsContentWrapper = document.querySelector('#tab-content-wrapper') + if (!tabsContentWrapper) { + return + } + + console.debug('creating tab: ', request) + + if (request.css) { + document.querySelector('body').insertAdjacentElement( + 'beforeend', + createElement('style', { id: `tab-${request.id}-css` }, undefined, request.css), + ) + } + + const label = typeof request.label === 'function' ? request.label() : request.label + const labelElement = label instanceof Node ? label : createElement('span', undefined, undefined, label) + + const tab = createElement( + 'span', + { id: `tab-${request.id}`, 'data-times-opened': 0 }, + ['tab'], + createElement( + 'span', + undefined, + undefined, + [ + createElement( + 'i', + { style: 'margin-right: 0.25em' }, + ['fa-solid', `${request.icon.startsWith('fa-') ? '' : 'fa-'}${request.icon}`, 'icon'], + ), + labelElement, + ], + ) + ) + + + tabsContainer.insertAdjacentElement('beforeend', tab) + + const wrapper = createElement('div', { id: request.id }, ['tab-content-inner'], 'Loading..') + + const tabContent = createElement('div', { id: `tab-content-${request.id}` }, ['tab-content'], wrapper) + tabsContentWrapper.insertAdjacentElement('beforeend', tabContent) + + linkTabContents(tab) + + function replaceContent(resultFactory) { + if (resultFactory === undefined || resultFactory === null) { + return + } + const result = typeof resultFactory === 'function' ? resultFactory() : resultFactory + if (result instanceof Promise) { + result.then(replaceContent) + } else if (result instanceof Node) { + wrapper.replaceChildren(result) + } else { + wrapper.innerHTML = result + } + } + + replaceContent(request.content) + + tab.addEventListener('click', (e) => { + const timesOpened = +(tab.dataset.timesOpened || 0) + 1 + tab.dataset.timesOpened = timesOpened + + if (request.onOpen) { + const result = request.onOpen( + { + contentElement: wrapper, + labelElement, + timesOpened, + firstOpen: timesOpened === 1, + }, + e, + ) + + replaceContent(result) + } + }) +} diff --git a/ui/plugins/ui/merge.plugin.js b/ui/plugins/ui/merge.plugin.js index 6ff97286..4fce1d84 100644 --- a/ui/plugins/ui/merge.plugin.js +++ b/ui/plugins/ui/merge.plugin.js @@ -130,34 +130,11 @@ } drawDiagram(fn) } - - /////////////////////// Tab implementation - document.querySelector('.tab-container')?.insertAdjacentHTML('beforeend', ` - - Merge models - - `) - - document.querySelector('#tab-content-wrapper')?.insertAdjacentHTML('beforeend', ` -
-
- Loading.. -
-
- `) - - const tabMerge = document.querySelector('#tab-merge') - if (tabMerge) { - linkTabContents(tabMerge) - } - const merge = document.querySelector('#merge') - if (!merge) { - // merge tab not found, dont exec plugin code. - return - } - - document.querySelector('body').insertAdjacentHTML('beforeend', ` - - `) - - merge.innerHTML = ` -
-
-

- -

- -

-

Important: Please merge models of similar type.
For e.g. SD 1.4 models with only SD 1.4/1.5 models,
SD 2.0 with SD 2.0-type, and SD 2.1 with SD 2.1-type models.

-
- - - - - - - - - - - - - -
Base name of the output file.
Mix ratio and file suffix will be appended to this.
- Image generation uses fp16, so it's a good choice.
Use fp32 if you want to use the result models for more mixes
-
-
-
-
-

-
-
-
-
-
- - Make a single file - - - Make multiple variations - -
-
-
-
- Saves a single merged model file, at the specified merge ratio.

- - - % - Model A's contribution to the mix. The rest will be from Model B. + }`, + content: ` +
+
+

+ +

+ +

+

Important: Please merge models of similar type.
For e.g. SD 1.4 models with only SD 1.4/1.5 models,
SD 2.0 with SD 2.0-type, and SD 2.1 with SD 2.1-type models.

+
+ + + + + + + + + + + + + +
Base name of the output file.
Mix ratio and file suffix will be appended to this.
+ Image generation uses fp16, so it's a good choice.
Use fp32 if you want to use the result models for more mixes
+
+
+
+
+

+
+
+
+
+
+ + Make a single file + + + Make multiple variations + +
+
+
+
+ Saves a single merged model file, at the specified merge ratio.

+ + + % + Model A's contribution to the mix. The rest will be from Model B. +
-
-
-
- Saves multiple variations of the model, at different merge ratios.
Each variation will be saved as a separate file.


- - - - - - - - - - - - - -
Number of models to create
% Smallest share of model A in the mix
% Share of model A added into the mix per step
Sigmoid function to be applied to the model share before mixing
-
- Preview of variation ratios:
- +
+
+ Saves multiple variations of the model, at different merge ratios.
Each variation will be saved as a separate file.


+ + + + + + + + + + + + + +
Number of models to create
% Smallest share of model A in the mix
% Share of model A added into the mix per step
Sigmoid function to be applied to the model share before mixing
+
+ Preview of variation ratios:
+ +
-
-
-
-
- -
-
` - - const tabSettingsSingle = document.querySelector('#tab-merge-opts-single') - const tabSettingsBatch = document.querySelector('#tab-merge-opts-batch') - linkTabContents(tabSettingsSingle) - linkTabContents(tabSettingsBatch) - - console.log('Activate') - let mergeModelAField = new ModelDropdown(document.querySelector('#mergeModelA'), 'stable-diffusion') - let mergeModelBField = new ModelDropdown(document.querySelector('#mergeModelB'), 'stable-diffusion') - updateChart() - - // slider - const singleMergeRatioField = document.querySelector('#single-merge-ratio') - const singleMergeRatioSlider = document.querySelector('#single-merge-ratio-slider') - - function updateSingleMergeRatio() { - singleMergeRatioField.value = singleMergeRatioSlider.value / 10 - singleMergeRatioField.dispatchEvent(new Event("change")) - } - - function updateSingleMergeRatioSlider() { - if (singleMergeRatioField.value < 0) { - singleMergeRatioField.value = 0 - } else if (singleMergeRatioField.value > 100) { - singleMergeRatioField.value = 100 - } - - singleMergeRatioSlider.value = singleMergeRatioField.value * 10 - singleMergeRatioSlider.dispatchEvent(new Event("change")) - } - - singleMergeRatioSlider.addEventListener('input', updateSingleMergeRatio) - singleMergeRatioField.addEventListener('input', updateSingleMergeRatioSlider) - updateSingleMergeRatio() - - document.querySelector('.merge-config').addEventListener('change', updateChart) - - document.querySelector('#merge-button').addEventListener('click', async function(e) { - // Build request template - let model0 = mergeModelAField.value - let model1 = mergeModelBField.value - let request = { model0: model0, model1: model1 } - request['use_fp16'] = document.querySelector('#merge-fp').value == 'fp16' - let iterations = document.querySelector('#merge-count').value>>0 - let start = parseFloat( document.querySelector('#merge-start').value ) - let step = parseFloat( document.querySelector('#merge-step').value ) - - if (isTabActive(tabSettingsSingle)) { - start = parseFloat(singleMergeRatioField.value) - step = 0 - iterations = 1 - addLogMessage(`merge ratio = ${start}%`) - } else { - addLogMessage(`start = ${start}%`) - addLogMessage(`step = ${step}%`) - } - - if (start + (iterations-1) * step >= 100) { - addLogMessage('Aborting: maximum ratio is ≥ 100%') - addLogMessage('Reduce the number of variations or the step size') - addLogSeparator() - document.querySelector('#merge-count').focus() - return - } - - if (document.querySelector('#merge-filename').value == "") { - addLogMessage('Aborting: No output file name specified') - addLogSeparator() - document.querySelector('#merge-filename').focus() - return - } - - // Disable merge button - e.target.disabled=true - e.target.classList.add('disabled') - let cursor = $("body").css("cursor"); - let label = document.querySelector('#merge-button').innerHTML - $("body").css("cursor", "progress"); - document.querySelector('#merge-button').innerHTML = 'Merging models ...' - - addLogMessage("Merging models") - addLogMessage("Model A: "+model0) - addLogMessage("Model B: "+model1) - - // Batch main loop - for (let i=0; i +
+
+ +
+
`, + onOpen: ({ firstOpen }) => { + if (!firstOpen) { + return } - addLogMessage(`merging batch job ${i+1}/${iterations}, alpha = ${alpha.toFixed(5)}...`) - request['out_path'] = document.querySelector('#merge-filename').value - request['out_path'] += '-' + alpha.toFixed(5) + '.' + document.querySelector('#merge-format').value - addLogMessage(`  filename: ${request['out_path']}`) + const tabSettingsSingle = document.querySelector('#tab-merge-opts-single') + const tabSettingsBatch = document.querySelector('#tab-merge-opts-batch') + linkTabContents(tabSettingsSingle) + linkTabContents(tabSettingsBatch) - request['ratio'] = alpha - let res = await fetch('/model/merge', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(request) }) - const data = await res.json(); - addLogMessage(JSON.stringify(data)) - } - addLogMessage("Done. The models have been saved to your models/stable-diffusion folder.") - addLogSeparator() - // Re-enable merge button - $("body").css("cursor", cursor); - document.querySelector('#merge-button').innerHTML = label - e.target.disabled=false - e.target.classList.remove('disabled') + console.log('Activate') + let mergeModelAField = new ModelDropdown(document.querySelector('#mergeModelA'), 'stable-diffusion') + let mergeModelBField = new ModelDropdown(document.querySelector('#mergeModelB'), 'stable-diffusion') + updateChart() - // Update model list - stableDiffusionModelField.innerHTML = '' - vaeModelField.innerHTML = '' - hypernetworkModelField.innerHTML = '' - await getModels() + // slider + const singleMergeRatioField = document.querySelector('#single-merge-ratio') + const singleMergeRatioSlider = document.querySelector('#single-merge-ratio-slider') + + function updateSingleMergeRatio() { + singleMergeRatioField.value = singleMergeRatioSlider.value / 10 + singleMergeRatioField.dispatchEvent(new Event("change")) + } + + function updateSingleMergeRatioSlider() { + if (singleMergeRatioField.value < 0) { + singleMergeRatioField.value = 0 + } else if (singleMergeRatioField.value > 100) { + singleMergeRatioField.value = 100 + } + + singleMergeRatioSlider.value = singleMergeRatioField.value * 10 + singleMergeRatioSlider.dispatchEvent(new Event("change")) + } + + singleMergeRatioSlider.addEventListener('input', updateSingleMergeRatio) + singleMergeRatioField.addEventListener('input', updateSingleMergeRatioSlider) + updateSingleMergeRatio() + + document.querySelector('.merge-config').addEventListener('change', updateChart) + + document.querySelector('#merge-button').addEventListener('click', async function(e) { + // Build request template + let model0 = mergeModelAField.value + let model1 = mergeModelBField.value + let request = { model0: model0, model1: model1 } + request['use_fp16'] = document.querySelector('#merge-fp').value == 'fp16' + let iterations = document.querySelector('#merge-count').value>>0 + let start = parseFloat( document.querySelector('#merge-start').value ) + let step = parseFloat( document.querySelector('#merge-step').value ) + + if (isTabActive(tabSettingsSingle)) { + start = parseFloat(singleMergeRatioField.value) + step = 0 + iterations = 1 + addLogMessage(`merge ratio = ${start}%`) + } else { + addLogMessage(`start = ${start}%`) + addLogMessage(`step = ${step}%`) + } + + if (start + (iterations-1) * step >= 100) { + addLogMessage('Aborting: maximum ratio is ≥ 100%') + addLogMessage('Reduce the number of variations or the step size') + addLogSeparator() + document.querySelector('#merge-count').focus() + return + } + + if (document.querySelector('#merge-filename').value == "") { + addLogMessage('Aborting: No output file name specified') + addLogSeparator() + document.querySelector('#merge-filename').focus() + return + } + + // Disable merge button + e.target.disabled=true + e.target.classList.add('disabled') + let cursor = $("body").css("cursor"); + let label = document.querySelector('#merge-button').innerHTML + $("body").css("cursor", "progress"); + document.querySelector('#merge-button').innerHTML = 'Merging models ...' + + addLogMessage("Merging models") + addLogMessage("Model A: "+model0) + addLogMessage("Model B: "+model1) + + // Batch main loop + for (let i=0; iDone. The models have been saved to your models/stable-diffusion folder.") + addLogSeparator() + // Re-enable merge button + $("body").css("cursor", cursor); + document.querySelector('#merge-button').innerHTML = label + e.target.disabled=false + e.target.classList.remove('disabled') + + // Update model list + stableDiffusionModelField.innerHTML = '' + vaeModelField.innerHTML = '' + hypernetworkModelField.innerHTML = '' + await getModels() + }) + }, }) })() diff --git a/ui/plugins/ui/release-notes.plugin.js b/ui/plugins/ui/release-notes.plugin.js index da7b79de..cb4db5e2 100644 --- a/ui/plugins/ui/release-notes.plugin.js +++ b/ui/plugins/ui/release-notes.plugin.js @@ -9,56 +9,41 @@ } } - document.querySelector('.tab-container')?.insertAdjacentHTML('beforeend', ` - - What's new? - - `) - - document.querySelector('#tab-content-wrapper')?.insertAdjacentHTML('beforeend', ` -
-
- Loading.. -
-
- `) - - const tabNews = document.querySelector('#tab-news') - if (tabNews) { - linkTabContents(tabNews) - } - const news = document.querySelector('#news') - if (!news) { - // news tab not found, dont exec plugin code. - return - } - - document.querySelector('body').insertAdjacentHTML('beforeend', ` - - `) + `, + onOpen: async ({ firstOpen }) => { + if (firstOpen) { + const loadMarkedScriptPromise = loadScript('/media/js/marked.min.js') - loadScript('/media/js/marked.min.js').then(async function() { - let appConfig = await fetch('/get/app_config') - if (!appConfig.ok) { - console.error('[release-notes] Failed to get app_config.') - return - } - appConfig = await appConfig.json() + let appConfig = await fetch('/get/app_config') + if (!appConfig.ok) { + console.error('[release-notes] Failed to get app_config.') + return + } + appConfig = await appConfig.json() + + const updateBranch = appConfig.update_branch || 'main' + + let releaseNotes = await fetch(`https://raw.githubusercontent.com/cmdr2/stable-diffusion-ui/${updateBranch}/CHANGES.md`) + if (!releaseNotes.ok) { + console.error('[release-notes] Failed to get CHANGES.md.') + return + } + releaseNotes = await releaseNotes.text() - const updateBranch = appConfig.update_branch || 'main' + await loadMarkedScriptPromise - let releaseNotes = await fetch(`https://raw.githubusercontent.com/cmdr2/stable-diffusion-ui/${updateBranch}/CHANGES.md`) - if (!releaseNotes.ok) { - console.error('[release-notes] Failed to get CHANGES.md.') - return - } - releaseNotes = await releaseNotes.text() - news.innerHTML = marked.parse(releaseNotes) + return marked.parse(releaseNotes) + } + }, }) })() \ No newline at end of file From 88d415d3f9ab764cc38e3c38cea712bbfeec89e9 Mon Sep 17 00:00:00 2001 From: Olivia Godone-Maresca Date: Mon, 3 Apr 2023 19:35:37 -0400 Subject: [PATCH 031/291] Add back/forward buttons to switch between images in tasks --- ui/media/js/image-modal.js | 119 ++++++++++++++++++++++++++++++++----- ui/media/js/main.js | 25 +++++++- 2 files changed, 129 insertions(+), 15 deletions(-) diff --git a/ui/media/js/image-modal.js b/ui/media/js/image-modal.js index 498c5f24..367c754c 100644 --- a/ui/media/js/image-modal.js +++ b/ui/media/js/image-modal.js @@ -1,6 +1,28 @@ "use strict" +/** + * @typedef {object} ImageModalRequest + * @property {string} src + * @property {ImageModalRequest | () => ImageModalRequest | undefined} previous + * @property {ImageModalRequest | () => ImageModalRequest | undefined} next + */ + +/** + * @type {(() => (string | ImageModalRequest) | string | ImageModalRequest) => {}} + */ const imageModal = (function() { + const backElem = createElement( + 'i', + undefined, + ['fa-solid', 'fa-arrow-left', 'tertiaryButton'], + ) + + const forwardElem = createElement( + 'i', + undefined, + ['fa-solid', 'fa-arrow-right', 'tertiaryButton'], + ) + const zoomElem = createElement( 'i', undefined, @@ -13,7 +35,7 @@ const imageModal = (function() { ['fa-solid', 'fa-xmark', 'tertiaryButton'], ) - const menuBarElem = createElement('div', undefined, 'menu-bar', [zoomElem, closeElem]) + const menuBarElem = createElement('div', undefined, 'menu-bar', [backElem, forwardElem, zoomElem, closeElem]) const imageContainer = createElement('div', undefined, 'image-wrapper') @@ -63,15 +85,87 @@ const imageModal = (function() { () => setZoomLevel(imageContainer.querySelector('img')?.classList?.contains('natural-zoom')), ) - const close = () => { + const state = { + previous: undefined, + next: undefined, + } + + const clear = () => { imageContainer.innerHTML = '' + + Object.keys(state).forEach(key => delete state[key]) + } + + const close = () => { + clear() modalElem.classList.remove('active') document.body.style.overflow = 'initial' } - window.addEventListener('keydown', (e) => { - if (e.key === 'Escape' && modalElem.classList.contains('active')) { + /** + * @param {() => (string | ImageModalRequest) | string | ImageModalRequest} optionsFactory + */ + function init(optionsFactory) { + if (!optionsFactory) { close() + return + } + + clear() + + const options = typeof optionsFactory === 'function' ? optionsFactory() : optionsFactory + const src = typeof options === 'string' ? options : options.src + + const imgElem = createElement('img', { src }, 'natural-zoom') + imageContainer.appendChild(imgElem) + modalElem.classList.add('active') + document.body.style.overflow = 'hidden' + setZoomLevel(false) + + if (typeof options === 'object' && options.previous) { + state.previous = options.previous + backElem.style.display = 'unset' + } else { + backElem.style.display = 'none' + } + + if (typeof options === 'object' && options.next) { + state.next = options.next + forwardElem.style.display = 'unset' + } else { + forwardElem.style.display = 'none' + } + } + + const back = () => { + if (state.previous) { + init(state.previous) + } else { + backElem.style.display = 'none' + } + } + + const forward = () => { + if (state.next) { + init(state.next) + } else { + forwardElem.style.display = 'none' + } + } + + window.addEventListener('keydown', (e) => { + if (modalElem.classList.contains('active')) { + switch (e.key) { + case 'Escape': + close() + break + case 'ArrowLeft': + back() + break + case 'ArrowRight': + forward() + break + } } }) window.addEventListener('click', (e) => { @@ -86,15 +180,12 @@ const imageModal = (function() { } }) - return (optionsFactory) => { - const options = typeof optionsFactory === 'function' ? optionsFactory() : optionsFactory - const src = typeof options === 'string' ? options : options.src + backElem.addEventListener('click', back) - // TODO center it if < window size - const imgElem = createElement('img', { src }, 'natural-zoom') - imageContainer.appendChild(imgElem) - modalElem.classList.add('active') - document.body.style.overflow = 'hidden' - setZoomLevel(false) - } + forwardElem.addEventListener('click', forward) + + /** + * @param {() => (string | ImageModalRequest) | string | ImageModalRequest} optionsFactory + */ + return (optionsFactory) => init(optionsFactory) })() diff --git a/ui/media/js/main.js b/ui/media/js/main.js index e9f3f9d8..f26730af 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -398,7 +398,30 @@ function showImages(reqBody, res, outputContainer, livePreview) { if ('seed' in result && !imageElem.hasAttribute('data-seed')) { const imageExpandBtn = imageItemElem.querySelector('.imgExpandBtn') imageExpandBtn.addEventListener('click', function() { - imageModal(imageElem.src) + function previousImage(img) { + const allImages = Array.from(outputContainer.parentNode.querySelectorAll('.imgItem img')) + const index = allImages.indexOf(img) + return allImages.slice(0, index).reverse()[0] + } + + function nextImage(img) { + const allImages = Array.from(outputContainer.parentNode.querySelectorAll('.imgItem img')) + const index = allImages.indexOf(img) + return allImages.slice(index + 1)[0] + } + + function imageModalParameter(img) { + const previousImg = previousImage(img) + const nextImg = nextImage(img) + + return { + src: img.src, + previous: previousImg ? () => imageModalParameter(previousImg) : undefined, + next: nextImg ? () => imageModalParameter(nextImg) : undefined, + } + } + + imageModal(imageModalParameter(imageElem)) }) const req = Object.assign({}, reqBody, { From 73d947c4a6c1aaaf19beec41cfad38324a612e75 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 4 Apr 2023 16:10:16 +0530 Subject: [PATCH 032/291] Don't show LoRA options in the main branch --- ui/media/js/parameters.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ui/media/js/parameters.js b/ui/media/js/parameters.js index cc12f32b..022b9a7f 100644 --- a/ui/media/js/parameters.js +++ b/ui/media/js/parameters.js @@ -321,7 +321,10 @@ async function getAppConfig() { if (config.net && config.net.listen_port !== undefined) { listenPortField.value = config.net.listen_port } - if (config.test_diffusers !== undefined) { + if (config.test_diffusers === undefined) { + document.querySelector("#lora_model_container").style.display = 'none' + document.querySelector("#lora_alpha_container").style.display = 'none' + } else { testDiffusers.checked = config.test_diffusers && config.update_branch === 'beta' document.querySelector("#lora_model_container").style.display = (testDiffusers.checked ? '' : 'none') document.querySelector("#lora_alpha_container").style.display = (testDiffusers.checked && loraModelField.value !== "" ? '' : 'none') From 84737eb2716ec07d586f6ed234d99e625afe5540 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 4 Apr 2023 16:14:07 +0530 Subject: [PATCH 033/291] Disable LoRA in the main branch --- ui/media/js/parameters.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/media/js/parameters.js b/ui/media/js/parameters.js index 022b9a7f..455844da 100644 --- a/ui/media/js/parameters.js +++ b/ui/media/js/parameters.js @@ -321,11 +321,11 @@ async function getAppConfig() { if (config.net && config.net.listen_port !== undefined) { listenPortField.value = config.net.listen_port } - if (config.test_diffusers === undefined) { + if (config.test_diffusers === undefined || config.update_branch === 'main') { document.querySelector("#lora_model_container").style.display = 'none' document.querySelector("#lora_alpha_container").style.display = 'none' } else { - testDiffusers.checked = config.test_diffusers && config.update_branch === 'beta' + testDiffusers.checked = config.test_diffusers && config.update_branch !== 'main' document.querySelector("#lora_model_container").style.display = (testDiffusers.checked ? '' : 'none') document.querySelector("#lora_alpha_container").style.display = (testDiffusers.checked && loraModelField.value !== "" ? '' : 'none') } From f3a0ab24c1144c9524ed0aa9380358b765cb6b5c Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 4 Apr 2023 16:26:16 +0530 Subject: [PATCH 034/291] Tag the Test Diffusers changelog entries to beta-only --- CHANGES.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 201a0249..074506f1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -21,16 +21,16 @@ Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. ### Detailed changelog -* 2.5.30 - 1 Apr 2023 - Slider to control the strength of the LoRA model. +* 2.5.30 - 1 Apr 2023 - (beta-only) Slider to control the strength of the LoRA model. * 2.5.30 - 28 Mar 2023 - Refactor task entry config to use a generating method. Added ability for plugins to easily add to this. Removed confusing sentence from `contributing.md` * 2.5.30 - 28 Mar 2023 - Allow the user to undo the deletion of tasks or images, instead of showing a pop-up each time. The new `Undo` button will be present at the top of the UI. Thanks @JeLuf. * 2.5.30 - 28 Mar 2023 - Support saving lossless WEBP images. Thanks @ogmaresca. * 2.5.30 - 28 Mar 2023 - Lots of bug fixes for the UI (Read LoRA flag in metadata files, new prompt weight format with scrollwheel, fix overflow with lots of tabs, clear button in image editor, shorter filenames in download). Thanks @patriceac, @JeLuf and @ogmaresca. -* 2.5.29 - 27 Mar 2023 - Fix a bug where some non-square images would fail while inpainting with a `The size of tensor a must match size of tensor b` error. -* 2.5.29 - 27 Mar 2023 - Fix the `incorrect number of channels` error, when given a PNG image with an alpha channel in `Test Diffusers`. -* 2.5.29 - 27 Mar 2023 - Fix broken inpainting in `Test Diffusers` (beta). -* 2.5.28 - 24 Mar 2023 - Support for weighted prompts and long prompt lengths (not limited to 77 tokens). This change requires enabling the `Test Diffusers` setting in beta (in the Settings tab), and restarting the program. -* 2.5.27 - 21 Mar 2023 - LoRA support, accessible by enabling the `Test Diffusers` setting (in the Settings tab in the UI). This change switches the internal engine to diffusers (if the `Test Diffusers` setting is enabled). If the `Test Diffusers` flag is disabled, it'll have no impact for the user. +* 2.5.29 - 27 Mar 2023 - (beta-only) Fix a bug where some non-square images would fail while inpainting with a `The size of tensor a must match size of tensor b` error. +* 2.5.29 - 27 Mar 2023 - (beta-only) Fix the `incorrect number of channels` error, when given a PNG image with an alpha channel in `Test Diffusers`. +* 2.5.29 - 27 Mar 2023 - (beta-only) Fix broken inpainting in `Test Diffusers`. +* 2.5.28 - 24 Mar 2023 - (beta-only) Support for weighted prompts and long prompt lengths (not limited to 77 tokens). This change requires enabling the `Test Diffusers` setting in beta (in the Settings tab), and restarting the program. +* 2.5.27 - 21 Mar 2023 - (beta-only) LoRA support, accessible by enabling the `Test Diffusers` setting (in the Settings tab in the UI). This change switches the internal engine to diffusers (if the `Test Diffusers` setting is enabled). If the `Test Diffusers` flag is disabled, it'll have no impact for the user. * 2.5.26 - 15 Mar 2023 - Allow styling the buttons displayed on an image. Update the API to allow multiple buttons and text labels in a single row. Thanks @ogmaresca. * 2.5.26 - 15 Mar 2023 - View images in full-screen, by either clicking on the image, or clicking the "Full screen" icon next to the Seed number on the image. Thanks @ogmaresca for the internal API. * 2.5.25 - 14 Mar 2023 - Button to download all the images, and all the metadata as a zip file. This is available at the top of the UI, as well as on each image. Thanks @JeLuf. From 899125cc41f9b454567875d14c47e6504ad6619e Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 5 Apr 2023 08:07:53 +0530 Subject: [PATCH 035/291] sdkit 1.0.64 - bug fix - metadata embedding would ignore the jpeg quality --- scripts/on_sd_start.bat | 4 ++-- scripts/on_sd_start.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/on_sd_start.bat b/scripts/on_sd_start.bat index 1b9ec588..c9b3235f 100644 --- a/scripts/on_sd_start.bat +++ b/scripts/on_sd_start.bat @@ -95,7 +95,7 @@ if "%ERRORLEVEL%" EQU "0" ( set PYTHONNOUSERSITE=1 set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages - call python -m pip install --upgrade sdkit==1.0.63 -q || ( + call python -m pip install --upgrade sdkit==1.0.64 -q || ( echo "Error updating sdkit" ) ) @@ -106,7 +106,7 @@ if "%ERRORLEVEL%" EQU "0" ( set PYTHONNOUSERSITE=1 set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages - call python -m pip install sdkit==1.0.63 || ( + call python -m pip install sdkit==1.0.64 || ( echo "Error installing sdkit. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" pause exit /b diff --git a/scripts/on_sd_start.sh b/scripts/on_sd_start.sh index cf74349c..78bcf0ee 100755 --- a/scripts/on_sd_start.sh +++ b/scripts/on_sd_start.sh @@ -103,7 +103,7 @@ if python ../scripts/check_modules.py sdkit sdkit.models ldm transformers numpy export PYTHONNOUSERSITE=1 export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages" - python -m pip install --upgrade sdkit==1.0.63 -q + python -m pip install --upgrade sdkit==1.0.64 -q fi else echo "Installing sdkit: https://pypi.org/project/sdkit/" @@ -111,7 +111,7 @@ else export PYTHONNOUSERSITE=1 export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages" - if python -m pip install sdkit==1.0.63 ; then + if python -m pip install sdkit==1.0.64 ; then echo "Installed." else fail "sdkit install failed" From 687da5b64a7ab0b06ae48fa2e0ea863c2f9244d4 Mon Sep 17 00:00:00 2001 From: patriceac <48073125+patriceac@users.noreply.github.com> Date: Thu, 6 Apr 2023 03:12:48 -0700 Subject: [PATCH 036/291] Undo UI cleanup (#1106) * Moving to InvokeAI attention weighting syntax * Fix restoration of disabled image tags Fix the restoration inactive image tags. * Undo feature UX cleanup Just show the undo button when there's no task for a more consistent UI. * cleanup code * Revert "cleanup code" This reverts commit 03199c5a4f9bf7dd798645a280735ff0f9f6a17a. * Update image-modifiers.js * Update image-modifiers.js --- ui/index.html | 12 ++++++------ ui/media/css/main.css | 4 ++++ ui/media/js/main.js | 6 ++++-- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/ui/index.html b/ui/index.html index fed2b3b9..702a4c1c 100644 --- a/ui/index.html +++ b/ui/index.html @@ -293,6 +293,12 @@
+
+ Type a prompt and press the "Make Image" button.

You can set an "Initial Image" if you want to guide the AI.

+ You can also add modifiers like "Realistic", "Pencil Sketch", "ArtStation" etc by browsing through the "Image Modifiers" section + and selecting the desired modifiers.

+ Click "Image Settings" for additional settings like seed, image size, number of images to generate etc.

Enjoy! :) +
@@ -326,12 +332,6 @@
-
- Type a prompt and press the "Make Image" button.

You can set an "Initial Image" if you want to guide the AI.

- You can also add modifiers like "Realistic", "Pencil Sketch", "ArtStation" etc by browsing through the "Image Modifiers" section - and selecting the desired modifiers.

- Click "Image Settings" for additional settings like seed, image size, number of images to generate etc.

Enjoy! :) -
diff --git a/ui/media/css/main.css b/ui/media/css/main.css index e6a95cda..08dea664 100644 --- a/ui/media/css/main.css +++ b/ui/media/css/main.css @@ -238,6 +238,10 @@ code { #stopImage:hover { background: rgb(177, 27, 0); } +#undo { + float: right; + margin-left: 5px; +} div#render-buttons { gap: 3px; diff --git a/ui/media/js/main.js b/ui/media/js/main.js index b22da732..0720b988 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -1310,13 +1310,15 @@ async function stopAllTasks() { function updateInitialText() { if (document.querySelector('.imageTaskContainer') === null) { - if (undoBuffer.length == 0) { - previewTools.classList.add('displayNone') + if (undoBuffer.length > 0) { + initialText.prepend(undoButton) } + previewTools.classList.add('displayNone') initialText.classList.remove('displayNone') } else { initialText.classList.add('displayNone') previewTools.classList.remove('displayNone') + document.querySelector('div.display-settings').prepend(undoButton) } } From 0d570b3fae85eb6c664a3456bc0515ed178ced77 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Thu, 6 Apr 2023 16:21:07 +0530 Subject: [PATCH 037/291] version --- ui/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/index.html b/ui/index.html index 702a4c1c..bb79f3be 100644 --- a/ui/index.html +++ b/ui/index.html @@ -30,7 +30,7 @@

Easy Diffusion - v2.5.30 + v2.5.31

From c115a9aa3ddbb2c98bd5ef8397f150a0ec59a08e Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Thu, 6 Apr 2023 16:39:20 +0530 Subject: [PATCH 038/291] changelog --- CHANGES.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 074506f1..bde70f1e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -21,6 +21,9 @@ Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. ### Detailed changelog +* 2.5.31 - 6 Apr 2023 - Allow seeds upto `4,294,967,295`. Thanks @ogmaresca. +* 2.5.31 - 6 Apr 2023 - Buttons to show the previous/next image in the image popup. Thanks @ogmaresca. +* 2.5.30 - 5 Apr 2023 - Fix a bug where the JPEG image quality wasn't being respected when embedding the metadata into it. Thanks @JeLuf. * 2.5.30 - 1 Apr 2023 - (beta-only) Slider to control the strength of the LoRA model. * 2.5.30 - 28 Mar 2023 - Refactor task entry config to use a generating method. Added ability for plugins to easily add to this. Removed confusing sentence from `contributing.md` * 2.5.30 - 28 Mar 2023 - Allow the user to undo the deletion of tasks or images, instead of showing a pop-up each time. The new `Undo` button will be present at the top of the UI. Thanks @JeLuf. From 9c091a9edf01c98d4bdd1828ac0b8a614817418a Mon Sep 17 00:00:00 2001 From: Olivia Godone-Maresca Date: Thu, 6 Apr 2023 16:37:17 -0400 Subject: [PATCH 039/291] Fix JSDoc --- ui/media/js/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/media/js/utils.js b/ui/media/js/utils.js index 5599bae4..8421bfa0 100644 --- a/ui/media/js/utils.js +++ b/ui/media/js/utils.js @@ -711,7 +711,7 @@ function createElement(tagName, attributes, classes, textOrElements) { return element } -/* +/** * Add a listener for arrays * @param {keyof Array} method * @param {(args) => {}} callback From 7d4d85284b8e506098c4ba0a88b68c98641f306a Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 7 Apr 2023 09:55:30 +0530 Subject: [PATCH 040/291] Disable ding sound when the UI loads. It'll now only play upon task completion --- ui/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/index.html b/ui/index.html index 3a248ea7..7e8680d0 100644 --- a/ui/index.html +++ b/ui/index.html @@ -520,7 +520,7 @@ async function init() { } }) - playSound() + // playSound() } init() From 17f60d3c7f7cea7721b9ce26c7c558b1c4930532 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 7 Apr 2023 09:55:30 +0530 Subject: [PATCH 041/291] Disable ding sound when the UI loads. It'll now only play upon task completion --- ui/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/index.html b/ui/index.html index fed2b3b9..cb3eae81 100644 --- a/ui/index.html +++ b/ui/index.html @@ -520,7 +520,7 @@ async function init() { } }) - playSound() + // playSound() } init() From 3ce6c3dc614b9211cebc213087aae404ea90dc5f Mon Sep 17 00:00:00 2001 From: JeLuF Date: Fri, 7 Apr 2023 08:46:19 +0200 Subject: [PATCH 042/291] fix font definition for some users I don't know why it breaks only for a few users, but this patch seems to fix the issue. https://discord.com/channels/1014774730907209781/1081867905907572746 https://discord.com/channels/1014774730907209781/1093215237307637901/1093223612615491604 --- ui/media/css/fonts.css | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ui/media/css/fonts.css b/ui/media/css/fonts.css index 8bd4f5d5..6a1fbbae 100644 --- a/ui/media/css/fonts.css +++ b/ui/media/css/fonts.css @@ -3,7 +3,7 @@ font-family: 'Work Sans'; font-style: normal; font-weight: 400; - src: local(''), + src: local('Work Sans'), url('/media/fonts/work-sans-v18-latin-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */ url('/media/fonts/work-sans-v18-latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ } @@ -13,7 +13,7 @@ font-family: 'Work Sans'; font-style: normal; font-weight: 600; - src: local(''), + src: local('Work Sans'), url('/media/fonts/work-sans-v18-latin-600.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */ url('/media/fonts/work-sans-v18-latin-600.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ } @@ -23,7 +23,7 @@ font-family: 'Work Sans'; font-style: normal; font-weight: 700; - src: local(''), + src: local('Work Sans'), url('/media/fonts/work-sans-v18-latin-700.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */ url('/media/fonts/work-sans-v18-latin-700.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ } @@ -33,8 +33,8 @@ font-family: 'Work Sans'; font-style: normal; font-weight: 800; - src: local(''), + src: local('Work Sans'), url('/media/fonts/work-sans-v18-latin-800.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */ url('/media/fonts/work-sans-v18-latin-800.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ } - \ No newline at end of file + From c438cd47b9bf681e77e61bc5ed9caefa60d68123 Mon Sep 17 00:00:00 2001 From: JeLuF Date: Fri, 7 Apr 2023 09:08:46 +0200 Subject: [PATCH 043/291] fix filename_format for 'show only upscaled' https://discord.com/channels/1014774730907209781/1093703857516843019 --- ui/easydiffusion/utils/save_utils.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ui/easydiffusion/utils/save_utils.py b/ui/easydiffusion/utils/save_utils.py index 71524cbd..384794d1 100644 --- a/ui/easydiffusion/utils/save_utils.py +++ b/ui/easydiffusion/utils/save_utils.py @@ -1,6 +1,5 @@ import os import time -import base64 import re from easydiffusion import app @@ -137,7 +136,13 @@ def save_images_to_disk(images: list, filtered_images: list, req: GenerateImageR file_format=task_data.output_format, ) else: - make_filter_filename = make_filename_callback(req, task_data, file_number, now=now, suffix="filtered") + make_filter_filename = make_filename_callback( + app_config.get("filename_format", "$p_$tsb64"), + req, + task_data, + file_number, + now=now, + suffix="filtered") save_images( images, From d7317e8252e1e1467766ea86ef62539e6fa6ea3e Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 7 Apr 2023 15:51:44 +0530 Subject: [PATCH 044/291] sdkit 1.0.65 - upgrade compel to 1.0.5 --- scripts/on_sd_start.bat | 4 ++-- scripts/on_sd_start.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/on_sd_start.bat b/scripts/on_sd_start.bat index c9b3235f..5e94261a 100644 --- a/scripts/on_sd_start.bat +++ b/scripts/on_sd_start.bat @@ -95,7 +95,7 @@ if "%ERRORLEVEL%" EQU "0" ( set PYTHONNOUSERSITE=1 set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages - call python -m pip install --upgrade sdkit==1.0.64 -q || ( + call python -m pip install --upgrade sdkit==1.0.65 -q || ( echo "Error updating sdkit" ) ) @@ -106,7 +106,7 @@ if "%ERRORLEVEL%" EQU "0" ( set PYTHONNOUSERSITE=1 set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages - call python -m pip install sdkit==1.0.64 || ( + call python -m pip install sdkit==1.0.65 || ( echo "Error installing sdkit. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" pause exit /b diff --git a/scripts/on_sd_start.sh b/scripts/on_sd_start.sh index 78bcf0ee..9cbfa322 100755 --- a/scripts/on_sd_start.sh +++ b/scripts/on_sd_start.sh @@ -103,7 +103,7 @@ if python ../scripts/check_modules.py sdkit sdkit.models ldm transformers numpy export PYTHONNOUSERSITE=1 export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages" - python -m pip install --upgrade sdkit==1.0.64 -q + python -m pip install --upgrade sdkit==1.0.65 -q fi else echo "Installing sdkit: https://pypi.org/project/sdkit/" @@ -111,7 +111,7 @@ else export PYTHONNOUSERSITE=1 export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages" - if python -m pip install sdkit==1.0.64 ; then + if python -m pip install sdkit==1.0.65 ; then echo "Installed." else fail "sdkit install failed" From 9e244f758cfcd4cbb6ba2c177309ef120a44deef Mon Sep 17 00:00:00 2001 From: JeLuF Date: Fri, 7 Apr 2023 22:24:35 +0200 Subject: [PATCH 045/291] Don't paste empty prompts When pasting e.g. an image, window.clipboardData).getData('text') returns an empty string, which would delete the prompt. https://discord.com/channels/1014774730907209781/1093186485563424838 --- ui/media/js/dnd.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/media/js/dnd.js b/ui/media/js/dnd.js index a16fbc65..1ac967ae 100644 --- a/ui/media/js/dnd.js +++ b/ui/media/js/dnd.js @@ -608,7 +608,7 @@ document.addEventListener('paste', async (event) => { } const paste = (event.clipboardData || window.clipboardData).getData('text') const selection = window.getSelection() - if (selection.toString().trim().length <= 0 && await parseContent(paste)) { + if (paste != "" && selection.toString().trim().length <= 0 && await parseContent(paste)) { event.preventDefault() return } From c086098af12531c4c8dcce2d26c57d5a58582bba Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Sat, 8 Apr 2023 11:11:25 +0530 Subject: [PATCH 046/291] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 51ba812a..4d73d311 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Easy Diffusion 2.5 -### The easiest way to install and use [Stable Diffusion](https://github.com/CompVis/stable-diffusion) on your own computer. +### The easiest way to install and use [Stable Diffusion](https://github.com/CompVis/stable-diffusion) on your computer. Does not require technical knowledge, does not require pre-installed software. 1-click install, powerful features, friendly community. From 0f4b62cb97b70c303345c7a35747c4ed66c50883 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Sat, 8 Apr 2023 20:13:44 +0530 Subject: [PATCH 047/291] Hotfix - apply the config overrides to the Settings UI *after* the default config-apply function, not before it --- ui/media/js/parameters.js | 70 +++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/ui/media/js/parameters.js b/ui/media/js/parameters.js index ed858ccb..af399205 100644 --- a/ui/media/js/parameters.js +++ b/ui/media/js/parameters.js @@ -3,7 +3,7 @@ * @readonly * @enum {string} */ - var ParameterType = { +var ParameterType = { checkbox: "checkbox", select: "select", select_multiple: "select_multiple", @@ -362,6 +362,9 @@ async function getAppConfig() { let res = await fetch('/get/app_config') const config = await res.json() + applySettingsFromConfig(config) + + // custom overrides if (config.update_branch === 'beta') { useBetaChannelField.checked = true document.querySelector("#updateBranchLabel").innerText = "(beta)" @@ -378,6 +381,7 @@ async function getAppConfig() { listenPortField.value = config.net.listen_port } if (config.test_diffusers === undefined || config.update_branch === 'main') { + testDiffusers.checked = false document.querySelector("#lora_model_container").style.display = 'none' document.querySelector("#lora_alpha_container").style.display = 'none' } else { @@ -386,37 +390,6 @@ async function getAppConfig() { document.querySelector("#lora_alpha_container").style.display = (testDiffusers.checked && loraModelField.value !== "" ? '' : 'none') } - Array.from(parametersTable.children).forEach(parameterRow => { - if (parameterRow.dataset.settingId in config && parameterRow.dataset.saveInAppConfig === 'true') { - const configValue = config[parameterRow.dataset.settingId] - const parameterElement = document.getElementById(parameterRow.dataset.settingId) || - parameterRow.querySelector('input') || parameterRow.querySelector('select') - - switch (parameterElement?.tagName) { - case 'INPUT': - if (parameterElement.type === 'checkbox') { - parameterElement.checked = configValue - } else { - parameterElement.value = configValue - } - parameterElement.dispatchEvent(new Event('change')) - break - case 'SELECT': - if (Array.isArray(configValue)) { - Array.from(parameterElement.options).forEach(option => { - if (configValue.includes(option.value || option.text)) { - option.selected = true - } - }) - } else { - parameterElement.value = configValue - } - parameterElement.dispatchEvent(new Event('change')) - break - } - } - }) - console.log('get config status response', config) return config @@ -427,6 +400,39 @@ async function getAppConfig() { } } +function applySettingsFromConfig(config) { + Array.from(parametersTable.children).forEach(parameterRow => { + if (parameterRow.dataset.settingId in config && parameterRow.dataset.saveInAppConfig === 'true') { + const configValue = config[parameterRow.dataset.settingId] + const parameterElement = document.getElementById(parameterRow.dataset.settingId) || + parameterRow.querySelector('input') || parameterRow.querySelector('select') + + switch (parameterElement?.tagName) { + case 'INPUT': + if (parameterElement.type === 'checkbox') { + parameterElement.checked = configValue + } else { + parameterElement.value = configValue + } + parameterElement.dispatchEvent(new Event('change')) + break + case 'SELECT': + if (Array.isArray(configValue)) { + Array.from(parameterElement.options).forEach(option => { + if (configValue.includes(option.value || option.text)) { + option.selected = true + } + }) + } else { + parameterElement.value = configValue + } + parameterElement.dispatchEvent(new Event('change')) + break + } + } + }) +} + saveToDiskField.addEventListener('change', function(e) { diskPathField.disabled = !this.checked metadataOutputFormatField.disabled = !this.checked From 4f2df2d1889a8289db4ce4bd63b0880dd2956e3a Mon Sep 17 00:00:00 2001 From: patriceac <48073125+patriceac@users.noreply.github.com> Date: Sat, 8 Apr 2023 18:19:03 -0700 Subject: [PATCH 048/291] Properly reset LoRA selection from Use Settings --- ui/media/js/dnd.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/media/js/dnd.js b/ui/media/js/dnd.js index 1ac967ae..ebcce132 100644 --- a/ui/media/js/dnd.js +++ b/ui/media/js/dnd.js @@ -350,7 +350,7 @@ function restoreTaskToUI(task, fieldsToSkip) { } if (!('use_lora_model' in task.reqBody)) { - loraModelField.value = "None" + loraModelField.value = "" loraModelField.dispatchEvent(new Event("change")) } From 5e45f3723264e99b3669ec525a5b18ef2ff35cfb Mon Sep 17 00:00:00 2001 From: patriceac <48073125+patriceac@users.noreply.github.com> Date: Sun, 9 Apr 2023 15:33:05 -0700 Subject: [PATCH 049/291] Fix the tooltip display for long modifiers --- ui/media/css/modifier-thumbnails.css | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ui/media/css/modifier-thumbnails.css b/ui/media/css/modifier-thumbnails.css index 9b462e57..c6fb8107 100644 --- a/ui/media/css/modifier-thumbnails.css +++ b/ui/media/css/modifier-thumbnails.css @@ -153,6 +153,10 @@ position: absolute; z-index: 3; } +.modifier-card-overlay:hover ~ .modifier-card-container .modifier-card-label.tooltip .tooltip-text { + visibility: visible; + opacity: 1; +} .modifier-card:hover > .modifier-card-image-container .modifier-card-image-overlay { opacity: 1; } @@ -220,4 +224,4 @@ #modifier-settings-config textarea { width: 90%; height: 150px; -} \ No newline at end of file +} From 9ee38d0b7004aaee712dc22d232eda66351dee11 Mon Sep 17 00:00:00 2001 From: patriceac <48073125+patriceac@users.noreply.github.com> Date: Sun, 9 Apr 2023 18:38:36 -0700 Subject: [PATCH 050/291] Fix the styling of disabled image modifiers Long custom modifiers in a disabled state (e.g. right-click on the image tag) are properly restored as disabled but are incorrectly shown as "active" in the UI. I somehow missed that in https://github.com/cmdr2/stable-diffusion-ui/pull/1062, so here is the fix for that. This change only applies to plugins that try to restore image modifiers, no change to the regular UI. --- ui/media/js/image-modifiers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/media/js/image-modifiers.js b/ui/media/js/image-modifiers.js index 194565c4..eaea07f6 100644 --- a/ui/media/js/image-modifiers.js +++ b/ui/media/js/image-modifiers.js @@ -230,7 +230,7 @@ function refreshInactiveTags(inactiveTags) { // update cards let overlays = document.querySelector('#editor-inputs-tags-list').querySelectorAll('.modifier-card-overlay') overlays.forEach (i => { - let modifierName = i.parentElement.getElementsByClassName('modifier-card-label')[0].getElementsByTagName("p")[0].innerText + let modifierName = i.parentElement.getElementsByClassName('modifier-card-label')[0].getElementsByTagName("p")[0].dataset.fullName if (inactiveTags?.find(element => element === modifierName) !== undefined) { i.parentElement.classList.add('modifier-toggle-inactive') } From 8a6cf3cfae7f69cc51decf34f220891e5dc6c8fd Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Mon, 10 Apr 2023 15:55:55 +0530 Subject: [PATCH 051/291] Remove redundant hotfix --- scripts/on_sd_start.bat | 2 +- scripts/on_sd_start.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/on_sd_start.bat b/scripts/on_sd_start.bat index 5e94261a..75cea7ed 100644 --- a/scripts/on_sd_start.bat +++ b/scripts/on_sd_start.bat @@ -34,7 +34,7 @@ call conda activate @REM remove the old version of the dev console script, if it's still present if exist "Open Developer Console.cmd" del "Open Developer Console.cmd" -@call python -c "import os; import shutil; frm = 'sd-ui-files\\ui\\hotfix\\9c24e6cd9f499d02c4f21a033736dabd365962dc80fe3aeb57a8f85ea45a20a3.26fead7ea4f0f843f6eb4055dfd25693f1a71f3c6871b184042d4b126244e142'; dst = os.path.join(os.path.expanduser('~'), '.cache', 'huggingface', 'transformers', '9c24e6cd9f499d02c4f21a033736dabd365962dc80fe3aeb57a8f85ea45a20a3.26fead7ea4f0f843f6eb4055dfd25693f1a71f3c6871b184042d4b126244e142'); shutil.copyfile(frm, dst) if os.path.exists(dst) else print(''); print('Hotfixed broken JSON file from OpenAI');" +@REM @call python -c "import os; import shutil; frm = 'sd-ui-files\\ui\\hotfix\\9c24e6cd9f499d02c4f21a033736dabd365962dc80fe3aeb57a8f85ea45a20a3.26fead7ea4f0f843f6eb4055dfd25693f1a71f3c6871b184042d4b126244e142'; dst = os.path.join(os.path.expanduser('~'), '.cache', 'huggingface', 'transformers', '9c24e6cd9f499d02c4f21a033736dabd365962dc80fe3aeb57a8f85ea45a20a3.26fead7ea4f0f843f6eb4055dfd25693f1a71f3c6871b184042d4b126244e142'); shutil.copyfile(frm, dst) if os.path.exists(dst) else print(''); print('Hotfixed broken JSON file from OpenAI');" @rem create the stable-diffusion folder, to work with legacy installations if not exist "stable-diffusion" mkdir stable-diffusion diff --git a/scripts/on_sd_start.sh b/scripts/on_sd_start.sh index 9cbfa322..9138ed6b 100755 --- a/scripts/on_sd_start.sh +++ b/scripts/on_sd_start.sh @@ -18,7 +18,7 @@ if [ -e "open_dev_console.sh" ]; then rm "open_dev_console.sh" fi -python -c "import os; import shutil; frm = 'sd-ui-files/ui/hotfix/9c24e6cd9f499d02c4f21a033736dabd365962dc80fe3aeb57a8f85ea45a20a3.26fead7ea4f0f843f6eb4055dfd25693f1a71f3c6871b184042d4b126244e142'; dst = os.path.join(os.path.expanduser('~'), '.cache', 'huggingface', 'transformers', '9c24e6cd9f499d02c4f21a033736dabd365962dc80fe3aeb57a8f85ea45a20a3.26fead7ea4f0f843f6eb4055dfd25693f1a71f3c6871b184042d4b126244e142'); shutil.copyfile(frm, dst) if os.path.exists(dst) else print(''); print('Hotfixed broken JSON file from OpenAI');" +# python -c "import os; import shutil; frm = 'sd-ui-files/ui/hotfix/9c24e6cd9f499d02c4f21a033736dabd365962dc80fe3aeb57a8f85ea45a20a3.26fead7ea4f0f843f6eb4055dfd25693f1a71f3c6871b184042d4b126244e142'; dst = os.path.join(os.path.expanduser('~'), '.cache', 'huggingface', 'transformers', '9c24e6cd9f499d02c4f21a033736dabd365962dc80fe3aeb57a8f85ea45a20a3.26fead7ea4f0f843f6eb4055dfd25693f1a71f3c6871b184042d4b126244e142'); shutil.copyfile(frm, dst) if os.path.exists(dst) else print(''); print('Hotfixed broken JSON file from OpenAI');" # Caution, this file will make your eyes and brain bleed. It's such an unholy mess. # Note to self: Please rewrite this in Python. For the sake of your own sanity. From 4cdb8a7d2a30cb3467bf48143e7d902d198eeaab Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Mon, 10 Apr 2023 16:29:30 +0530 Subject: [PATCH 052/291] sdkit 1.0.66 - lower VRAM usage for realesrgan upscaling --- scripts/on_sd_start.bat | 4 ++-- scripts/on_sd_start.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/on_sd_start.bat b/scripts/on_sd_start.bat index 75cea7ed..03f49212 100644 --- a/scripts/on_sd_start.bat +++ b/scripts/on_sd_start.bat @@ -95,7 +95,7 @@ if "%ERRORLEVEL%" EQU "0" ( set PYTHONNOUSERSITE=1 set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages - call python -m pip install --upgrade sdkit==1.0.65 -q || ( + call python -m pip install --upgrade sdkit==1.0.66 -q || ( echo "Error updating sdkit" ) ) @@ -106,7 +106,7 @@ if "%ERRORLEVEL%" EQU "0" ( set PYTHONNOUSERSITE=1 set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages - call python -m pip install sdkit==1.0.65 || ( + call python -m pip install sdkit==1.0.66 || ( echo "Error installing sdkit. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" pause exit /b diff --git a/scripts/on_sd_start.sh b/scripts/on_sd_start.sh index 9138ed6b..b058176c 100755 --- a/scripts/on_sd_start.sh +++ b/scripts/on_sd_start.sh @@ -103,7 +103,7 @@ if python ../scripts/check_modules.py sdkit sdkit.models ldm transformers numpy export PYTHONNOUSERSITE=1 export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages" - python -m pip install --upgrade sdkit==1.0.65 -q + python -m pip install --upgrade sdkit==1.0.66 -q fi else echo "Installing sdkit: https://pypi.org/project/sdkit/" @@ -111,7 +111,7 @@ else export PYTHONNOUSERSITE=1 export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages" - if python -m pip install sdkit==1.0.65 ; then + if python -m pip install sdkit==1.0.66 ; then echo "Installed." else fail "sdkit install failed" From b07046f6a278d5b315734b41bb6c0cf3e4842638 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Mon, 10 Apr 2023 16:31:24 +0530 Subject: [PATCH 053/291] gc between rendering images and applying filters --- ui/easydiffusion/renderer.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ui/easydiffusion/renderer.py b/ui/easydiffusion/renderer.py index c8004fee..8a155f82 100644 --- a/ui/easydiffusion/renderer.py +++ b/ui/easydiffusion/renderer.py @@ -10,7 +10,13 @@ from easydiffusion.utils import get_printable_request, save_images_to_disk, log from sdkit import Context from sdkit.generate import generate_images from sdkit.filter import apply_filters -from sdkit.utils import img_to_buffer, img_to_base64_str, latent_samples_to_images, diffusers_latent_samples_to_images +from sdkit.utils import ( + img_to_buffer, + img_to_base64_str, + latent_samples_to_images, + diffusers_latent_samples_to_images, + gc, +) context = Context() # thread-local """ @@ -62,7 +68,6 @@ def print_task_info(req: GenerateImageRequest, task_data: TaskData): def make_images_internal( req: GenerateImageRequest, task_data: TaskData, data_queue: queue.Queue, task_temp_images: list, step_callback ): - images, user_stopped = generate_images_internal( req, task_data, @@ -72,6 +77,7 @@ def make_images_internal( task_data.stream_image_progress, task_data.stream_image_progress_interval, ) + gc(context) filtered_images = filter_images(task_data, images, user_stopped) if task_data.save_to_disk_path is not None: From df806d5dfae680089cb193ed69254b6f282cfed6 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Mon, 10 Apr 2023 16:31:58 +0530 Subject: [PATCH 054/291] changelog --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index bde70f1e..f858700e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -21,6 +21,7 @@ Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. ### Detailed changelog +* 2.5.31 - 10 Apr 2023 - Reduce VRAM usage while upscaling. * 2.5.31 - 6 Apr 2023 - Allow seeds upto `4,294,967,295`. Thanks @ogmaresca. * 2.5.31 - 6 Apr 2023 - Buttons to show the previous/next image in the image popup. Thanks @ogmaresca. * 2.5.30 - 5 Apr 2023 - Fix a bug where the JPEG image quality wasn't being respected when embedding the metadata into it. Thanks @JeLuf. From 210429a25900e56382e9982b0317848703c960ac Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Mon, 10 Apr 2023 17:59:14 +0530 Subject: [PATCH 055/291] sdkit 1.0.67 - detect black-images on model load and use fp32 attention precision or full precision if needed --- scripts/on_sd_start.bat | 4 ++-- scripts/on_sd_start.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/on_sd_start.bat b/scripts/on_sd_start.bat index 03f49212..256ef752 100644 --- a/scripts/on_sd_start.bat +++ b/scripts/on_sd_start.bat @@ -95,7 +95,7 @@ if "%ERRORLEVEL%" EQU "0" ( set PYTHONNOUSERSITE=1 set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages - call python -m pip install --upgrade sdkit==1.0.66 -q || ( + call python -m pip install --upgrade sdkit==1.0.67 -q || ( echo "Error updating sdkit" ) ) @@ -106,7 +106,7 @@ if "%ERRORLEVEL%" EQU "0" ( set PYTHONNOUSERSITE=1 set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages - call python -m pip install sdkit==1.0.66 || ( + call python -m pip install sdkit==1.0.67 || ( echo "Error installing sdkit. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" pause exit /b diff --git a/scripts/on_sd_start.sh b/scripts/on_sd_start.sh index b058176c..1117ce00 100755 --- a/scripts/on_sd_start.sh +++ b/scripts/on_sd_start.sh @@ -103,7 +103,7 @@ if python ../scripts/check_modules.py sdkit sdkit.models ldm transformers numpy export PYTHONNOUSERSITE=1 export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages" - python -m pip install --upgrade sdkit==1.0.66 -q + python -m pip install --upgrade sdkit==1.0.67 -q fi else echo "Installing sdkit: https://pypi.org/project/sdkit/" @@ -111,7 +111,7 @@ else export PYTHONNOUSERSITE=1 export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages" - if python -m pip install sdkit==1.0.66 ; then + if python -m pip install sdkit==1.0.67 ; then echo "Installed." else fail "sdkit install failed" From 424ec40fa54b05581225041935ddb7d16dbb92cd Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 11 Apr 2023 12:00:03 +0530 Subject: [PATCH 056/291] sdkit 1.0.68 - fix brainfade --- scripts/on_sd_start.bat | 4 ++-- scripts/on_sd_start.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/on_sd_start.bat b/scripts/on_sd_start.bat index 256ef752..4d0340be 100644 --- a/scripts/on_sd_start.bat +++ b/scripts/on_sd_start.bat @@ -95,7 +95,7 @@ if "%ERRORLEVEL%" EQU "0" ( set PYTHONNOUSERSITE=1 set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages - call python -m pip install --upgrade sdkit==1.0.67 -q || ( + call python -m pip install --upgrade sdkit==1.0.68 -q || ( echo "Error updating sdkit" ) ) @@ -106,7 +106,7 @@ if "%ERRORLEVEL%" EQU "0" ( set PYTHONNOUSERSITE=1 set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages - call python -m pip install sdkit==1.0.67 || ( + call python -m pip install sdkit==1.0.68 || ( echo "Error installing sdkit. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" pause exit /b diff --git a/scripts/on_sd_start.sh b/scripts/on_sd_start.sh index 1117ce00..acff28f7 100755 --- a/scripts/on_sd_start.sh +++ b/scripts/on_sd_start.sh @@ -103,7 +103,7 @@ if python ../scripts/check_modules.py sdkit sdkit.models ldm transformers numpy export PYTHONNOUSERSITE=1 export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages" - python -m pip install --upgrade sdkit==1.0.67 -q + python -m pip install --upgrade sdkit==1.0.68 -q fi else echo "Installing sdkit: https://pypi.org/project/sdkit/" @@ -111,7 +111,7 @@ else export PYTHONNOUSERSITE=1 export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages" - if python -m pip install sdkit==1.0.67 ; then + if python -m pip install sdkit==1.0.68 ; then echo "Installed." else fail "sdkit install failed" From 8ba0b3485380d5c45750a57d4e0fb2caa3e02ac9 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 11 Apr 2023 15:13:41 +0530 Subject: [PATCH 057/291] Log the stack trace when the model fails to load --- ui/easydiffusion/model_manager.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui/easydiffusion/model_manager.py b/ui/easydiffusion/model_manager.py index a06c56cf..29f5f88e 100644 --- a/ui/easydiffusion/model_manager.py +++ b/ui/easydiffusion/model_manager.py @@ -44,8 +44,7 @@ def load_default_models(context: Context): load_model(context, model_type) except Exception as e: log.error(f"[red]Error while loading {model_type} model: {context.model_paths[model_type]}[/red]") - log.error(f"[red]Error: {e}[/red]") - log.error(f"[red]Consider removing the model from the model folder.[red]") + log.exception(e) def unload_all(context: Context): From a3d2c71ed6ff7d0303df8459bed21c56ea7c5839 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 11 Apr 2023 15:34:44 +0530 Subject: [PATCH 058/291] sdkit 1.0.69 - allow loading models without vae weights in diffusers --- scripts/on_sd_start.bat | 4 ++-- scripts/on_sd_start.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/on_sd_start.bat b/scripts/on_sd_start.bat index 4d0340be..f4bdc410 100644 --- a/scripts/on_sd_start.bat +++ b/scripts/on_sd_start.bat @@ -95,7 +95,7 @@ if "%ERRORLEVEL%" EQU "0" ( set PYTHONNOUSERSITE=1 set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages - call python -m pip install --upgrade sdkit==1.0.68 -q || ( + call python -m pip install --upgrade sdkit==1.0.69 -q || ( echo "Error updating sdkit" ) ) @@ -106,7 +106,7 @@ if "%ERRORLEVEL%" EQU "0" ( set PYTHONNOUSERSITE=1 set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages - call python -m pip install sdkit==1.0.68 || ( + call python -m pip install sdkit==1.0.69 || ( echo "Error installing sdkit. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" pause exit /b diff --git a/scripts/on_sd_start.sh b/scripts/on_sd_start.sh index acff28f7..7b91cbfd 100755 --- a/scripts/on_sd_start.sh +++ b/scripts/on_sd_start.sh @@ -103,7 +103,7 @@ if python ../scripts/check_modules.py sdkit sdkit.models ldm transformers numpy export PYTHONNOUSERSITE=1 export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages" - python -m pip install --upgrade sdkit==1.0.68 -q + python -m pip install --upgrade sdkit==1.0.69 -q fi else echo "Installing sdkit: https://pypi.org/project/sdkit/" @@ -111,7 +111,7 @@ else export PYTHONNOUSERSITE=1 export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages" - if python -m pip install sdkit==1.0.68 ; then + if python -m pip install sdkit==1.0.69 ; then echo "Installed." else fail "sdkit install failed" From 03c8a0fca5e9d88de6be344524baa9ba681e30f4 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 12 Apr 2023 15:31:10 +0530 Subject: [PATCH 059/291] sdkit 1.0.70 - use plms for warming up the model, avoiding any non-deterministic effects from the default ancestral sampler --- scripts/on_sd_start.bat | 4 ++-- scripts/on_sd_start.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/on_sd_start.bat b/scripts/on_sd_start.bat index f4bdc410..7efd4577 100644 --- a/scripts/on_sd_start.bat +++ b/scripts/on_sd_start.bat @@ -95,7 +95,7 @@ if "%ERRORLEVEL%" EQU "0" ( set PYTHONNOUSERSITE=1 set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages - call python -m pip install --upgrade sdkit==1.0.69 -q || ( + call python -m pip install --upgrade sdkit==1.0.70 -q || ( echo "Error updating sdkit" ) ) @@ -106,7 +106,7 @@ if "%ERRORLEVEL%" EQU "0" ( set PYTHONNOUSERSITE=1 set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages - call python -m pip install sdkit==1.0.69 || ( + call python -m pip install sdkit==1.0.70 || ( echo "Error installing sdkit. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" pause exit /b diff --git a/scripts/on_sd_start.sh b/scripts/on_sd_start.sh index 7b91cbfd..fcf7dc53 100755 --- a/scripts/on_sd_start.sh +++ b/scripts/on_sd_start.sh @@ -103,7 +103,7 @@ if python ../scripts/check_modules.py sdkit sdkit.models ldm transformers numpy export PYTHONNOUSERSITE=1 export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages" - python -m pip install --upgrade sdkit==1.0.69 -q + python -m pip install --upgrade sdkit==1.0.70 -q fi else echo "Installing sdkit: https://pypi.org/project/sdkit/" @@ -111,7 +111,7 @@ else export PYTHONNOUSERSITE=1 export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages" - if python -m pip install sdkit==1.0.69 ; then + if python -m pip install sdkit==1.0.70 ; then echo "Installed." else fail "sdkit install failed" From 4192f87d6b490b37b0fa416834eef1305bedaac3 Mon Sep 17 00:00:00 2001 From: JeLuF Date: Fri, 14 Apr 2023 13:41:35 +0200 Subject: [PATCH 060/291] Don't scan safetensors files in load_default_models() (#1155) * Don't scan safetensors when loading them * Don't scan safetensors files * Update model_manager.py --------- Co-authored-by: cmdr2 --- ui/easydiffusion/model_manager.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ui/easydiffusion/model_manager.py b/ui/easydiffusion/model_manager.py index 29f5f88e..296c4192 100644 --- a/ui/easydiffusion/model_manager.py +++ b/ui/easydiffusion/model_manager.py @@ -41,12 +41,15 @@ def load_default_models(context: Context): for model_type in MODELS_TO_LOAD_ON_START: context.model_paths[model_type] = resolve_model_to_use(model_type=model_type) try: - load_model(context, model_type) + load_model( + context, + model_type, + scan_model = context.model_paths[model_type] != None and not context.model_paths[model_type].endswith('.safetensors') + ) except Exception as e: log.error(f"[red]Error while loading {model_type} model: {context.model_paths[model_type]}[/red]") log.exception(e) - def unload_all(context: Context): for model_type in KNOWN_MODEL_TYPES: unload_model(context, model_type) From 80bcfabc480979c58c0ec383df05dca7d56a5aa3 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 14 Apr 2023 17:32:27 +0530 Subject: [PATCH 061/291] Upgrade to PyTorch 2.0; Doesn't use a special repo url for pytorch on Linux --- scripts/check_modules.py | 18 ++++++++++++++---- scripts/on_sd_start.bat | 17 ++++++++++++----- scripts/on_sd_start.sh | 38 +++++++++++++++----------------------- 3 files changed, 41 insertions(+), 32 deletions(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 416ad851..991acee7 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -1,13 +1,23 @@ -''' +""" This script checks if the given modules exist -''' + +E.g. python check_modules.py sdkit==1.0.3 sdkit.models ldm transformers numpy antlr4 gfpgan realesrgan +""" import sys import pkgutil +from importlib.metadata import version modules = sys.argv[1:] missing_modules = [] for m in modules: - if pkgutil.find_loader(m) is None: - print('module', m, 'not found') + m = m.split("==") + module_name = m[0] + module_version = m[1] if len(m) > 1 else None + is_installed = pkgutil.find_loader(module_name) is not None + if not is_installed: + print("module", module_name, "not found") + exit(1) + elif module_version and version(module_name) != module_version: + print("module version is different! expected: ", module_version, ", actual: ", version(module_name)) exit(1) diff --git a/scripts/on_sd_start.bat b/scripts/on_sd_start.bat index 7efd4577..0a890c21 100644 --- a/scripts/on_sd_start.bat +++ b/scripts/on_sd_start.bat @@ -7,6 +7,11 @@ @copy sd-ui-files\scripts\bootstrap.bat scripts\ /Y @copy sd-ui-files\scripts\check_modules.py scripts\ /Y +set TORCH_VERSION=2.0.0+cu117 +set TORCHVISION_VERSION=0.15.1+cu117 +set SDKIT_VERSION=1.0.71 +set SD_VERSION=2.1.4 + if exist "%cd%\profile" ( set USERPROFILE=%cd%\profile ) @@ -65,7 +70,7 @@ if not exist "%INSTALL_ENV_DIR%\DLLs\libssl-1_1-x64.dll" copy "%INSTALL_ENV_D if not exist "%INSTALL_ENV_DIR%\DLLs\libcrypto-1_1-x64.dll" copy "%INSTALL_ENV_DIR%\Library\bin\libcrypto-1_1-x64.dll" "%INSTALL_ENV_DIR%\DLLs\" @rem install torch and torchvision -call python ..\scripts\check_modules.py torch torchvision +call python ..\scripts\check_modules.py torch==%TORCH_VERSION% torchvision==%TORCHVISION_VERSION% if "%ERRORLEVEL%" EQU "0" ( echo "torch and torchvision have already been installed." ) else ( @@ -75,13 +80,15 @@ if "%ERRORLEVEL%" EQU "0" ( set PYTHONNOUSERSITE=1 set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages - call python -m pip install --upgrade torch==1.13.1+cu116 torchvision==0.14.1+cu116 --extra-index-url https://download.pytorch.org/whl/cu116 || ( + call python -m pip install --upgrade torch==%TORCH_VERSION% torchvision==%TORCHVISION_VERSION% --index-url https://download.pytorch.org/whl/cu117 || ( echo "Error installing torch. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" pause exit /b ) ) +call python -c "from importlib.metadata import version; print('torch version:', version('torch'))" + set PATH=C:\Windows\System32;%PATH% @rem install/upgrade sdkit @@ -95,7 +102,7 @@ if "%ERRORLEVEL%" EQU "0" ( set PYTHONNOUSERSITE=1 set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages - call python -m pip install --upgrade sdkit==1.0.70 -q || ( + call python -m pip install --upgrade sdkit==%SDKIT_VERSION% -q || ( echo "Error updating sdkit" ) ) @@ -106,7 +113,7 @@ if "%ERRORLEVEL%" EQU "0" ( set PYTHONNOUSERSITE=1 set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages - call python -m pip install sdkit==1.0.70 || ( + call python -m pip install sdkit==%SDKIT_VERSION% || ( echo "Error installing sdkit. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" pause exit /b @@ -116,7 +123,7 @@ if "%ERRORLEVEL%" EQU "0" ( call python -c "from importlib.metadata import version; print('sdkit version:', version('sdkit'))" @rem upgrade stable-diffusion-sdkit -call python -m pip install --upgrade stable-diffusion-sdkit==2.1.4 -q || ( +call python -m pip install --upgrade stable-diffusion-sdkit==%SD_VERSION% -q || ( echo "Error updating stable-diffusion-sdkit" ) call python -c "from importlib.metadata import version; print('stable-diffusion version:', version('stable-diffusion-sdkit'))" diff --git a/scripts/on_sd_start.sh b/scripts/on_sd_start.sh index fcf7dc53..6b62c468 100755 --- a/scripts/on_sd_start.sh +++ b/scripts/on_sd_start.sh @@ -7,6 +7,11 @@ cp sd-ui-files/scripts/check_modules.py scripts/ source ./scripts/functions.sh +TORCH_VERSION="2.0.0" +TORCHVISION_VERSION="0.15.1" +SDKIT_VERSION="1.0.71" +SD_VERSION="2.1.4" + # activate the installer env CONDA_BASEPATH=$(conda info --base) source "$CONDA_BASEPATH/etc/profile.d/conda.sh" # avoids the 'shell not initialized' error @@ -64,14 +69,7 @@ case "${OS_NAME}" in esac # install torch and torchvision -if python ../scripts/check_modules.py torch torchvision; then - # temp fix for installations that installed torch 2.0 by mistake - if [ "$OS_NAME" == "linux" ]; then - python -m pip install --upgrade torch==1.13.1+cu116 torchvision==0.14.1+cu116 --extra-index-url https://download.pytorch.org/whl/cu116 -q - elif [ "$OS_NAME" == "macos" ]; then - python -m pip install --upgrade torch==1.13.1 torchvision==0.14.1 -q - fi - +if python ../scripts/check_modules.py torch==$TORCH_VERSION torchvision==$TORCHVISION_VERSION; then echo "torch and torchvision have already been installed." else echo "Installing torch and torchvision.." @@ -79,21 +77,15 @@ else export PYTHONNOUSERSITE=1 export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages" - if [ "$OS_NAME" == "linux" ]; then - if python -m pip install --upgrade torch==1.13.1+cu116 torchvision==0.14.1+cu116 --extra-index-url https://download.pytorch.org/whl/cu116 ; then - echo "Installed." - else - fail "torch install failed" - fi - elif [ "$OS_NAME" == "macos" ]; then - if python -m pip install --upgrade torch==1.13.1 torchvision==0.14.1 ; then - echo "Installed." - else - fail "torch install failed" - fi + if python -m pip install --upgrade torch==$TORCH_VERSION torchvision==$TORCHVISION_VERSION ; then + echo "Installed." + else + fail "torch install failed" fi fi +python -c "from importlib.metadata import version; print('torch version:', version('torch'))" + # install/upgrade sdkit if python ../scripts/check_modules.py sdkit sdkit.models ldm transformers numpy antlr4 gfpgan realesrgan ; then echo "sdkit is already installed." @@ -103,7 +95,7 @@ if python ../scripts/check_modules.py sdkit sdkit.models ldm transformers numpy export PYTHONNOUSERSITE=1 export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages" - python -m pip install --upgrade sdkit==1.0.70 -q + python -m pip install --upgrade sdkit==$SDKIT_VERSION -q fi else echo "Installing sdkit: https://pypi.org/project/sdkit/" @@ -111,7 +103,7 @@ else export PYTHONNOUSERSITE=1 export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages" - if python -m pip install sdkit==1.0.70 ; then + if python -m pip install sdkit==$SDKIT_VERSION ; then echo "Installed." else fail "sdkit install failed" @@ -121,7 +113,7 @@ fi python -c "from importlib.metadata import version; print('sdkit version:', version('sdkit'))" # upgrade stable-diffusion-sdkit -python -m pip install --upgrade stable-diffusion-sdkit==2.1.4 -q +python -m pip install --upgrade stable-diffusion-sdkit==$SD_VERSION -q python -c "from importlib.metadata import version; print('stable-diffusion version:', version('stable-diffusion-sdkit'))" # install rich From 1ba3a139d912d6806a02302adb20abae7ca35181 Mon Sep 17 00:00:00 2001 From: JeLuF Date: Sun, 16 Apr 2023 21:59:29 +0200 Subject: [PATCH 062/291] Don't save model_path if initial load fails Fixes #882 If the load of the model fails during the initialization, an attempt to render an image using the same model fails because ED doesn't notice that the model has to be loaded. This PR ensures that the model is being reloaded if the initial load fails. If the second load attempt fails as well, the user will get a more helpful error message than 'model not loaded yet'. --- ui/easydiffusion/model_manager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/easydiffusion/model_manager.py b/ui/easydiffusion/model_manager.py index 296c4192..ca92d1ab 100644 --- a/ui/easydiffusion/model_manager.py +++ b/ui/easydiffusion/model_manager.py @@ -49,6 +49,7 @@ def load_default_models(context: Context): except Exception as e: log.error(f"[red]Error while loading {model_type} model: {context.model_paths[model_type]}[/red]") log.exception(e) + del context.model_paths[model_type] def unload_all(context: Context): for model_type in KNOWN_MODEL_TYPES: From 0c0525e11b6c025c3dcfc19b8bd692ce47536d9e Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Mon, 17 Apr 2023 15:43:08 +0530 Subject: [PATCH 063/291] sdkit 1.0.72 - use the extra attn precision yaml code for diffusers, which doesn't auto-detect black images yet --- scripts/on_sd_start.bat | 4 ++-- scripts/on_sd_start.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/on_sd_start.bat b/scripts/on_sd_start.bat index 7efd4577..a1d0836e 100644 --- a/scripts/on_sd_start.bat +++ b/scripts/on_sd_start.bat @@ -95,7 +95,7 @@ if "%ERRORLEVEL%" EQU "0" ( set PYTHONNOUSERSITE=1 set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages - call python -m pip install --upgrade sdkit==1.0.70 -q || ( + call python -m pip install --upgrade sdkit==1.0.72 -q || ( echo "Error updating sdkit" ) ) @@ -106,7 +106,7 @@ if "%ERRORLEVEL%" EQU "0" ( set PYTHONNOUSERSITE=1 set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages - call python -m pip install sdkit==1.0.70 || ( + call python -m pip install sdkit==1.0.72 || ( echo "Error installing sdkit. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" pause exit /b diff --git a/scripts/on_sd_start.sh b/scripts/on_sd_start.sh index fcf7dc53..4907e7a5 100755 --- a/scripts/on_sd_start.sh +++ b/scripts/on_sd_start.sh @@ -103,7 +103,7 @@ if python ../scripts/check_modules.py sdkit sdkit.models ldm transformers numpy export PYTHONNOUSERSITE=1 export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages" - python -m pip install --upgrade sdkit==1.0.70 -q + python -m pip install --upgrade sdkit==1.0.72 -q fi else echo "Installing sdkit: https://pypi.org/project/sdkit/" @@ -111,7 +111,7 @@ else export PYTHONNOUSERSITE=1 export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages" - if python -m pip install sdkit==1.0.70 ; then + if python -m pip install sdkit==1.0.72 ; then echo "Installed." else fail "sdkit install failed" From f2b5843e6ce65e4cc15aa5e08af507514d28827b Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Mon, 17 Apr 2023 15:50:51 +0530 Subject: [PATCH 064/291] merge beta --- scripts/on_sd_start.bat | 2 +- scripts/on_sd_start.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/on_sd_start.bat b/scripts/on_sd_start.bat index 0a890c21..424171fd 100644 --- a/scripts/on_sd_start.bat +++ b/scripts/on_sd_start.bat @@ -9,7 +9,7 @@ set TORCH_VERSION=2.0.0+cu117 set TORCHVISION_VERSION=0.15.1+cu117 -set SDKIT_VERSION=1.0.71 +set SDKIT_VERSION=1.0.72 set SD_VERSION=2.1.4 if exist "%cd%\profile" ( diff --git a/scripts/on_sd_start.sh b/scripts/on_sd_start.sh index 6b62c468..8dccb12b 100755 --- a/scripts/on_sd_start.sh +++ b/scripts/on_sd_start.sh @@ -9,7 +9,7 @@ source ./scripts/functions.sh TORCH_VERSION="2.0.0" TORCHVISION_VERSION="0.15.1" -SDKIT_VERSION="1.0.71" +SDKIT_VERSION="1.0.72" SD_VERSION="2.1.4" # activate the installer env From dc21cbe59d9769952be2867476982423c91f1157 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Mon, 17 Apr 2023 16:25:17 +0530 Subject: [PATCH 065/291] Typo --- scripts/on_sd_start.bat | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/on_sd_start.bat b/scripts/on_sd_start.bat index eb78b41d..f53f4210 100644 --- a/scripts/on_sd_start.bat +++ b/scripts/on_sd_start.bat @@ -3,9 +3,9 @@ @REM Caution, this file will make your eyes and brain bleed. It's such an unholy mess. @REM Note to self: Please rewrite this in Python. For the sake of your own sanity. -@REM @copy sd-ui-files\scripts\on_env_start.bat scripts\ /Y -@REM @copy sd-ui-files\scripts\bootstrap.bat scripts\ /Y -@REM @copy sd-ui-files\scripts\check_modules.py scripts\ /Y +@copy sd-ui-files\scripts\on_env_start.bat scripts\ /Y +@copy sd-ui-files\scripts\bootstrap.bat scripts\ /Y +@copy sd-ui-files\scripts\check_modules.py scripts\ /Y set TORCH_VERSION=2.0.0+cu117 set TORCHVISION_VERSION=0.15.1+cu117 From 44824fb5f938ff93cd2c507ba3c89ca0f3d4f7f8 Mon Sep 17 00:00:00 2001 From: JeLuF Date: Mon, 17 Apr 2023 23:22:44 +0200 Subject: [PATCH 066/291] Don't download 1.4 if other models are available --- scripts/on_sd_start.bat | 24 +++++++++++++----------- scripts/on_sd_start.sh | 7 +++++-- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/scripts/on_sd_start.bat b/scripts/on_sd_start.bat index 7efd4577..728e0f82 100644 --- a/scripts/on_sd_start.bat +++ b/scripts/on_sd_start.bat @@ -186,22 +186,24 @@ call WHERE uvicorn > .tmp ) ) -@if not exist "..\models\stable-diffusion\sd-v1-4.ckpt" ( - @echo. & echo "Downloading data files (weights) for Stable Diffusion.." & echo. +@if not exist "..\models\stable-diffusion\*.ckpt" ( + @if not exist "..\models\stable-diffusion\*.safetensors" ( + @echo. & echo "Downloading data files (weights) for Stable Diffusion.." & echo. - @call curl -L -k https://huggingface.co/CompVis/stable-diffusion-v-1-4-original/resolve/main/sd-v1-4.ckpt > ..\models\stable-diffusion\sd-v1-4.ckpt + @call curl -L -k https://huggingface.co/CompVis/stable-diffusion-v-1-4-original/resolve/main/sd-v1-4.ckpt > ..\models\stable-diffusion\sd-v1-4.ckpt - @if exist "..\models\stable-diffusion\sd-v1-4.ckpt" ( - for %%I in ("..\models\stable-diffusion\sd-v1-4.ckpt") do if "%%~zI" NEQ "4265380512" ( - echo. & echo "Error: The downloaded model file was invalid! Bytes downloaded: %%~zI" & echo. - echo. & echo "Error downloading the data files (weights) for Stable Diffusion. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo. + @if exist "..\models\stable-diffusion\sd-v1-4.ckpt" ( + for %%I in ("..\models\stable-diffusion\sd-v1-4.ckpt") do if "%%~zI" NEQ "4265380512" ( + echo. & echo "Error: The downloaded model file was invalid! Bytes downloaded: %%~zI" & echo. + echo. & echo "Error downloading the data files (weights) for Stable Diffusion. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo. + pause + exit /b + ) + ) else ( + @echo. & echo "Error downloading the data files (weights) for Stable Diffusion. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo. pause exit /b ) - ) else ( - @echo. & echo "Error downloading the data files (weights) for Stable Diffusion. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo. - pause - exit /b ) ) diff --git a/scripts/on_sd_start.sh b/scripts/on_sd_start.sh index fcf7dc53..7582547b 100755 --- a/scripts/on_sd_start.sh +++ b/scripts/on_sd_start.sh @@ -170,8 +170,11 @@ if [ -f "../models/stable-diffusion/sd-v1-4.ckpt" ]; then fi fi -if [ ! -f "../models/stable-diffusion/sd-v1-4.ckpt" ]; then - echo "Downloading data files (weights) for Stable Diffusion.." + +if find "../models/stable-diffusion/" -name "*.ckpt" -or -name "*.safetensors" > /dev/null; then + echo "Found existing model file(s)." +else + echo "Downloading data files (weights) for Stable Diffusion..." curl -L -k https://huggingface.co/CompVis/stable-diffusion-v-1-4-original/resolve/main/sd-v1-4.ckpt > ../models/stable-diffusion/sd-v1-4.ckpt From 893b6d985cf5d00da06ff4f6805c4c175857d667 Mon Sep 17 00:00:00 2001 From: JeLuF Date: Tue, 18 Apr 2023 09:12:26 +0200 Subject: [PATCH 067/291] Add "Start scanning..." to getModels() Provide a hint to users what ED is currently using. Use case: User has built an infinite loop using symlinks, ED model scan will never finish. https://discord.com/channels/1014774730907209781/1097764777553580092 --- ui/easydiffusion/model_manager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/easydiffusion/model_manager.py b/ui/easydiffusion/model_manager.py index 296c4192..f32cfd9b 100644 --- a/ui/easydiffusion/model_manager.py +++ b/ui/easydiffusion/model_manager.py @@ -245,6 +245,7 @@ def getModels(): except MaliciousModelException as e: models["scan-error"] = e + log.info(f"[green]Scanning all model folders for models...[/]") # custom models listModels(model_type="stable-diffusion") listModels(model_type="vae") From e7dc41e2715c28f41928530cf6f9bb7ec91408f7 Mon Sep 17 00:00:00 2001 From: Diana <5275194+DianaNites@users.noreply.github.com> Date: Tue, 18 Apr 2023 02:32:39 -0700 Subject: [PATCH 068/291] Automatic AMD GPU detection on Linux (#1078) * Automatic AMD GPU detection on Linux Automatically detects AMD GPUs and installs the ROCm version of PyTorch instead of the cuda one A later improvement may be to detect the GPU ROCm version and handle GPUs that dont work on upstream ROCm, ether because they're too old and need a special patched version, or too new and need `HSA_OVERRIDE_GFX_VERSION=10.3.0` added, possibly check through `rocminfo`? * Address stdout suppression and download failure * If any NVIDIA GPU is found, always use it * Use /proc/bus/pci/devices to detect GPUs * Fix comparisons `-eq` and `-ne` only work for numbers * Add back -q --------- Co-authored-by: JeLuF --- scripts/on_sd_start.sh | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/scripts/on_sd_start.sh b/scripts/on_sd_start.sh index 53f2c3b0..bc4b1608 100755 --- a/scripts/on_sd_start.sh +++ b/scripts/on_sd_start.sh @@ -63,11 +63,30 @@ case "${OS_NAME}" in *) echo "Unknown OS: $OS_NAME! This script runs only on Linux or Mac" && exit esac +# Detect GPU types + +if grep -q amdgpu /proc/bus/pci/devices; then + echo AMD GPU detected + HAS_AMD=yes +fi + +if grep -q nvidia /proc/bus/pci/devices; then + echo NVidia GPU detected + HAS_NVIDIA=yes +fi + + + # install torch and torchvision if python ../scripts/check_modules.py torch torchvision; then # temp fix for installations that installed torch 2.0 by mistake if [ "$OS_NAME" == "linux" ]; then - python -m pip install --upgrade torch==1.13.1+cu116 torchvision==0.14.1+cu116 --extra-index-url https://download.pytorch.org/whl/cu116 -q + # Check for AMD and NVIDIA dGPUs, always preferring an NVIDIA GPU if available + if [ "$HAS_NVIDIA" != "yes" -a "$HAS_AMD" = "yes" ]; then + python -m pip install --upgrade torch torchvision --extra-index-url "https://download.pytorch.org/whl/rocm5.4.2" -q || fail "Installation of torch and torchvision for AMD failed" + else + python -m pip install --upgrade torch==1.13.1+cu116 torchvision==0.14.1+cu116 --extra-index-url "https://download.pytorch.org/whl/cu116" -q || fail "Installation of torch and torchvision for CUDA failed" + fi elif [ "$OS_NAME" == "macos" ]; then python -m pip install --upgrade torch==1.13.1 torchvision==0.14.1 -q fi @@ -80,11 +99,13 @@ else export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages" if [ "$OS_NAME" == "linux" ]; then - if python -m pip install --upgrade torch==1.13.1+cu116 torchvision==0.14.1+cu116 --extra-index-url https://download.pytorch.org/whl/cu116 ; then - echo "Installed." + # Check for AMD and NVIDIA dGPUs, always preferring an NVIDIA GPU if available + if [ "$HAS_NVIDIA" != "yes" -a "$HAS_AMD" = "yes" ]; then + python -m pip install --upgrade torch torchvision --extra-index-url "https://download.pytorch.org/whl/rocm5.4.2" || fail "Installation of torch and torchvision for ROCm failed" else - fail "torch install failed" + python -m pip install --upgrade torch==1.13.1+cu116 torchvision==0.14.1+cu116 --extra-index-url "https://download.pytorch.org/whl/cu116" || fail "Installation of torch and torchvision for CUDA failed" fi + echo "Installed." elif [ "$OS_NAME" == "macos" ]; then if python -m pip install --upgrade torch==1.13.1 torchvision==0.14.1 ; then echo "Installed." @@ -151,7 +172,7 @@ else if conda install -c conda-forge -y uvicorn fastapi ; then echo "Installed. Testing.." else - fail "'conda install uvicorn' failed" + fail "'conda install uvicorn' failed" fi if ! command -v uvicorn &> /dev/null; then @@ -181,7 +202,7 @@ else if [ -f "../models/stable-diffusion/sd-v1-4.ckpt" ]; then model_size=`filesize "../models/stable-diffusion/sd-v1-4.ckpt"` if [ ! "$model_size" == "4265380512" ]; then - fail "The downloaded model file was invalid! Bytes downloaded: $model_size" + fail "The downloaded model file was invalid! Bytes downloaded: $model_size" fi else fail "Error downloading the data files (weights) for Stable Diffusion" From 80384e6ee1b370dd51324c2385c60d651c12b2e5 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 18 Apr 2023 15:42:33 +0530 Subject: [PATCH 069/291] Install PyTorch 2.0 by default, but allow existing PyTorch 1.13.1 installations to continue running; Unify and streamline the installation of dependencies --- scripts/check_modules.py | 126 +++++++++++++++++++++++++++++++++------ scripts/on_sd_start.bat | 103 +++----------------------------- scripts/on_sd_start.sh | 110 ++-------------------------------- 3 files changed, 120 insertions(+), 219 deletions(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 991acee7..7365000d 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -1,23 +1,111 @@ """ -This script checks if the given modules exist - -E.g. python check_modules.py sdkit==1.0.3 sdkit.models ldm transformers numpy antlr4 gfpgan realesrgan +This script checks and installs the required modules. """ -import sys -import pkgutil -from importlib.metadata import version +import os +from importlib.metadata import version as pkg_version +import platform -modules = sys.argv[1:] -missing_modules = [] -for m in modules: - m = m.split("==") - module_name = m[0] - module_version = m[1] if len(m) > 1 else None - is_installed = pkgutil.find_loader(module_name) is not None - if not is_installed: - print("module", module_name, "not found") - exit(1) - elif module_version and version(module_name) != module_version: - print("module version is different! expected: ", module_version, ", actual: ", version(module_name)) - exit(1) +os_name = platform.system() + +modules_to_check = { + "torch": ("1.13.1", "2.0.0"), + "torchvision": ("0.14.1", "0.15.1"), + "sdkit": "1.0.72", + "stable-diffusion-sdkit": "2.1.4", + "rich": "12.6.0", + "uvicorn": "0.19.0", + "fastapi": "0.85.1", +} + + +def version(module_name: str) -> str: + try: + return pkg_version(module_name) + except: + return None + + +def install(module_name: str, module_version: str): + index_url = None + if module_name in ("torch", "torchvision"): + module_version, index_url = apply_torch_install_overrides(module_version) + + install_cmd = f"python -m pip install --upgrade {module_name}=={module_version}" + if index_url: + install_cmd += f" --index-url {index_url}" + if module_name == "sdkit": + install_cmd += " -q" + + print(">", install_cmd) + os.system(install_cmd) + + +def init(): + for module_name, allowed_versions in modules_to_check.items(): + if os.path.exists(f"../src/{module_name}"): + print(f"Skipping {module_name} update, since it's in developer/editable mode") + continue + + allowed_versions, latest_version = get_allowed_versions(module_name, allowed_versions) + if version(module_name) not in allowed_versions: + try: + install(module_name, latest_version) + except: + fail(module_name) + + print(f"{module_name}: {version(module_name)}") + + +### utilities + + +def get_allowed_versions(module_name: str, allowed_versions: tuple[str]): + allowed_versions = (allowed_versions,) if isinstance(allowed_versions, str) else allowed_versions + latest_version = allowed_versions[-1] + + if module_name in ("torch", "torchvision"): + allowed_versions = include_cuda_versions(allowed_versions) + + return allowed_versions, latest_version + + +def apply_torch_install_overrides(module_version: str): + index_url = None + if os_name == "Windows": + module_version += "+cu117" + index_url = "https://download.pytorch.org/whl/cu117" + elif os_name == "Linux": + with open("/proc/bus/pci/devices", "r") as f: + device_info = f.read() + if "amdgpu" in device_info: + index_url = "https://download.pytorch.org/whl/rocm5.4.2" + + return module_version, index_url + + +def include_cuda_versions(module_versions: tuple) -> tuple: + "Adds CUDA-specific versions to the list of allowed version numbers" + + allowed_versions = tuple(module_versions) + allowed_versions += tuple(f"{v}+cu116" for v in module_versions) + allowed_versions += tuple(f"{v}+cu117" for v in module_versions) + + return allowed_versions + + +def fail(module_name): + print( + f"""Error installing {module_name}. Sorry about that, please try to: +1. Run this installer again. +2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting +3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB +4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues +Thanks!""" + ) + exit(1) + + +### start + +init() diff --git a/scripts/on_sd_start.bat b/scripts/on_sd_start.bat index 52bf3330..0b74e9f0 100644 --- a/scripts/on_sd_start.bat +++ b/scripts/on_sd_start.bat @@ -7,12 +7,6 @@ @copy sd-ui-files\scripts\bootstrap.bat scripts\ /Y @copy sd-ui-files\scripts\check_modules.py scripts\ /Y -set TORCH_VERSION=2.0.0+cu117 -set TORCHVISION_VERSION=0.15.1+cu117 -set TORCH_INDEX_URL=https://download.pytorch.org/whl/cu117 -set SDKIT_VERSION=1.0.72 -set SD_VERSION=2.1.4 - if exist "%cd%\profile" ( set USERPROFILE=%cd%\profile ) @@ -70,98 +64,17 @@ if exist "RealESRGAN_x4plus_anime_6B.pth" move RealESRGAN_x4plus_anime_6B.pth .. if not exist "%INSTALL_ENV_DIR%\DLLs\libssl-1_1-x64.dll" copy "%INSTALL_ENV_DIR%\Library\bin\libssl-1_1-x64.dll" "%INSTALL_ENV_DIR%\DLLs\" if not exist "%INSTALL_ENV_DIR%\DLLs\libcrypto-1_1-x64.dll" copy "%INSTALL_ENV_DIR%\Library\bin\libcrypto-1_1-x64.dll" "%INSTALL_ENV_DIR%\DLLs\" -@rem install torch and torchvision -call python ..\scripts\check_modules.py torch==%TORCH_VERSION% torchvision==%TORCHVISION_VERSION% -if "%ERRORLEVEL%" EQU "0" ( - echo "torch and torchvision have already been installed." -) else ( - echo "Installing torch and torchvision.." - - @REM prevent from using packages from the user's home directory, to avoid conflicts - set PYTHONNOUSERSITE=1 - set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages - - call python -m pip install --upgrade torch==%TORCH_VERSION% torchvision==%TORCHVISION_VERSION% --index-url %TORCH_INDEX_URL% || ( - echo "Error installing torch. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" - pause - exit /b - ) -) - -call python -c "from importlib.metadata import version; print('torch version:', version('torch'))" - +@rem install or upgrade the required modules set PATH=C:\Windows\System32;%PATH% -@rem install/upgrade sdkit -call python ..\scripts\check_modules.py sdkit sdkit.models ldm transformers numpy antlr4 gfpgan realesrgan -if "%ERRORLEVEL%" EQU "0" ( - echo "sdkit is already installed." +@REM prevent from using packages from the user's home directory, to avoid conflicts +set PYTHONNOUSERSITE=1 +set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages - @rem skip sdkit upgrade if in developer-mode - if not exist "..\src\sdkit" ( - @REM prevent from using packages from the user's home directory, to avoid conflicts - set PYTHONNOUSERSITE=1 - set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages - - call python -m pip install --upgrade sdkit==%SDKIT_VERSION% -q || ( - echo "Error updating sdkit" - ) - ) -) else ( - echo "Installing sdkit: https://pypi.org/project/sdkit/" - - @REM prevent from using packages from the user's home directory, to avoid conflicts - set PYTHONNOUSERSITE=1 - set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages - - call python -m pip install sdkit==%SDKIT_VERSION% || ( - echo "Error installing sdkit. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" - pause - exit /b - ) -) - -call python -c "from importlib.metadata import version; print('sdkit version:', version('sdkit'))" - -@rem upgrade stable-diffusion-sdkit -call python -m pip install --upgrade stable-diffusion-sdkit==%SD_VERSION% -q || ( - echo "Error updating stable-diffusion-sdkit" -) -call python -c "from importlib.metadata import version; print('stable-diffusion version:', version('stable-diffusion-sdkit'))" - -@rem install rich -call python ..\scripts\check_modules.py rich -if "%ERRORLEVEL%" EQU "0" ( - echo "rich has already been installed." -) else ( - echo "Installing rich.." - - set PYTHONNOUSERSITE=1 - set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages - - call python -m pip install rich || ( - echo "Error installing rich. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" - pause - exit /b - ) -) - -set PATH=C:\Windows\System32;%PATH% - -call python ..\scripts\check_modules.py uvicorn fastapi -@if "%ERRORLEVEL%" EQU "0" ( - echo "Packages necessary for Easy Diffusion were already installed" -) else ( - @echo. & echo "Downloading packages necessary for Easy Diffusion..." & echo. - - set PYTHONNOUSERSITE=1 - set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages - - @call conda install -c conda-forge -y uvicorn fastapi || ( - echo "Error installing the packages necessary for Easy Diffusion. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" - pause - exit /b - ) +call python ..\scripts\check_modules.py +if "%ERRORLEVEL%" NEQ "0" ( + pause + exit /b ) call WHERE uvicorn > .tmp diff --git a/scripts/on_sd_start.sh b/scripts/on_sd_start.sh index 275dfc1a..fbf96de0 100755 --- a/scripts/on_sd_start.sh +++ b/scripts/on_sd_start.sh @@ -7,11 +7,6 @@ cp sd-ui-files/scripts/check_modules.py scripts/ source ./scripts/functions.sh -TORCH_VERSION="2.0.0" -TORCHVISION_VERSION="0.15.1" -SDKIT_VERSION="1.0.72" -SD_VERSION="2.1.4" - # activate the installer env CONDA_BASEPATH=$(conda info --base) source "$CONDA_BASEPATH/etc/profile.d/conda.sh" # avoids the 'shell not initialized' error @@ -61,108 +56,13 @@ if [ -e "GFPGANv1.3.pth" ]; then mv GFPGANv1.3.pth ../models/gfpgan/; fi if [ -e "RealESRGAN_x4plus.pth" ]; then mv RealESRGAN_x4plus.pth ../models/realesrgan/; fi if [ -e "RealESRGAN_x4plus_anime_6B.pth" ]; then mv RealESRGAN_x4plus_anime_6B.pth ../models/realesrgan/; fi -OS_NAME=$(uname -s) -case "${OS_NAME}" in - Linux*) OS_NAME="linux";; - Darwin*) OS_NAME="macos";; - *) echo "Unknown OS: $OS_NAME! This script runs only on Linux or Mac" && exit -esac - -# Detect GPU types - -if grep -q amdgpu /proc/bus/pci/devices; then - echo AMD GPU detected - HAS_AMD=yes +if ! python ../scripts/check_modules.py; then + read -p "Press any key to continue" + exit 1 fi -if grep -q nvidia /proc/bus/pci/devices; then - echo NVidia GPU detected - HAS_NVIDIA=yes -fi - - - -# install torch and torchvision -if python ../scripts/check_modules.py torch==$TORCH_VERSION torchvision==$TORCHVISION_VERSION; then - echo "torch and torchvision have already been installed." -else - echo "Installing torch and torchvision.." - - export PYTHONNOUSERSITE=1 - export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages" - - if python -m pip install --upgrade torch==$TORCH_VERSION torchvision==$TORCHVISION_VERSION ; then - echo "Installed." - else - fail "torch install failed" - fi -fi - -python -c "from importlib.metadata import version; print('torch version:', version('torch'))" - -# install/upgrade sdkit -if python ../scripts/check_modules.py sdkit sdkit.models ldm transformers numpy antlr4 gfpgan realesrgan ; then - echo "sdkit is already installed." - - # skip sdkit upgrade if in developer-mode - if [ ! -e "../src/sdkit" ]; then - export PYTHONNOUSERSITE=1 - export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages" - - python -m pip install --upgrade sdkit==$SDKIT_VERSION -q - fi -else - echo "Installing sdkit: https://pypi.org/project/sdkit/" - - export PYTHONNOUSERSITE=1 - export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages" - - if python -m pip install sdkit==$SDKIT_VERSION ; then - echo "Installed." - else - fail "sdkit install failed" - fi -fi - -python -c "from importlib.metadata import version; print('sdkit version:', version('sdkit'))" - -# upgrade stable-diffusion-sdkit -python -m pip install --upgrade stable-diffusion-sdkit==$SD_VERSION -q -python -c "from importlib.metadata import version; print('stable-diffusion version:', version('stable-diffusion-sdkit'))" - -# install rich -if python ../scripts/check_modules.py rich; then - echo "rich has already been installed." -else - echo "Installing rich.." - - export PYTHONNOUSERSITE=1 - export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages" - - if python -m pip install rich ; then - echo "Installed." - else - fail "Install failed for rich" - fi -fi - -if python ../scripts/check_modules.py uvicorn fastapi ; then - echo "Packages necessary for Easy Diffusion were already installed" -else - printf "\n\nDownloading packages necessary for Easy Diffusion..\n\n" - - export PYTHONNOUSERSITE=1 - export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages" - - if conda install -c conda-forge -y uvicorn fastapi ; then - echo "Installed. Testing.." - else - fail "'conda install uvicorn' failed" - fi - - if ! command -v uvicorn &> /dev/null; then - fail "UI packages not found!" - fi +if ! command -v uvicorn &> /dev/null; then + fail "UI packages not found!" fi if [ -f "../models/stable-diffusion/sd-v1-4.ckpt" ]; then From d8d44c579ce3783fd7c0be5b557efc8c090992c2 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 18 Apr 2023 15:43:56 +0530 Subject: [PATCH 070/291] Typo --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 7365000d..60e12fa4 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -60,7 +60,7 @@ def init(): ### utilities -def get_allowed_versions(module_name: str, allowed_versions: tuple[str]): +def get_allowed_versions(module_name: str, allowed_versions: tuple): allowed_versions = (allowed_versions,) if isinstance(allowed_versions, str) else allowed_versions latest_version = allowed_versions[-1] From 30a133bad9206e6646db25292cdaa1629e299728 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 18 Apr 2023 16:10:46 +0530 Subject: [PATCH 071/291] Allow torch 1.11 to continue being installed --- scripts/check_modules.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 60e12fa4..2b47527f 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -1,5 +1,9 @@ """ This script checks and installs the required modules. + +TODO - Maybe replace the bulk of this script with a call to `pip install -f requirements.txt`, with +a custom index URL depending on the platform. + """ import os @@ -9,8 +13,8 @@ import platform os_name = platform.system() modules_to_check = { - "torch": ("1.13.1", "2.0.0"), - "torchvision": ("0.14.1", "0.15.1"), + "torch": ("1.11.0", "1.13.1", "2.0.0"), + "torchvision": ("0.12.0", "0.14.1", "0.15.1"), "sdkit": "1.0.72", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", From bf3df097b8763611101e588d053821af3f65d1bf Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 18 Apr 2023 17:14:24 +0530 Subject: [PATCH 072/291] Don't use ROCm on Linux if an NVIDIA card is present --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 2b47527f..0abf754e 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -82,7 +82,7 @@ def apply_torch_install_overrides(module_version: str): elif os_name == "Linux": with open("/proc/bus/pci/devices", "r") as f: device_info = f.read() - if "amdgpu" in device_info: + if "amdgpu" in device_info and "nvidia" not in device_info: index_url = "https://download.pytorch.org/whl/rocm5.4.2" return module_version, index_url From becbef4fac4da4c314333ac1aecb8f5482b125c9 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 18 Apr 2023 17:36:52 +0530 Subject: [PATCH 073/291] Include ROCm in the list of allowed versions --- scripts/check_modules.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 0abf754e..fff51aa8 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -94,6 +94,7 @@ def include_cuda_versions(module_versions: tuple) -> tuple: allowed_versions = tuple(module_versions) allowed_versions += tuple(f"{v}+cu116" for v in module_versions) allowed_versions += tuple(f"{v}+cu117" for v in module_versions) + allowed_versions += tuple(f"{v}+rocm5.4.2" for v in module_versions) return allowed_versions From c1e8637a9fb16a213b7a1154e6c1eee079fed899 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 19 Apr 2023 16:11:16 +0530 Subject: [PATCH 074/291] Re-implement the code for downloading models in python. Save some eyeballs from bleeding --- scripts/check_models.py | 99 ++++++++++++++++++++++ scripts/check_modules.py | 4 +- scripts/on_sd_start.bat | 173 ++------------------------------------- scripts/on_sd_start.sh | 157 ++--------------------------------- 4 files changed, 113 insertions(+), 320 deletions(-) create mode 100644 scripts/check_models.py diff --git a/scripts/check_models.py b/scripts/check_models.py new file mode 100644 index 00000000..16dc93b3 --- /dev/null +++ b/scripts/check_models.py @@ -0,0 +1,99 @@ +# this script runs inside the legacy "stable-diffusion" folder + +from sdkit.models import download_model, get_model_info_from_db +from sdkit.utils import hash_file_quick + +import os +import shutil +from glob import glob + +models_base_dir = os.path.abspath(os.path.join("..", "models")) + +models_to_check = { + "stable-diffusion": [ + {"file_name": "sd-v1-4.ckpt", "model_id": "1.4"}, + ], + "gfpgan": [ + {"file_name": "GFPGANv1.4.pth", "model_id": "1.4"}, + ], + "realesrgan": [ + {"file_name": "RealESRGAN_x4plus.pth", "model_id": "x4plus"}, + {"file_name": "RealESRGAN_x4plus_anime_6B.pth", "model_id": "x4plus_anime_6"}, + ], + "vae": [ + {"file_name": "vae-ft-mse-840000-ema-pruned.ckpt", "model_id": "vae-ft-mse-840000-ema-pruned"}, + ], +} +MODEL_EXTENSIONS = { # copied from easydiffusion/model_manager.py + "stable-diffusion": [".ckpt", ".safetensors"], + "vae": [".vae.pt", ".ckpt", ".safetensors"], + "hypernetwork": [".pt", ".safetensors"], + "gfpgan": [".pth"], + "realesrgan": [".pth"], + "lora": [".ckpt", ".safetensors"], +} + + +def download_if_necessary(model_type: str, file_name: str, model_id: str): + model_path = os.path.join(models_base_dir, model_type, file_name) + expected_hash = get_model_info_from_db(model_type=model_type, model_id=model_id)["quick_hash"] + + other_models_exist = any_model_exists(model_type) + known_model_exists = os.path.exists(model_path) + known_model_is_corrupt = known_model_exists and hash_file_quick(model_path) != expected_hash + + if known_model_is_corrupt or (not other_models_exist and not known_model_exists): + print("> download", model_type, model_id) + download_model(model_type, model_id, download_base_dir=models_base_dir) + + +def init(): + migrate_legacy_model_location() + + for model_type, models in models_to_check.items(): + for model in models: + try: + download_if_necessary(model_type, model["file_name"], model["model_id"]) + except: + fail(model_type) + + print(model_type, "model(s) found.") + + +### utilities +def any_model_exists(model_type: str) -> bool: + extensions = MODEL_EXTENSIONS.get(model_type, []) + for ext in extensions: + if any(glob(f"{models_base_dir}/{model_type}/**/*{ext}", recursive=True)): + return True + + return False + + +def migrate_legacy_model_location(): + 'Move the models inside the legacy "stable-diffusion" folder, to their respective folders' + + for model_type, models in models_to_check.items(): + for model in models: + file_name = model["file_name"] + if os.path.exists(file_name): + dest_dir = os.path.join(models_base_dir, model_type) + os.makedirs(dest_dir, exist_ok=True) + shutil.move(file_name, os.path.join(dest_dir, file_name)) + + +def fail(model_name): + print( + f"""Error downloading the {model_name} model. Sorry about that, please try to: +1. Run this installer again. +2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting +3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB +4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues +Thanks!""" + ) + exit(1) + + +### start + +init() diff --git a/scripts/check_modules.py b/scripts/check_modules.py index fff51aa8..ffbbd08b 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -1,6 +1,8 @@ """ This script checks and installs the required modules. +This script runs inside the legacy "stable-diffusion" folder + TODO - Maybe replace the bulk of this script with a call to `pip install -f requirements.txt`, with a custom index URL depending on the platform. @@ -15,7 +17,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.72", + "sdkit": "1.0.73", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", diff --git a/scripts/on_sd_start.bat b/scripts/on_sd_start.bat index 0b74e9f0..1edd18d6 100644 --- a/scripts/on_sd_start.bat +++ b/scripts/on_sd_start.bat @@ -49,17 +49,6 @@ if exist "env" ( if exist src rename src src-old if exist ldm rename ldm ldm-old -if not exist "..\models\stable-diffusion" mkdir "..\models\stable-diffusion" -if not exist "..\models\gfpgan" mkdir "..\models\gfpgan" -if not exist "..\models\realesrgan" mkdir "..\models\realesrgan" -if not exist "..\models\vae" mkdir "..\models\vae" - -@rem migrate the legacy models to the correct path (if already downloaded) -if exist "sd-v1-4.ckpt" move sd-v1-4.ckpt ..\models\stable-diffusion\ -if exist "custom-model.ckpt" move custom-model.ckpt ..\models\stable-diffusion\ -if exist "GFPGANv1.3.pth" move GFPGANv1.3.pth ..\models\gfpgan\ -if exist "RealESRGAN_x4plus.pth" move RealESRGAN_x4plus.pth ..\models\realesrgan\ -if exist "RealESRGAN_x4plus_anime_6B.pth" move RealESRGAN_x4plus_anime_6B.pth ..\models\realesrgan\ if not exist "%INSTALL_ENV_DIR%\DLLs\libssl-1_1-x64.dll" copy "%INSTALL_ENV_DIR%\Library\bin\libssl-1_1-x64.dll" "%INSTALL_ENV_DIR%\DLLs\" if not exist "%INSTALL_ENV_DIR%\DLLs\libcrypto-1_1-x64.dll" copy "%INSTALL_ENV_DIR%\Library\bin\libcrypto-1_1-x64.dll" "%INSTALL_ENV_DIR%\DLLs\" @@ -71,6 +60,7 @@ set PATH=C:\Windows\System32;%PATH% set PYTHONNOUSERSITE=1 set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages +@rem Download the required packages call python ..\scripts\check_modules.py if "%ERRORLEVEL%" NEQ "0" ( pause @@ -90,162 +80,11 @@ call WHERE uvicorn > .tmp @echo conda_sd_ui_deps_installed >> ..\scripts\install_status.txt ) -@if exist "..\models\stable-diffusion\sd-v1-4.ckpt" ( - for %%I in ("..\models\stable-diffusion\sd-v1-4.ckpt") do if "%%~zI" EQU "4265380512" ( - echo "Data files (weights) necessary for Stable Diffusion were already downloaded. Using the HuggingFace 4 GB Model." - ) else ( - for %%J in ("..\models\stable-diffusion\sd-v1-4.ckpt") do if "%%~zJ" EQU "7703807346" ( - echo "Data files (weights) necessary for Stable Diffusion were already downloaded. Using the HuggingFace 7 GB Model." - ) else ( - for %%K in ("..\models\stable-diffusion\sd-v1-4.ckpt") do if "%%~zK" EQU "7703810927" ( - echo "Data files (weights) necessary for Stable Diffusion were already downloaded. Using the Waifu Model." - ) else ( - echo. & echo "The model file present at models\stable-diffusion\sd-v1-4.ckpt is invalid. It is only %%~zK bytes in size. Re-downloading.." & echo. - del "..\models\stable-diffusion\sd-v1-4.ckpt" - ) - ) - ) -) - -@if not exist "..\models\stable-diffusion\*.ckpt" ( - @if not exist "..\models\stable-diffusion\*.safetensors" ( - @echo. & echo "Downloading data files (weights) for Stable Diffusion.." & echo. - - @call curl -L -k https://huggingface.co/CompVis/stable-diffusion-v-1-4-original/resolve/main/sd-v1-4.ckpt > ..\models\stable-diffusion\sd-v1-4.ckpt - - @if exist "..\models\stable-diffusion\sd-v1-4.ckpt" ( - for %%I in ("..\models\stable-diffusion\sd-v1-4.ckpt") do if "%%~zI" NEQ "4265380512" ( - echo. & echo "Error: The downloaded model file was invalid! Bytes downloaded: %%~zI" & echo. - echo. & echo "Error downloading the data files (weights) for Stable Diffusion. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo. - pause - exit /b - ) - ) else ( - @echo. & echo "Error downloading the data files (weights) for Stable Diffusion. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo. - pause - exit /b - ) - ) -) - - - -@if exist "..\models\gfpgan\GFPGANv1.3.pth" ( - for %%I in ("..\models\gfpgan\GFPGANv1.3.pth") do if "%%~zI" EQU "348632874" ( - echo "Data files (weights) necessary for GFPGAN (Face Correction) were already downloaded" - ) else ( - echo. & echo "The GFPGAN model file present at models\gfpgan\GFPGANv1.3.pth is invalid. It is only %%~zI bytes in size. Re-downloading.." & echo. - del "..\models\gfpgan\GFPGANv1.3.pth" - ) -) - -@if not exist "..\models\gfpgan\GFPGANv1.3.pth" ( - @echo. & echo "Downloading data files (weights) for GFPGAN (Face Correction).." & echo. - - @call curl -L -k https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.3.pth > ..\models\gfpgan\GFPGANv1.3.pth - - @if exist "..\models\gfpgan\GFPGANv1.3.pth" ( - for %%I in ("..\models\gfpgan\GFPGANv1.3.pth") do if "%%~zI" NEQ "348632874" ( - echo. & echo "Error: The downloaded GFPGAN model file was invalid! Bytes downloaded: %%~zI" & echo. - echo. & echo "Error downloading the data files (weights) for GFPGAN (Face Correction). Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo. - pause - exit /b - ) - ) else ( - @echo. & echo "Error downloading the data files (weights) for GFPGAN (Face Correction). Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo. - pause - exit /b - ) -) - - - -@if exist "..\models\realesrgan\RealESRGAN_x4plus.pth" ( - for %%I in ("..\models\realesrgan\RealESRGAN_x4plus.pth") do if "%%~zI" EQU "67040989" ( - echo "Data files (weights) necessary for ESRGAN (Resolution Upscaling) x4plus were already downloaded" - ) else ( - echo. & echo "The RealESRGAN model file present at models\realesrgan\RealESRGAN_x4plus.pth is invalid. It is only %%~zI bytes in size. Re-downloading.." & echo. - del "..\models\realesrgan\RealESRGAN_x4plus.pth" - ) -) - -@if not exist "..\models\realesrgan\RealESRGAN_x4plus.pth" ( - @echo. & echo "Downloading data files (weights) for ESRGAN (Resolution Upscaling) x4plus.." & echo. - - @call curl -L -k https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth > ..\models\realesrgan\RealESRGAN_x4plus.pth - - @if exist "..\models\realesrgan\RealESRGAN_x4plus.pth" ( - for %%I in ("..\models\realesrgan\RealESRGAN_x4plus.pth") do if "%%~zI" NEQ "67040989" ( - echo. & echo "Error: The downloaded ESRGAN x4plus model file was invalid! Bytes downloaded: %%~zI" & echo. - echo. & echo "Error downloading the data files (weights) for ESRGAN (Resolution Upscaling) x4plus. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo. - pause - exit /b - ) - ) else ( - @echo. & echo "Error downloading the data files (weights) for ESRGAN (Resolution Upscaling) x4plus. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo. - pause - exit /b - ) -) - - - -@if exist "..\models\realesrgan\RealESRGAN_x4plus_anime_6B.pth" ( - for %%I in ("..\models\realesrgan\RealESRGAN_x4plus_anime_6B.pth") do if "%%~zI" EQU "17938799" ( - echo "Data files (weights) necessary for ESRGAN (Resolution Upscaling) x4plus_anime were already downloaded" - ) else ( - echo. & echo "The RealESRGAN model file present at models\realesrgan\RealESRGAN_x4plus_anime_6B.pth is invalid. It is only %%~zI bytes in size. Re-downloading.." & echo. - del "..\models\realesrgan\RealESRGAN_x4plus_anime_6B.pth" - ) -) - -@if not exist "..\models\realesrgan\RealESRGAN_x4plus_anime_6B.pth" ( - @echo. & echo "Downloading data files (weights) for ESRGAN (Resolution Upscaling) x4plus_anime.." & echo. - - @call curl -L -k https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.2.4/RealESRGAN_x4plus_anime_6B.pth > ..\models\realesrgan\RealESRGAN_x4plus_anime_6B.pth - - @if exist "..\models\realesrgan\RealESRGAN_x4plus_anime_6B.pth" ( - for %%I in ("..\models\realesrgan\RealESRGAN_x4plus_anime_6B.pth") do if "%%~zI" NEQ "17938799" ( - echo. & echo "Error: The downloaded ESRGAN x4plus_anime model file was invalid! Bytes downloaded: %%~zI" & echo. - echo. & echo "Error downloading the data files (weights) for ESRGAN (Resolution Upscaling) x4plus_anime. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo. - pause - exit /b - ) - ) else ( - @echo. & echo "Error downloading the data files (weights) for ESRGAN (Resolution Upscaling) x4plus_anime. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo. - pause - exit /b - ) -) - - - -@if exist "..\models\vae\vae-ft-mse-840000-ema-pruned.ckpt" ( - for %%I in ("..\models\vae\vae-ft-mse-840000-ema-pruned.ckpt") do if "%%~zI" EQU "334695179" ( - echo "Data files (weights) necessary for the default VAE (sd-vae-ft-mse-original) were already downloaded" - ) else ( - echo. & echo "The default VAE (sd-vae-ft-mse-original) file present at models\vae\vae-ft-mse-840000-ema-pruned.ckpt is invalid. It is only %%~zI bytes in size. Re-downloading.." & echo. - del "..\models\vae\vae-ft-mse-840000-ema-pruned.ckpt" - ) -) - -@if not exist "..\models\vae\vae-ft-mse-840000-ema-pruned.ckpt" ( - @echo. & echo "Downloading data files (weights) for the default VAE (sd-vae-ft-mse-original).." & echo. - - @call curl -L -k https://huggingface.co/stabilityai/sd-vae-ft-mse-original/resolve/main/vae-ft-mse-840000-ema-pruned.ckpt > ..\models\vae\vae-ft-mse-840000-ema-pruned.ckpt - - @if exist "..\models\vae\vae-ft-mse-840000-ema-pruned.ckpt" ( - for %%I in ("..\models\vae\vae-ft-mse-840000-ema-pruned.ckpt") do if "%%~zI" NEQ "334695179" ( - echo. & echo "Error: The downloaded default VAE (sd-vae-ft-mse-original) file was invalid! Bytes downloaded: %%~zI" & echo. - echo. & echo "Error downloading the data files (weights) for the default VAE (sd-vae-ft-mse-original). Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo. - pause - exit /b - ) - ) else ( - @echo. & echo "Error downloading the data files (weights) for the default VAE (sd-vae-ft-mse-original). Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo. - pause - exit /b - ) +@rem Download the required models +call python ..\scripts\check_models.py +if "%ERRORLEVEL%" NEQ "0" ( + pause + exit /b ) @>nul findstr /m "sd_install_complete" ..\scripts\install_status.txt diff --git a/scripts/on_sd_start.sh b/scripts/on_sd_start.sh index fbf96de0..51a03d18 100755 --- a/scripts/on_sd_start.sh +++ b/scripts/on_sd_start.sh @@ -44,18 +44,7 @@ fi if [ -e "src" ]; then mv src src-old; fi if [ -e "ldm" ]; then mv ldm ldm-old; fi -mkdir -p "../models/stable-diffusion" -mkdir -p "../models/gfpgan" -mkdir -p "../models/realesrgan" -mkdir -p "../models/vae" - -# migrate the legacy models to the correct path (if already downloaded) -if [ -e "sd-v1-4.ckpt" ]; then mv sd-v1-4.ckpt ../models/stable-diffusion/; fi -if [ -e "custom-model.ckpt" ]; then mv custom-model.ckpt ../models/stable-diffusion/; fi -if [ -e "GFPGANv1.3.pth" ]; then mv GFPGANv1.3.pth ../models/gfpgan/; fi -if [ -e "RealESRGAN_x4plus.pth" ]; then mv RealESRGAN_x4plus.pth ../models/realesrgan/; fi -if [ -e "RealESRGAN_x4plus_anime_6B.pth" ]; then mv RealESRGAN_x4plus_anime_6B.pth ../models/realesrgan/; fi - +# Download the required packages if ! python ../scripts/check_modules.py; then read -p "Press any key to continue" exit 1 @@ -65,146 +54,10 @@ if ! command -v uvicorn &> /dev/null; then fail "UI packages not found!" fi -if [ -f "../models/stable-diffusion/sd-v1-4.ckpt" ]; then - model_size=`filesize "../models/stable-diffusion/sd-v1-4.ckpt"` - - if [ "$model_size" -eq "4265380512" ] || [ "$model_size" -eq "7703807346" ] || [ "$model_size" -eq "7703810927" ]; then - echo "Data files (weights) necessary for Stable Diffusion were already downloaded" - else - printf "\n\nThe model file present at models/stable-diffusion/sd-v1-4.ckpt is invalid. It is only $model_size bytes in size. Re-downloading.." - rm ../models/stable-diffusion/sd-v1-4.ckpt - fi -fi - - -if find "../models/stable-diffusion/" -name "*.ckpt" -or -name "*.safetensors" > /dev/null; then - echo "Found existing model file(s)." -else - echo "Downloading data files (weights) for Stable Diffusion..." - - curl -L -k https://huggingface.co/CompVis/stable-diffusion-v-1-4-original/resolve/main/sd-v1-4.ckpt > ../models/stable-diffusion/sd-v1-4.ckpt - - if [ -f "../models/stable-diffusion/sd-v1-4.ckpt" ]; then - model_size=`filesize "../models/stable-diffusion/sd-v1-4.ckpt"` - if [ ! "$model_size" == "4265380512" ]; then - fail "The downloaded model file was invalid! Bytes downloaded: $model_size" - fi - else - fail "Error downloading the data files (weights) for Stable Diffusion" - fi -fi - - -if [ -f "../models/gfpgan/GFPGANv1.3.pth" ]; then - model_size=`filesize "../models/gfpgan/GFPGANv1.3.pth"` - - if [ "$model_size" -eq "348632874" ]; then - echo "Data files (weights) necessary for GFPGAN (Face Correction) were already downloaded" - else - printf "\n\nThe model file present at models/gfpgan/GFPGANv1.3.pth is invalid. It is only $model_size bytes in size. Re-downloading.." - rm ../models/gfpgan/GFPGANv1.3.pth - fi -fi - -if [ ! -f "../models/gfpgan/GFPGANv1.3.pth" ]; then - echo "Downloading data files (weights) for GFPGAN (Face Correction).." - - curl -L -k https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.3.pth > ../models/gfpgan/GFPGANv1.3.pth - - if [ -f "../models/gfpgan/GFPGANv1.3.pth" ]; then - model_size=`filesize "../models/gfpgan/GFPGANv1.3.pth"` - if [ ! "$model_size" -eq "348632874" ]; then - fail "The downloaded GFPGAN model file was invalid! Bytes downloaded: $model_size" - fi - else - fail "Error downloading the data files (weights) for GFPGAN (Face Correction)." - fi -fi - - -if [ -f "../models/realesrgan/RealESRGAN_x4plus.pth" ]; then - model_size=`filesize "../models/realesrgan/RealESRGAN_x4plus.pth"` - - if [ "$model_size" -eq "67040989" ]; then - echo "Data files (weights) necessary for ESRGAN (Resolution Upscaling) x4plus were already downloaded" - else - printf "\n\nThe model file present at models/realesrgan/RealESRGAN_x4plus.pth is invalid. It is only $model_size bytes in size. Re-downloading.." - rm ../models/realesrgan/RealESRGAN_x4plus.pth - fi -fi - -if [ ! -f "../models/realesrgan/RealESRGAN_x4plus.pth" ]; then - echo "Downloading data files (weights) for ESRGAN (Resolution Upscaling) x4plus.." - - curl -L -k https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth > ../models/realesrgan/RealESRGAN_x4plus.pth - - if [ -f "../models/realesrgan/RealESRGAN_x4plus.pth" ]; then - model_size=`filesize "../models/realesrgan/RealESRGAN_x4plus.pth"` - if [ ! "$model_size" -eq "67040989" ]; then - fail "The downloaded ESRGAN x4plus model file was invalid! Bytes downloaded: $model_size" - fi - else - fail "Error downloading the data files (weights) for ESRGAN (Resolution Upscaling) x4plus" - fi -fi - - -if [ -f "../models/realesrgan/RealESRGAN_x4plus_anime_6B.pth" ]; then - model_size=`filesize "../models/realesrgan/RealESRGAN_x4plus_anime_6B.pth"` - - if [ "$model_size" -eq "17938799" ]; then - echo "Data files (weights) necessary for ESRGAN (Resolution Upscaling) x4plus_anime were already downloaded" - else - printf "\n\nThe model file present at models/realesrgan/RealESRGAN_x4plus_anime_6B.pth is invalid. It is only $model_size bytes in size. Re-downloading.." - rm ../models/realesrgan/RealESRGAN_x4plus_anime_6B.pth - fi -fi - -if [ ! -f "../models/realesrgan/RealESRGAN_x4plus_anime_6B.pth" ]; then - echo "Downloading data files (weights) for ESRGAN (Resolution Upscaling) x4plus_anime.." - - curl -L -k https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.2.4/RealESRGAN_x4plus_anime_6B.pth > ../models/realesrgan/RealESRGAN_x4plus_anime_6B.pth - - if [ -f "../models/realesrgan/RealESRGAN_x4plus_anime_6B.pth" ]; then - model_size=`filesize "../models/realesrgan/RealESRGAN_x4plus_anime_6B.pth"` - if [ ! "$model_size" -eq "17938799" ]; then - fail "The downloaded ESRGAN x4plus_anime model file was invalid! Bytes downloaded: $model_size" - fi - else - fail "Error downloading the data files (weights) for ESRGAN (Resolution Upscaling) x4plus_anime." - fi -fi - - -if [ -f "../models/vae/vae-ft-mse-840000-ema-pruned.ckpt" ]; then - model_size=`filesize "../models/vae/vae-ft-mse-840000-ema-pruned.ckpt"` - - if [ "$model_size" -eq "334695179" ]; then - echo "Data files (weights) necessary for the default VAE (sd-vae-ft-mse-original) were already downloaded" - else - printf "\n\nThe model file present at models/vae/vae-ft-mse-840000-ema-pruned.ckpt is invalid. It is only $model_size bytes in size. Re-downloading.." - rm ../models/vae/vae-ft-mse-840000-ema-pruned.ckpt - fi -fi - -if [ ! -f "../models/vae/vae-ft-mse-840000-ema-pruned.ckpt" ]; then - echo "Downloading data files (weights) for the default VAE (sd-vae-ft-mse-original).." - - curl -L -k https://huggingface.co/stabilityai/sd-vae-ft-mse-original/resolve/main/vae-ft-mse-840000-ema-pruned.ckpt > ../models/vae/vae-ft-mse-840000-ema-pruned.ckpt - - if [ -f "../models/vae/vae-ft-mse-840000-ema-pruned.ckpt" ]; then - model_size=`filesize "../models/vae/vae-ft-mse-840000-ema-pruned.ckpt"` - if [ ! "$model_size" -eq "334695179" ]; then - printf "\n\nError: The downloaded default VAE (sd-vae-ft-mse-original) file was invalid! Bytes downloaded: $model_size\n\n" - printf "\n\nError downloading the data files (weights) for the default VAE (sd-vae-ft-mse-original). Sorry about that, please try to:\n 1. Run this installer again.\n 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting\n 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB\n 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues\nThanks!\n\n" - read -p "Press any key to continue" - exit - fi - else - printf "\n\nError downloading the data files (weights) for the default VAE (sd-vae-ft-mse-original). Sorry about that, please try to:\n 1. Run this installer again.\n 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting\n 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB\n 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues\nThanks!\n\n" - read -p "Press any key to continue" - exit - fi +# Download the required models +if ! python ../scripts/check_models.py; then + read -p "Press any key to continue" + exit 1 fi if [ `grep -c sd_install_complete ../scripts/install_status.txt` -gt "0" ]; then From 34ea49147c0ac50cc74c3ccabea39a63c9ba3a34 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 19 Apr 2023 16:13:29 +0530 Subject: [PATCH 075/291] Update the check_models.py script during startup --- scripts/on_env_start.bat | 1 + scripts/on_env_start.sh | 1 + scripts/on_sd_start.bat | 1 + scripts/on_sd_start.sh | 1 + 4 files changed, 4 insertions(+) diff --git a/scripts/on_env_start.bat b/scripts/on_env_start.bat index 9b461b78..ee702bb5 100644 --- a/scripts/on_env_start.bat +++ b/scripts/on_env_start.bat @@ -57,6 +57,7 @@ if "%update_branch%"=="" ( @xcopy sd-ui-files\ui ui /s /i /Y /q @copy sd-ui-files\scripts\on_sd_start.bat scripts\ /Y @copy sd-ui-files\scripts\check_modules.py scripts\ /Y +@copy sd-ui-files\scripts\check_models.py scripts\ /Y @copy "sd-ui-files\scripts\Start Stable Diffusion UI.cmd" . /Y @copy "sd-ui-files\scripts\Developer Console.cmd" . /Y diff --git a/scripts/on_env_start.sh b/scripts/on_env_start.sh index 39d3939e..4e73ca4e 100755 --- a/scripts/on_env_start.sh +++ b/scripts/on_env_start.sh @@ -43,6 +43,7 @@ cp -Rf sd-ui-files/ui . cp sd-ui-files/scripts/on_sd_start.sh scripts/ cp sd-ui-files/scripts/bootstrap.sh scripts/ cp sd-ui-files/scripts/check_modules.py scripts/ +cp sd-ui-files/scripts/check_models.py scripts/ cp sd-ui-files/scripts/start.sh . cp sd-ui-files/scripts/developer_console.sh . cp sd-ui-files/scripts/functions.sh scripts/ diff --git a/scripts/on_sd_start.bat b/scripts/on_sd_start.bat index 1edd18d6..c01b0a41 100644 --- a/scripts/on_sd_start.bat +++ b/scripts/on_sd_start.bat @@ -6,6 +6,7 @@ @copy sd-ui-files\scripts\on_env_start.bat scripts\ /Y @copy sd-ui-files\scripts\bootstrap.bat scripts\ /Y @copy sd-ui-files\scripts\check_modules.py scripts\ /Y +@copy sd-ui-files\scripts\check_models.py scripts\ /Y if exist "%cd%\profile" ( set USERPROFILE=%cd%\profile diff --git a/scripts/on_sd_start.sh b/scripts/on_sd_start.sh index 51a03d18..accc3442 100755 --- a/scripts/on_sd_start.sh +++ b/scripts/on_sd_start.sh @@ -4,6 +4,7 @@ cp sd-ui-files/scripts/functions.sh scripts/ cp sd-ui-files/scripts/on_env_start.sh scripts/ cp sd-ui-files/scripts/bootstrap.sh scripts/ cp sd-ui-files/scripts/check_modules.py scripts/ +cp sd-ui-files/scripts/check_models.py scripts/ source ./scripts/functions.sh From 7c75a61700ae38777fdb35f30fe964f2f82c94eb Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 19 Apr 2023 16:15:15 +0530 Subject: [PATCH 076/291] Typo --- scripts/check_models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_models.py b/scripts/check_models.py index 16dc93b3..aeae27d9 100644 --- a/scripts/check_models.py +++ b/scripts/check_models.py @@ -57,7 +57,7 @@ def init(): except: fail(model_type) - print(model_type, "model(s) found.") + print(model_type, "model(s) found.") ### utilities From 35c75115de3b3635aa8b888dadbc588fe848651b Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 19 Apr 2023 16:20:08 +0530 Subject: [PATCH 077/291] Log errors during module and model initialization --- scripts/check_models.py | 2 ++ scripts/check_modules.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/scripts/check_models.py b/scripts/check_models.py index aeae27d9..0513bdbf 100644 --- a/scripts/check_models.py +++ b/scripts/check_models.py @@ -6,6 +6,7 @@ from sdkit.utils import hash_file_quick import os import shutil from glob import glob +import traceback models_base_dir = os.path.abspath(os.path.join("..", "models")) @@ -55,6 +56,7 @@ def init(): try: download_if_necessary(model_type, model["file_name"], model["model_id"]) except: + traceback.print_exc() fail(model_type) print(model_type, "model(s) found.") diff --git a/scripts/check_modules.py b/scripts/check_modules.py index ffbbd08b..b457ba8a 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -11,6 +11,7 @@ a custom index URL depending on the platform. import os from importlib.metadata import version as pkg_version import platform +import traceback os_name = platform.system() @@ -58,6 +59,7 @@ def init(): try: install(module_name, latest_version) except: + traceback.print_exc() fail(module_name) print(f"{module_name}: {version(module_name)}") From 83c34ea52ff3fdc994f84699fa364b88b2ee6d04 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 19 Apr 2023 16:31:04 +0530 Subject: [PATCH 078/291] Remove unnecessary hotfix --- scripts/on_sd_start.bat | 2 - scripts/on_sd_start.sh | 5 - ...55dfd25693f1a71f3c6871b184042d4b126244e142 | 171 ------------------ 3 files changed, 178 deletions(-) delete mode 100644 ui/hotfix/9c24e6cd9f499d02c4f21a033736dabd365962dc80fe3aeb57a8f85ea45a20a3.26fead7ea4f0f843f6eb4055dfd25693f1a71f3c6871b184042d4b126244e142 diff --git a/scripts/on_sd_start.bat b/scripts/on_sd_start.bat index c01b0a41..e0b8c5fb 100644 --- a/scripts/on_sd_start.bat +++ b/scripts/on_sd_start.bat @@ -35,8 +35,6 @@ call conda activate @REM remove the old version of the dev console script, if it's still present if exist "Open Developer Console.cmd" del "Open Developer Console.cmd" -@REM @call python -c "import os; import shutil; frm = 'sd-ui-files\\ui\\hotfix\\9c24e6cd9f499d02c4f21a033736dabd365962dc80fe3aeb57a8f85ea45a20a3.26fead7ea4f0f843f6eb4055dfd25693f1a71f3c6871b184042d4b126244e142'; dst = os.path.join(os.path.expanduser('~'), '.cache', 'huggingface', 'transformers', '9c24e6cd9f499d02c4f21a033736dabd365962dc80fe3aeb57a8f85ea45a20a3.26fead7ea4f0f843f6eb4055dfd25693f1a71f3c6871b184042d4b126244e142'); shutil.copyfile(frm, dst) if os.path.exists(dst) else print(''); print('Hotfixed broken JSON file from OpenAI');" - @rem create the stable-diffusion folder, to work with legacy installations if not exist "stable-diffusion" mkdir stable-diffusion cd stable-diffusion diff --git a/scripts/on_sd_start.sh b/scripts/on_sd_start.sh index accc3442..858fa768 100755 --- a/scripts/on_sd_start.sh +++ b/scripts/on_sd_start.sh @@ -19,11 +19,6 @@ if [ -e "open_dev_console.sh" ]; then rm "open_dev_console.sh" fi -# python -c "import os; import shutil; frm = 'sd-ui-files/ui/hotfix/9c24e6cd9f499d02c4f21a033736dabd365962dc80fe3aeb57a8f85ea45a20a3.26fead7ea4f0f843f6eb4055dfd25693f1a71f3c6871b184042d4b126244e142'; dst = os.path.join(os.path.expanduser('~'), '.cache', 'huggingface', 'transformers', '9c24e6cd9f499d02c4f21a033736dabd365962dc80fe3aeb57a8f85ea45a20a3.26fead7ea4f0f843f6eb4055dfd25693f1a71f3c6871b184042d4b126244e142'); shutil.copyfile(frm, dst) if os.path.exists(dst) else print(''); print('Hotfixed broken JSON file from OpenAI');" - -# Caution, this file will make your eyes and brain bleed. It's such an unholy mess. -# Note to self: Please rewrite this in Python. For the sake of your own sanity. - # set the correct installer path (current vs legacy) if [ -e "installer_files/env" ]; then export INSTALL_ENV_DIR="$(pwd)/installer_files/env" diff --git a/ui/hotfix/9c24e6cd9f499d02c4f21a033736dabd365962dc80fe3aeb57a8f85ea45a20a3.26fead7ea4f0f843f6eb4055dfd25693f1a71f3c6871b184042d4b126244e142 b/ui/hotfix/9c24e6cd9f499d02c4f21a033736dabd365962dc80fe3aeb57a8f85ea45a20a3.26fead7ea4f0f843f6eb4055dfd25693f1a71f3c6871b184042d4b126244e142 deleted file mode 100644 index 2c19f666..00000000 --- a/ui/hotfix/9c24e6cd9f499d02c4f21a033736dabd365962dc80fe3aeb57a8f85ea45a20a3.26fead7ea4f0f843f6eb4055dfd25693f1a71f3c6871b184042d4b126244e142 +++ /dev/null @@ -1,171 +0,0 @@ -{ - "_name_or_path": "clip-vit-large-patch14/", - "architectures": [ - "CLIPModel" - ], - "initializer_factor": 1.0, - "logit_scale_init_value": 2.6592, - "model_type": "clip", - "projection_dim": 768, - "text_config": { - "_name_or_path": "", - "add_cross_attention": false, - "architectures": null, - "attention_dropout": 0.0, - "bad_words_ids": null, - "bos_token_id": 0, - "chunk_size_feed_forward": 0, - "cross_attention_hidden_size": null, - "decoder_start_token_id": null, - "diversity_penalty": 0.0, - "do_sample": false, - "dropout": 0.0, - "early_stopping": false, - "encoder_no_repeat_ngram_size": 0, - "eos_token_id": 2, - "finetuning_task": null, - "forced_bos_token_id": null, - "forced_eos_token_id": null, - "hidden_act": "quick_gelu", - "hidden_size": 768, - "id2label": { - "0": "LABEL_0", - "1": "LABEL_1" - }, - "initializer_factor": 1.0, - "initializer_range": 0.02, - "intermediate_size": 3072, - "is_decoder": false, - "is_encoder_decoder": false, - "label2id": { - "LABEL_0": 0, - "LABEL_1": 1 - }, - "layer_norm_eps": 1e-05, - "length_penalty": 1.0, - "max_length": 20, - "max_position_embeddings": 77, - "min_length": 0, - "model_type": "clip_text_model", - "no_repeat_ngram_size": 0, - "num_attention_heads": 12, - "num_beam_groups": 1, - "num_beams": 1, - "num_hidden_layers": 12, - "num_return_sequences": 1, - "output_attentions": false, - "output_hidden_states": false, - "output_scores": false, - "pad_token_id": 1, - "prefix": null, - "problem_type": null, - "projection_dim" : 768, - "pruned_heads": {}, - "remove_invalid_values": false, - "repetition_penalty": 1.0, - "return_dict": true, - "return_dict_in_generate": false, - "sep_token_id": null, - "task_specific_params": null, - "temperature": 1.0, - "tie_encoder_decoder": false, - "tie_word_embeddings": true, - "tokenizer_class": null, - "top_k": 50, - "top_p": 1.0, - "torch_dtype": null, - "torchscript": false, - "transformers_version": "4.16.0.dev0", - "use_bfloat16": false, - "vocab_size": 49408 - }, - "text_config_dict": { - "hidden_size": 768, - "intermediate_size": 3072, - "num_attention_heads": 12, - "num_hidden_layers": 12, - "projection_dim": 768 - }, - "torch_dtype": "float32", - "transformers_version": null, - "vision_config": { - "_name_or_path": "", - "add_cross_attention": false, - "architectures": null, - "attention_dropout": 0.0, - "bad_words_ids": null, - "bos_token_id": null, - "chunk_size_feed_forward": 0, - "cross_attention_hidden_size": null, - "decoder_start_token_id": null, - "diversity_penalty": 0.0, - "do_sample": false, - "dropout": 0.0, - "early_stopping": false, - "encoder_no_repeat_ngram_size": 0, - "eos_token_id": null, - "finetuning_task": null, - "forced_bos_token_id": null, - "forced_eos_token_id": null, - "hidden_act": "quick_gelu", - "hidden_size": 1024, - "id2label": { - "0": "LABEL_0", - "1": "LABEL_1" - }, - "image_size": 224, - "initializer_factor": 1.0, - "initializer_range": 0.02, - "intermediate_size": 4096, - "is_decoder": false, - "is_encoder_decoder": false, - "label2id": { - "LABEL_0": 0, - "LABEL_1": 1 - }, - "layer_norm_eps": 1e-05, - "length_penalty": 1.0, - "max_length": 20, - "min_length": 0, - "model_type": "clip_vision_model", - "no_repeat_ngram_size": 0, - "num_attention_heads": 16, - "num_beam_groups": 1, - "num_beams": 1, - "num_hidden_layers": 24, - "num_return_sequences": 1, - "output_attentions": false, - "output_hidden_states": false, - "output_scores": false, - "pad_token_id": null, - "patch_size": 14, - "prefix": null, - "problem_type": null, - "projection_dim" : 768, - "pruned_heads": {}, - "remove_invalid_values": false, - "repetition_penalty": 1.0, - "return_dict": true, - "return_dict_in_generate": false, - "sep_token_id": null, - "task_specific_params": null, - "temperature": 1.0, - "tie_encoder_decoder": false, - "tie_word_embeddings": true, - "tokenizer_class": null, - "top_k": 50, - "top_p": 1.0, - "torch_dtype": null, - "torchscript": false, - "transformers_version": "4.16.0.dev0", - "use_bfloat16": false - }, - "vision_config_dict": { - "hidden_size": 1024, - "intermediate_size": 4096, - "num_attention_heads": 16, - "num_hidden_layers": 24, - "patch_size": 14, - "projection_dim": 768 - } -} From cb527919a2f785039b29295c38256887feb28a22 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 19 Apr 2023 16:45:28 +0530 Subject: [PATCH 079/291] sdkit 1.0.75 - upgrade to diffusers 0.15.1 --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index b457ba8a..91d24caa 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.73", + "sdkit": "1.0.75", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", From fcbcb7d471bf988d6be6935f23eed9b5aaa33b95 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 19 Apr 2023 16:46:34 +0530 Subject: [PATCH 080/291] changelog --- CHANGES.md | 1 + ui/index.html | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index f858700e..7e61b5aa 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -21,6 +21,7 @@ Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. ### Detailed changelog +* 2.5.32 - 19 Apr 2023 - Automatically check for black images, and set full-precision if necessary (for attn). This means custom models based on Stable Diffusion v2.1 will just work, without needing special command-line arguments or editing of yaml config files. * 2.5.31 - 10 Apr 2023 - Reduce VRAM usage while upscaling. * 2.5.31 - 6 Apr 2023 - Allow seeds upto `4,294,967,295`. Thanks @ogmaresca. * 2.5.31 - 6 Apr 2023 - Buttons to show the previous/next image in the image popup. Thanks @ogmaresca. diff --git a/ui/index.html b/ui/index.html index 7e8680d0..2e89aaf4 100644 --- a/ui/index.html +++ b/ui/index.html @@ -30,7 +30,7 @@

Easy Diffusion - v2.5.31 + v2.5.32

From 6287bcd00a2747df7fddb6327ed9ef344c5c4263 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Thu, 20 Apr 2023 16:17:27 +0530 Subject: [PATCH 081/291] sdkit 1.0.76 - use 256 as the tile size for realesrgan, instead of 128. slightly more VRAM, but faster upscaling --- scripts/check_modules.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 91d24caa..85f64976 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.75", + "sdkit": "1.0.76", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", @@ -34,6 +34,9 @@ def version(module_name: str) -> str: def install(module_name: str, module_version: str): + if module_name == "xformers" and os_name == "Darwin": # xformers is not available on mac + return + index_url = None if module_name in ("torch", "torchvision"): module_version, index_url = apply_torch_install_overrides(module_version) From d0f4476ba589f6e9bc9e94478558b15a15db0640 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Thu, 20 Apr 2023 16:22:42 +0530 Subject: [PATCH 082/291] Suggest downloading a model downloading in the troubleshooting steps. Thanks JeLuf --- scripts/check_models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_models.py b/scripts/check_models.py index 0513bdbf..4b8d68c6 100644 --- a/scripts/check_models.py +++ b/scripts/check_models.py @@ -88,7 +88,7 @@ def fail(model_name): print( f"""Error downloading the {model_name} model. Sorry about that, please try to: 1. Run this installer again. -2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting +2. If that doesn't fix it, please try to download the file manually. The address to download from, and the destination to save to are printed above this message. 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues Thanks!""" From 534bb2dd842b11ba8b731bf9871ba924cdda8cb7 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Thu, 20 Apr 2023 16:44:06 +0530 Subject: [PATCH 083/291] Use xformers 0.0.16 to speed up image generation --- scripts/check_modules.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 85f64976..042086a1 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -23,6 +23,7 @@ modules_to_check = { "rich": "12.6.0", "uvicorn": "0.19.0", "fastapi": "0.85.1", + "xformers": "0.0.16", } From 6c148f1791b39d678a502724077200eef85664d1 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Thu, 20 Apr 2023 16:48:38 +0530 Subject: [PATCH 084/291] Don't install xformers for AMD on Linux; changelog --- CHANGES.md | 1 + scripts/check_modules.py | 19 +++++++++++++------ ui/index.html | 2 +- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 7e61b5aa..ed648dd4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -21,6 +21,7 @@ Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. ### Detailed changelog +* 2.5.33 - 20 Apr 2023 - Use `xformers` to speed up image generation. * 2.5.32 - 19 Apr 2023 - Automatically check for black images, and set full-precision if necessary (for attn). This means custom models based on Stable Diffusion v2.1 will just work, without needing special command-line arguments or editing of yaml config files. * 2.5.31 - 10 Apr 2023 - Reduce VRAM usage while upscaling. * 2.5.31 - 6 Apr 2023 - Allow seeds upto `4,294,967,295`. Thanks @ogmaresca. diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 042086a1..80d198e2 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -35,7 +35,7 @@ def version(module_name: str) -> str: def install(module_name: str, module_version: str): - if module_name == "xformers" and os_name == "Darwin": # xformers is not available on mac + if module_name == "xformers" and (os_name == "Darwin" or is_amd_on_linux()): return index_url = None @@ -87,11 +87,8 @@ def apply_torch_install_overrides(module_version: str): if os_name == "Windows": module_version += "+cu117" index_url = "https://download.pytorch.org/whl/cu117" - elif os_name == "Linux": - with open("/proc/bus/pci/devices", "r") as f: - device_info = f.read() - if "amdgpu" in device_info and "nvidia" not in device_info: - index_url = "https://download.pytorch.org/whl/rocm5.4.2" + elif is_amd_on_linux(): + index_url = "https://download.pytorch.org/whl/rocm5.4.2" return module_version, index_url @@ -107,6 +104,16 @@ def include_cuda_versions(module_versions: tuple) -> tuple: return allowed_versions +def is_amd_on_linux(): + if os_name == "Linux": + with open("/proc/bus/pci/devices", "r") as f: + device_info = f.read() + if "amdgpu" in device_info and "nvidia" not in device_info: + return True + + return False + + def fail(module_name): print( f"""Error installing {module_name}. Sorry about that, please try to: diff --git a/ui/index.html b/ui/index.html index 2e89aaf4..404d88af 100644 --- a/ui/index.html +++ b/ui/index.html @@ -30,7 +30,7 @@

Easy Diffusion - v2.5.32 + v2.5.33

From bc142c9ecd676713350bc26fd5dae485d0150cd1 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Thu, 20 Apr 2023 16:55:34 +0530 Subject: [PATCH 085/291] Fetch release notes only from the main or beta branches --- ui/plugins/ui/release-notes.plugin.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/plugins/ui/release-notes.plugin.js b/ui/plugins/ui/release-notes.plugin.js index da7b79de..5a462008 100644 --- a/ui/plugins/ui/release-notes.plugin.js +++ b/ui/plugins/ui/release-notes.plugin.js @@ -51,9 +51,9 @@ } appConfig = await appConfig.json() - const updateBranch = appConfig.update_branch || 'main' + const notesBranch = (appConfig.update_branch !== "beta" ? "main" : "beta") - let releaseNotes = await fetch(`https://raw.githubusercontent.com/cmdr2/stable-diffusion-ui/${updateBranch}/CHANGES.md`) + let releaseNotes = await fetch(`https://raw.githubusercontent.com/cmdr2/stable-diffusion-ui/${notesBranch}/CHANGES.md`) if (!releaseNotes.ok) { console.error('[release-notes] Failed to get CHANGES.md.') return From cde57109e4674ffbe4f60c99d863be27d99179be Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Thu, 20 Apr 2023 16:57:52 +0530 Subject: [PATCH 086/291] Revert "Fetch release notes only from the main or beta branches" This reverts commit bc142c9ecd676713350bc26fd5dae485d0150cd1. --- ui/plugins/ui/release-notes.plugin.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/plugins/ui/release-notes.plugin.js b/ui/plugins/ui/release-notes.plugin.js index 5a462008..da7b79de 100644 --- a/ui/plugins/ui/release-notes.plugin.js +++ b/ui/plugins/ui/release-notes.plugin.js @@ -51,9 +51,9 @@ } appConfig = await appConfig.json() - const notesBranch = (appConfig.update_branch !== "beta" ? "main" : "beta") + const updateBranch = appConfig.update_branch || 'main' - let releaseNotes = await fetch(`https://raw.githubusercontent.com/cmdr2/stable-diffusion-ui/${notesBranch}/CHANGES.md`) + let releaseNotes = await fetch(`https://raw.githubusercontent.com/cmdr2/stable-diffusion-ui/${updateBranch}/CHANGES.md`) if (!releaseNotes.ok) { console.error('[release-notes] Failed to get CHANGES.md.') return From 76e379d7e17a98ad1e3730a68e91202c37d6fcdc Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Thu, 20 Apr 2023 17:07:10 +0530 Subject: [PATCH 087/291] Don't install xformers, it downgrades the torch version. Still need to fix this --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 80d198e2..83208b28 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -23,7 +23,7 @@ modules_to_check = { "rich": "12.6.0", "uvicorn": "0.19.0", "fastapi": "0.85.1", - "xformers": "0.0.16", + # "xformers": "0.0.16", } From 670410b539568240f3d816937ca6b7d0a63dcf1f Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Thu, 20 Apr 2023 17:42:12 +0530 Subject: [PATCH 088/291] sdkit 1.0.77 - fix inpainting bug on diffusers --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 83208b28..f1f22d32 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.76", + "sdkit": "1.0.77", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", From 023b78d1c9411ac93863aeaee0f94c47a28d18e1 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Thu, 20 Apr 2023 17:46:34 +0530 Subject: [PATCH 089/291] Allow rocm5.2 --- scripts/check_modules.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index f1f22d32..89297d69 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -99,6 +99,7 @@ def include_cuda_versions(module_versions: tuple) -> tuple: allowed_versions = tuple(module_versions) allowed_versions += tuple(f"{v}+cu116" for v in module_versions) allowed_versions += tuple(f"{v}+cu117" for v in module_versions) + allowed_versions += tuple(f"{v}+rocm5.2" for v in module_versions) allowed_versions += tuple(f"{v}+rocm5.4.2" for v in module_versions) return allowed_versions From 526fc989c1ffd54f9c44080c34cd491d466f9031 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Thu, 20 Apr 2023 18:40:45 +0530 Subject: [PATCH 090/291] Allow any version of torch/torchvision --- scripts/check_modules.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 89297d69..7b3eb458 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -59,7 +59,15 @@ def init(): continue allowed_versions, latest_version = get_allowed_versions(module_name, allowed_versions) - if version(module_name) not in allowed_versions: + + requires_install = False + if module_name in ("torch", "torchvision"): + if version(module_name) is None: # allow any torch version + requires_install = True + elif version(module_name) not in allowed_versions: + requires_install = True + + if requires_install: try: install(module_name, latest_version) except: From 5eec05c0c45af4d091675c7869e61c90d2d04efb Mon Sep 17 00:00:00 2001 From: JeLuF Date: Fri, 21 Apr 2023 00:09:27 +0200 Subject: [PATCH 091/291] Don't write config.bat and config.sh any more --- scripts/get_config.py | 45 ++++++++++++++++++++++++++++++++++++++++ scripts/on_env_start.bat | 11 ++++++++++ scripts/on_env_start.sh | 6 ++++++ scripts/on_sd_start.bat | 17 ++++++++++++--- scripts/on_sd_start.sh | 11 +++++++++- ui/easydiffusion/app.py | 45 ---------------------------------------- 6 files changed, 86 insertions(+), 49 deletions(-) create mode 100644 scripts/get_config.py diff --git a/scripts/get_config.py b/scripts/get_config.py new file mode 100644 index 00000000..02523364 --- /dev/null +++ b/scripts/get_config.py @@ -0,0 +1,45 @@ +import os +import argparse + +# The config file is in the same directory as this script +config_directory = os.path.dirname(__file__) +config_yaml = os.path.join(config_directory, "config.yaml") +config_json = os.path.join(config_directory, "config.json") + +parser = argparse.ArgumentParser(description='Get values from config file') +parser.add_argument('--default', dest='default', action='store', + help='default value, to be used if the setting is not defined in the config file') +parser.add_argument('key', metavar='key', nargs='+', + help='config key to return') + +args = parser.parse_args() + + +if os.path.isfile(config_yaml): + import yaml + with open(config_yaml, 'r') as configfile: + try: + config = yaml.safe_load(configfile) + except Exception as e: + print(e) + exit() +elif os.path.isfile(config_json): + import json + with open(config_json, 'r') as configfile: + try: + config = json.load(configfile) + except Exception as e: + print(e) + exit() +else: + config = {} + +for k in args.key: + if k in config: + config = config[k] + else: + if args.default != None: + print(args.default) + exit() + +print(config) diff --git a/scripts/on_env_start.bat b/scripts/on_env_start.bat index ee702bb5..44144cfa 100644 --- a/scripts/on_env_start.bat +++ b/scripts/on_env_start.bat @@ -12,6 +12,16 @@ if exist "scripts\user_config.bat" ( @call scripts\user_config.bat ) +if exist "stable-diffusion\env" ( + @set PYTHONPATH=%PYTHONPATH%;%cd%\stable-diffusion\env\lib\site-packages +) + +if exist "scripts\get_config.py" ( + @FOR /F "tokens=* USEBACKQ" %%F IN (`python scripts\get_config.py --default=main update_branch`) DO ( + @SET update_branch=%%F + ) +) + if "%update_branch%"=="" ( set update_branch=main ) @@ -58,6 +68,7 @@ if "%update_branch%"=="" ( @copy sd-ui-files\scripts\on_sd_start.bat scripts\ /Y @copy sd-ui-files\scripts\check_modules.py scripts\ /Y @copy sd-ui-files\scripts\check_models.py scripts\ /Y +@copy sd-ui-files\scripts\get_config.py scripts\ /Y @copy "sd-ui-files\scripts\Start Stable Diffusion UI.cmd" . /Y @copy "sd-ui-files\scripts\Developer Console.cmd" . /Y diff --git a/scripts/on_env_start.sh b/scripts/on_env_start.sh index 4e73ca4e..3d4d990d 100755 --- a/scripts/on_env_start.sh +++ b/scripts/on_env_start.sh @@ -12,6 +12,11 @@ if [ -f "scripts/user_config.sh" ]; then source scripts/user_config.sh fi +export PYTHONPATH=$(pwd)/installer_files/env/lib/python3.8/site-packages:$(pwd)/stable-diffusion/env/lib/python3.8/site-packages + +if [ -f "scripts/get_config.py" ]; then + export update_branch="$( python scripts/get_config.py --default=main update_branch )" +fi if [ "$update_branch" == "" ]; then export update_branch="main" @@ -44,6 +49,7 @@ cp sd-ui-files/scripts/on_sd_start.sh scripts/ cp sd-ui-files/scripts/bootstrap.sh scripts/ cp sd-ui-files/scripts/check_modules.py scripts/ cp sd-ui-files/scripts/check_models.py scripts/ +cp sd-ui-files/scripts/get_config.py scripts/ cp sd-ui-files/scripts/start.sh . cp sd-ui-files/scripts/developer_console.sh . cp sd-ui-files/scripts/functions.sh scripts/ diff --git a/scripts/on_sd_start.bat b/scripts/on_sd_start.bat index e0b8c5fb..e5d94d2e 100644 --- a/scripts/on_sd_start.bat +++ b/scripts/on_sd_start.bat @@ -104,14 +104,25 @@ call python --version @cd .. @set SD_UI_PATH=%cd%\ui + +@FOR /F "tokens=* USEBACKQ" %%F IN (`python scripts\get_config.py --default=9000 net listen_port`) DO ( + @SET ED_BIND_PORT=%%F +) + +@FOR /F "tokens=* USEBACKQ" %%F IN (`python scripts\get_config.py --default=False net listen_to_network`) DO ( + if "%%F" EQU "True" ( + @SET ED_BIND_IP=0.0.0.0 + ) else ( + @SET ED_BIND_IP=127.0.0.1 + ) +) + @cd stable-diffusion @rem set any overrides set HF_HUB_DISABLE_SYMLINKS_WARNING=true -@if NOT DEFINED SD_UI_BIND_PORT set SD_UI_BIND_PORT=9000 -@if NOT DEFINED SD_UI_BIND_IP set SD_UI_BIND_IP=0.0.0.0 -@uvicorn main:server_api --app-dir "%SD_UI_PATH%" --port %SD_UI_BIND_PORT% --host %SD_UI_BIND_IP% --log-level error +@uvicorn main:server_api --app-dir "%SD_UI_PATH%" --port %ED_BIND_PORT% --host %ED_BIND_IP% --log-level error @pause diff --git a/scripts/on_sd_start.sh b/scripts/on_sd_start.sh index 858fa768..f0eeb743 100755 --- a/scripts/on_sd_start.sh +++ b/scripts/on_sd_start.sh @@ -74,8 +74,17 @@ python --version cd .. export SD_UI_PATH=`pwd`/ui +export ED_BIND_PORT="$( python scripts/get_config.py --default=9000 net listen_port )" +case "$( python scripts/get_config.py --default=False net listen_to_network )" in + "True") + export ED_BIND_IP=0.0.0.0 + ;; + "False") + export ED_BIND_IP=127.0.0.1 + ;; +esac cd stable-diffusion -uvicorn main:server_api --app-dir "$SD_UI_PATH" --port ${SD_UI_BIND_PORT:-9000} --host ${SD_UI_BIND_IP:-0.0.0.0} --log-level error +uvicorn main:server_api --app-dir "$SD_UI_PATH" --port "$ED_BIND_PORT" --host "$ED_BIND_IP" --log-level error read -p "Press any key to continue" diff --git a/ui/easydiffusion/app.py b/ui/easydiffusion/app.py index 83bb08c1..12f10a5a 100644 --- a/ui/easydiffusion/app.py +++ b/ui/easydiffusion/app.py @@ -101,51 +101,6 @@ def setConfig(config): except: log.error(traceback.format_exc()) - try: # config.bat - config_bat_path = os.path.join(CONFIG_DIR, "config.bat") - config_bat = [] - - if "update_branch" in config: - config_bat.append(f"@set update_branch={config['update_branch']}") - - config_bat.append(f"@set SD_UI_BIND_PORT={config['net']['listen_port']}") - bind_ip = "0.0.0.0" if config["net"]["listen_to_network"] else "127.0.0.1" - config_bat.append(f"@set SD_UI_BIND_IP={bind_ip}") - - # Preserve these variables if they are set - for var in PRESERVE_CONFIG_VARS: - if os.getenv(var) is not None: - config_bat.append(f"@set {var}={os.getenv(var)}") - - if len(config_bat) > 0: - with open(config_bat_path, "w", encoding="utf-8") as f: - f.write("\n".join(config_bat)) - except: - log.error(traceback.format_exc()) - - try: # config.sh - config_sh_path = os.path.join(CONFIG_DIR, "config.sh") - config_sh = ["#!/bin/bash"] - - if "update_branch" in config: - config_sh.append(f"export update_branch={config['update_branch']}") - - config_sh.append(f"export SD_UI_BIND_PORT={config['net']['listen_port']}") - bind_ip = "0.0.0.0" if config["net"]["listen_to_network"] else "127.0.0.1" - config_sh.append(f"export SD_UI_BIND_IP={bind_ip}") - - # Preserve these variables if they are set - for var in PRESERVE_CONFIG_VARS: - if os.getenv(var) is not None: - config_bat.append(f'export {var}="{shlex.quote(os.getenv(var))}"') - - if len(config_sh) > 1: - with open(config_sh_path, "w", encoding="utf-8") as f: - f.write("\n".join(config_sh)) - except: - log.error(traceback.format_exc()) - - def save_to_config(ckpt_model_name, vae_model_name, hypernetwork_model_name, vram_usage_level): config = getConfig() if "model" not in config: From 09215736443ffe22c3b2413be40b8daa0791f450 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 21 Apr 2023 15:11:26 +0530 Subject: [PATCH 092/291] sdkit 1.0.78 - fix the 'astype' error with the new diffusers version --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 7b3eb458..32692f50 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.77", + "sdkit": "1.0.78", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", From e1b6cc2a862d8bc1cbed5021fa93cdd1eb6c6a77 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 21 Apr 2023 15:13:29 +0530 Subject: [PATCH 093/291] typo --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 32692f50..891109c3 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.78", + "sdkit": "1.0.79", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", From fea77e97a0b49fa7062ad4c3d223b3d59e6ca00c Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 21 Apr 2023 15:26:14 +0530 Subject: [PATCH 094/291] actually fix the img2img error in the new diffusers version --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 891109c3..13ead165 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.79", + "sdkit": "1.0.80", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", From 2e84a421f3e324dc891cb6b65a8126a3f251aeff Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 21 Apr 2023 15:49:38 +0530 Subject: [PATCH 095/291] Show sdkit installation progress during the first run --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 13ead165..775724b8 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -45,7 +45,7 @@ def install(module_name: str, module_version: str): install_cmd = f"python -m pip install --upgrade {module_name}=={module_version}" if index_url: install_cmd += f" --index-url {index_url}" - if module_name == "sdkit": + if module_name == "sdkit" and version("sdkit") is not None: install_cmd += " -q" print(">", install_cmd) From 0b19adba751f077ec469789ec8f8c91ee0a87258 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 21 Apr 2023 16:01:24 +0530 Subject: [PATCH 096/291] changelog --- CHANGES.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index ed648dd4..7e61b5aa 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -21,7 +21,6 @@ Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. ### Detailed changelog -* 2.5.33 - 20 Apr 2023 - Use `xformers` to speed up image generation. * 2.5.32 - 19 Apr 2023 - Automatically check for black images, and set full-precision if necessary (for attn). This means custom models based on Stable Diffusion v2.1 will just work, without needing special command-line arguments or editing of yaml config files. * 2.5.31 - 10 Apr 2023 - Reduce VRAM usage while upscaling. * 2.5.31 - 6 Apr 2023 - Allow seeds upto `4,294,967,295`. Thanks @ogmaresca. From 1864921d1de6accf112a41ad09c99fe843299fa5 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 21 Apr 2023 16:09:32 +0530 Subject: [PATCH 097/291] Don't copy bootstrap.bat unnecessarily --- scripts/on_sd_start.bat | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/on_sd_start.bat b/scripts/on_sd_start.bat index e0b8c5fb..d8c6f763 100644 --- a/scripts/on_sd_start.bat +++ b/scripts/on_sd_start.bat @@ -4,7 +4,6 @@ @REM Note to self: Please rewrite this in Python. For the sake of your own sanity. @copy sd-ui-files\scripts\on_env_start.bat scripts\ /Y -@copy sd-ui-files\scripts\bootstrap.bat scripts\ /Y @copy sd-ui-files\scripts\check_modules.py scripts\ /Y @copy sd-ui-files\scripts\check_models.py scripts\ /Y From 1967299417e8d826f56055aeee0060cc3cb79afc Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 21 Apr 2023 16:21:24 +0530 Subject: [PATCH 098/291] Download GFPGAN 1.4 by default on new Windows installations (NSIS) --- NSIS/sdui.nsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NSIS/sdui.nsi b/NSIS/sdui.nsi index 0a1a8f9d..aabe6775 100644 --- a/NSIS/sdui.nsi +++ b/NSIS/sdui.nsi @@ -235,7 +235,7 @@ Section "MainSection" SEC01 NScurl::http get "https://huggingface.co/CompVis/stable-diffusion-v-1-4-original/resolve/main/sd-v1-4.ckpt" "$INSTDIR\models\stable-diffusion\sd-v1-4.ckpt" /CANCEL /INSIST /END DetailPrint 'Downloading the GFPGAN model...' - NScurl::http get "https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.3.pth" "$INSTDIR\models\gfpgan\GFPGANv1.3.pth" /CANCEL /INSIST /END + NScurl::http get "https://github.com/TencentARC/GFPGAN/releases/download/v1.3.4/GFPGANv1.4.pth" "$INSTDIR\models\gfpgan\GFPGANv1.4.pth" /CANCEL /INSIST /END DetailPrint 'Downloading the RealESRGAN_x4plus model...' NScurl::http get "https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth" "$INSTDIR\models\realesrgan\RealESRGAN_x4plus.pth" /CANCEL /INSIST /END From eb16296873baa3b1b10ae4f88c3c668d411a852f Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 21 Apr 2023 19:08:51 +0530 Subject: [PATCH 099/291] Restrict AMD cards on Linux to torch 1.13.1 and ROCm 5.2. Avoids black images on some AMD cards. Temp hack until AMD works properly on torch 2.0 --- scripts/check_modules.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 775724b8..8ef43b09 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -42,6 +42,12 @@ def install(module_name: str, module_version: str): if module_name in ("torch", "torchvision"): module_version, index_url = apply_torch_install_overrides(module_version) + if is_amd_on_linux(): # hack until AMD works properly on torch 2.0 (avoids black images on some cards) + if module_name == "torch": + module_version = "1.13.1+rocm5.2" + elif module_name == "torchvision": + module_version = "0.14.1+rocm5.2" + install_cmd = f"python -m pip install --upgrade {module_name}=={module_version}" if index_url: install_cmd += f" --index-url {index_url}" @@ -96,7 +102,7 @@ def apply_torch_install_overrides(module_version: str): module_version += "+cu117" index_url = "https://download.pytorch.org/whl/cu117" elif is_amd_on_linux(): - index_url = "https://download.pytorch.org/whl/rocm5.4.2" + index_url = "https://download.pytorch.org/whl/rocm5.2" return module_version, index_url From f7235cf82c3e13c836d266b95c9dcdb4653ec7d5 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 21 Apr 2023 20:59:14 +0530 Subject: [PATCH 100/291] Keep the task alive during step callbacks. Thanks Madrang --- ui/easydiffusion/task_manager.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ui/easydiffusion/task_manager.py b/ui/easydiffusion/task_manager.py index 31fdaa6f..91adc04b 100644 --- a/ui/easydiffusion/task_manager.py +++ b/ui/easydiffusion/task_manager.py @@ -317,6 +317,9 @@ def thread_render(device): def step_callback(): global current_state_error + task_cache.keep(id(task), TASK_TTL) + session_cache.keep(task.task_data.session_id, TASK_TTL) + if ( isinstance(current_state_error, SystemExit) or isinstance(current_state_error, StopAsyncIteration) From 3d740555c3397ad3cea03c380f36602da7038cbc Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Sat, 22 Apr 2023 14:54:52 +0530 Subject: [PATCH 101/291] Force mac to downgrade from torch 2.0 --- scripts/check_modules.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 8ef43b09..6d39fcfc 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -47,6 +47,11 @@ def install(module_name: str, module_version: str): module_version = "1.13.1+rocm5.2" elif module_name == "torchvision": module_version = "0.14.1+rocm5.2" + elif os_name == "Darwin": + if module_name == "torch": + module_version = "1.13.1" + elif module_name == "torchvision": + module_version = "0.14.1" install_cmd = f"python -m pip install --upgrade {module_name}=={module_version}" if index_url: @@ -70,6 +75,10 @@ def init(): if module_name in ("torch", "torchvision"): if version(module_name) is None: # allow any torch version requires_install = True + elif os_name == "Darwin" and ( # force mac to downgrade from torch 2.0 + version("torch").startswith("2.") or version("torchvision").startswith("0.15.") + ): + requires_install = True elif version(module_name) not in allowed_versions: requires_install = True From 991f9cda4223519354a03b66745a9fcdca51f837 Mon Sep 17 00:00:00 2001 From: JeLuF Date: Sat, 22 Apr 2023 11:42:22 +0200 Subject: [PATCH 102/291] Add splash screen for testDiffusers users The splash screen will only be shown once. The splash screen version number can be used to roll out a new splash screen, which will also be shown only once. Clicking on the EasyAndroidLady icon shows the splash screen again. --- ui/index.html | 49 ++++++++++++++++++++++++++++++++++++++++++- ui/media/css/main.css | 18 ++++++++++++++++ ui/media/js/main.js | 15 +++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) diff --git a/ui/index.html b/ui/index.html index 404d88af..0f163af0 100644 --- a/ui/index.html +++ b/ui/index.html @@ -30,7 +30,7 @@

Easy Diffusion - v2.5.33 + v2.5.33

@@ -399,6 +399,52 @@
+ `; + taskConfig += `
${createTaskConfig(task)}
` - let taskEntry = document.createElement('div') + let taskEntry = document.createElement("div") taskEntry.id = `imageTaskContainer-${Date.now()}` - taskEntry.className = 'imageTaskContainer' + taskEntry.className = "imageTaskContainer" taskEntry.innerHTML = `
Enqueued
@@ -1051,46 +1115,49 @@ function createTask(task) { createCollapsibles(taskEntry) - let draghandle = taskEntry.querySelector('.drag-handle') - draghandle.addEventListener('mousedown', (e) => { - taskEntry.setAttribute('draggable', true) + let draghandle = taskEntry.querySelector(".drag-handle") + draghandle.addEventListener("mousedown", (e) => { + taskEntry.setAttribute("draggable", true) }) // Add a debounce delay to allow mobile to bouble tap. - draghandle.addEventListener('mouseup', debounce((e) => { - taskEntry.setAttribute('draggable', false) - }, 2000)) - draghandle.addEventListener('click', (e) => { + draghandle.addEventListener( + "mouseup", + debounce((e) => { + taskEntry.setAttribute("draggable", false) + }, 2000) + ) + draghandle.addEventListener("click", (e) => { e.preventDefault() // Don't allow the results to be collapsed... }) - taskEntry.addEventListener('dragend', (e) => { - taskEntry.setAttribute('draggable', false); - imagePreview.querySelectorAll(".imageTaskContainer").forEach(itc => { - itc.classList.remove('dropTargetBefore','dropTargetAfter'); - }); - imagePreview.removeEventListener("dragover", onTaskEntryDragOver ); + taskEntry.addEventListener("dragend", (e) => { + taskEntry.setAttribute("draggable", false) + imagePreview.querySelectorAll(".imageTaskContainer").forEach((itc) => { + itc.classList.remove("dropTargetBefore", "dropTargetAfter") + }) + imagePreview.removeEventListener("dragover", onTaskEntryDragOver) }) - taskEntry.addEventListener('dragstart', function(e) { - imagePreview.addEventListener("dragover", onTaskEntryDragOver ); - e.dataTransfer.setData("text/plain", taskEntry.id); - startX = e.target.closest(".imageTaskContainer").offsetLeft; - startY = e.target.closest(".imageTaskContainer").offsetTop; + taskEntry.addEventListener("dragstart", function(e) { + imagePreview.addEventListener("dragover", onTaskEntryDragOver) + e.dataTransfer.setData("text/plain", taskEntry.id) + startX = e.target.closest(".imageTaskContainer").offsetLeft + startY = e.target.closest(".imageTaskContainer").offsetTop }) if (task.reqBody.init_image !== undefined) { createInitImageHover(taskEntry) } - task['taskStatusLabel'] = taskEntry.querySelector('.taskStatusLabel') - task['outputContainer'] = taskEntry.querySelector('.img-preview') - task['outputMsg'] = taskEntry.querySelector('.outputMsg') - task['previewPrompt'] = taskEntry.querySelector('.preview-prompt') - task['progressBar'] = taskEntry.querySelector('.progress-bar') - task['stopTask'] = taskEntry.querySelector('.stopTask') + task["taskStatusLabel"] = taskEntry.querySelector(".taskStatusLabel") + task["outputContainer"] = taskEntry.querySelector(".img-preview") + task["outputMsg"] = taskEntry.querySelector(".outputMsg") + task["previewPrompt"] = taskEntry.querySelector(".preview-prompt") + task["progressBar"] = taskEntry.querySelector(".progress-bar") + task["stopTask"] = taskEntry.querySelector(".stopTask") - task['stopTask'].addEventListener('click', (e) => { + task["stopTask"].addEventListener("click", (e) => { e.stopPropagation() - if (task['isProcessing']) { + if (task["isProcessing"]) { shiftOrConfirm(e, "Stop this task?", async function(e) { if (task.batchesDone <= 0 || !task.isProcessing) { removeTask(taskEntry) @@ -1102,8 +1169,8 @@ function createTask(task) { } }) - task['useSettings'] = taskEntry.querySelector('.useSettings') - task['useSettings'].addEventListener('click', function(e) { + task["useSettings"] = taskEntry.querySelector(".useSettings") + task["useSettings"].addEventListener("click", function(e) { e.stopPropagation() restoreTaskToUI(task, TASK_REQ_NO_EXPORT) }) @@ -1113,8 +1180,8 @@ function createTask(task) { htmlTaskMap.set(taskEntry, task) task.previewPrompt.innerText = task.reqBody.prompt - if (task.previewPrompt.innerText.trim() === '') { - task.previewPrompt.innerHTML = ' ' // allows the results to be collapsed + if (task.previewPrompt.innerText.trim() === "") { + task.previewPrompt.innerHTML = " " // allows the results to be collapsed } return taskEntry.id } @@ -1122,7 +1189,7 @@ function createTask(task) { function getCurrentUserRequest() { const numOutputsTotal = parseInt(numOutputsTotalField.value) const numOutputsParallel = parseInt(numOutputsParallelField.value) - const seed = (randomSeedField.checked ? Math.floor(Math.random() * (2**32 - 1)) : parseInt(seedField.value)) + const seed = randomSeedField.checked ? Math.floor(Math.random() * (2 ** 32 - 1)) : parseInt(seedField.value) const newTask = { batchesDone: 0, @@ -1145,7 +1212,7 @@ function getCurrentUserRequest() { use_stable_diffusion_model: stableDiffusionModelField.value, use_vae_model: vaeModelField.value, stream_progress_updates: true, - stream_image_progress: (numOutputsTotal > 50 ? false : streamImageProgressField.checked), + stream_image_progress: numOutputsTotal > 50 ? false : streamImageProgressField.checked, show_only_filtered_image: showOnlyFilteredImageField.checked, block_nsfw: blockNSFWField.checked, output_format: outputFormatField.value, @@ -1153,8 +1220,8 @@ function getCurrentUserRequest() { output_lossless: outputLosslessField.checked, metadata_output_format: metadataOutputFormatField.value, original_prompt: promptField.value, - active_tags: (activeTags.map(x => x.name)), - inactive_tags: (activeTags.filter(tag => tag.inactive === true).map(x => x.name)) + active_tags: activeTags.map((x) => x.name), + inactive_tags: activeTags.filter((tag) => tag.inactive === true).map((x) => x.name) } } if (IMAGE_REGEX.test(initImagePreview.src)) { @@ -1168,10 +1235,10 @@ function getCurrentUserRequest() { } newTask.reqBody.preserve_init_image_color_profile = applyColorCorrectionField.checked if (!testDiffusers.checked) { - newTask.reqBody.sampler_name = 'ddim' + newTask.reqBody.sampler_name = "ddim" } } - if (saveToDiskField.checked && diskPathField.value.trim() !== '') { + if (saveToDiskField.checked && diskPathField.value.trim() !== "") { newTask.reqBody.save_to_disk_path = diskPathField.value.trim() } if (useFaceCorrectionField.checked) { @@ -1193,30 +1260,28 @@ function getCurrentUserRequest() { } function getPrompts(prompts) { - if (typeof prompts === 'undefined') { + if (typeof prompts === "undefined") { prompts = promptField.value } - if (prompts.trim() === '' && activeTags.length === 0) { - return [''] + if (prompts.trim() === "" && activeTags.length === 0) { + return [""] } let promptsToMake = [] - if (prompts.trim() !== '') { - prompts = prompts.split('\n') - prompts = prompts.map(prompt => prompt.trim()) - prompts = prompts.filter(prompt => prompt !== '') - + if (prompts.trim() !== "") { + prompts = prompts.split("\n") + prompts = prompts.map((prompt) => prompt.trim()) + prompts = prompts.filter((prompt) => prompt !== "") + promptsToMake = applyPermuteOperator(prompts) promptsToMake = applySetOperator(promptsToMake) } - const newTags = activeTags.filter(tag => tag.inactive === undefined || tag.inactive === false) + const newTags = activeTags.filter((tag) => tag.inactive === undefined || tag.inactive === false) if (newTags.length > 0) { - const promptTags = newTags.map(x => x.name).join(", ") + const promptTags = newTags.map((x) => x.name).join(", ") if (promptsToMake.length > 0) { promptsToMake = promptsToMake.map((prompt) => `${prompt}, ${promptTags}`) - } - else - { + } else { promptsToMake.push(promptTags) } } @@ -1224,7 +1289,9 @@ function getPrompts(prompts) { promptsToMake = applyPermuteOperator(promptsToMake) promptsToMake = applySetOperator(promptsToMake) - PLUGINS['GET_PROMPTS_HOOK'].forEach(fn => { promptsToMake = fn(promptsToMake) }) + PLUGINS["GET_PROMPTS_HOOK"].forEach((fn) => { + promptsToMake = fn(promptsToMake) + }) return promptsToMake } @@ -1232,7 +1299,7 @@ function getPrompts(prompts) { function applySetOperator(prompts) { let promptsToMake = [] let braceExpander = new BraceExpander() - prompts.forEach(prompt => { + prompts.forEach((prompt) => { let expandedPrompts = braceExpander.expand(prompt) promptsToMake = promptsToMake.concat(expandedPrompts) }) @@ -1242,13 +1309,13 @@ function applySetOperator(prompts) { function applyPermuteOperator(prompts) { let promptsToMake = [] - prompts.forEach(prompt => { - let promptMatrix = prompt.split('|') + prompts.forEach((prompt) => { + let promptMatrix = prompt.split("|") prompt = promptMatrix.shift().trim() promptsToMake.push(prompt) - promptMatrix = promptMatrix.map(p => p.trim()) - promptMatrix = promptMatrix.filter(p => p !== '') + promptMatrix = promptMatrix.map((p) => p.trim()) + promptMatrix = promptMatrix.filter((p) => p !== "") if (promptMatrix.length > 0) { let promptPermutations = permutePrompts(prompt, promptMatrix) @@ -1262,16 +1329,16 @@ function applyPermuteOperator(prompts) { function permutePrompts(promptBase, promptMatrix) { let prompts = [] let permutations = permute(promptMatrix) - permutations.forEach(perm => { + permutations.forEach((perm) => { let prompt = promptBase if (perm.length > 0) { - let promptAddition = perm.join(', ') - if (promptAddition.trim() === '') { + let promptAddition = perm.join(", ") + if (promptAddition.trim() === "") { return } - prompt += ', ' + promptAddition + prompt += ", " + promptAddition } prompts.push(prompt) @@ -1283,9 +1350,8 @@ function permutePrompts(promptBase, promptMatrix) { // create a file name with embedded prompt and metadata // for easier cateloging and comparison function createFileName(prompt, seed, steps, guidance, outputFormat) { - // Most important information is the prompt - let underscoreName = prompt.replace(/[^a-zA-Z0-9]/g, '_') + let underscoreName = prompt.replace(/[^a-zA-Z0-9]/g, "_") underscoreName = underscoreName.substring(0, 70) // name and the top level metadata @@ -1296,9 +1362,9 @@ function createFileName(prompt, seed, steps, guidance, outputFormat) { async function stopAllTasks() { getUncompletedTaskEntries().forEach((taskEntry) => { - const taskStatusLabel = taskEntry.querySelector('.taskStatusLabel') + const taskStatusLabel = taskEntry.querySelector(".taskStatusLabel") if (taskStatusLabel) { - taskStatusLabel.style.display = 'none' + taskStatusLabel.style.display = "none" } const task = htmlTaskMap.get(taskEntry) if (!task) { @@ -1309,16 +1375,16 @@ async function stopAllTasks() { } function updateInitialText() { - if (document.querySelector('.imageTaskContainer') === null) { + if (document.querySelector(".imageTaskContainer") === null) { if (undoBuffer.length > 0) { initialText.prepend(undoButton) } - previewTools.classList.add('displayNone') - initialText.classList.remove('displayNone') + previewTools.classList.add("displayNone") + initialText.classList.remove("displayNone") } else { - initialText.classList.add('displayNone') - previewTools.classList.remove('displayNone') - document.querySelector('div.display-settings').prepend(undoButton) + initialText.classList.add("displayNone") + previewTools.classList.remove("displayNone") + document.querySelector("div.display-settings").prepend(undoButton) } } @@ -1327,30 +1393,37 @@ function removeTask(taskToRemove) { updateInitialText() } -clearAllPreviewsBtn.addEventListener('click', (e) => { shiftOrConfirm(e, "Clear all the results and tasks in this window?", async function() { - await stopAllTasks() +clearAllPreviewsBtn.addEventListener("click", (e) => { + shiftOrConfirm(e, "Clear all the results and tasks in this window?", async function() { + await stopAllTasks() - let taskEntries = document.querySelectorAll('.imageTaskContainer') - taskEntries.forEach(removeTask) -})}) + let taskEntries = document.querySelectorAll(".imageTaskContainer") + taskEntries.forEach(removeTask) + }) +}) /* Download images popup */ -showDownloadPopupBtn.addEventListener("click", (e) => { saveAllImagesPopup.classList.add("active") }) +showDownloadPopupBtn.addEventListener("click", (e) => { + saveAllImagesPopup.classList.add("active") +}) -saveAllZipToggle.addEventListener('change', (e) => { +saveAllZipToggle.addEventListener("change", (e) => { if (saveAllZipToggle.checked) { - saveAllFoldersOption.classList.remove('displayNone') + saveAllFoldersOption.classList.remove("displayNone") } else { - saveAllFoldersOption.classList.add('displayNone') + saveAllFoldersOption.classList.add("displayNone") } }) // convert base64 to raw binary data held in a string function dataURItoBlob(dataURI) { - var byteString = atob(dataURI.split(',')[1]) + var byteString = atob(dataURI.split(",")[1]) // separate out the mime component - var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0] + var mimeString = dataURI + .split(",")[0] + .split(":")[1] + .split(";")[0] // write the bytes of the string to an ArrayBuffer var ab = new ArrayBuffer(byteString.length) @@ -1364,97 +1437,112 @@ function dataURItoBlob(dataURI) { } // write the ArrayBuffer to a blob, and you're done - return new Blob([ab], {type: mimeString}) + return new Blob([ab], { type: mimeString }) } function downloadAllImages() { let i = 0 - let optZIP = saveAllZipToggle.checked + let optZIP = saveAllZipToggle.checked let optTree = optZIP && saveAllTreeToggle.checked let optJSON = saveAllJSONToggle.checked - + let zip = new JSZip() let folder = zip - document.querySelectorAll(".imageTaskContainer").forEach(container => { + document.querySelectorAll(".imageTaskContainer").forEach((container) => { if (optTree) { - let name = ++i + '-' + container.querySelector('.preview-prompt').textContent.replace(/[^a-zA-Z0-9]/g, '_').substring(0,25) + let name = + ++i + + "-" + + container + .querySelector(".preview-prompt") + .textContent.replace(/[^a-zA-Z0-9]/g, "_") + .substring(0, 25) folder = zip.folder(name) } - container.querySelectorAll(".imgContainer img").forEach(img => { - let imgItem = img.closest('.imgItem') + container.querySelectorAll(".imgContainer img").forEach((img) => { + let imgItem = img.closest(".imgItem") - if (imgItem.style.display === 'none') { + if (imgItem.style.display === "none") { return } - let req = imageRequest[img.dataset['imagecounter']] + let req = imageRequest[img.dataset["imagecounter"]] if (optZIP) { - let suffix = img.dataset['imagecounter'] + '.' + req['output_format'] + let suffix = img.dataset["imagecounter"] + "." + req["output_format"] folder.file(getDownloadFilename(img, suffix), dataURItoBlob(img.src)) if (optJSON) { - suffix = img.dataset['imagecounter'] + '.json' + suffix = img.dataset["imagecounter"] + ".json" folder.file(getDownloadFilename(img, suffix), JSON.stringify(req, null, 2)) } } else { - setTimeout(() => {imgItem.querySelector('.download-img').click()}, i*200) - i = i+1 + setTimeout(() => { + imgItem.querySelector(".download-img").click() + }, i * 200) + i = i + 1 if (optJSON) { - setTimeout(() => {imgItem.querySelector('.download-json').click()}, i*200) - i = i+1 + setTimeout(() => { + imgItem.querySelector(".download-json").click() + }, i * 200) + i = i + 1 } } }) }) if (optZIP) { - let now = Date.now().toString(36).toUpperCase() - zip.generateAsync({type:"blob"}).then(function (blob) { - saveAs(blob, `EasyDiffusion-Images-${now}.zip`); + let now = Date.now() + .toString(36) + .toUpperCase() + zip.generateAsync({ type: "blob" }).then(function(blob) { + saveAs(blob, `EasyDiffusion-Images-${now}.zip`) }) } +} -} +saveAllImagesBtn.addEventListener("click", (e) => { + downloadAllImages() +}) -saveAllImagesBtn.addEventListener('click', (e) => { downloadAllImages() }) +stopImageBtn.addEventListener("click", (e) => { + shiftOrConfirm(e, "Stop all the tasks?", async function(e) { + await stopAllTasks() + }) +}) -stopImageBtn.addEventListener('click', (e) => { shiftOrConfirm(e, "Stop all the tasks?", async function(e) { - await stopAllTasks() -})}) - -widthField.addEventListener('change', onDimensionChange) -heightField.addEventListener('change', onDimensionChange) +widthField.addEventListener("change", onDimensionChange) +heightField.addEventListener("change", onDimensionChange) function renameMakeImageButton() { - let totalImages = Math.max(parseInt(numOutputsTotalField.value), parseInt(numOutputsParallelField.value)) * getPrompts().length - let imageLabel = 'Image' + let totalImages = + Math.max(parseInt(numOutputsTotalField.value), parseInt(numOutputsParallelField.value)) * getPrompts().length + let imageLabel = "Image" if (totalImages > 1) { - imageLabel = totalImages + ' Images' + imageLabel = totalImages + " Images" } if (SD.activeTasks.size == 0) { - makeImageBtn.innerText = 'Make ' + imageLabel + makeImageBtn.innerText = "Make " + imageLabel } else { - makeImageBtn.innerText = 'Enqueue Next ' + imageLabel + makeImageBtn.innerText = "Enqueue Next " + imageLabel } } -numOutputsTotalField.addEventListener('change', renameMakeImageButton) -numOutputsTotalField.addEventListener('keyup', debounce(renameMakeImageButton, 300)) -numOutputsParallelField.addEventListener('change', renameMakeImageButton) -numOutputsParallelField.addEventListener('keyup', debounce(renameMakeImageButton, 300)) +numOutputsTotalField.addEventListener("change", renameMakeImageButton) +numOutputsTotalField.addEventListener("keyup", debounce(renameMakeImageButton, 300)) +numOutputsParallelField.addEventListener("change", renameMakeImageButton) +numOutputsParallelField.addEventListener("keyup", debounce(renameMakeImageButton, 300)) function onDimensionChange() { let widthValue = parseInt(widthField.value) let heightValue = parseInt(heightField.value) if (!initImagePreviewContainer.classList.contains("has-image")) { imageEditor.setImage(null, widthValue, heightValue) - } - else { + } else { imageInpainter.setImage(initImagePreview.src, widthValue, heightValue) } - if ( widthValue < 512 && heightValue < 512 ) { - smallImageWarning.classList.remove('displayNone') + if (widthValue < 512 && heightValue < 512) { + smallImageWarning.classList.remove("displayNone") } else { - smallImageWarning.classList.add('displayNone') + smallImageWarning.classList.add("displayNone") } } @@ -1462,21 +1550,21 @@ diskPathField.disabled = !saveToDiskField.checked metadataOutputFormatField.disabled = !saveToDiskField.checked gfpganModelField.disabled = !useFaceCorrectionField.checked -useFaceCorrectionField.addEventListener('change', function(e) { +useFaceCorrectionField.addEventListener("change", function(e) { gfpganModelField.disabled = !this.checked }) upscaleModelField.disabled = !useUpscalingField.checked upscaleAmountField.disabled = !useUpscalingField.checked -useUpscalingField.addEventListener('change', function(e) { +useUpscalingField.addEventListener("change", function(e) { upscaleModelField.disabled = !this.checked upscaleAmountField.disabled = !this.checked }) -makeImageBtn.addEventListener('click', makeImage) +makeImageBtn.addEventListener("click", makeImage) document.onkeydown = function(e) { - if (e.ctrlKey && e.code === 'Enter') { + if (e.ctrlKey && e.code === "Enter") { makeImage() e.preventDefault() } @@ -1499,8 +1587,8 @@ function updateGuidanceScaleSlider() { guidanceScaleSlider.dispatchEvent(new Event("change")) } -guidanceScaleSlider.addEventListener('input', updateGuidanceScale) -guidanceScaleField.addEventListener('input', updateGuidanceScaleSlider) +guidanceScaleSlider.addEventListener("input", updateGuidanceScale) +guidanceScaleField.addEventListener("input", updateGuidanceScaleSlider) updateGuidanceScale() /********************* Prompt Strength *******************/ @@ -1520,8 +1608,8 @@ function updatePromptStrengthSlider() { promptStrengthSlider.dispatchEvent(new Event("change")) } -promptStrengthSlider.addEventListener('input', updatePromptStrength) -promptStrengthField.addEventListener('input', updatePromptStrengthSlider) +promptStrengthSlider.addEventListener("input", updatePromptStrength) +promptStrengthField.addEventListener("input", updatePromptStrengthSlider) updatePromptStrength() /********************* Hypernetwork Strength **********************/ @@ -1541,14 +1629,15 @@ function updateHypernetworkStrengthSlider() { hypernetworkStrengthSlider.dispatchEvent(new Event("change")) } -hypernetworkStrengthSlider.addEventListener('input', updateHypernetworkStrength) -hypernetworkStrengthField.addEventListener('input', updateHypernetworkStrengthSlider) +hypernetworkStrengthSlider.addEventListener("input", updateHypernetworkStrength) +hypernetworkStrengthField.addEventListener("input", updateHypernetworkStrengthSlider) updateHypernetworkStrength() function updateHypernetworkStrengthContainer() { - document.querySelector("#hypernetwork_strength_container").style.display = (hypernetworkModelField.value === "" ? 'none' : '') + document.querySelector("#hypernetwork_strength_container").style.display = + hypernetworkModelField.value === "" ? "none" : "" } -hypernetworkModelField.addEventListener('change', updateHypernetworkStrengthContainer) +hypernetworkModelField.addEventListener("change", updateHypernetworkStrengthContainer) updateHypernetworkStrengthContainer() /********************* LoRA alpha **********************/ @@ -1568,19 +1657,19 @@ function updateLoraAlphaSlider() { loraAlphaSlider.dispatchEvent(new Event("change")) } -loraAlphaSlider.addEventListener('input', updateLoraAlpha) -loraAlphaField.addEventListener('input', updateLoraAlphaSlider) +loraAlphaSlider.addEventListener("input", updateLoraAlpha) +loraAlphaField.addEventListener("input", updateLoraAlphaSlider) updateLoraAlpha() function updateLoraAlphaContainer() { - document.querySelector("#lora_alpha_container").style.display = (loraModelField.value === "" ? 'none' : '') + document.querySelector("#lora_alpha_container").style.display = loraModelField.value === "" ? "none" : "" } -loraModelField.addEventListener('change', updateLoraAlphaContainer) +loraModelField.addEventListener("change", updateLoraAlphaContainer) updateLoraAlphaContainer() /********************* JPEG/WEBP Quality **********************/ function updateOutputQuality() { - outputQualityField.value = 0 | outputQualitySlider.value + outputQualityField.value = 0 | outputQualitySlider.value outputQualityField.dispatchEvent(new Event("change")) } @@ -1591,45 +1680,44 @@ function updateOutputQualitySlider() { outputQualityField.value = 95 } - outputQualitySlider.value = 0 | outputQualityField.value + outputQualitySlider.value = 0 | outputQualityField.value outputQualitySlider.dispatchEvent(new Event("change")) } -outputQualitySlider.addEventListener('input', updateOutputQuality) -outputQualityField.addEventListener('input', debounce(updateOutputQualitySlider, 1500)) +outputQualitySlider.addEventListener("input", updateOutputQuality) +outputQualityField.addEventListener("input", debounce(updateOutputQualitySlider, 1500)) updateOutputQuality() function updateOutputQualityVisibility() { - if (outputFormatField.value === 'webp') { - outputLosslessContainer.classList.remove('displayNone') + if (outputFormatField.value === "webp") { + outputLosslessContainer.classList.remove("displayNone") if (outputLosslessField.checked) { - outputQualityRow.classList.add('displayNone') + outputQualityRow.classList.add("displayNone") } else { - outputQualityRow.classList.remove('displayNone') + outputQualityRow.classList.remove("displayNone") } - } - else if (outputFormatField.value === 'png') { - outputQualityRow.classList.add('displayNone') - outputLosslessContainer.classList.add('displayNone') + } else if (outputFormatField.value === "png") { + outputQualityRow.classList.add("displayNone") + outputLosslessContainer.classList.add("displayNone") } else { - outputQualityRow.classList.remove('displayNone') - outputLosslessContainer.classList.add('displayNone') + outputQualityRow.classList.remove("displayNone") + outputLosslessContainer.classList.add("displayNone") } } -outputFormatField.addEventListener('change', updateOutputQualityVisibility) -outputLosslessField.addEventListener('change', updateOutputQualityVisibility) +outputFormatField.addEventListener("change", updateOutputQualityVisibility) +outputLosslessField.addEventListener("change", updateOutputQualityVisibility) /********************* Zoom Slider **********************/ -thumbnailSizeField.addEventListener('change', () => { - (function (s) { - for (var j =0; j < document.styleSheets.length; j++) { +thumbnailSizeField.addEventListener("change", () => { + ;(function(s) { + for (var j = 0; j < document.styleSheets.length; j++) { let cssSheet = document.styleSheets[j] for (var i = 0; i < cssSheet.cssRules.length; i++) { - var rule = cssSheet.cssRules[i]; + var rule = cssSheet.cssRules[i] if (rule.selectorText == "div.img-preview img") { - rule.style['max-height'] = s+'vh'; - rule.style['max-width'] = s+'vw'; - return; + rule.style["max-height"] = s + "vh" + rule.style["max-width"] = s + "vw" + return } } } @@ -1638,18 +1726,18 @@ thumbnailSizeField.addEventListener('change', () => { function onAutoScrollUpdate() { if (autoScroll.checked) { - autoscrollBtn.classList.add('pressed') + autoscrollBtn.classList.add("pressed") } else { - autoscrollBtn.classList.remove('pressed') + autoscrollBtn.classList.remove("pressed") } - autoscrollBtn.querySelector(".state").innerHTML = (autoScroll.checked ? "ON" : "OFF") + autoscrollBtn.querySelector(".state").innerHTML = autoScroll.checked ? "ON" : "OFF" } -autoscrollBtn.addEventListener('click', function() { +autoscrollBtn.addEventListener("click", function() { autoScroll.checked = !autoScroll.checked autoScroll.dispatchEvent(new Event("change")) onAutoScrollUpdate() }) -autoScroll.addEventListener('change', onAutoScrollUpdate) +autoScroll.addEventListener("change", onAutoScrollUpdate) function checkRandomSeed() { if (randomSeedField.checked) { @@ -1659,7 +1747,7 @@ function checkRandomSeed() { seedField.disabled = false } } -randomSeedField.addEventListener('input', checkRandomSeed) +randomSeedField.addEventListener("input", checkRandomSeed) checkRandomSeed() function loadImg2ImgFromFile() { @@ -1670,7 +1758,7 @@ function loadImg2ImgFromFile() { let reader = new FileReader() let file = initImageSelector.files[0] - reader.addEventListener('load', function(event) { + reader.addEventListener("load", function(event) { initImagePreview.src = reader.result }) @@ -1678,16 +1766,16 @@ function loadImg2ImgFromFile() { reader.readAsDataURL(file) } } -initImageSelector.addEventListener('change', loadImg2ImgFromFile) +initImageSelector.addEventListener("change", loadImg2ImgFromFile) loadImg2ImgFromFile() function img2imgLoad() { - promptStrengthContainer.style.display = 'table-row' + promptStrengthContainer.style.display = "table-row" if (!testDiffusers.checked) { samplerSelectionContainer.style.display = "none" } initImagePreviewContainer.classList.add("has-image") - colorCorrectionSetting.style.display = '' + colorCorrectionSetting.style.display = "" initImageSizeBox.textContent = initImagePreview.naturalWidth + " x " + initImagePreview.naturalHeight imageEditor.setImage(this.src, initImagePreview.naturalWidth, initImagePreview.naturalHeight) @@ -1696,7 +1784,7 @@ function img2imgLoad() { function img2imgUnload() { initImageSelector.value = null - initImagePreview.src = '' + initImagePreview.src = "" maskSetting.checked = false promptStrengthContainer.style.display = "none" @@ -1704,22 +1792,21 @@ function img2imgUnload() { samplerSelectionContainer.style.display = "" } initImagePreviewContainer.classList.remove("has-image") - colorCorrectionSetting.style.display = 'none' + colorCorrectionSetting.style.display = "none" imageEditor.setImage(null, parseInt(widthField.value), parseInt(heightField.value)) - } -initImagePreview.addEventListener('load', img2imgLoad) -initImageClearBtn.addEventListener('click', img2imgUnload) +initImagePreview.addEventListener("load", img2imgLoad) +initImageClearBtn.addEventListener("click", img2imgUnload) -maskSetting.addEventListener('click', function() { +maskSetting.addEventListener("click", function() { onDimensionChange() }) -promptsFromFileBtn.addEventListener('click', function() { +promptsFromFileBtn.addEventListener("click", function() { promptsFromFileSelector.click() }) -promptsFromFileSelector.addEventListener('change', async function() { +promptsFromFileSelector.addEventListener("change", async function() { if (promptsFromFileSelector.files.length === 0) { return } @@ -1727,7 +1814,7 @@ promptsFromFileSelector.addEventListener('change', async function() { let reader = new FileReader() let file = promptsFromFileSelector.files[0] - reader.addEventListener('load', async function() { + reader.addEventListener("load", async function() { await parseContent(reader.result) }) @@ -1737,15 +1824,15 @@ promptsFromFileSelector.addEventListener('change', async function() { }) /* setup popup handlers */ -document.querySelectorAll('.popup').forEach(popup => { - popup.addEventListener('click', event => { +document.querySelectorAll(".popup").forEach((popup) => { + popup.addEventListener("click", (event) => { if (event.target == popup) { popup.classList.remove("active") } }) var closeButton = popup.querySelector(".close-button") if (closeButton) { - closeButton.addEventListener('click', () => { + closeButton.addEventListener("click", () => { popup.classList.remove("active") }) } @@ -1753,9 +1840,9 @@ document.querySelectorAll('.popup').forEach(popup => { var tabElements = [] function selectTab(tab_id) { - let tabInfo = tabElements.find(t => t.tab.id == tab_id) + let tabInfo = tabElements.find((t) => t.tab.id == tab_id) if (!tabInfo.tab.classList.contains("active")) { - tabElements.forEach(info => { + tabElements.forEach((info) => { if (info.tab.classList.contains("active") && info.tab.parentNode === tabInfo.tab.parentNode) { info.tab.classList.toggle("active") info.content.classList.toggle("active") @@ -1764,7 +1851,7 @@ function selectTab(tab_id) { tabInfo.tab.classList.toggle("active") tabInfo.content.classList.toggle("active") } - document.dispatchEvent(new CustomEvent('tabClick', { detail: tabInfo })) + document.dispatchEvent(new CustomEvent("tabClick", { detail: tabInfo })) } function linkTabContents(tab) { var name = tab.id.replace("tab-", "") @@ -1775,7 +1862,7 @@ function linkTabContents(tab) { content: content }) - tab.addEventListener("click", event => selectTab(tab.id)) + tab.addEventListener("click", (event) => selectTab(tab.id)) } function isTabActive(tab) { return tab.classList.contains("active") @@ -1785,54 +1872,53 @@ let pauseClient = false function resumeClient() { if (pauseClient) { - document.body.classList.remove('wait-pause') - document.body.classList.add('pause') + document.body.classList.remove("wait-pause") + document.body.classList.add("pause") } - return new Promise(resolve => { - let playbuttonclick = function () { - resumeBtn.removeEventListener("click", playbuttonclick); - resolve("resolved"); + return new Promise((resolve) => { + let playbuttonclick = function() { + resumeBtn.removeEventListener("click", playbuttonclick) + resolve("resolved") } resumeBtn.addEventListener("click", playbuttonclick) }) } -promptField.addEventListener("input", debounce( renameMakeImageButton, 1000) ) +promptField.addEventListener("input", debounce(renameMakeImageButton, 1000)) - -pauseBtn.addEventListener("click", function () { +pauseBtn.addEventListener("click", function() { pauseClient = true - pauseBtn.style.display="none" + pauseBtn.style.display = "none" resumeBtn.style.display = "inline" - document.body.classList.add('wait-pause') + document.body.classList.add("wait-pause") }) -resumeBtn.addEventListener("click", function () { +resumeBtn.addEventListener("click", function() { pauseClient = false resumeBtn.style.display = "none" pauseBtn.style.display = "inline" - document.body.classList.remove('pause') - document.body.classList.remove('wait-pause') + document.body.classList.remove("pause") + document.body.classList.remove("wait-pause") }) /* Pause function */ document.querySelectorAll(".tab").forEach(linkTabContents) window.addEventListener("beforeunload", function(e) { - const msg = "Unsaved pictures will be lost!"; + const msg = "Unsaved pictures will be lost!" - let elementList = document.getElementsByClassName("imageTaskContainer"); + let elementList = document.getElementsByClassName("imageTaskContainer") if (elementList.length != 0) { - e.preventDefault(); - (e || window.event).returnValue = msg; - return msg; + e.preventDefault() + ;(e || window.event).returnValue = msg + return msg } else { - return true; + return true } -}); +}) createCollapsibles() -prettifyInputs(document); +prettifyInputs(document) // set the textbox as focused on start promptField.focus() diff --git a/ui/media/js/parameters.js b/ui/media/js/parameters.js index af399205..7a263315 100644 --- a/ui/media/js/parameters.js +++ b/ui/media/js/parameters.js @@ -8,8 +8,8 @@ var ParameterType = { select: "select", select_multiple: "select_multiple", slider: "slider", - custom: "custom", -}; + custom: "custom" +} /** * JSDoc style @@ -24,7 +24,6 @@ var ParameterType = { * @property {boolean?} saveInAppConfig */ - /** @type {Array.} */ var PARAMETERS = [ { @@ -33,7 +32,8 @@ var PARAMETERS = [ label: "Theme", default: "theme-default", note: "customize the look and feel of the ui", - options: [ // Note: options expanded dynamically + options: [ + // Note: options expanded dynamically { value: "theme-default", label: "Default" @@ -47,7 +47,7 @@ var PARAMETERS = [ label: "Auto-Save Images", note: "automatically saves images to the specified location", icon: "fa-download", - default: false, + default: false }, { id: "diskPath", @@ -82,13 +82,13 @@ var PARAMETERS = [ }, { value: "embed,txt", - label: "embed & txt", + label: "embed & txt" }, { value: "embed,json", - label: "embed & json", - }, - ], + label: "embed & json" + } + ] }, { id: "block_nsfw", @@ -96,7 +96,7 @@ var PARAMETERS = [ label: "Block NSFW images", note: "blurs out NSFW images", icon: "fa-land-mine-on", - default: false, + default: false }, { id: "sound_toggle", @@ -104,7 +104,7 @@ var PARAMETERS = [ label: "Enable Sound", note: "plays a sound on task completion", icon: "fa-volume-low", - default: true, + default: true }, { id: "process_order_toggle", @@ -112,7 +112,7 @@ var PARAMETERS = [ label: "Process newest jobs first", note: "reverse the normal processing order", icon: "fa-arrow-down-short-wide", - default: false, + default: false }, { id: "ui_open_browser_on_start", @@ -121,23 +121,24 @@ var PARAMETERS = [ note: "starts the default browser on startup", icon: "fa-window-restore", default: true, - saveInAppConfig: true, + saveInAppConfig: true }, { id: "vram_usage_level", type: ParameterType.select, label: "GPU Memory Usage", - note: "Faster performance requires more GPU memory (VRAM)

" + - "Balanced: nearly as fast as High, much lower VRAM usage
" + - "High: fastest, maximum GPU memory usage
" + - "Low: slowest, recommended for GPUs with 3 to 4 GB memory", + note: + "Faster performance requires more GPU memory (VRAM)

" + + "Balanced: nearly as fast as High, much lower VRAM usage
" + + "High: fastest, maximum GPU memory usage
" + + "Low: slowest, recommended for GPUs with 3 to 4 GB memory", icon: "fa-forward", default: "balanced", options: [ - {value: "balanced", label: "Balanced"}, - {value: "high", label: "High"}, - {value: "low", label: "Low"} - ], + { value: "balanced", label: "Balanced" }, + { value: "high", label: "High" }, + { value: "low", label: "Low" } + ] }, { id: "use_cpu", @@ -145,20 +146,20 @@ var PARAMETERS = [ label: "Use CPU (not GPU)", note: "warning: this will be *very* slow", icon: "fa-microchip", - default: false, + default: false }, { id: "auto_pick_gpus", type: ParameterType.checkbox, label: "Automatically pick the GPUs (experimental)", - default: false, + default: false }, { id: "use_gpus", type: ParameterType.select_multiple, label: "GPUs to use (experimental)", note: "to process in parallel", - default: false, + default: false }, { id: "auto_save_settings", @@ -166,15 +167,16 @@ var PARAMETERS = [ label: "Auto-Save Settings", note: "restores settings on browser load", icon: "fa-gear", - default: true, + default: true }, { id: "confirm_dangerous_actions", type: ParameterType.checkbox, label: "Confirm dangerous actions", - note: "Actions that might lead to data loss must either be clicked with the shift key pressed, or confirmed in an 'Are you sure?' dialog", + note: + "Actions that might lead to data loss must either be clicked with the shift key pressed, or confirmed in an 'Are you sure?' dialog", icon: "fa-check-double", - default: true, + default: true }, { id: "listen_to_network", @@ -183,7 +185,7 @@ var PARAMETERS = [ note: "Other devices on your network can access this web page", icon: "fa-network-wired", default: true, - saveInAppConfig: true, + saveInAppConfig: true }, { id: "listen_port", @@ -194,29 +196,31 @@ var PARAMETERS = [ render: (parameter) => { return `` }, - saveInAppConfig: true, + saveInAppConfig: true }, { id: "use_beta_channel", type: ParameterType.checkbox, label: "Beta channel", - note: "Get the latest features immediately (but could be less stable). Please restart the program after changing this.", + note: + "Get the latest features immediately (but could be less stable). Please restart the program after changing this.", icon: "fa-fire", - default: false, + default: false }, { id: "test_diffusers", type: ParameterType.checkbox, label: "Test Diffusers", - note: "Experimental! Can have bugs! Use upcoming features (like LoRA) in our new engine. Please press Save, then restart the program after changing this.", + note: + "Experimental! Can have bugs! Use upcoming features (like LoRA) in our new engine. Please press Save, then restart the program after changing this.", icon: "fa-bolt", default: false, - saveInAppConfig: true, - }, -]; + saveInAppConfig: true + } +] function getParameterSettingsEntry(id) { - let parameter = PARAMETERS.filter(p => p.id === id) + let parameter = PARAMETERS.filter((p) => p.id === id) if (parameter.length === 0) { return } @@ -224,37 +228,39 @@ function getParameterSettingsEntry(id) { } function sliderUpdate(event) { - if (event.srcElement.id.endsWith('-input')) { - let slider = document.getElementById(event.srcElement.id.slice(0,-6)) + if (event.srcElement.id.endsWith("-input")) { + let slider = document.getElementById(event.srcElement.id.slice(0, -6)) slider.value = event.srcElement.value slider.dispatchEvent(new Event("change")) } else { - let field = document.getElementById(event.srcElement.id+'-input') + let field = document.getElementById(event.srcElement.id + "-input") field.value = event.srcElement.value field.dispatchEvent(new Event("change")) } } /** - * @param {Parameter} parameter + * @param {Parameter} parameter * @returns {string | HTMLElement} */ function getParameterElement(parameter) { switch (parameter.type) { case ParameterType.checkbox: - var is_checked = parameter.default ? " checked" : ""; + var is_checked = parameter.default ? " checked" : "" return `` case ParameterType.select: case ParameterType.select_multiple: - var options = (parameter.options || []).map(option => ``).join("") - var multiple = (parameter.type == ParameterType.select_multiple ? 'multiple' : '') + var options = (parameter.options || []) + .map((option) => ``) + .join("") + var multiple = parameter.type == ParameterType.select_multiple ? "multiple" : "" return `` case ParameterType.slider: return `  ${parameter.slider_unit}` case ParameterType.custom: return parameter.render(parameter) default: - console.error(`Invalid type ${parameter.type} for parameter ${parameter.id}`); + console.error(`Invalid type ${parameter.type} for parameter ${parameter.id}`) return "ERROR: Invalid Type" } } @@ -265,31 +271,31 @@ let parametersTable = document.querySelector("#system-settings .parameters-table * @param {Array | undefined} parameters * */ function initParameters(parameters) { - parameters.forEach(parameter => { + parameters.forEach((parameter) => { const element = getParameterElement(parameter) - const elementWrapper = createElement('div') + const elementWrapper = createElement("div") if (element instanceof Node) { elementWrapper.appendChild(element) } else { elementWrapper.innerHTML = element } - const note = typeof parameter.note === 'function' ? parameter.note(parameter) : parameter.note + const note = typeof parameter.note === "function" ? parameter.note(parameter) : parameter.note const noteElements = [] if (note) { - const noteElement = createElement('small') + const noteElement = createElement("small") if (note instanceof Node) { noteElement.appendChild(note) } else { - noteElement.innerHTML = note || '' + noteElement.innerHTML = note || "" } noteElements.push(noteElement) } - const icon = parameter.icon ? [createElement('i', undefined, ['fa', parameter.icon])] : [] + const icon = parameter.icon ? [createElement("i", undefined, ["fa", parameter.icon])] : [] - const label = typeof parameter.label === 'function' ? parameter.label(parameter) : parameter.label - const labelElement = createElement('label', { for: parameter.id }) + const label = typeof parameter.label === "function" ? parameter.label(parameter) : parameter.label + const labelElement = createElement("label", { for: parameter.id }) if (label instanceof Node) { labelElement.appendChild(label) } else { @@ -297,13 +303,13 @@ function initParameters(parameters) { } const newrow = createElement( - 'div', - { 'data-setting-id': parameter.id, 'data-save-in-app-config': parameter.saveInAppConfig }, + "div", + { "data-setting-id": parameter.id, "data-save-in-app-config": parameter.saveInAppConfig }, undefined, [ - createElement('div', undefined, undefined, icon), - createElement('div', undefined, undefined, [labelElement, ...noteElements]), - elementWrapper, + createElement("div", undefined, undefined, icon), + createElement("div", undefined, undefined, [labelElement, ...noteElements]), + elementWrapper ] ) parametersTable.appendChild(newrow) @@ -314,22 +320,25 @@ function initParameters(parameters) { initParameters(PARAMETERS) // listen to parameters from plugins -PARAMETERS.addEventListener('push', (...items) => { +PARAMETERS.addEventListener("push", (...items) => { initParameters(items) - - if (items.find(item => item.saveInAppConfig)) { - console.log('Reloading app config for new parameters', items.map(p => p.id)) + + if (items.find((item) => item.saveInAppConfig)) { + console.log( + "Reloading app config for new parameters", + items.map((p) => p.id) + ) getAppConfig() } }) -let vramUsageLevelField = document.querySelector('#vram_usage_level') -let useCPUField = document.querySelector('#use_cpu') -let autoPickGPUsField = document.querySelector('#auto_pick_gpus') -let useGPUsField = document.querySelector('#use_gpus') -let saveToDiskField = document.querySelector('#save_to_disk') -let diskPathField = document.querySelector('#diskPath') -let metadataOutputFormatField = document.querySelector('#metadata_output_format') +let vramUsageLevelField = document.querySelector("#vram_usage_level") +let useCPUField = document.querySelector("#use_cpu") +let autoPickGPUsField = document.querySelector("#auto_pick_gpus") +let useGPUsField = document.querySelector("#use_gpus") +let saveToDiskField = document.querySelector("#save_to_disk") +let diskPathField = document.querySelector("#diskPath") +let metadataOutputFormatField = document.querySelector("#metadata_output_format") let listenToNetworkField = document.querySelector("#listen_to_network") let listenPortField = document.querySelector("#listen_port") let useBetaChannelField = document.querySelector("#use_beta_channel") @@ -337,35 +346,34 @@ let uiOpenBrowserOnStartField = document.querySelector("#ui_open_browser_on_star let confirmDangerousActionsField = document.querySelector("#confirm_dangerous_actions") let testDiffusers = document.querySelector("#test_diffusers") -let saveSettingsBtn = document.querySelector('#save-system-settings-btn') - +let saveSettingsBtn = document.querySelector("#save-system-settings-btn") async function changeAppConfig(configDelta) { try { - let res = await fetch('/app_config', { - method: 'POST', + let res = await fetch("/app_config", { + method: "POST", headers: { - 'Content-Type': 'application/json' + "Content-Type": "application/json" }, body: JSON.stringify(configDelta) }) res = await res.json() - console.log('set config status response', res) + console.log("set config status response", res) } catch (e) { - console.log('set config status error', e) + console.log("set config status error", e) } } async function getAppConfig() { try { - let res = await fetch('/get/app_config') + let res = await fetch("/get/app_config") const config = await res.json() applySettingsFromConfig(config) // custom overrides - if (config.update_branch === 'beta') { + if (config.update_branch === "beta") { useBetaChannelField.checked = true document.querySelector("#updateBranchLabel").innerText = "(beta)" } else { @@ -380,45 +388,48 @@ async function getAppConfig() { if (config.net && config.net.listen_port !== undefined) { listenPortField.value = config.net.listen_port } - if (config.test_diffusers === undefined || config.update_branch === 'main') { + if (config.test_diffusers === undefined || config.update_branch === "main") { testDiffusers.checked = false - document.querySelector("#lora_model_container").style.display = 'none' - document.querySelector("#lora_alpha_container").style.display = 'none' + document.querySelector("#lora_model_container").style.display = "none" + document.querySelector("#lora_alpha_container").style.display = "none" } else { - testDiffusers.checked = config.test_diffusers && config.update_branch !== 'main' - document.querySelector("#lora_model_container").style.display = (testDiffusers.checked ? '' : 'none') - document.querySelector("#lora_alpha_container").style.display = (testDiffusers.checked && loraModelField.value !== "" ? '' : 'none') + testDiffusers.checked = config.test_diffusers && config.update_branch !== "main" + document.querySelector("#lora_model_container").style.display = testDiffusers.checked ? "" : "none" + document.querySelector("#lora_alpha_container").style.display = + testDiffusers.checked && loraModelField.value !== "" ? "" : "none" } - console.log('get config status response', config) + console.log("get config status response", config) return config } catch (e) { - console.log('get config status error', e) + console.log("get config status error", e) return {} } } function applySettingsFromConfig(config) { - Array.from(parametersTable.children).forEach(parameterRow => { - if (parameterRow.dataset.settingId in config && parameterRow.dataset.saveInAppConfig === 'true') { + Array.from(parametersTable.children).forEach((parameterRow) => { + if (parameterRow.dataset.settingId in config && parameterRow.dataset.saveInAppConfig === "true") { const configValue = config[parameterRow.dataset.settingId] - const parameterElement = document.getElementById(parameterRow.dataset.settingId) || - parameterRow.querySelector('input') || parameterRow.querySelector('select') + const parameterElement = + document.getElementById(parameterRow.dataset.settingId) || + parameterRow.querySelector("input") || + parameterRow.querySelector("select") switch (parameterElement?.tagName) { - case 'INPUT': - if (parameterElement.type === 'checkbox') { + case "INPUT": + if (parameterElement.type === "checkbox") { parameterElement.checked = configValue } else { parameterElement.value = configValue } - parameterElement.dispatchEvent(new Event('change')) + parameterElement.dispatchEvent(new Event("change")) break - case 'SELECT': + case "SELECT": if (Array.isArray(configValue)) { - Array.from(parameterElement.options).forEach(option => { + Array.from(parameterElement.options).forEach((option) => { if (configValue.includes(option.value || option.text)) { option.selected = true } @@ -426,82 +437,85 @@ function applySettingsFromConfig(config) { } else { parameterElement.value = configValue } - parameterElement.dispatchEvent(new Event('change')) + parameterElement.dispatchEvent(new Event("change")) break } } }) } -saveToDiskField.addEventListener('change', function(e) { +saveToDiskField.addEventListener("change", function(e) { diskPathField.disabled = !this.checked metadataOutputFormatField.disabled = !this.checked }) function getCurrentRenderDeviceSelection() { - let selectedGPUs = $('#use_gpus').val() + let selectedGPUs = $("#use_gpus").val() if (useCPUField.checked && !autoPickGPUsField.checked) { - return 'cpu' + return "cpu" } if (autoPickGPUsField.checked || selectedGPUs.length == 0) { - return 'auto' + return "auto" } - return selectedGPUs.join(',') + return selectedGPUs.join(",") } -useCPUField.addEventListener('click', function() { - let gpuSettingEntry = getParameterSettingsEntry('use_gpus') - let autoPickGPUSettingEntry = getParameterSettingsEntry('auto_pick_gpus') +useCPUField.addEventListener("click", function() { + let gpuSettingEntry = getParameterSettingsEntry("use_gpus") + let autoPickGPUSettingEntry = getParameterSettingsEntry("auto_pick_gpus") if (this.checked) { - gpuSettingEntry.style.display = 'none' - autoPickGPUSettingEntry.style.display = 'none' - autoPickGPUsField.setAttribute('data-old-value', autoPickGPUsField.checked) + gpuSettingEntry.style.display = "none" + autoPickGPUSettingEntry.style.display = "none" + autoPickGPUsField.setAttribute("data-old-value", autoPickGPUsField.checked) autoPickGPUsField.checked = false } else if (useGPUsField.options.length >= MIN_GPUS_TO_SHOW_SELECTION) { - gpuSettingEntry.style.display = '' - autoPickGPUSettingEntry.style.display = '' - let oldVal = autoPickGPUsField.getAttribute('data-old-value') - if (oldVal === null || oldVal === undefined) { // the UI started with CPU selected by default + gpuSettingEntry.style.display = "" + autoPickGPUSettingEntry.style.display = "" + let oldVal = autoPickGPUsField.getAttribute("data-old-value") + if (oldVal === null || oldVal === undefined) { + // the UI started with CPU selected by default autoPickGPUsField.checked = true } else { - autoPickGPUsField.checked = (oldVal === 'true') + autoPickGPUsField.checked = oldVal === "true" } - gpuSettingEntry.style.display = (autoPickGPUsField.checked ? 'none' : '') + gpuSettingEntry.style.display = autoPickGPUsField.checked ? "none" : "" } }) -useGPUsField.addEventListener('click', function() { - let selectedGPUs = $('#use_gpus').val() - autoPickGPUsField.checked = (selectedGPUs.length === 0) +useGPUsField.addEventListener("click", function() { + let selectedGPUs = $("#use_gpus").val() + autoPickGPUsField.checked = selectedGPUs.length === 0 }) -autoPickGPUsField.addEventListener('click', function() { +autoPickGPUsField.addEventListener("click", function() { if (this.checked) { - $('#use_gpus').val([]) + $("#use_gpus").val([]) } - let gpuSettingEntry = getParameterSettingsEntry('use_gpus') - gpuSettingEntry.style.display = (this.checked ? 'none' : '') + let gpuSettingEntry = getParameterSettingsEntry("use_gpus") + gpuSettingEntry.style.display = this.checked ? "none" : "" }) -async function setDiskPath(defaultDiskPath, force=false) { +async function setDiskPath(defaultDiskPath, force = false) { var diskPath = getSetting("diskPath") - if (force || diskPath == '' || diskPath == undefined || diskPath == "undefined") { + if (force || diskPath == "" || diskPath == undefined || diskPath == "undefined") { setSetting("diskPath", defaultDiskPath) } } function setDeviceInfo(devices) { let cpu = devices.all.cpu.name - let allGPUs = Object.keys(devices.all).filter(d => d != 'cpu') + let allGPUs = Object.keys(devices.all).filter((d) => d != "cpu") let activeGPUs = Object.keys(devices.active) function ID_TO_TEXT(d) { let info = devices.all[d] if ("mem_free" in info && "mem_total" in info) { - return `${info.name} (${d}) (${info.mem_free.toFixed(1)}Gb free / ${info.mem_total.toFixed(1)} Gb total)` + return `${info.name} (${d}) (${info.mem_free.toFixed(1)}Gb free / ${info.mem_total.toFixed( + 1 + )} Gb total)` } else { return `${info.name} (${d}) (no memory info)` } @@ -510,35 +524,35 @@ function setDeviceInfo(devices) { allGPUs = allGPUs.map(ID_TO_TEXT) activeGPUs = activeGPUs.map(ID_TO_TEXT) - let systemInfoEl = document.querySelector('#system-info') - systemInfoEl.querySelector('#system-info-cpu').innerText = cpu - systemInfoEl.querySelector('#system-info-gpus-all').innerHTML = allGPUs.join('
') - systemInfoEl.querySelector('#system-info-rendering-devices').innerHTML = activeGPUs.join('
') + let systemInfoEl = document.querySelector("#system-info") + systemInfoEl.querySelector("#system-info-cpu").innerText = cpu + systemInfoEl.querySelector("#system-info-gpus-all").innerHTML = allGPUs.join("
") + systemInfoEl.querySelector("#system-info-rendering-devices").innerHTML = activeGPUs.join("
") } function setHostInfo(hosts) { let port = listenPortField.value - hosts = hosts.map(addr => `http://${addr}:${port}/`).map(url => ``) - document.querySelector('#system-info-server-hosts').innerHTML = hosts.join('') + hosts = hosts.map((addr) => `http://${addr}:${port}/`).map((url) => ``) + document.querySelector("#system-info-server-hosts").innerHTML = hosts.join("") } async function getSystemInfo() { try { const res = await SD.getSystemInfo() - let devices = res['devices'] + let devices = res["devices"] - let allDeviceIds = Object.keys(devices['all']).filter(d => d !== 'cpu') - let activeDeviceIds = Object.keys(devices['active']).filter(d => d !== 'cpu') + let allDeviceIds = Object.keys(devices["all"]).filter((d) => d !== "cpu") + let activeDeviceIds = Object.keys(devices["active"]).filter((d) => d !== "cpu") if (activeDeviceIds.length === 0) { useCPUField.checked = true } if (allDeviceIds.length < MIN_GPUS_TO_SHOW_SELECTION || useCPUField.checked) { - let gpuSettingEntry = getParameterSettingsEntry('use_gpus') - gpuSettingEntry.style.display = 'none' - let autoPickGPUSettingEntry = getParameterSettingsEntry('auto_pick_gpus') - autoPickGPUSettingEntry.style.display = 'none' + let gpuSettingEntry = getParameterSettingsEntry("use_gpus") + gpuSettingEntry.style.display = "none" + let autoPickGPUSettingEntry = getParameterSettingsEntry("auto_pick_gpus") + autoPickGPUSettingEntry.style.display = "none" } if (allDeviceIds.length === 0) { @@ -546,86 +560,90 @@ async function getSystemInfo() { useCPUField.disabled = true // no compatible GPUs, so make the CPU mandatory } - autoPickGPUsField.checked = (devices['config'] === 'auto') + autoPickGPUsField.checked = devices["config"] === "auto" - useGPUsField.innerHTML = '' - allDeviceIds.forEach(device => { - let deviceName = devices['all'][device]['name'] + useGPUsField.innerHTML = "" + allDeviceIds.forEach((device) => { + let deviceName = devices["all"][device]["name"] let deviceOption = `` - useGPUsField.insertAdjacentHTML('beforeend', deviceOption) + useGPUsField.insertAdjacentHTML("beforeend", deviceOption) }) if (autoPickGPUsField.checked) { - let gpuSettingEntry = getParameterSettingsEntry('use_gpus') - gpuSettingEntry.style.display = 'none' + let gpuSettingEntry = getParameterSettingsEntry("use_gpus") + gpuSettingEntry.style.display = "none" } else { - $('#use_gpus').val(activeDeviceIds) + $("#use_gpus").val(activeDeviceIds) } setDeviceInfo(devices) - setHostInfo(res['hosts']) + setHostInfo(res["hosts"]) let force = false - if (res['enforce_output_dir'] !== undefined) { - force = res['enforce_output_dir'] + if (res["enforce_output_dir"] !== undefined) { + force = res["enforce_output_dir"] if (force == true) { - saveToDiskField.checked = true - metadataOutputFormatField.disabled = false + saveToDiskField.checked = true + metadataOutputFormatField.disabled = false } saveToDiskField.disabled = force diskPathField.disabled = force } - setDiskPath(res['default_output_dir'], force) + setDiskPath(res["default_output_dir"], force) } catch (e) { - console.log('error fetching devices', e) + console.log("error fetching devices", e) } } -saveSettingsBtn.addEventListener('click', function() { - if (listenPortField.value == '') { - alert('The network port field must not be empty.') +saveSettingsBtn.addEventListener("click", function() { + if (listenPortField.value == "") { + alert("The network port field must not be empty.") return } if (listenPortField.value < 1 || listenPortField.value > 65535) { - alert('The network port must be a number from 1 to 65535') + alert("The network port must be a number from 1 to 65535") return } - const updateBranch = (useBetaChannelField.checked ? 'beta' : 'main') + const updateBranch = useBetaChannelField.checked ? "beta" : "main" const updateAppConfigRequest = { - 'render_devices': getCurrentRenderDeviceSelection(), - 'update_branch': updateBranch, + render_devices: getCurrentRenderDeviceSelection(), + update_branch: updateBranch } - Array.from(parametersTable.children).forEach(parameterRow => { - if (parameterRow.dataset.saveInAppConfig === 'true') { - const parameterElement = document.getElementById(parameterRow.dataset.settingId) || - parameterRow.querySelector('input') || parameterRow.querySelector('select') + Array.from(parametersTable.children).forEach((parameterRow) => { + if (parameterRow.dataset.saveInAppConfig === "true") { + const parameterElement = + document.getElementById(parameterRow.dataset.settingId) || + parameterRow.querySelector("input") || + parameterRow.querySelector("select") switch (parameterElement?.tagName) { - case 'INPUT': - if (parameterElement.type === 'checkbox') { + case "INPUT": + if (parameterElement.type === "checkbox") { updateAppConfigRequest[parameterRow.dataset.settingId] = parameterElement.checked } else { updateAppConfigRequest[parameterRow.dataset.settingId] = parameterElement.value } break - case 'SELECT': + case "SELECT": if (parameterElement.multiple) { updateAppConfigRequest[parameterRow.dataset.settingId] = Array.from(parameterElement.options) - .filter(option => option.selected) - .map(option => option.value || option.text) + .filter((option) => option.selected) + .map((option) => option.value || option.text) } else { updateAppConfigRequest[parameterRow.dataset.settingId] = parameterElement.value } break default: - console.error(`Setting parameter ${parameterRow.dataset.settingId} couldn't be saved to app.config - element #${parameter.id} is a <${parameterElement?.tagName} /> instead of a or a or a ` - } + }, }, { id: "metadata_output_format", @@ -66,29 +66,29 @@ var PARAMETERS = [ options: [ { value: "none", - label: "none" + label: "none", }, { value: "txt", - label: "txt" + label: "txt", }, { value: "json", - label: "json" + label: "json", }, { value: "embed", - label: "embed" + label: "embed", }, { value: "embed,txt", - label: "embed & txt" + label: "embed & txt", }, { value: "embed,json", - label: "embed & json" - } - ] + label: "embed & json", + }, + ], }, { id: "block_nsfw", @@ -96,7 +96,7 @@ var PARAMETERS = [ label: "Block NSFW images", note: "blurs out NSFW images", icon: "fa-land-mine-on", - default: false + default: false, }, { id: "sound_toggle", @@ -104,7 +104,7 @@ var PARAMETERS = [ label: "Enable Sound", note: "plays a sound on task completion", icon: "fa-volume-low", - default: true + default: true, }, { id: "process_order_toggle", @@ -112,7 +112,7 @@ var PARAMETERS = [ label: "Process newest jobs first", note: "reverse the normal processing order", icon: "fa-arrow-down-short-wide", - default: false + default: false, }, { id: "ui_open_browser_on_start", @@ -121,7 +121,7 @@ var PARAMETERS = [ note: "starts the default browser on startup", icon: "fa-window-restore", default: true, - saveInAppConfig: true + saveInAppConfig: true, }, { id: "vram_usage_level", @@ -137,8 +137,8 @@ var PARAMETERS = [ options: [ { value: "balanced", label: "Balanced" }, { value: "high", label: "High" }, - { value: "low", label: "Low" } - ] + { value: "low", label: "Low" }, + ], }, { id: "use_cpu", @@ -146,20 +146,20 @@ var PARAMETERS = [ label: "Use CPU (not GPU)", note: "warning: this will be *very* slow", icon: "fa-microchip", - default: false + default: false, }, { id: "auto_pick_gpus", type: ParameterType.checkbox, label: "Automatically pick the GPUs (experimental)", - default: false + default: false, }, { id: "use_gpus", type: ParameterType.select_multiple, label: "GPUs to use (experimental)", note: "to process in parallel", - default: false + default: false, }, { id: "auto_save_settings", @@ -167,7 +167,7 @@ var PARAMETERS = [ label: "Auto-Save Settings", note: "restores settings on browser load", icon: "fa-gear", - default: true + default: true, }, { id: "confirm_dangerous_actions", @@ -176,7 +176,7 @@ var PARAMETERS = [ note: "Actions that might lead to data loss must either be clicked with the shift key pressed, or confirmed in an 'Are you sure?' dialog", icon: "fa-check-double", - default: true + default: true, }, { id: "listen_to_network", @@ -185,7 +185,7 @@ var PARAMETERS = [ note: "Other devices on your network can access this web page", icon: "fa-network-wired", default: true, - saveInAppConfig: true + saveInAppConfig: true, }, { id: "listen_port", @@ -196,7 +196,7 @@ var PARAMETERS = [ render: (parameter) => { return `` }, - saveInAppConfig: true + saveInAppConfig: true, }, { id: "use_beta_channel", @@ -205,7 +205,7 @@ var PARAMETERS = [ note: "Get the latest features immediately (but could be less stable). Please restart the program after changing this.", icon: "fa-fire", - default: false + default: false, }, { id: "test_diffusers", @@ -215,8 +215,8 @@ var PARAMETERS = [ "Experimental! Can have bugs! Use upcoming features (like LoRA) in our new engine. Please press Save, then restart the program after changing this.", icon: "fa-bolt", default: false, - saveInAppConfig: true - } + saveInAppConfig: true, + }, ] function getParameterSettingsEntry(id) { @@ -309,7 +309,7 @@ function initParameters(parameters) { [ createElement("div", undefined, undefined, icon), createElement("div", undefined, undefined, [labelElement, ...noteElements]), - elementWrapper + elementWrapper, ] ) parametersTable.appendChild(newrow) @@ -353,9 +353,9 @@ async function changeAppConfig(configDelta) { let res = await fetch("/app_config", { method: "POST", headers: { - "Content-Type": "application/json" + "Content-Type": "application/json", }, - body: JSON.stringify(configDelta) + body: JSON.stringify(configDelta), }) res = await res.json() @@ -607,7 +607,7 @@ saveSettingsBtn.addEventListener("click", function() { const updateAppConfigRequest = { render_devices: getCurrentRenderDeviceSelection(), - update_branch: updateBranch + update_branch: updateBranch, } Array.from(parametersTable.children).forEach((parameterRow) => { diff --git a/ui/media/js/plugins.js b/ui/media/js/plugins.js index 85f2e9d9..85cc48d4 100644 --- a/ui/media/js/plugins.js +++ b/ui/media/js/plugins.js @@ -38,7 +38,7 @@ const PLUGINS = { function webp() { return (reqBody) => new SD.RenderTask(reqBody) } - ) + ), } PLUGINS.OUTPUTS_FORMATS.register = function(...args) { const service = ServiceContainer.prototype.register.apply(this, args) diff --git a/ui/media/js/searchable-models.js b/ui/media/js/searchable-models.js index 936f4c57..9a723d15 100644 --- a/ui/media/js/searchable-models.js +++ b/ui/media/js/searchable-models.js @@ -483,7 +483,7 @@ class ModelDropdown { createElement("i", { id: `${this.modelFilter.id}-model-filter-arrow` }, [ "model-selector-arrow", "fa-solid", - "fa-angle-down" + "fa-angle-down", ]) ) this.modelFilter.classList.add("model-selector") @@ -547,7 +547,7 @@ class ModelDropdown { model, createElement("li", { "data-path": fullPath }, classes, [ createElement("i", undefined, ["fa-regular", "fa-file", "icon"]), - model + model, ]) ) } @@ -596,7 +596,7 @@ class ModelDropdown { if (modelTree.length > 0) { const containerListItem = createElement("li", { id: `${this.modelFilter.id}-model-result` }, [ - "model-result" + "model-result", ]) //console.log(containerListItem) containerListItem.appendChild(this.createModelNodeList(undefined, modelTree, true)) diff --git a/ui/media/js/themes.js b/ui/media/js/themes.js index 33c8fca0..a541eb44 100644 --- a/ui/media/js/themes.js +++ b/ui/media/js/themes.js @@ -30,14 +30,14 @@ function initTheme() { THEMES.push({ key: theme_key, name: getThemeName(theme_key), - rule: rule + rule: rule, }) } if (selector && selector == ":root") { DEFAULT_THEME = { key: "theme-default", name: "Default", - rule: rule + rule: rule, } } }) diff --git a/ui/media/js/utils.js b/ui/media/js/utils.js index 6bc5a65d..6558f7d2 100644 --- a/ui/media/js/utils.js +++ b/ui/media/js/utils.js @@ -111,7 +111,7 @@ function tryLoadOldCollapsibles() { const old_map = { advancedPanelOpen: "editor-settings", modifiersPanelOpen: "editor-modifiers", - negativePromptPanelOpen: "editor-inputs-prompt" + negativePromptPanelOpen: "editor-inputs-prompt", } if (localStorage.getItem(Object.keys(old_map)[0])) { let result = {} @@ -195,7 +195,7 @@ function BraceExpander() { ? bracePair(tkns, iPosn + 1, n, lst) : { close: iPosn, - commas: lst + commas: lst, } } @@ -207,7 +207,7 @@ function BraceExpander() { ? dctSofar : { fn: and, - args: [] + args: [], }, head = tkns[0], tail = head ? tkns.slice(1) : [], @@ -217,7 +217,7 @@ function BraceExpander() { return andTree( { fn: and, - args: dctParse.args.concat(lstOR ? orTree(dctParse, lstOR[0], dctBrace.commas) : head) + args: dctParse.args.concat(lstOR ? orTree(dctParse, lstOR[0], dctBrace.commas) : head), }, lstOR ? lstOR[1] : tail ) @@ -238,7 +238,7 @@ function BraceExpander() { }) .map(function(ts) { return ts.length > 1 ? andTree(null, ts)[0] : ts[0] - }) + }), } } @@ -344,11 +344,11 @@ function PromiseSource() { const srcPromise = new Promise((resolve, reject) => { Object.defineProperties(this, { resolve: { value: resolve, writable: false }, - reject: { value: reject, writable: false } + reject: { value: reject, writable: false }, }) }) Object.defineProperties(this, { - promise: { value: makeQuerablePromise(srcPromise), writable: false } + promise: { value: makeQuerablePromise(srcPromise), writable: false }, }) } @@ -471,20 +471,20 @@ function makeQuerablePromise(promise) { ) Object.defineProperties(qurPro, { isResolved: { - get: () => isResolved + get: () => isResolved, }, resolvedValue: { - get: () => resolvedValue + get: () => resolvedValue, }, isPending: { - get: () => isPending + get: () => isPending, }, isRejected: { - get: () => isRejected + get: () => isRejected, }, rejectReason: { - get: () => rejectReason - } + get: () => rejectReason, + }, }) return qurPro } @@ -790,9 +790,9 @@ function createTab(request) { createElement("i", { style: "margin-right: 0.25em" }, [ "fa-solid", `${request.icon.startsWith("fa-") ? "" : "fa-"}${request.icon}`, - "icon" + "icon", ]), - labelElement + labelElement, ]) ) @@ -831,7 +831,7 @@ function createTab(request) { contentElement: wrapper, labelElement, timesOpened, - firstOpen: timesOpened === 1 + firstOpen: timesOpened === 1, }, e ) diff --git a/ui/plugins/ui/Autoscroll.plugin.js b/ui/plugins/ui/Autoscroll.plugin.js index 364e702d..26969365 100644 --- a/ui/plugins/ui/Autoscroll.plugin.js +++ b/ui/plugins/ui/Autoscroll.plugin.js @@ -14,7 +14,7 @@ observer.observe(document.getElementById("preview"), { childList: true, - subtree: true + subtree: true, }) function Autoscroll(target) { diff --git a/ui/plugins/ui/Modifiers-dnd.plugin.js b/ui/plugins/ui/Modifiers-dnd.plugin.js index 075a073f..d38307cf 100644 --- a/ui/plugins/ui/Modifiers-dnd.plugin.js +++ b/ui/plugins/ui/Modifiers-dnd.plugin.js @@ -25,7 +25,7 @@ }) observer.observe(editorModifierTagsList, { - childList: true + childList: true, }) let current diff --git a/ui/plugins/ui/Modifiers-wheel.plugin.js b/ui/plugins/ui/Modifiers-wheel.plugin.js index afb65bd0..df3a684f 100644 --- a/ui/plugins/ui/Modifiers-wheel.plugin.js +++ b/ui/plugins/ui/Modifiers-wheel.plugin.js @@ -18,7 +18,7 @@ }) observer.observe(editorModifierTagsList, { - childList: true + childList: true, }) function ModifierMouseWheel(target) { diff --git a/ui/plugins/ui/custom-modifiers.plugin.js b/ui/plugins/ui/custom-modifiers.plugin.js index e30930b6..91d2acc4 100644 --- a/ui/plugins/ui/custom-modifiers.plugin.js +++ b/ui/plugins/ui/custom-modifiers.plugin.js @@ -13,19 +13,19 @@ customModifiers = customModifiers.filter((m) => m.trim() !== "") customModifiers = customModifiers.map(function(m) { return { - modifier: m + modifier: m, } }) let customGroup = { category: "Custom Modifiers", - modifiers: customModifiers + modifiers: customModifiers, } customModifiersGroupElement = createModifierGroup(customGroup, true) createCollapsibles(customModifiersGroupElement) } - } + }, }) })() diff --git a/ui/plugins/ui/jasmine/boot1.js b/ui/plugins/ui/jasmine/boot1.js index 54ccb7d7..39bfe0fa 100644 --- a/ui/plugins/ui/jasmine/boot1.js +++ b/ui/plugins/ui/jasmine/boot1.js @@ -45,7 +45,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. const queryString = new jasmine.QueryString({ getWindowLocation: function() { return window.location - } + }, }) const filterSpecs = !!queryString.getParam("spec") @@ -53,7 +53,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. const config = { stopOnSpecFailure: queryString.getParam("stopOnSpecFailure"), stopSpecOnExpectationFailure: queryString.getParam("stopSpecOnExpectationFailure"), - hideDisabled: queryString.getParam("hideDisabled") + hideDisabled: queryString.getParam("hideDisabled"), } const random = queryString.getParam("random") @@ -89,7 +89,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. return document.createTextNode.apply(document, arguments) }, timer: new jasmine.Timer(), - filterSpecs: filterSpecs + filterSpecs: filterSpecs, }) /** @@ -104,7 +104,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. const specFilter = new jasmine.HtmlSpecFilter({ filterString: function() { return queryString.getParam("spec") - } + }, }) config.specFilter = function(spec) { diff --git a/ui/plugins/ui/jasmine/jasmine-html.js b/ui/plugins/ui/jasmine/jasmine-html.js index 24157bd2..2d09f498 100644 --- a/ui/plugins/ui/jasmine/jasmine-html.js +++ b/ui/plugins/ui/jasmine/jasmine-html.js @@ -106,7 +106,7 @@ jasmineRequire.HtmlReporter = function(j$) { createDom("a", { className: "jasmine-title", href: "http://jasmine.github.io/", - target: "_blank" + target: "_blank", }), createDom("span", { className: "jasmine-version" }, j$.version) ), @@ -163,7 +163,7 @@ jasmineRequire.HtmlReporter = function(j$) { createDom("li", { className: this.displaySpecInCorrectFormat(result), id: "spec_" + result.id, - title: result.fullName + title: result.fullName, }) ) @@ -246,7 +246,7 @@ jasmineRequire.HtmlReporter = function(j$) { "a", { title: "randomized with seed " + order.seed, - href: seedHref(order.seed) + href: seedHref(order.seed), }, order.seed ) @@ -428,7 +428,7 @@ jasmineRequire.HtmlReporter = function(j$) { createDom( "li", { - className: "jasmine-suite-detail jasmine-" + resultNode.result.status + className: "jasmine-suite-detail jasmine-" + resultNode.result.status, }, createDom("a", { href: specHref(resultNode.result) }, resultNode.result.description) ) @@ -454,7 +454,7 @@ jasmineRequire.HtmlReporter = function(j$) { "li", { className: "jasmine-" + resultNode.result.status, - id: "spec-" + resultNode.result.id + id: "spec-" + resultNode.result.id, }, createDom("a", { href: specHref(resultNode.result) }, specDescription) ) @@ -477,7 +477,7 @@ jasmineRequire.HtmlReporter = function(j$) { createDom("input", { className: "jasmine-fail-fast", id: "jasmine-fail-fast", - type: "checkbox" + type: "checkbox", }), createDom( "label", @@ -491,7 +491,7 @@ jasmineRequire.HtmlReporter = function(j$) { createDom("input", { className: "jasmine-throw", id: "jasmine-throw-failures", - type: "checkbox" + type: "checkbox", }), createDom( "label", @@ -505,7 +505,7 @@ jasmineRequire.HtmlReporter = function(j$) { createDom("input", { className: "jasmine-random", id: "jasmine-random-order", - type: "checkbox" + type: "checkbox", }), createDom( "label", @@ -519,7 +519,7 @@ jasmineRequire.HtmlReporter = function(j$) { createDom("input", { className: "jasmine-disabled", id: "jasmine-hide-disabled", - type: "checkbox" + type: "checkbox", }), createDom( "label", @@ -608,7 +608,7 @@ jasmineRequire.HtmlReporter = function(j$) { message: warning, stack: result.deprecationWarnings[i].stack, runnableName: result.fullName, - runnableType: runnableType + runnableType: runnableType, }) } } diff --git a/ui/plugins/ui/jasmine/jasmine.js b/ui/plugins/ui/jasmine/jasmine.js index 470b7503..5bbcadfc 100644 --- a/ui/plugins/ui/jasmine/jasmine.js +++ b/ui/plugins/ui/jasmine/jasmine.js @@ -152,7 +152,7 @@ getJasmineRequireObj().requireMatchers = function(jRequire, j$) { "toMatch", "toThrow", "toThrowError", - "toThrowMatching" + "toThrowMatching", ], matchers = {} @@ -212,7 +212,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { set: function(newValue) { j$.util.validateTimeout(newValue, "jasmine.DEFAULT_TIMEOUT_INTERVAL") DEFAULT_TIMEOUT_INTERVAL = newValue - } + }, }) j$.getGlobal = function() { @@ -763,7 +763,7 @@ getJasmineRequireObj().Spec = function(j$) { pendingReason: "", duration: null, properties: null, - debugLogs: null + debugLogs: null, } this.reportedDone = false @@ -810,7 +810,7 @@ getJasmineRequireObj().Spec = function(j$) { fn: (done) => { this.timer.start() this.onStart(this, done) - } + }, } const complete = { @@ -827,7 +827,7 @@ getJasmineRequireObj().Spec = function(j$) { this.resultCallback(this.result, done) }, - type: "specCleanup" + type: "specCleanup", } const fns = this.beforeAndAfterFns() @@ -857,7 +857,7 @@ getJasmineRequireObj().Spec = function(j$) { } }, userContext: this.userContext(), - runnableName: this.getFullName.bind(this) + runnableName: this.getFullName.bind(this), } if (this.markedPending || excluded === true) { @@ -881,7 +881,7 @@ getJasmineRequireObj().Spec = function(j$) { pendingReason: this.excludeMessage, duration: null, properties: null, - debugLogs: null + debugLogs: null, } this.markedPending = this.markedExcluding this.reportedDone = false @@ -904,7 +904,7 @@ getJasmineRequireObj().Spec = function(j$) { passed: false, expected: "", actual: "", - error: e + error: e, }, true ) @@ -983,7 +983,7 @@ getJasmineRequireObj().Spec = function(j$) { */ this.result.debugLogs.push({ message: msg, - timestamp: this.timer.elapsed() + timestamp: this.timer.elapsed(), }) } @@ -1035,12 +1035,12 @@ getJasmineRequireObj().Spec = function(j$) { * @returns {string} * @since 2.0.0 */ - getFullName: this.getFullName.bind(this) + getFullName: this.getFullName.bind(this), } } return this.metadata_ - } + }, }) return Spec @@ -1132,7 +1132,7 @@ getJasmineRequireObj().Env = function(j$) { const r = runner.currentRunable() return r ? r.id : null }, - globalErrors + globalErrors, }) let reporter @@ -1236,7 +1236,7 @@ getJasmineRequireObj().Env = function(j$) { * @type Boolean * @default false */ - verboseDeprecations: false + verboseDeprecations: false, } if (!options.suppressLoadErrors) { @@ -1248,7 +1248,7 @@ getJasmineRequireObj().Env = function(j$) { message: message, stack: err && err.stack, filename: filename, - lineno: lineno + lineno: lineno, }) }) } @@ -1267,7 +1267,7 @@ getJasmineRequireObj().Env = function(j$) { "hideDisabled", "stopOnSpecFailure", "stopSpecOnExpectationFailure", - "autoCleanClosures" + "autoCleanClosures", ] booleanProps.forEach(function(prop) { @@ -1337,7 +1337,7 @@ getJasmineRequireObj().Env = function(j$) { matchersUtil: runableResources.makeMatchersUtil(), customMatchers: runableResources.customMatchers(), actual: actual, - addExpectationResult: addExpectationResult + addExpectationResult: addExpectationResult, }) function addExpectationResult(passed, result) { @@ -1356,7 +1356,7 @@ getJasmineRequireObj().Env = function(j$) { passed: false, matcherName: "", expected: "", - actual: "" + actual: "", }) routeLateFailure(result) } @@ -1413,7 +1413,7 @@ getJasmineRequireObj().Env = function(j$) { matchersUtil: runableResources.makeMatchersUtil(), customAsyncMatchers: runableResources.customAsyncMatchers(), actual: actual, - addExpectationResult: addExpectationResult + addExpectationResult: addExpectationResult, }) function addExpectationResult(passed, result) { @@ -1455,7 +1455,7 @@ getJasmineRequireObj().Env = function(j$) { options.clearStack = options.clearStack || clearStack options.timeout = { setTimeout: realSetTimeout, - clearTimeout: realClearTimeout + clearTimeout: realClearTimeout, } options.fail = self.fail options.globalErrors = globalErrors @@ -1476,7 +1476,7 @@ getJasmineRequireObj().Env = function(j$) { onLateError: recordLateError, specResultCallback, specStarted, - queueRunnerFactory + queueRunnerFactory, }) topSuite = suiteBuilder.topSuite const deprecator = new j$.Deprecator(topSuite) @@ -1563,7 +1563,7 @@ getJasmineRequireObj().Env = function(j$) { * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. * @see async */ - "specDone" + "specDone", ], function(options) { options.SkipPolicy = j$.NeverSkipPolicy @@ -1580,7 +1580,7 @@ getJasmineRequireObj().Env = function(j$) { reporter, queueRunnerFactory, getConfig: () => config, - reportSpecDone + reportSpecDone, }) /** @@ -1709,7 +1709,7 @@ getJasmineRequireObj().Env = function(j$) { expected: "", actual: "", message, - error: null + error: null, }) } @@ -1908,7 +1908,7 @@ getJasmineRequireObj().Env = function(j$) { expected: "", actual: "", message: message, - error: error && error.message ? error : null + error: error && error.message ? error : null, }) if (config.stopSpecOnExpectationFailure) { @@ -2342,7 +2342,7 @@ getJasmineRequireObj().ObjectContaining = function(j$) { if (!j$.isObject_(other)) { return { self: this.jasmineToString(pp), - other: other + other: other, } } @@ -2355,7 +2355,7 @@ getJasmineRequireObj().ObjectContaining = function(j$) { return { self: this.sample, - other: filteredOther + other: filteredOther, } } @@ -2485,7 +2485,7 @@ getJasmineRequireObj().buildExpectationResult = function(j$) { matcherName: options.matcherName, message: message(), stack: options.omitStackTrace ? "" : stack(), - passed: options.passed + passed: options.passed, } if (!result.passed) { @@ -2785,13 +2785,13 @@ getJasmineRequireObj().Clock = function() { setTimeout: global.setTimeout, clearTimeout: global.clearTimeout, setInterval: global.setInterval, - clearInterval: global.clearInterval + clearInterval: global.clearInterval, } const fakeTimingFunctions = { setTimeout: setTimeout, clearTimeout: clearTimeout, setInterval: setInterval, - clearInterval: clearInterval + clearInterval: clearInterval, } let installed = false let delayedFunctionScheduler @@ -3045,7 +3045,7 @@ getJasmineRequireObj().DelayedFunctionScheduler = function(j$) { recurring: recurring, params: params, timeoutKey: timeoutKey, - millis: millis + millis: millis, } if (runAtMillis in this.scheduledFunctions_) { @@ -3257,7 +3257,7 @@ getJasmineRequireObj().Deprecator = function(j$) { runnable.addDeprecationWarning({ message: deprecation, - omitStackTrace: options.omitStackTrace || false + omitStackTrace: options.omitStackTrace || false, }) } @@ -3271,7 +3271,7 @@ getJasmineRequireObj().errors = function() { ExpectationFailed.prototype.constructor = ExpectationFailed return { - ExpectationFailed: ExpectationFailed + ExpectationFailed: ExpectationFailed, } } @@ -3286,7 +3286,7 @@ getJasmineRequireObj().ExceptionFormatter = function(j$) { "lineNumber", "column", "description", - "jasmineMessage" + "jasmineMessage", ] function ExceptionFormatter(options) { @@ -3321,7 +3321,7 @@ getJasmineRequireObj().ExceptionFormatter = function(j$) { } const lines = this.stack_(error, { - messageHandling: omitMessage ? "omit" : undefined + messageHandling: omitMessage ? "omit" : undefined, }) return lines.join("\n") } @@ -3345,7 +3345,7 @@ getJasmineRequireObj().ExceptionFormatter = function(j$) { if (error.cause) { const substack = this.stack_(error.cause, { - messageHandling: "require" + messageHandling: "require", }) substack[0] = "Caused by: " + substack[0] lines = lines.concat(substack) @@ -3434,7 +3434,7 @@ getJasmineRequireObj().Expectation = function(j$) { Object.defineProperty(Expectation.prototype, "not", { get: function() { return addFilter(this, syncNegatingFilter) - } + }, }) /** @@ -3503,7 +3503,7 @@ getJasmineRequireObj().Expectation = function(j$) { Object.defineProperty(AsyncExpectation.prototype, "not", { get: function() { return addFilter(this, asyncNegatingFilter) - } + }, }) /** @@ -3521,7 +3521,7 @@ getJasmineRequireObj().Expectation = function(j$) { Object.defineProperty(AsyncExpectation.prototype, "already", { get: function() { return addFilter(this, expectSettledPromiseFilter) - } + }, }) function wrapSyncCompare(name, matcherFactory) { @@ -3584,7 +3584,7 @@ getJasmineRequireObj().Expectation = function(j$) { return matcher.negativeCompare || defaultNegativeCompare }, - buildFailureMessage: negatedFailureMessage + buildFailureMessage: negatedFailureMessage, } const asyncNegatingFilter = { @@ -3595,7 +3595,7 @@ getJasmineRequireObj().Expectation = function(j$) { return matcher.negativeCompare || defaultNegativeCompare }, - buildFailureMessage: negatedFailureMessage + buildFailureMessage: negatedFailureMessage, } const expectSettledPromiseFilter = { @@ -3609,14 +3609,14 @@ getJasmineRequireObj().Expectation = function(j$) { pass: false, message: "Expected a promise to be settled (via " + - "expectAsync(...).already) but it was pending." + "expectAsync(...).already) but it was pending.", } } else { return matcher.compare.apply(null, matcherArgs) } }) } - } + }, } function ContextAddingFilter(message) { @@ -3647,7 +3647,7 @@ getJasmineRequireObj().Expectation = function(j$) { }, addAsyncCoreMatchers: function(matchers) { addCoreMatchers(AsyncExpectation.prototype, matchers, wrapAsyncCompare) - } + }, } } @@ -3686,7 +3686,7 @@ getJasmineRequireObj().ExpectationFilterChain = function() { if (this.filter_ && this.filter_[fname]) { return { found: true, - result: this.filter_[fname].apply(this.filter_, args) + result: this.filter_[fname].apply(this.filter_, args), } } @@ -3699,7 +3699,7 @@ getJasmineRequireObj().ExpectationFilterChain = function() { getJasmineRequireObj().Expector = function(j$) { function Expector(options) { this.matchersUtil = options.matchersUtil || { - buildFailureMessage: function() {} + buildFailureMessage: function() {}, } this.actual = options.actual this.addExpectationResult = options.addExpectationResult || function() {} @@ -3772,7 +3772,7 @@ getJasmineRequireObj().Expector = function(j$) { error: errorForStack ? undefined : result.error, errorForStack: errorForStack || undefined, actual: this.actual, - expected: this.expected // TODO: this may need to be arrayified/sliced + expected: this.expected, // TODO: this may need to be arrayified/sliced }) } @@ -3960,7 +3960,7 @@ getJasmineRequireObj().toBePending = function(j$) { return { pass: false } } ) - } + }, } } } @@ -3991,7 +3991,7 @@ getJasmineRequireObj().toBeRejected = function(j$) { return { pass: true } } ) - } + }, } } } @@ -4029,25 +4029,25 @@ getJasmineRequireObj().toBeRejectedWith = function(j$) { function() { return { pass: false, - message: prefix(false) + " but it was resolved." + message: prefix(false) + " but it was resolved.", } }, function(actualValue) { if (matchersUtil.equals(actualValue, expectedValue)) { return { pass: true, - message: prefix(true) + "." + message: prefix(true) + ".", } } else { return { pass: false, message: - prefix(false) + " but it was rejected with " + matchersUtil.pp(actualValue) + "." + prefix(false) + " but it was rejected with " + matchersUtil.pp(actualValue) + ".", } } } ) - } + }, } } } @@ -4081,14 +4081,14 @@ getJasmineRequireObj().toBeRejectedWithError = function(j$) { function() { return { pass: false, - message: "Expected a promise to be rejected but it was resolved." + message: "Expected a promise to be rejected but it was resolved.", } }, function(actualValue) { return matchError(actualValue, expected, matchersUtil) } ) - } + }, } } @@ -4117,14 +4117,14 @@ getJasmineRequireObj().toBeRejectedWithError = function(j$) { function pass(expected) { return { pass: true, - message: "Expected a promise not to be rejected with " + expected.printValue + ", but it was." + message: "Expected a promise not to be rejected with " + expected.printValue + ", but it was.", } } function fail(expected, message) { return { pass: false, - message: "Expected a promise to be rejected with " + expected.printValue + " but it was " + message + "." + message: "Expected a promise to be rejected with " + expected.printValue + " but it was " + message + ".", } } @@ -4142,7 +4142,7 @@ getJasmineRequireObj().toBeRejectedWithError = function(j$) { return { error: error, message: message, - printValue: j$.fnNameFor(error) + (typeof message === "undefined" ? "" : ": " + matchersUtil.pp(message)) + printValue: j$.fnNameFor(error) + (typeof message === "undefined" ? "" : ": " + matchersUtil.pp(message)), } } @@ -4181,11 +4181,11 @@ getJasmineRequireObj().toBeResolved = function(j$) { "Expected a promise to be resolved but it was " + "rejected with " + matchersUtil.pp(e) + - "." + ".", } } ) - } + }, } } } @@ -4224,23 +4224,24 @@ getJasmineRequireObj().toBeResolvedTo = function(j$) { if (matchersUtil.equals(actualValue, expectedValue)) { return { pass: true, - message: prefix(true) + "." + message: prefix(true) + ".", } } else { return { pass: false, - message: prefix(false) + " but it was resolved to " + matchersUtil.pp(actualValue) + "." + message: + prefix(false) + " but it was resolved to " + matchersUtil.pp(actualValue) + ".", } } }, function(e) { return { pass: false, - message: prefix(false) + " but it was rejected with " + matchersUtil.pp(e) + "." + message: prefix(false) + " but it was rejected with " + matchersUtil.pp(e) + ".", } } ) - } + }, } } } @@ -4718,11 +4719,11 @@ getJasmineRequireObj().MatchersUtil = function(j$) { // For both sets, check they are all contained in the other set const setPairs = [ [valuesA, valuesB], - [valuesB, valuesA] + [valuesB, valuesA], ] const stackPairs = [ [aStack, bStack], - [bStack, aStack] + [bStack, aStack], ] for (let i = 0; result && i < setPairs.length; i++) { const baseValues = setPairs[i][0] @@ -5032,9 +5033,9 @@ getJasmineRequireObj().nothing = function() { return { compare: function() { return { - pass: true + pass: true, } - } + }, } } @@ -5048,7 +5049,7 @@ getJasmineRequireObj().NullDiffBuilder = function(j$) { block() }, setRoots: function() {}, - recordMismatch: function() {} + recordMismatch: function() {}, } } } @@ -5106,7 +5107,7 @@ getJasmineRequireObj().requireAsyncMatchers = function(jRequire, j$) { "toBeRejected", "toBeResolvedTo", "toBeRejectedWith", - "toBeRejectedWithError" + "toBeRejectedWithError", ], matchers = {} @@ -5133,7 +5134,7 @@ getJasmineRequireObj().toBe = function(j$) { return { compare: function(actual, expected) { const result = { - pass: actual === expected + pass: actual === expected, } if (typeof expected === "object") { @@ -5141,7 +5142,7 @@ getJasmineRequireObj().toBe = function(j$) { } return result - } + }, } } @@ -5181,7 +5182,7 @@ getJasmineRequireObj().toBeCloseTo = function() { // regardless of the precision. if (expected === Infinity || expected === -Infinity) { return { - pass: actual === expected + pass: actual === expected, } } @@ -5190,9 +5191,9 @@ getJasmineRequireObj().toBeCloseTo = function() { const maxDelta = Math.pow(10, -precision) / 2 return { - pass: Math.round(delta * pow) <= maxDelta * pow + pass: Math.round(delta * pow) <= maxDelta * pow, } - } + }, } } @@ -5212,9 +5213,9 @@ getJasmineRequireObj().toBeDefined = function() { return { compare: function(actual) { return { - pass: void 0 !== actual + pass: void 0 !== actual, } - } + }, } } @@ -5234,9 +5235,9 @@ getJasmineRequireObj().toBeFalse = function() { return { compare: function(actual) { return { - pass: actual === false + pass: actual === false, } - } + }, } } @@ -5256,9 +5257,9 @@ getJasmineRequireObj().toBeFalsy = function() { return { compare: function(actual) { return { - pass: !actual + pass: !actual, } - } + }, } } @@ -5279,9 +5280,9 @@ getJasmineRequireObj().toBeGreaterThan = function() { return { compare: function(actual, expected) { return { - pass: actual > expected + pass: actual > expected, } - } + }, } } @@ -5302,9 +5303,9 @@ getJasmineRequireObj().toBeGreaterThanOrEqual = function() { return { compare: function(actual, expected) { return { - pass: actual >= expected + pass: actual >= expected, } - } + }, } } @@ -5344,15 +5345,15 @@ getJasmineRequireObj().toBeInstanceOf = function(j$) { if (pass) { return { pass: true, - message: "Expected instance of " + actualType + " not to be an instance of " + expectedType + message: "Expected instance of " + actualType + " not to be an instance of " + expectedType, } } else { return { pass: false, - message: "Expected instance of " + actualType + " to be an instance of " + expectedType + message: "Expected instance of " + actualType + " to be an instance of " + expectedType, } } - } + }, } } @@ -5373,9 +5374,9 @@ getJasmineRequireObj().toBeLessThan = function() { return { compare: function(actual, expected) { return { - pass: actual < expected + pass: actual < expected, } - } + }, } } @@ -5396,9 +5397,9 @@ getJasmineRequireObj().toBeLessThanOrEqual = function() { return { compare: function(actual, expected) { return { - pass: actual <= expected + pass: actual <= expected, } - } + }, } } @@ -5418,7 +5419,7 @@ getJasmineRequireObj().toBeNaN = function(j$) { return { compare: function(actual) { const result = { - pass: actual !== actual + pass: actual !== actual, } if (result.pass) { @@ -5430,7 +5431,7 @@ getJasmineRequireObj().toBeNaN = function(j$) { } return result - } + }, } } @@ -5450,7 +5451,7 @@ getJasmineRequireObj().toBeNegativeInfinity = function(j$) { return { compare: function(actual) { const result = { - pass: actual === Number.NEGATIVE_INFINITY + pass: actual === Number.NEGATIVE_INFINITY, } if (result.pass) { @@ -5462,7 +5463,7 @@ getJasmineRequireObj().toBeNegativeInfinity = function(j$) { } return result - } + }, } } @@ -5482,9 +5483,9 @@ getJasmineRequireObj().toBeNull = function() { return { compare: function(actual) { return { - pass: actual === null + pass: actual === null, } - } + }, } } @@ -5504,7 +5505,7 @@ getJasmineRequireObj().toBePositiveInfinity = function(j$) { return { compare: function(actual) { const result = { - pass: actual === Number.POSITIVE_INFINITY + pass: actual === Number.POSITIVE_INFINITY, } if (result.pass) { @@ -5516,7 +5517,7 @@ getJasmineRequireObj().toBePositiveInfinity = function(j$) { } return result - } + }, } } @@ -5536,9 +5537,9 @@ getJasmineRequireObj().toBeTrue = function() { return { compare: function(actual) { return { - pass: actual === true + pass: actual === true, } - } + }, } } @@ -5558,9 +5559,9 @@ getJasmineRequireObj().toBeTruthy = function() { return { compare: function(actual) { return { - pass: !!actual + pass: !!actual, } - } + }, } } @@ -5580,9 +5581,9 @@ getJasmineRequireObj().toBeUndefined = function() { return { compare: function(actual) { return { - pass: void 0 === actual + pass: void 0 === actual, } - } + }, } } @@ -5604,9 +5605,9 @@ getJasmineRequireObj().toContain = function() { return { compare: function(actual, expected) { return { - pass: matchersUtil.contains(actual, expected) + pass: matchersUtil.contains(actual, expected), } - } + }, } } @@ -5627,7 +5628,7 @@ getJasmineRequireObj().toEqual = function(j$) { return { compare: function(actual, expected) { const result = { - pass: false + pass: false, }, diffBuilder = new j$.DiffBuilder({ prettyPrinter: matchersUtil.pp }) @@ -5637,7 +5638,7 @@ getJasmineRequireObj().toEqual = function(j$) { result.message = diffBuilder.getMessage() return result - } + }, } } @@ -5676,7 +5677,7 @@ getJasmineRequireObj().toHaveBeenCalled = function(j$) { : "Expected spy " + actual.and.identity + " to have been called." return result - } + }, } } @@ -5759,7 +5760,7 @@ getJasmineRequireObj().toHaveBeenCalledBefore = function(j$) { } return result - } + }, } } @@ -5808,7 +5809,7 @@ getJasmineRequireObj().toHaveBeenCalledOnceWith = function(j$) { "\n" + "But the actual call was:\n" + prettyPrintedCalls.join(",\n") + - ".\n\n" + ".\n\n", } } @@ -5846,9 +5847,9 @@ getJasmineRequireObj().toHaveBeenCalledOnceWith = function(j$) { " " + util.pp(expectedArgs) + "\n" + - butString() + butString(), } - } + }, } } @@ -5903,7 +5904,7 @@ getJasmineRequireObj().toHaveBeenCalledTimes = function(j$) { calls + " times." return result - } + }, } } @@ -5992,7 +5993,7 @@ getJasmineRequireObj().toHaveBeenCalledWith = function(j$) { } return result - } + }, } } @@ -6019,9 +6020,9 @@ getJasmineRequireObj().toHaveClass = function(j$) { } return { - pass: actual.classList.contains(expected) + pass: actual.classList.contains(expected), } - } + }, } } @@ -6047,7 +6048,7 @@ getJasmineRequireObj().toHaveSize = function(j$) { return { compare: function(actual, expected) { const result = { - pass: false + pass: false, } if (j$.isA_("WeakSet", actual) || j$.isWeakMap(actual) || j$.isDataView(actual)) { @@ -6063,7 +6064,7 @@ getJasmineRequireObj().toHaveSize = function(j$) { } return result - } + }, } } @@ -6132,7 +6133,7 @@ getJasmineRequireObj().toHaveSpyInteractions = function(j$) { result.message = resultMessage return result - } + }, } } @@ -6162,9 +6163,9 @@ getJasmineRequireObj().toMatch = function(j$) { const regexp = new RegExp(expected) return { - pass: regexp.test(actual) + pass: regexp.test(actual), } - } + }, } } @@ -6234,7 +6235,7 @@ getJasmineRequireObj().toThrow = function(j$) { } return result - } + }, } } @@ -6286,7 +6287,7 @@ getJasmineRequireObj().toThrowError = function(j$) { } return errorMatcher.match(thrown) - } + }, } function getMatcher() { @@ -6317,7 +6318,7 @@ getJasmineRequireObj().toThrowError = function(j$) { return { match: function(error) { return pass("Expected function not to throw an Error, but it threw " + j$.fnNameFor(error) + ".") - } + }, } } @@ -6386,7 +6387,7 @@ getJasmineRequireObj().toThrowError = function(j$) { ) }) } - } + }, } } @@ -6408,14 +6409,14 @@ getJasmineRequireObj().toThrowError = function(j$) { function pass(message) { return { pass: true, - message: message + message: message, } } function fail(message) { return { pass: false, - message: message + message: message, } } @@ -6469,7 +6470,7 @@ getJasmineRequireObj().toThrowMatching = function(j$) { ) }) } - } + }, } function thrownDescription(thrown) { @@ -6484,14 +6485,14 @@ getJasmineRequireObj().toThrowMatching = function(j$) { function pass(message) { return { pass: true, - message: message + message: message, } } function fail(message) { return { pass: false, - message: message + message: message, } } @@ -6999,12 +7000,12 @@ getJasmineRequireObj().QueueRunner = function(j$) { this.userContext = attrs.userContext || new j$.UserContext() this.timeout = attrs.timeout || { setTimeout: setTimeout, - clearTimeout: clearTimeout + clearTimeout: clearTimeout, } this.fail = attrs.fail || emptyFn this.globalErrors = attrs.globalErrors || { pushListener: emptyFn, - popListener: emptyFn + popListener: emptyFn, } const SkipPolicy = attrs.SkipPolicy || j$.NeverSkipPolicy @@ -7277,7 +7278,7 @@ getJasmineRequireObj().ReportDispatcher = function(j$) { "An asynchronous reporter callback called its 'done' callback " + "more than once." ) ) - } + }, }) }) } @@ -7293,13 +7294,13 @@ getJasmineRequireObj().ReportDispatcher = function(j$) { fns.push({ fn: function() { return fn.apply(reporter, thisArgs) - } + }, }) } else { fns.push({ fn: function(done) { return fn.apply(reporter, thisArgs.concat([done])) - } + }, }) } } @@ -7603,13 +7604,13 @@ getJasmineRequireObj().interface = function(jasmine, env) { }, jsApiReporter: new jasmine.JsApiReporter({ - timer: new jasmine.Timer() + timer: new jasmine.Timer(), }), /** * @namespace jasmine */ - jasmine: jasmine + jasmine: jasmine, } /** @@ -7760,7 +7761,7 @@ getJasmineRequireObj().RunableResources = function(j$) { this.spyRegistry = new j$.SpyRegistry({ currentSpies: () => this.spies(), - createSpy: (name, originalFn) => this.spyFactory.createSpy(name, originalFn) + createSpy: (name, originalFn) => this.spyFactory.createSpy(name, originalFn), }) } @@ -7772,7 +7773,7 @@ getJasmineRequireObj().RunableResources = function(j$) { customSpyStrategies: {}, customObjectFormatters: [], defaultSpyStrategy: undefined, - spies: [] + spies: [], }) const parentRes = this.byRunableId_[parentId] @@ -7784,7 +7785,7 @@ getJasmineRequireObj().RunableResources = function(j$) { "customMatchers", "customAsyncMatchers", "customObjectFormatters", - "customSpyStrategies" + "customSpyStrategies", ] for (const k of toClone) { @@ -7865,7 +7866,7 @@ getJasmineRequireObj().RunableResources = function(j$) { if (this.getCurrentRunableId_()) { return new j$.MatchersUtil({ customTesters: this.customEqualityTesters(), - pp: this.makePrettyPrinter() + pp: this.makePrettyPrinter(), }) } else { return new j$.MatchersUtil({ pp: j$.basicPrettyPrinter_ }) @@ -7936,7 +7937,7 @@ getJasmineRequireObj().Runner = function(j$) { const order = new j$.Order({ random: config.random, - seed: j$.isNumber_(config.seed) ? config.seed + "" : config.seed + seed: j$.isNumber_(config.seed) ? config.seed + "" : config.seed, }) const processor = new j$.TreeProcessor({ @@ -7990,7 +7991,7 @@ getJasmineRequireObj().Runner = function(j$) { }, excludeNode: function(spec) { return !config.specFilter(spec) - } + }, }) if (!processor.processTree().valid) { @@ -8016,7 +8017,7 @@ getJasmineRequireObj().Runner = function(j$) { */ await this.reporter_.jasmineStarted({ totalSpecsDefined, - order: order + order: order, }) this.currentlyExecutingSuites_.push(this.topSuite_) @@ -8059,7 +8060,7 @@ getJasmineRequireObj().Runner = function(j$) { incompleteReason: incompleteReason, order: order, failedExpectations: this.topSuite_.result.failedExpectations, - deprecationWarnings: this.topSuite_.result.deprecationWarnings + deprecationWarnings: this.topSuite_.result.deprecationWarnings, } this.topSuite_.reportedDone = true await this.reporter_.jasmineDone(jasmineDoneInfo) @@ -8093,7 +8094,7 @@ getJasmineRequireObj().Runner = function(j$) { message: "Not run because a beforeAll function failed. The " + "beforeAll failure will be reported on the suite that " + - "caused it." + "caused it.", }, true ) @@ -8172,7 +8173,7 @@ getJasmineRequireObj().Spy = function(j$) { const callData = { object: context, invocationOrder: nextOrder(), - args: Array.prototype.slice.apply(args) + args: Array.prototype.slice.apply(args), } callTracker.track(callData) @@ -8194,7 +8195,7 @@ getJasmineRequireObj().Spy = function(j$) { getSpy: function() { return wrapper }, - customStrategies: customStrategies + customStrategies: customStrategies, }, matchersUtil ), @@ -8339,7 +8340,7 @@ getJasmineRequireObj().Spy = function(j$) { strategy = this.strategyFactory() this.strategies.push({ args: args, - strategy: strategy + strategy: strategy, }) } @@ -8368,7 +8369,7 @@ getJasmineRequireObj().SpyFactory = function(j$) { return j$.Spy(name, getMatchersUtil(), { originalFn, customStrategies: getCustomStrategies(), - defaultStrategyFn: getDefaultStrategyFn() + defaultStrategyFn: getDefaultStrategyFn(), }) } @@ -8396,7 +8397,7 @@ getJasmineRequireObj().SpyFactory = function(j$) { const descriptor = { enumerable: true, get: this.createSpy(baseName + "." + properties[i][0] + ".get"), - set: this.createSpy(baseName + "." + properties[i][0] + ".set") + set: this.createSpy(baseName + "." + properties[i][0] + ".set"), } if (properties[i].length > 1) { descriptor.get.and.returnValue(properties[i][1]) @@ -8496,7 +8497,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) { } currentSpies().push({ - restoreObjectToOriginalState: restoreStrategy + restoreObjectToOriginalState: restoreStrategy, }) obj[methodName] = spiedMethod @@ -8554,7 +8555,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) { } currentSpies().push({ - restoreObjectToOriginalState: restoreStrategy + restoreObjectToOriginalState: restoreStrategy, }) descriptor[accessType] = spy @@ -8837,7 +8838,7 @@ getJasmineRequireObj().StackTrace = function(j$) { re: /^\s*at ([^\)]+) \(([^\)]+)\)$/, fnIx: 1, fileLineColIx: 2, - style: "v8" + style: "v8", }, // NodeJS alternate form, often mixed in with the Chrome style @@ -8851,8 +8852,8 @@ getJasmineRequireObj().StackTrace = function(j$) { re: /^(?:(([^@\s]+)@)|@)?([^\s]+)$/, fnIx: 2, fileLineColIx: 3, - style: "webkit" - } + style: "webkit", + }, ] // regexes should capture the function name (if any) as group 1 @@ -8876,7 +8877,7 @@ getJasmineRequireObj().StackTrace = function(j$) { raw: line, file: fileLineColMatch[1], line: parseInt(fileLineColMatch[2], 10), - func: overallMatch[pattern.fnIx] + func: overallMatch[pattern.fnIx], } }) @@ -8885,7 +8886,7 @@ getJasmineRequireObj().StackTrace = function(j$) { return { style: style, - frames: frames + frames: frames, } } @@ -8905,7 +8906,7 @@ getJasmineRequireObj().StackTrace = function(j$) { if (len > 0) { return { message: stackLines.slice(0, len).join("\n"), - remainder: stackLines.slice(len) + remainder: stackLines.slice(len), } } } @@ -9049,7 +9050,7 @@ getJasmineRequireObj().Suite = function(j$) { failedExpectations: [], deprecationWarnings: [], duration: null, - properties: null + properties: null, } this.markedPending = this.markedExcluding this.children.forEach(function(child) { @@ -9105,7 +9106,7 @@ getJasmineRequireObj().Suite = function(j$) { passed: false, expected: "", actual: "", - error: arguments[0] + error: arguments[0], } const failedExpectation = j$.buildExpectationResult(data) @@ -9176,7 +9177,7 @@ getJasmineRequireObj().Suite = function(j$) { } return this.metadata_ - } + }, }) /** @@ -9233,7 +9234,7 @@ getJasmineRequireObj().Suite = function(j$) { Object.defineProperty(SuiteMetadata.prototype, "children", { get: function() { return this.suite_.children.map((child) => child.metadata) - } + }, }) function isFailure(args) { @@ -9346,7 +9347,7 @@ getJasmineRequireObj().SuiteBuilder = function(j$) { this.currentDeclarationSuite_.beforeEach({ fn: beforeEachFunction, - timeout: timeout || 0 + timeout: timeout || 0, }) } @@ -9359,7 +9360,7 @@ getJasmineRequireObj().SuiteBuilder = function(j$) { this.currentDeclarationSuite_.beforeAll({ fn: beforeAllFunction, - timeout: timeout || 0 + timeout: timeout || 0, }) } @@ -9373,7 +9374,7 @@ getJasmineRequireObj().SuiteBuilder = function(j$) { afterEachFunction.isCleanup = true this.currentDeclarationSuite_.afterEach({ fn: afterEachFunction, - timeout: timeout || 0 + timeout: timeout || 0, }) } @@ -9386,7 +9387,7 @@ getJasmineRequireObj().SuiteBuilder = function(j$) { this.currentDeclarationSuite_.afterAll({ fn: afterAllFunction, - timeout: timeout || 0 + timeout: timeout || 0, }) } @@ -9415,7 +9416,7 @@ getJasmineRequireObj().SuiteBuilder = function(j$) { asyncExpectationFactory: this.suiteAsyncExpectationFactory_, throwOnExpectationFailure: config.stopSpecOnExpectationFailure, autoCleanClosures: config.autoCleanClosures, - onLateError: this.onLateError_ + onLateError: this.onLateError_, }) } @@ -9462,11 +9463,11 @@ getJasmineRequireObj().SuiteBuilder = function(j$) { }, queueableFn: { fn: fn, - timeout: timeout || 0 + timeout: timeout || 0, }, throwOnExpectationFailure: config.stopSpecOnExpectationFailure, autoCleanClosures: config.autoCleanClosures, - timer: new j$.Timer() + timer: new j$.Timer(), }) return spec } @@ -9523,7 +9524,7 @@ getJasmineRequireObj().SuiteBuilder = function(j$) { return { befores: befores.reverse(), - afters: afters + afters: afters, } } } @@ -9614,7 +9615,7 @@ getJasmineRequireObj().TreeProcessor = function() { tree.handleException.apply(tree, arguments) }, onComplete: resolve, - onMultipleDone: tree.onMultipleDone ? tree.onMultipleDone.bind(tree) : null + onMultipleDone: tree.onMultipleDone ? tree.onMultipleDone.bind(tree) : null, }) }) } @@ -9645,9 +9646,9 @@ getJasmineRequireObj().TreeProcessor = function() { owner: node, nodes: [node], min: startingMin(executableIndex), - max: startingMax(executableIndex) - } - ] + max: startingMax(executableIndex), + }, + ], } } else { let hasExecutableChild = false @@ -9670,7 +9671,7 @@ getJasmineRequireObj().TreeProcessor = function() { stats[node.id] = { excluded: parentExcluded, - willExecute: hasExecutableChild + willExecute: hasExecutableChild, } segmentChildren(node, orderedChildren, stats[node.id], executableIndex) @@ -9695,7 +9696,7 @@ getJasmineRequireObj().TreeProcessor = function() { owner: node, nodes: [], min: startingMin(executableIndex), - max: startingMax(executableIndex) + max: startingMax(executableIndex), }, result = [currentSegment], lastMax = defaultMax, @@ -9716,7 +9717,7 @@ getJasmineRequireObj().TreeProcessor = function() { owner: node, nodes: [], min: defaultMin, - max: defaultMax + max: defaultMax, } result.push(currentSegment) } @@ -9763,7 +9764,7 @@ getJasmineRequireObj().TreeProcessor = function() { const onStart = { fn: function(next) { nodeStart(node, next) - } + }, } queueRunnerFactory({ @@ -9779,15 +9780,15 @@ getJasmineRequireObj().TreeProcessor = function() { onException: function() { node.handleException.apply(node, arguments) }, - onMultipleDone: node.onMultipleDone ? node.onMultipleDone.bind(node) : null + onMultipleDone: node.onMultipleDone ? node.onMultipleDone.bind(node) : null, }) - } + }, } } else { return { fn: function(done) { node.execute(queueRunnerFactory, done, stats[node.id].excluded, failSpecWithNoExpectations) - } + }, } } } diff --git a/ui/plugins/ui/jasmineSpec.js b/ui/plugins/ui/jasmineSpec.js index a0d50fe2..9637e9ac 100644 --- a/ui/plugins/ui/jasmineSpec.js +++ b/ui/plugins/ui/jasmineSpec.js @@ -9,11 +9,11 @@ beforeEach(function() { return { compare: function(actual, expected) { return { - pass: expected.includes(actual) + pass: expected.includes(actual), } - } + }, } - } + }, }) }) describe("stable-diffusion-ui", function() { @@ -116,7 +116,7 @@ describe("stable-diffusion-ui", function() { value["foo"] = "bar" } return { value, done } - } + }, }) const gen2 = SD.Task.asGenerator({ generator: gen1, @@ -128,7 +128,7 @@ describe("stable-diffusion-ui", function() { value.test = 2 * value.test } return { value, done } - } + }, }) expect(await SD.Task.enqueue(gen2)).toEqual({ test: 32, foo: "bar" }) }) @@ -153,7 +153,7 @@ describe("stable-diffusion-ui", function() { expect(typeof missing).toBe("undefined") return { foo: "bar" } }, - dependencies: ["ctx", "missing", "one", "foo"] + dependencies: ["ctx", "missing", "one", "foo"], } ) const fooObj = cont.get("foo") @@ -180,7 +180,7 @@ describe("stable-diffusion-ui", function() { let res = await fetch("/render", { method: "POST", headers: { - "Content-Type": "application/json" + "Content-Type": "application/json", }, body: JSON.stringify({ prompt: "a photograph of an astronaut riding a horse", @@ -199,8 +199,8 @@ describe("stable-diffusion-ui", function() { show_only_filtered_image: true, output_format: "jpeg", - session_id: JASMINE_SESSION_ID - }) + session_id: JASMINE_SESSION_ID, + }), }) expect(res.ok).toBeTruthy() const renderRequest = await res.json() @@ -283,7 +283,7 @@ describe("stable-diffusion-ui", function() { show_only_filtered_image: false, //"use_face_correction": 'GFPGANv1.3', use_upscale: "RealESRGAN_x4plus", - session_id: JASMINE_SESSION_ID + session_id: JASMINE_SESSION_ID, }, function(event) { console.log(this, event) @@ -314,7 +314,7 @@ describe("stable-diffusion-ui", function() { height: 128, seed: SD.MAX_SEED_VALUE, num_inference_steps: 10, - session_id: JASMINE_SESSION_ID + session_id: JASMINE_SESSION_ID, }) expect(renderTask.status).toBe(SD.TaskStatus.init) @@ -326,7 +326,7 @@ describe("stable-diffusion-ui", function() { await renderTask.waitUntil({ state: SD.TaskStatus.processing, - callback: () => console.log("Waiting for render task to start...") + callback: () => console.log("Waiting for render task to start..."), }) expect(renderTask.status).toBe(SD.TaskStatus.processing) @@ -362,7 +362,7 @@ describe("stable-diffusion-ui", function() { show_only_filtered_image: false, //"use_face_correction": 'GFPGANv1.3', use_upscale: "RealESRGAN_x4plus", - session_id: JASMINE_SESSION_ID + session_id: JASMINE_SESSION_ID, }) await renderTask.enqueue(function(event) { console.log(this, event) diff --git a/ui/plugins/ui/merge.plugin.js b/ui/plugins/ui/merge.plugin.js index 1364d0a3..5ce97b2d 100644 --- a/ui/plugins/ui/merge.plugin.js +++ b/ui/plugins/ui/merge.plugin.js @@ -424,7 +424,7 @@ let res = await fetch("/model/merge", { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify(request) + body: JSON.stringify(request), }) const data = await res.json() addLogMessage(JSON.stringify(data)) @@ -445,6 +445,6 @@ hypernetworkModelField.innerHTML = "" await getModels() }) - } + }, }) })() diff --git a/ui/plugins/ui/modifiers-toggle.plugin.js b/ui/plugins/ui/modifiers-toggle.plugin.js index 0d026bd3..74b3666e 100644 --- a/ui/plugins/ui/modifiers-toggle.plugin.js +++ b/ui/plugins/ui/modifiers-toggle.plugin.js @@ -21,7 +21,7 @@ }) observer.observe(editorModifierTagsList, { - childList: true + childList: true, }) function ModifierToggle() { diff --git a/ui/plugins/ui/release-notes.plugin.js b/ui/plugins/ui/release-notes.plugin.js index 418a18d6..fdd37eb4 100644 --- a/ui/plugins/ui/release-notes.plugin.js +++ b/ui/plugins/ui/release-notes.plugin.js @@ -48,6 +48,6 @@ return marked.parse(releaseNotes) } - } + }, }) })() diff --git a/ui/plugins/ui/selftest.plugin.js b/ui/plugins/ui/selftest.plugin.js index 23eefdf1..b615852d 100644 --- a/ui/plugins/ui/selftest.plugin.js +++ b/ui/plugins/ui/selftest.plugin.js @@ -16,7 +16,7 @@ stopSpecOnExpectationFailure: "true", stopOnSpecFailure: "false", random: "false", - hideDisabled: "false" + hideDisabled: "false", } const optStr = Object.entries(options) .map(([key, val]) => `${key}=${val}`) From a46ff731d8b9d849c03a3acd393d8250a228d05e Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 28 Apr 2023 16:03:35 +0530 Subject: [PATCH 124/291] sdkit 1.0.82 - VAE slicing for pytorch 2.0, don't fail to hash files smaller than 3 MB --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 38545715..8ff1ffdb 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.81", + "sdkit": "1.0.82", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", From 07f52c38ef1ec87cf0b1a598d9c8fdefcf33a6ae Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 28 Apr 2023 16:35:30 +0530 Subject: [PATCH 125/291] sdkit 1.0.83 - formatting --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 8ff1ffdb..c8955e0a 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.82", + "sdkit": "1.0.83", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", From d18cefc519640c3cdafac8f1066c8e574e39a37d Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 28 Apr 2023 16:38:55 +0530 Subject: [PATCH 126/291] Formatting --- ui/easydiffusion/app.py | 101 +++++++++++++++++++-------- ui/easydiffusion/device_manager.py | 17 +++-- ui/easydiffusion/model_manager.py | 34 ++++++--- ui/easydiffusion/renderer.py | 44 ++++++++---- ui/easydiffusion/server.py | 38 ++++++---- ui/easydiffusion/task_manager.py | 24 ++++--- ui/easydiffusion/types.py | 3 +- ui/easydiffusion/utils/save_utils.py | 57 +++++++++------ 8 files changed, 213 insertions(+), 105 deletions(-) diff --git a/ui/easydiffusion/app.py b/ui/easydiffusion/app.py index 658de841..da755c21 100644 --- a/ui/easydiffusion/app.py +++ b/ui/easydiffusion/app.py @@ -1,17 +1,15 @@ +import json +import logging import os import socket import sys -import json import traceback -import logging -import shlex import urllib -from rich.logging import RichHandler - -from sdkit.utils import log as sdkit_log # hack, so we can overwrite the log config from easydiffusion import task_manager from easydiffusion.utils import log +from rich.logging import RichHandler +from sdkit.utils import log as sdkit_log # hack, so we can overwrite the log config # Remove all handlers associated with the root logger object. for handler in logging.root.handlers[:]: @@ -55,10 +53,34 @@ APP_CONFIG_DEFAULTS = { }, } -IMAGE_EXTENSIONS = [".png", ".apng", ".jpg", ".jpeg", ".jfif", ".pjpeg", ".pjp", ".jxl", ".gif", ".webp", ".avif", ".svg"] +IMAGE_EXTENSIONS = [ + ".png", + ".apng", + ".jpg", + ".jpeg", + ".jfif", + ".pjpeg", + ".pjp", + ".jxl", + ".gif", + ".webp", + ".avif", + ".svg", +] CUSTOM_MODIFIERS_DIR = os.path.abspath(os.path.join(SD_DIR, "..", "modifiers")) -CUSTOM_MODIFIERS_PORTRAIT_EXTENSIONS=[".portrait", "_portrait", " portrait", "-portrait"] -CUSTOM_MODIFIERS_LANDSCAPE_EXTENSIONS=[".landscape", "_landscape", " landscape", "-landscape"] +CUSTOM_MODIFIERS_PORTRAIT_EXTENSIONS = [ + ".portrait", + "_portrait", + " portrait", + "-portrait", +] +CUSTOM_MODIFIERS_LANDSCAPE_EXTENSIONS = [ + ".landscape", + "_landscape", + " landscape", + "-landscape", +] + def init(): os.makedirs(USER_UI_PLUGINS_DIR, exist_ok=True) @@ -84,7 +106,7 @@ def getConfig(default_val=APP_CONFIG_DEFAULTS): if os.getenv("SD_UI_BIND_IP") is not None: config["net"]["listen_to_network"] = os.getenv("SD_UI_BIND_IP") == "0.0.0.0" return config - except Exception as e: + except Exception: log.warn(traceback.format_exc()) return default_val @@ -97,6 +119,7 @@ def setConfig(config): except: log.error(traceback.format_exc()) + def save_to_config(ckpt_model_name, vae_model_name, hypernetwork_model_name, vram_usage_level): config = getConfig() if "model" not in config: @@ -191,11 +214,12 @@ def open_browser(): webbrowser.open(f"http://localhost:{port}") + def get_image_modifiers(): modifiers_json_path = os.path.join(SD_UI_DIR, "modifiers.json") modifier_categories = {} - original_category_order=[] + original_category_order = [] with open(modifiers_json_path, "r", encoding="utf-8") as f: modifiers_file = json.load(f) @@ -205,14 +229,14 @@ def get_image_modifiers(): # convert modifiers from a list of objects to a dict of dicts for category_item in modifiers_file: - category_name = category_item['category'] + category_name = category_item["category"] original_category_order.append(category_name) category = {} - for modifier_item in category_item['modifiers']: + for modifier_item in category_item["modifiers"]: modifier = {} - for preview_item in modifier_item['previews']: - modifier[preview_item['name']] = preview_item['path'] - category[modifier_item['modifier']] = modifier + for preview_item in modifier_item["previews"]: + modifier[preview_item["name"]] = preview_item["path"] + category[modifier_item["modifier"]] = modifier modifier_categories[category_name] = category def scan_directory(directory_path: str, category_name="Modifiers"): @@ -225,12 +249,27 @@ def get_image_modifiers(): modifier_name = entry.name[: -len(file_extension[0])] modifier_path = f"custom/{entry.path[len(CUSTOM_MODIFIERS_DIR) + 1:]}" # URL encode path segments - modifier_path = "/".join(map(lambda segment: urllib.parse.quote(segment), modifier_path.split("/"))) + modifier_path = "/".join( + map( + lambda segment: urllib.parse.quote(segment), + modifier_path.split("/"), + ) + ) is_portrait = True is_landscape = True - portrait_extension = list(filter(lambda e: modifier_name.lower().endswith(e), CUSTOM_MODIFIERS_PORTRAIT_EXTENSIONS)) - landscape_extension = list(filter(lambda e: modifier_name.lower().endswith(e), CUSTOM_MODIFIERS_LANDSCAPE_EXTENSIONS)) + portrait_extension = list( + filter( + lambda e: modifier_name.lower().endswith(e), + CUSTOM_MODIFIERS_PORTRAIT_EXTENSIONS, + ) + ) + landscape_extension = list( + filter( + lambda e: modifier_name.lower().endswith(e), + CUSTOM_MODIFIERS_LANDSCAPE_EXTENSIONS, + ) + ) if len(portrait_extension) > 0: is_landscape = False @@ -238,24 +277,24 @@ def get_image_modifiers(): elif len(landscape_extension) > 0: is_portrait = False modifier_name = modifier_name[: -len(landscape_extension[0])] - - if (category_name not in modifier_categories): + + if category_name not in modifier_categories: modifier_categories[category_name] = {} - + category = modifier_categories[category_name] - if (modifier_name not in category): + if modifier_name not in category: category[modifier_name] = {} - if (is_portrait or "portrait" not in category[modifier_name]): + if is_portrait or "portrait" not in category[modifier_name]: category[modifier_name]["portrait"] = modifier_path - - if (is_landscape or "landscape" not in category[modifier_name]): + + if is_landscape or "landscape" not in category[modifier_name]: category[modifier_name]["landscape"] = modifier_path elif entry.is_dir(): scan_directory( entry.path, - entry.name if directory_path==CUSTOM_MODIFIERS_DIR else f"{category_name}/{entry.name}", + entry.name if directory_path == CUSTOM_MODIFIERS_DIR else f"{category_name}/{entry.name}", ) scan_directory(CUSTOM_MODIFIERS_DIR) @@ -268,12 +307,12 @@ def get_image_modifiers(): # convert the modifiers back into a list of objects modifier_categories_list = [] for category_name in [*original_category_order, *custom_categories]: - category = { 'category': category_name, 'modifiers': [] } + category = {"category": category_name, "modifiers": []} for modifier_name in sorted(modifier_categories[category_name].keys(), key=str.casefold): - modifier = { 'modifier': modifier_name, 'previews': [] } + modifier = {"modifier": modifier_name, "previews": []} for preview_name, preview_path in modifier_categories[category_name][modifier_name].items(): - modifier['previews'].append({ 'name': preview_name, 'path': preview_path }) - category['modifiers'].append(modifier) + modifier["previews"].append({"name": preview_name, "path": preview_path}) + category["modifiers"].append(modifier) modifier_categories_list.append(category) return modifier_categories_list diff --git a/ui/easydiffusion/device_manager.py b/ui/easydiffusion/device_manager.py index 18069a82..59c07ea3 100644 --- a/ui/easydiffusion/device_manager.py +++ b/ui/easydiffusion/device_manager.py @@ -1,9 +1,9 @@ import os import platform -import torch -import traceback import re +import traceback +import torch from easydiffusion.utils import log """ @@ -98,8 +98,8 @@ def auto_pick_devices(currently_active_devices): continue mem_free, mem_total = torch.cuda.mem_get_info(device) - mem_free /= float(10**9) - mem_total /= float(10**9) + mem_free /= float(10 ** 9) + mem_total /= float(10 ** 9) device_name = torch.cuda.get_device_name(device) log.debug( f"{device} detected: {device_name} - Memory (free/total): {round(mem_free, 2)}Gb / {round(mem_total, 2)}Gb" @@ -118,7 +118,10 @@ def auto_pick_devices(currently_active_devices): # These already-running devices probably aren't terrible, since they were picked in the past. # Worst case, the user can restart the program and that'll get rid of them. devices = list( - filter((lambda x: x["mem_free"] > mem_free_threshold or x["device"] in currently_active_devices), devices) + filter( + (lambda x: x["mem_free"] > mem_free_threshold or x["device"] in currently_active_devices), + devices, + ) ) devices = list(map(lambda x: x["device"], devices)) return devices @@ -178,7 +181,7 @@ def get_max_vram_usage_level(device): else: return "high" - mem_total /= float(10**9) + mem_total /= float(10 ** 9) if mem_total < 4.5: return "low" elif mem_total < 6.5: @@ -220,7 +223,7 @@ def is_device_compatible(device): # Memory check try: _, mem_total = torch.cuda.mem_get_info(device) - mem_total /= float(10**9) + mem_total /= float(10 ** 9) if mem_total < 3.0: if is_device_compatible.history.get(device) == None: log.warn(f"GPU {device} with less than 3 GB of VRAM is not compatible with Stable Diffusion") diff --git a/ui/easydiffusion/model_manager.py b/ui/easydiffusion/model_manager.py index dc727eeb..7bf56575 100644 --- a/ui/easydiffusion/model_manager.py +++ b/ui/easydiffusion/model_manager.py @@ -3,11 +3,17 @@ import os from easydiffusion import app from easydiffusion.types import TaskData from easydiffusion.utils import log - from sdkit import Context -from sdkit.models import load_model, unload_model, scan_model +from sdkit.models import load_model, scan_model, unload_model -KNOWN_MODEL_TYPES = ["stable-diffusion", "vae", "hypernetwork", "gfpgan", "realesrgan", "lora"] +KNOWN_MODEL_TYPES = [ + "stable-diffusion", + "vae", + "hypernetwork", + "gfpgan", + "realesrgan", + "lora", +] MODEL_EXTENSIONS = { "stable-diffusion": [".ckpt", ".safetensors"], "vae": [".vae.pt", ".ckpt", ".safetensors"], @@ -44,13 +50,15 @@ def load_default_models(context: Context): load_model( context, model_type, - scan_model = context.model_paths[model_type] != None and not context.model_paths[model_type].endswith('.safetensors') + scan_model=context.model_paths[model_type] != None + and not context.model_paths[model_type].endswith(".safetensors"), ) except Exception as e: log.error(f"[red]Error while loading {model_type} model: {context.model_paths[model_type]}[/red]") log.exception(e) del context.model_paths[model_type] + def unload_all(context: Context): for model_type in KNOWN_MODEL_TYPES: unload_model(context, model_type) @@ -170,13 +178,23 @@ def is_malicious_model(file_path): if scan_result.issues_count > 0 or scan_result.infected_files > 0: log.warn( ":warning: [bold red]Scan %s: %d scanned, %d issue, %d infected.[/bold red]" - % (file_path, scan_result.scanned_files, scan_result.issues_count, scan_result.infected_files) + % ( + file_path, + scan_result.scanned_files, + scan_result.issues_count, + scan_result.infected_files, + ) ) return True else: log.debug( "Scan %s: [green]%d scanned, %d issue, %d infected.[/green]" - % (file_path, scan_result.scanned_files, scan_result.issues_count, scan_result.infected_files) + % ( + file_path, + scan_result.scanned_files, + scan_result.issues_count, + scan_result.infected_files, + ) ) return False except Exception as e: @@ -204,13 +222,13 @@ def getModels(): class MaliciousModelException(Exception): "Raised when picklescan reports a problem with a model" - pass def scan_directory(directory, suffixes, directoriesFirst: bool = True): nonlocal models_scanned tree = [] for entry in sorted( - os.scandir(directory), key=lambda entry: (entry.is_file() == directoriesFirst, entry.name.lower()) + os.scandir(directory), + key=lambda entry: (entry.is_file() == directoriesFirst, entry.name.lower()), ): if entry.is_file(): matching_suffix = list(filter(lambda s: entry.name.endswith(s), suffixes)) diff --git a/ui/easydiffusion/renderer.py b/ui/easydiffusion/renderer.py index 8a155f82..6c10dca1 100644 --- a/ui/easydiffusion/renderer.py +++ b/ui/easydiffusion/renderer.py @@ -1,21 +1,22 @@ -import queue -import time import json import pprint +import queue +import time from easydiffusion import device_manager -from easydiffusion.types import TaskData, Response, Image as ResponseImage, UserInitiatedStop, GenerateImageRequest -from easydiffusion.utils import get_printable_request, save_images_to_disk, log - +from easydiffusion.types import GenerateImageRequest +from easydiffusion.types import Image as ResponseImage +from easydiffusion.types import Response, TaskData, UserInitiatedStop +from easydiffusion.utils import get_printable_request, log, save_images_to_disk from sdkit import Context -from sdkit.generate import generate_images from sdkit.filter import apply_filters +from sdkit.generate import generate_images from sdkit.utils import ( - img_to_buffer, - img_to_base64_str, - latent_samples_to_images, diffusers_latent_samples_to_images, gc, + img_to_base64_str, + img_to_buffer, + latent_samples_to_images, ) context = Context() # thread-local @@ -43,14 +44,22 @@ def init(device): def make_images( - req: GenerateImageRequest, task_data: TaskData, data_queue: queue.Queue, task_temp_images: list, step_callback + req: GenerateImageRequest, + task_data: TaskData, + data_queue: queue.Queue, + task_temp_images: list, + step_callback, ): context.stop_processing = False print_task_info(req, task_data) images, seeds = make_images_internal(req, task_data, data_queue, task_temp_images, step_callback) - res = Response(req, task_data, images=construct_response(images, seeds, task_data, base_seed=req.seed)) + res = Response( + req, + task_data, + images=construct_response(images, seeds, task_data, base_seed=req.seed), + ) res = res.json() data_queue.put(json.dumps(res)) log.info("Task completed") @@ -66,7 +75,11 @@ def print_task_info(req: GenerateImageRequest, task_data: TaskData): def make_images_internal( - req: GenerateImageRequest, task_data: TaskData, data_queue: queue.Queue, task_temp_images: list, step_callback + req: GenerateImageRequest, + task_data: TaskData, + data_queue: queue.Queue, + task_temp_images: list, + step_callback, ): images, user_stopped = generate_images_internal( req, @@ -155,7 +168,12 @@ def filter_images(task_data: TaskData, images: list, user_stopped): def construct_response(images: list, seeds: list, task_data: TaskData, base_seed: int): return [ ResponseImage( - data=img_to_base64_str(img, task_data.output_format, task_data.output_quality, task_data.output_lossless), + data=img_to_base64_str( + img, + task_data.output_format, + task_data.output_quality, + task_data.output_lossless, + ), seed=seed, ) for img, seed in zip(images, seeds) diff --git a/ui/easydiffusion/server.py b/ui/easydiffusion/server.py index 92453917..a1aab6c0 100644 --- a/ui/easydiffusion/server.py +++ b/ui/easydiffusion/server.py @@ -2,28 +2,30 @@ Notes: async endpoints always run on the main thread. Without they run on the thread pool. """ +import datetime +import mimetypes import os import traceback -import datetime from typing import List, Union +from easydiffusion import app, model_manager, task_manager +from easydiffusion.types import GenerateImageRequest, MergeRequest, TaskData +from easydiffusion.utils import log from fastapi import FastAPI, HTTPException from fastapi.staticfiles import StaticFiles -from starlette.responses import FileResponse, JSONResponse, StreamingResponse from pydantic import BaseModel, Extra - -from easydiffusion import app, model_manager, task_manager -from easydiffusion.types import TaskData, GenerateImageRequest, MergeRequest -from easydiffusion.utils import log - -import mimetypes +from starlette.responses import FileResponse, JSONResponse, StreamingResponse log.info(f"started in {app.SD_DIR}") log.info(f"started at {datetime.datetime.now():%x %X}") server_api = FastAPI() -NOCACHE_HEADERS = {"Cache-Control": "no-cache, no-store, must-revalidate", "Pragma": "no-cache", "Expires": "0"} +NOCACHE_HEADERS = { + "Cache-Control": "no-cache, no-store, must-revalidate", + "Pragma": "no-cache", + "Expires": "0", +} class NoCacheStaticFiles(StaticFiles): @@ -65,11 +67,17 @@ def init(): name="custom-thumbnails", ) - server_api.mount("/media", NoCacheStaticFiles(directory=os.path.join(app.SD_UI_DIR, "media")), name="media") + server_api.mount( + "/media", + NoCacheStaticFiles(directory=os.path.join(app.SD_UI_DIR, "media")), + name="media", + ) for plugins_dir, dir_prefix in app.UI_PLUGINS_SOURCES: server_api.mount( - f"/plugins/{dir_prefix}", NoCacheStaticFiles(directory=plugins_dir), name=f"plugins-{dir_prefix}" + f"/plugins/{dir_prefix}", + NoCacheStaticFiles(directory=plugins_dir), + name=f"plugins-{dir_prefix}", ) @server_api.post("/app_config") @@ -246,8 +254,8 @@ def render_internal(req: dict): def model_merge_internal(req: dict): try: - from sdkit.train import merge_models from easydiffusion.utils.save_utils import filename_regex + from sdkit.train import merge_models mergeReq: MergeRequest = MergeRequest.parse_obj(req) @@ -255,7 +263,11 @@ def model_merge_internal(req: dict): model_manager.resolve_model_to_use(mergeReq.model0, "stable-diffusion"), model_manager.resolve_model_to_use(mergeReq.model1, "stable-diffusion"), mergeReq.ratio, - os.path.join(app.MODELS_DIR, "stable-diffusion", filename_regex.sub("_", mergeReq.out_path)), + os.path.join( + app.MODELS_DIR, + "stable-diffusion", + filename_regex.sub("_", mergeReq.out_path), + ), mergeReq.use_fp16, ) return JSONResponse({"status": "OK"}, headers=NOCACHE_HEADERS) diff --git a/ui/easydiffusion/task_manager.py b/ui/easydiffusion/task_manager.py index 91adc04b..450636cf 100644 --- a/ui/easydiffusion/task_manager.py +++ b/ui/easydiffusion/task_manager.py @@ -9,14 +9,16 @@ import traceback TASK_TTL = 15 * 60 # seconds, Discard last session's task timeout -import torch -import queue, threading, time, weakref +import queue +import threading +import time +import weakref from typing import Any, Hashable +import torch from easydiffusion import device_manager -from easydiffusion.types import TaskData, GenerateImageRequest +from easydiffusion.types import GenerateImageRequest, TaskData from easydiffusion.utils import log - from sdkit.utils import gc THREAD_NAME_PREFIX = "" @@ -167,7 +169,7 @@ class DataCache: raise Exception("DataCache.put" + ERR_LOCK_FAILED) try: self._base[key] = (self._get_ttl_time(ttl), value) - except Exception as e: + except Exception: log.error(traceback.format_exc()) return False else: @@ -264,7 +266,7 @@ def thread_get_next_task(): def thread_render(device): global current_state, current_state_error - from easydiffusion import renderer, model_manager + from easydiffusion import model_manager, renderer try: renderer.init(device) @@ -337,7 +339,11 @@ def thread_render(device): current_state = ServerStates.Rendering task.response = renderer.make_images( - task.render_request, task.task_data, task.buffer_queue, task.temp_images, step_callback + task.render_request, + task.task_data, + task.buffer_queue, + task.temp_images, + step_callback, ) # Before looping back to the generator, mark cache as still alive. task_cache.keep(id(task), TASK_TTL) @@ -392,8 +398,8 @@ def get_devices(): return {"name": device_manager.get_processor_name()} mem_free, mem_total = torch.cuda.mem_get_info(device) - mem_free /= float(10**9) - mem_total /= float(10**9) + mem_free /= float(10 ** 9) + mem_total /= float(10 ** 9) return { "name": torch.cuda.get_device_name(device), diff --git a/ui/easydiffusion/types.py b/ui/easydiffusion/types.py index bbec0afa..7462355f 100644 --- a/ui/easydiffusion/types.py +++ b/ui/easydiffusion/types.py @@ -1,6 +1,7 @@ -from pydantic import BaseModel from typing import Any +from pydantic import BaseModel + class GenerateImageRequest(BaseModel): prompt: str = "" diff --git a/ui/easydiffusion/utils/save_utils.py b/ui/easydiffusion/utils/save_utils.py index 384794d1..a7043f27 100644 --- a/ui/easydiffusion/utils/save_utils.py +++ b/ui/easydiffusion/utils/save_utils.py @@ -1,14 +1,13 @@ import os -import time import re +import time +from datetime import datetime +from functools import reduce from easydiffusion import app -from easydiffusion.types import TaskData, GenerateImageRequest -from functools import reduce -from datetime import datetime - -from sdkit.utils import save_images, save_dicts +from easydiffusion.types import GenerateImageRequest, TaskData from numpy import base_repr +from sdkit.utils import save_dicts, save_images filename_regex = re.compile("[^a-zA-Z0-9._-]") img_number_regex = re.compile("([0-9]{5,})") @@ -50,6 +49,7 @@ other_placeholders = { "$s": lambda req, task_data: str(req.seed), } + class ImageNumber: _factory = None _evaluated = False @@ -57,12 +57,14 @@ class ImageNumber: def __init__(self, factory): self._factory = factory self._evaluated = None + def __call__(self) -> int: if self._evaluated is None: self._evaluated = self._factory() return self._evaluated -def format_placeholders(format: str, req: GenerateImageRequest, task_data: TaskData, now = None): + +def format_placeholders(format: str, req: GenerateImageRequest, task_data: TaskData, now=None): if now is None: now = time.time() @@ -75,10 +77,12 @@ def format_placeholders(format: str, req: GenerateImageRequest, task_data: TaskD return format + def format_folder_name(format: str, req: GenerateImageRequest, task_data: TaskData): format = format_placeholders(format, req, task_data) return filename_regex.sub("_", format) + def format_file_name( format: str, req: GenerateImageRequest, @@ -88,19 +92,22 @@ def format_file_name( folder_img_number: ImageNumber, ): format = format_placeholders(format, req, task_data, now) - + if "$n" in format: format = format.replace("$n", f"{folder_img_number():05}") - + if "$tsb64" in format: - img_id = base_repr(int(now * 10000), 36)[-7:] + base_repr(int(batch_file_number), 36) # Base 36 conversion, 0-9, A-Z + img_id = base_repr(int(now * 10000), 36)[-7:] + base_repr( + int(batch_file_number), 36 + ) # Base 36 conversion, 0-9, A-Z format = format.replace("$tsb64", img_id) - + if "$ts" in format: format = format.replace("$ts", str(int(now * 1000) + batch_file_number)) return filename_regex.sub("_", format) + def save_images_to_disk(images: list, filtered_images: list, req: GenerateImageRequest, task_data: TaskData): now = time.time() app_config = app.getConfig() @@ -126,7 +133,7 @@ def save_images_to_disk(images: list, filtered_images: list, req: GenerateImageR output_lossless=task_data.output_lossless, ) if task_data.metadata_output_format: - for metadata_output_format in task_data.metadata_output_format.split(','): + for metadata_output_format in task_data.metadata_output_format.split(","): if metadata_output_format.lower() in ["json", "txt", "embed"]: save_dicts( metadata_entries, @@ -142,7 +149,8 @@ def save_images_to_disk(images: list, filtered_images: list, req: GenerateImageR task_data, file_number, now=now, - suffix="filtered") + suffix="filtered", + ) save_images( images, @@ -233,27 +241,28 @@ def make_filename_callback( return make_filename + def _calculate_img_number(save_dir_path: str, task_data: TaskData): def get_highest_img_number(accumulator: int, file: os.DirEntry) -> int: if not file.is_file: return accumulator - + if len(list(filter(lambda e: file.name.endswith(e), app.IMAGE_EXTENSIONS))) == 0: return accumulator - + get_highest_img_number.number_of_images = get_highest_img_number.number_of_images + 1 - + number_match = img_number_regex.match(file.name) if not number_match: return accumulator - - file_number = number_match.group().lstrip('0') - + + file_number = number_match.group().lstrip("0") + # Handle 00000 return int(file_number) if file_number else 0 - + get_highest_img_number.number_of_images = 0 - + highest_file_number = -1 if os.path.isdir(save_dir_path): @@ -267,13 +276,15 @@ def _calculate_img_number(save_dir_path: str, task_data: TaskData): _calculate_img_number.session_img_numbers[task_data.session_id], calculated_img_number, ) - + calculated_img_number = calculated_img_number + 1 - + _calculate_img_number.session_img_numbers[task_data.session_id] = calculated_img_number return calculated_img_number + _calculate_img_number.session_img_numbers = {} + def calculate_img_number(save_dir_path: str, task_data: TaskData): return ImageNumber(lambda: _calculate_img_number(save_dir_path, task_data)) From 9af511e457cf9a126af0020513632d35e5a577e7 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 28 Apr 2023 16:42:38 +0530 Subject: [PATCH 127/291] Check prettier style in a github action --- .github/workflows/prettier.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/prettier.yml diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml new file mode 100644 index 00000000..c76c6648 --- /dev/null +++ b/.github/workflows/prettier.yml @@ -0,0 +1,22 @@ +name: Check JavaScript for conformance with Prettier + +on: + push: + pull_request: + +jobs: + prettier: + runs-on: ubuntu-latest + steps: + - name: Check out repo + uses: actions/checkout@v2 + - uses: actions/cache@v2 + name: Configure npm caching + with: + path: ~/.npm + key: ${{ runner.os }}-npm-${{ hashFiles('**/workflows/prettier.yml') }} + restore-keys: | + ${{ runner.os }}-npm- + - name: Run prettier + run: |- + npx prettier --check "./**/*.js" ui \ No newline at end of file From 1381be16ad7a669797fa6df460e9543327eb0c24 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 28 Apr 2023 17:55:03 +0530 Subject: [PATCH 128/291] Fix formatting --- ui/index.html | 2 +- ui/media/css/searchable-models.css | 2 +- ui/media/css/themes.css | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/index.html b/ui/index.html index 3ba84e1a..da84242d 100644 --- a/ui/index.html +++ b/ui/index.html @@ -139,7 +139,7 @@ --> - + Click to learn more about VAEs diff --git a/ui/media/css/searchable-models.css b/ui/media/css/searchable-models.css index 06d24acb..5eecd1e0 100644 --- a/ui/media/css/searchable-models.css +++ b/ui/media/css/searchable-models.css @@ -58,7 +58,7 @@ font-size: 10pt; font-weight: normal; transition: none; - transition:property: none; + transition-property: none; cursor: default; } diff --git a/ui/media/css/themes.css b/ui/media/css/themes.css index 053199f8..e9eca84f 100644 --- a/ui/media/css/themes.css +++ b/ui/media/css/themes.css @@ -33,7 +33,7 @@ --input-height: 18px; --tertiary-background-color: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) + (2 * var(--value-step)))); --tertiary-border-color: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) + (3 * var(--value-step)))); - --tertiary-color: var(--input-text-color) + --tertiary-color: var(--input-text-color); /* Main theme color, hex color fallback. */ --theme-color-fallback: #673AB6; From 039395f221a12f7bdc2878f083393708d8b42cf3 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 28 Apr 2023 17:57:26 +0530 Subject: [PATCH 129/291] Fix formatting --- ui/index.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/index.html b/ui/index.html index da84242d..869d4c97 100644 --- a/ui/index.html +++ b/ui/index.html @@ -135,7 +135,7 @@ Click to learn more about custom models - @@ -217,14 +217,14 @@
- +
- + From 03fedfd0d5e08b8e8e1b9c5d8289b86fdeca1fc5 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 28 Apr 2023 18:07:43 +0530 Subject: [PATCH 130/291] fix formatting --- ui/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/index.html b/ui/index.html index 869d4c97..3db86593 100644 --- a/ui/index.html +++ b/ui/index.html @@ -238,7 +238,7 @@ - + From 2e362d57eb777314d998ace14bd4a32a5bf72255 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 28 Apr 2023 18:10:58 +0530 Subject: [PATCH 131/291] Adjust prettier config --- .github/workflows/prettier.yml | 2 +- .prettierignore | 8 ++------ package.json | 3 ++- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml index c76c6648..2ea9aab9 100644 --- a/.github/workflows/prettier.yml +++ b/.github/workflows/prettier.yml @@ -19,4 +19,4 @@ jobs: ${{ runner.os }}-npm- - name: Run prettier run: |- - npx prettier --check "./**/*.js" ui \ No newline at end of file + npm run prettier-check \ No newline at end of file diff --git a/.prettierignore b/.prettierignore index 1f28e901..60ba6412 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,8 +1,4 @@ *.min.* *.py -/* -!/ui -/ui/easydiffusion -/ui/hotfix -!/ui/plugins -!/ui/media \ No newline at end of file +*.json +*.html diff --git a/package.json b/package.json index c9c03893..388b29c8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "scripts": { - "prettier": "prettier --write \"./**/*.js\"" + "prettier-fix": "npx prettier --write ui/**", + "prettier-check": "npx prettier --check ui/**" }, "devDependencies": { "prettier": "^1.19.1" From da3f894ed47adf31508bcca257ec6c44413fb017 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 28 Apr 2023 18:13:38 +0530 Subject: [PATCH 132/291] another --- .prettierignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.prettierignore b/.prettierignore index 60ba6412..b0f8227f 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,3 +2,8 @@ *.py *.json *.html +/* +!/ui +/ui/easydiffusion +!/ui/plugins +!/ui/media \ No newline at end of file From 70a37fda573ae22030eee447e8b5a7fcf2abb7d9 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 28 Apr 2023 18:15:57 +0530 Subject: [PATCH 133/291] prettier --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 388b29c8..cedf812b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "scripts": { - "prettier-fix": "npx prettier --write ui/**", - "prettier-check": "npx prettier --check ui/**" + "prettier-fix": "npx prettier --write -c ui", + "prettier-check": "npx prettier --check -c ui" }, "devDependencies": { "prettier": "^1.19.1" From ae52d9ef2274f734b18bcb70d31da849f61ab147 Mon Sep 17 00:00:00 2001 From: JeLuF Date: Fri, 28 Apr 2023 22:52:21 +0200 Subject: [PATCH 134/291] Disable "TypedStorage is deprecated" user warnings These warnings clog up the logfiles and worry users. --- ui/easydiffusion/app.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ui/easydiffusion/app.py b/ui/easydiffusion/app.py index da755c21..b6318f01 100644 --- a/ui/easydiffusion/app.py +++ b/ui/easydiffusion/app.py @@ -5,6 +5,7 @@ import socket import sys import traceback import urllib +import warnings from easydiffusion import task_manager from easydiffusion.utils import log @@ -86,6 +87,9 @@ def init(): os.makedirs(USER_UI_PLUGINS_DIR, exist_ok=True) os.makedirs(USER_SERVER_PLUGINS_DIR, exist_ok=True) + # https://pytorch.org/docs/stable/storage.html + warnings.filterwarnings('ignore', category=UserWarning, message='TypedStorage is deprecated') + load_server_plugins() update_render_threads() From 3100fae11866a6f6203a00486b8d68348b662012 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Sat, 29 Apr 2023 07:05:50 +0530 Subject: [PATCH 135/291] Delete prettier.yml --- .github/workflows/prettier.yml | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 .github/workflows/prettier.yml diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml deleted file mode 100644 index 2ea9aab9..00000000 --- a/.github/workflows/prettier.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Check JavaScript for conformance with Prettier - -on: - push: - pull_request: - -jobs: - prettier: - runs-on: ubuntu-latest - steps: - - name: Check out repo - uses: actions/checkout@v2 - - uses: actions/cache@v2 - name: Configure npm caching - with: - path: ~/.npm - key: ${{ runner.os }}-npm-${{ hashFiles('**/workflows/prettier.yml') }} - restore-keys: | - ${{ runner.os }}-npm- - - name: Run prettier - run: |- - npm run prettier-check \ No newline at end of file From 0ebad7708391126fde6f2b2689ead398734398d4 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Sat, 29 Apr 2023 07:09:57 +0530 Subject: [PATCH 136/291] One more try --- .github/workflows/prettier.yml | 22 ++++++++++++++++++++++ package.json | 4 ++-- 2 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/prettier.yml diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml new file mode 100644 index 00000000..2ea9aab9 --- /dev/null +++ b/.github/workflows/prettier.yml @@ -0,0 +1,22 @@ +name: Check JavaScript for conformance with Prettier + +on: + push: + pull_request: + +jobs: + prettier: + runs-on: ubuntu-latest + steps: + - name: Check out repo + uses: actions/checkout@v2 + - uses: actions/cache@v2 + name: Configure npm caching + with: + path: ~/.npm + key: ${{ runner.os }}-npm-${{ hashFiles('**/workflows/prettier.yml') }} + restore-keys: | + ${{ runner.os }}-npm- + - name: Run prettier + run: |- + npm run prettier-check \ No newline at end of file diff --git a/package.json b/package.json index cedf812b..fbf1dadb 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "scripts": { - "prettier-fix": "npx prettier --write -c ui", - "prettier-check": "npx prettier --check -c ui" + "prettier-fix": "npx prettier --write \"./**/*.js\"", + "prettier-check": "npx prettier --check \"./**/*.js\"" }, "devDependencies": { "prettier": "^1.19.1" From e550b150947b4a5725909b1f0adce8a7d7251e6c Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Sat, 29 Apr 2023 07:14:42 +0530 Subject: [PATCH 137/291] Use the prettier config file --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index fbf1dadb..3c071b84 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "scripts": { - "prettier-fix": "npx prettier --write \"./**/*.js\"", - "prettier-check": "npx prettier --check \"./**/*.js\"" + "prettier-fix": "npx prettier --write \"./**/*.js\" --config .prettierrc.json", + "prettier-check": "npx prettier --check \"./**/*.js\" --config .prettierrc.json" }, "devDependencies": { "prettier": "^1.19.1" From a6fe023519ee16be038e27fb19a754cfa63a59bf Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Sat, 29 Apr 2023 07:23:54 +0530 Subject: [PATCH 138/291] Try using a third-party action for prettier --- .github/workflows/prettier.yml | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml index 2ea9aab9..5163a5bf 100644 --- a/.github/workflows/prettier.yml +++ b/.github/workflows/prettier.yml @@ -10,13 +10,7 @@ jobs: steps: - name: Check out repo uses: actions/checkout@v2 - - uses: actions/cache@v2 - name: Configure npm caching + - uses: actionsx/prettier@v2 with: - path: ~/.npm - key: ${{ runner.os }}-npm-${{ hashFiles('**/workflows/prettier.yml') }} - restore-keys: | - ${{ runner.os }}-npm- - - name: Run prettier - run: |- - npm run prettier-check \ No newline at end of file + # prettier CLI arguments. + args: --check \"./**/*.js\" --config .prettierrc.json \ No newline at end of file From f90a13571c50ac7e3f56107b4a248a9619f284a7 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Sat, 29 Apr 2023 07:25:20 +0530 Subject: [PATCH 139/291] typo --- .github/workflows/prettier.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml index 5163a5bf..edc58515 100644 --- a/.github/workflows/prettier.yml +++ b/.github/workflows/prettier.yml @@ -13,4 +13,4 @@ jobs: - uses: actionsx/prettier@v2 with: # prettier CLI arguments. - args: --check \"./**/*.js\" --config .prettierrc.json \ No newline at end of file + args: --check "./**/*.js" --config .prettierrc.json \ No newline at end of file From 6cf05df5eef3698e4e338184821eab3691e9aa47 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Sat, 29 Apr 2023 07:31:09 +0530 Subject: [PATCH 140/291] Add the config explicitly --- .github/workflows/prettier.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml index edc58515..b342a871 100644 --- a/.github/workflows/prettier.yml +++ b/.github/workflows/prettier.yml @@ -13,4 +13,4 @@ jobs: - uses: actionsx/prettier@v2 with: # prettier CLI arguments. - args: --check "./**/*.js" --config .prettierrc.json \ No newline at end of file + args: --print-width 120 --tab-width 4 --no-semi --arrow-parens always --trailing-comma es5 --check "./**/*.js" \ No newline at end of file From 31c54c4a4131561f52a3e42c2aab7e5a7a679552 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Sat, 29 Apr 2023 07:34:13 +0530 Subject: [PATCH 141/291] Print the diff --- .github/workflows/prettier.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml index b342a871..d2d873f4 100644 --- a/.github/workflows/prettier.yml +++ b/.github/workflows/prettier.yml @@ -13,4 +13,5 @@ jobs: - uses: actionsx/prettier@v2 with: # prettier CLI arguments. - args: --print-width 120 --tab-width 4 --no-semi --arrow-parens always --trailing-comma es5 --check "./**/*.js" \ No newline at end of file + args: --print-width 120 --tab-width 4 --no-semi --arrow-parens always --trailing-comma es5 --write "./**/*.js" + - uses: technote-space/get-diff-action@v6 \ No newline at end of file From 262a1464c3b0045a5c2d855bb9ffb60120b84472 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Sat, 29 Apr 2023 07:38:11 +0530 Subject: [PATCH 142/291] Print the diff --- .github/workflows/prettier.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml index d2d873f4..09fd8c1b 100644 --- a/.github/workflows/prettier.yml +++ b/.github/workflows/prettier.yml @@ -14,4 +14,12 @@ jobs: with: # prettier CLI arguments. args: --print-width 120 --tab-width 4 --no-semi --arrow-parens always --trailing-comma es5 --write "./**/*.js" - - uses: technote-space/get-diff-action@v6 \ No newline at end of file + - uses: GrantBirki/git-diff-action@vX.X.X + id: git-diff-action + with: + json_diff_file_output: diff.json + raw_diff_file_output: diff.txt + - name: print raw diff + env: + DIFF: ${{ steps.git-diff-action.outputs.raw-diff-path }} + run: cat $DIFF \ No newline at end of file From 20d6e17d4dc950b3686fc2babec85f095e92ef3a Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Sat, 29 Apr 2023 07:40:24 +0530 Subject: [PATCH 143/291] Print the diff --- .github/workflows/prettier.yml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml index 09fd8c1b..4a754965 100644 --- a/.github/workflows/prettier.yml +++ b/.github/workflows/prettier.yml @@ -14,12 +14,7 @@ jobs: with: # prettier CLI arguments. args: --print-width 120 --tab-width 4 --no-semi --arrow-parens always --trailing-comma es5 --write "./**/*.js" - - uses: GrantBirki/git-diff-action@vX.X.X - id: git-diff-action + - uses: technote-space/get-diff-action@v6 with: - json_diff_file_output: diff.json - raw_diff_file_output: diff.txt - - name: print raw diff - env: - DIFF: ${{ steps.git-diff-action.outputs.raw-diff-path }} - run: cat $DIFF \ No newline at end of file + PATTERNS: | + +(ui)/**/*.js \ No newline at end of file From e7fd0b3a052f214a276c4a8bd2040ef9c05cbd49 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Sat, 29 Apr 2023 07:42:10 +0530 Subject: [PATCH 144/291] Print the diff --- .github/workflows/prettier.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml index 4a754965..dcc209f9 100644 --- a/.github/workflows/prettier.yml +++ b/.github/workflows/prettier.yml @@ -14,7 +14,5 @@ jobs: with: # prettier CLI arguments. args: --print-width 120 --tab-width 4 --no-semi --arrow-parens always --trailing-comma es5 --write "./**/*.js" - - uses: technote-space/get-diff-action@v6 - with: - PATTERNS: | - +(ui)/**/*.js \ No newline at end of file + - name: Get Diff + run: git diff \ No newline at end of file From 50fdc32ff8c88837cce7b594a8fbecd9bb0eed82 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Sat, 29 Apr 2023 07:44:38 +0530 Subject: [PATCH 145/291] Print the diff --- .github/workflows/prettier.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml index dcc209f9..06765500 100644 --- a/.github/workflows/prettier.yml +++ b/.github/workflows/prettier.yml @@ -13,6 +13,6 @@ jobs: - uses: actionsx/prettier@v2 with: # prettier CLI arguments. - args: --print-width 120 --tab-width 4 --no-semi --arrow-parens always --trailing-comma es5 --write "./**/*.js" + args: --write "./**/*.js" - name: Get Diff run: git diff \ No newline at end of file From 1b40a6baa31e2f26aff53f3461b05fa0e544f957 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Sat, 29 Apr 2023 07:54:14 +0530 Subject: [PATCH 146/291] Print the diff --- .github/workflows/prettier.yml | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml index 06765500..488c2675 100644 --- a/.github/workflows/prettier.yml +++ b/.github/workflows/prettier.yml @@ -10,9 +10,18 @@ jobs: steps: - name: Check out repo uses: actions/checkout@v2 - - uses: actionsx/prettier@v2 + - uses: actions/cache@v2 + name: Configure npm caching with: - # prettier CLI arguments. - args: --write "./**/*.js" - - name: Get Diff - run: git diff \ No newline at end of file + path: ~/.npm + key: ${{ runner.os }}-npm-${{ hashFiles('**/workflows/prettier.yml') }} + restore-keys: | + ${{ runner.os }}-npm- + - name: Run prettier + run: |- + npm run prettier-write + - name: Print the diff + run: git diff; git reset --hard + - name: Run prettier + run: |- + npm run prettier-check \ No newline at end of file From 4dc2a96d416f77c1a734528abd062c3e901c678a Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Sat, 29 Apr 2023 07:55:11 +0530 Subject: [PATCH 147/291] Print the diff --- .github/workflows/prettier.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml index 488c2675..60cfbfd6 100644 --- a/.github/workflows/prettier.yml +++ b/.github/workflows/prettier.yml @@ -19,7 +19,7 @@ jobs: ${{ runner.os }}-npm- - name: Run prettier run: |- - npm run prettier-write + npm run prettier-fix - name: Print the diff run: git diff; git reset --hard - name: Run prettier From 51e067b050bd19111a5121b4b59bc75cc9b189e8 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Sat, 29 Apr 2023 08:04:15 +0530 Subject: [PATCH 148/291] Try using the pinned version of prettier --- .github/workflows/prettier.yml | 14 ++++++-------- package-lock.json | 32 ++++++++++++++++++++++++++++++++ package.json | 6 +++--- 3 files changed, 41 insertions(+), 11 deletions(-) create mode 100644 package-lock.json diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml index 60cfbfd6..891220f0 100644 --- a/.github/workflows/prettier.yml +++ b/.github/workflows/prettier.yml @@ -10,18 +10,16 @@ jobs: steps: - name: Check out repo uses: actions/checkout@v2 - - uses: actions/cache@v2 - name: Configure npm caching + - uses: actions/setup-node@v3 with: - path: ~/.npm - key: ${{ runner.os }}-npm-${{ hashFiles('**/workflows/prettier.yml') }} - restore-keys: | - ${{ runner.os }}-npm- + node-version: 16 + cache: 'npm' + - run: npm ci - name: Run prettier run: |- npm run prettier-fix - - name: Print the diff + - name: Print the required changes run: git diff; git reset --hard - - name: Run prettier + - name: Check prettier run: |- npm run prettier-check \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..d6923948 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,32 @@ +{ + "name": "stable-diffusion-ui", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "devDependencies": { + "prettier": "1.19.1" + } + }, + "node_modules/prettier": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", + "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=4" + } + } + }, + "dependencies": { + "prettier": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", + "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", + "dev": true + } + } +} diff --git a/package.json b/package.json index 3c071b84..c57b0587 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "scripts": { - "prettier-fix": "npx prettier --write \"./**/*.js\" --config .prettierrc.json", - "prettier-check": "npx prettier --check \"./**/*.js\" --config .prettierrc.json" + "prettier-fix": "npx prettier --write \"./**/*.js\"", + "prettier-check": "npx prettier --check \"./**/*.js\"" }, "devDependencies": { - "prettier": "^1.19.1" + "prettier": "1.19.1" } } From 729f7eb24a77a69d0bc35edcfda8fd9484402116 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Sat, 29 Apr 2023 08:18:36 +0530 Subject: [PATCH 149/291] Remove prettier github action --- .github/workflows/prettier.yml | 25 ------------------------- package-lock.json | 32 -------------------------------- package.json | 2 +- yarn.lock | 8 -------- 4 files changed, 1 insertion(+), 66 deletions(-) delete mode 100644 .github/workflows/prettier.yml delete mode 100644 package-lock.json delete mode 100644 yarn.lock diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml deleted file mode 100644 index 891220f0..00000000 --- a/.github/workflows/prettier.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: Check JavaScript for conformance with Prettier - -on: - push: - pull_request: - -jobs: - prettier: - runs-on: ubuntu-latest - steps: - - name: Check out repo - uses: actions/checkout@v2 - - uses: actions/setup-node@v3 - with: - node-version: 16 - cache: 'npm' - - run: npm ci - - name: Run prettier - run: |- - npm run prettier-fix - - name: Print the required changes - run: git diff; git reset --hard - - name: Check prettier - run: |- - npm run prettier-check \ No newline at end of file diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index d6923948..00000000 --- a/package-lock.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "stable-diffusion-ui", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "devDependencies": { - "prettier": "1.19.1" - } - }, - "node_modules/prettier": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", - "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", - "dev": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=4" - } - } - }, - "dependencies": { - "prettier": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", - "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", - "dev": true - } - } -} diff --git a/package.json b/package.json index c57b0587..fbf1dadb 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,6 @@ "prettier-check": "npx prettier --check \"./**/*.js\"" }, "devDependencies": { - "prettier": "1.19.1" + "prettier": "^1.19.1" } } diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index 6fc84143..00000000 --- a/yarn.lock +++ /dev/null @@ -1,8 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -prettier@^1.19.1: - version "1.19.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" - integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== From dde51c0cefaf36857a54441f68d313ad13ed3e67 Mon Sep 17 00:00:00 2001 From: patriceac <48073125+patriceac@users.noreply.github.com> Date: Sat, 29 Apr 2023 19:25:10 -0700 Subject: [PATCH 150/291] Toast notifications for ED Adding support for toast notifications for use in Core and user plugins. --- ui/media/css/main.css | 51 ++++++++++++++++++++++++++++++++++++++ ui/media/js/utils.js | 57 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) diff --git a/ui/media/css/main.css b/ui/media/css/main.css index 08dea664..ba513237 100644 --- a/ui/media/css/main.css +++ b/ui/media/css/main.css @@ -1302,3 +1302,54 @@ body.wait-pause { .displayNone { display:none !important; } + +/* TOAST NOTIFICATIONS */ +.toast-notification { + position: fixed; + bottom: 10px; + right: -300px; + width: 300px; + background-color: #333; + color: #fff; + padding: 10px 20px; + border-radius: 5px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); + z-index: 9999; + animation: slideInRight 0.5s ease forwards; + transition: bottom 0.5s ease; // Add a transition to smoothly reposition the toasts +} + +.toast-notification-error { + color: red; +} + +@keyframes slideInRight { + from { + right: -300px; + } + to { + right: 10px; + } +} + +.toast-notification.hide { + animation: slideOutRight 0.5s ease forwards; +} + +@keyframes slideOutRight { + from { + right: 10px; + } + to { + right: -300px; + } +} + +@keyframes slideDown { + from { + bottom: 10px; + } + to { + bottom: 0; + } +} diff --git a/ui/media/js/utils.js b/ui/media/js/utils.js index 6558f7d2..d1578d8e 100644 --- a/ui/media/js/utils.js +++ b/ui/media/js/utils.js @@ -840,3 +840,60 @@ function createTab(request) { } }) } + +/* TOAST NOTIFICATIONS */ +function showToast(message, duration = 5000, error = false) { + const toast = document.createElement("div"); + toast.classList.add("toast-notification"); + if (error === true) { + toast.classList.add("toast-notification-error"); + } + toast.innerHTML = message; + document.body.appendChild(toast); + + // Set the position of the toast on the screen + const toastCount = document.querySelectorAll(".toast-notification").length; + const toastHeight = toast.offsetHeight; + const previousToastsHeight = Array.from(document.querySelectorAll(".toast-notification")) + .slice(0, -1) // exclude current toast + .reduce((totalHeight, toast) => totalHeight + toast.offsetHeight + 10, 0); // add 10 pixels for spacing + toast.style.bottom = `${10 + previousToastsHeight}px`; + toast.style.right = "10px"; + + // Delay the removal of the toast until animation has completed + const removeToast = () => { + toast.classList.add("hide"); + const removeTimeoutId = setTimeout(() => { + toast.remove(); + // Adjust the position of remaining toasts + const remainingToasts = document.querySelectorAll(".toast-notification"); + const removedToastBottom = toast.getBoundingClientRect().bottom; + + remainingToasts.forEach((toast) => { + if (toast.getBoundingClientRect().bottom < removedToastBottom) { + toast.classList.add("slide-down"); + } + }); + + // Wait for the slide-down animation to complete + setTimeout(() => { + // Remove the slide-down class after the animation has completed + const slidingToasts = document.querySelectorAll(".slide-down"); + slidingToasts.forEach((toast) => { + toast.classList.remove("slide-down"); + }); + + // Adjust the position of remaining toasts again, in case there are multiple toasts being removed at once + const remainingToastsDown = document.querySelectorAll(".toast-notification"); + let heightSoFar = 0; + remainingToastsDown.forEach((toast) => { + toast.style.bottom = `${10 + heightSoFar}px`; + heightSoFar += toast.offsetHeight + 10; // add 10 pixels for spacing + }); + }, 0); // The duration of the slide-down animation (in milliseconds) + }, 500); + }; + + // Remove the toast after specified duration + setTimeout(removeToast, duration); +} From 654749de403ed2cc7de60d273dc6b07962580125 Mon Sep 17 00:00:00 2001 From: patriceac <48073125+patriceac@users.noreply.github.com> Date: Sat, 29 Apr 2023 19:26:11 -0700 Subject: [PATCH 151/291] Revert "Toast notifications for ED" This reverts commit dde51c0cefaf36857a54441f68d313ad13ed3e67. --- ui/media/css/main.css | 51 -------------------------------------- ui/media/js/utils.js | 57 ------------------------------------------- 2 files changed, 108 deletions(-) diff --git a/ui/media/css/main.css b/ui/media/css/main.css index ba513237..08dea664 100644 --- a/ui/media/css/main.css +++ b/ui/media/css/main.css @@ -1302,54 +1302,3 @@ body.wait-pause { .displayNone { display:none !important; } - -/* TOAST NOTIFICATIONS */ -.toast-notification { - position: fixed; - bottom: 10px; - right: -300px; - width: 300px; - background-color: #333; - color: #fff; - padding: 10px 20px; - border-radius: 5px; - box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); - z-index: 9999; - animation: slideInRight 0.5s ease forwards; - transition: bottom 0.5s ease; // Add a transition to smoothly reposition the toasts -} - -.toast-notification-error { - color: red; -} - -@keyframes slideInRight { - from { - right: -300px; - } - to { - right: 10px; - } -} - -.toast-notification.hide { - animation: slideOutRight 0.5s ease forwards; -} - -@keyframes slideOutRight { - from { - right: 10px; - } - to { - right: -300px; - } -} - -@keyframes slideDown { - from { - bottom: 10px; - } - to { - bottom: 0; - } -} diff --git a/ui/media/js/utils.js b/ui/media/js/utils.js index d1578d8e..6558f7d2 100644 --- a/ui/media/js/utils.js +++ b/ui/media/js/utils.js @@ -840,60 +840,3 @@ function createTab(request) { } }) } - -/* TOAST NOTIFICATIONS */ -function showToast(message, duration = 5000, error = false) { - const toast = document.createElement("div"); - toast.classList.add("toast-notification"); - if (error === true) { - toast.classList.add("toast-notification-error"); - } - toast.innerHTML = message; - document.body.appendChild(toast); - - // Set the position of the toast on the screen - const toastCount = document.querySelectorAll(".toast-notification").length; - const toastHeight = toast.offsetHeight; - const previousToastsHeight = Array.from(document.querySelectorAll(".toast-notification")) - .slice(0, -1) // exclude current toast - .reduce((totalHeight, toast) => totalHeight + toast.offsetHeight + 10, 0); // add 10 pixels for spacing - toast.style.bottom = `${10 + previousToastsHeight}px`; - toast.style.right = "10px"; - - // Delay the removal of the toast until animation has completed - const removeToast = () => { - toast.classList.add("hide"); - const removeTimeoutId = setTimeout(() => { - toast.remove(); - // Adjust the position of remaining toasts - const remainingToasts = document.querySelectorAll(".toast-notification"); - const removedToastBottom = toast.getBoundingClientRect().bottom; - - remainingToasts.forEach((toast) => { - if (toast.getBoundingClientRect().bottom < removedToastBottom) { - toast.classList.add("slide-down"); - } - }); - - // Wait for the slide-down animation to complete - setTimeout(() => { - // Remove the slide-down class after the animation has completed - const slidingToasts = document.querySelectorAll(".slide-down"); - slidingToasts.forEach((toast) => { - toast.classList.remove("slide-down"); - }); - - // Adjust the position of remaining toasts again, in case there are multiple toasts being removed at once - const remainingToastsDown = document.querySelectorAll(".toast-notification"); - let heightSoFar = 0; - remainingToastsDown.forEach((toast) => { - toast.style.bottom = `${10 + heightSoFar}px`; - heightSoFar += toast.offsetHeight + 10; // add 10 pixels for spacing - }); - }, 0); // The duration of the slide-down animation (in milliseconds) - }, 500); - }; - - // Remove the toast after specified duration - setTimeout(removeToast, duration); -} From 2b8c199e561089b8f37fc7348cf2987bfae2c25a Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 2 May 2023 10:52:58 +0530 Subject: [PATCH 152/291] Create PRIVACY.md --- PRIVACY.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 PRIVACY.md diff --git a/PRIVACY.md b/PRIVACY.md new file mode 100644 index 00000000..e33e2180 --- /dev/null +++ b/PRIVACY.md @@ -0,0 +1,8 @@ +// placeholder until a more formal and legal-sounding privacy policy document is written. but the information below is true. + +This is a summary of how Easy Diffusion uses your data or tracks you: +* The short answer is - Easy Diffusion does *not* use your data, and does not track you. +* Easy Diffusion does not send your prompts or usage or analytics to anyone. There is no tracking. We don't even know how many people use Easy Diffusion, let alone their prompts. +* Easy Diffusion fetches updates to the code whenever it starts up. It does this by contacting GitHub directly, via SSL (secure connection). Only your computer and GitHub and [this repository](https://github.com/cmdr2/stable-diffusion-ui) are involved, and no third party is involved. Some countries intercepts SSL connections, that's not something we can do much about. +* Easy Diffusion fetches the models from huggingface.co and github.com, if they don't exist on your PC. For e.g. if the safety checker (NSFW) model doesn't exist, it'll try to download it. +* Occasionally, antivirus software are known to *incorrectly* flag and delete some model files, which will result in Easy Diffusion re-downloading `pytorch_model.bin`. This *incorrect deletion* affects other Stable Diffusion UIs as well, like Invoke AI - https://itch.io/post/7509488 From 35d36f9eb300b0da640311805141b186e19b8ee7 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 2 May 2023 10:53:17 +0530 Subject: [PATCH 153/291] Update PRIVACY.md --- PRIVACY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PRIVACY.md b/PRIVACY.md index e33e2180..9d813724 100644 --- a/PRIVACY.md +++ b/PRIVACY.md @@ -1,7 +1,7 @@ // placeholder until a more formal and legal-sounding privacy policy document is written. but the information below is true. This is a summary of how Easy Diffusion uses your data or tracks you: -* The short answer is - Easy Diffusion does *not* use your data, and does not track you. +* The short answer is - Easy Diffusion does *not* use your data, and does *not* track you. * Easy Diffusion does not send your prompts or usage or analytics to anyone. There is no tracking. We don't even know how many people use Easy Diffusion, let alone their prompts. * Easy Diffusion fetches updates to the code whenever it starts up. It does this by contacting GitHub directly, via SSL (secure connection). Only your computer and GitHub and [this repository](https://github.com/cmdr2/stable-diffusion-ui) are involved, and no third party is involved. Some countries intercepts SSL connections, that's not something we can do much about. * Easy Diffusion fetches the models from huggingface.co and github.com, if they don't exist on your PC. For e.g. if the safety checker (NSFW) model doesn't exist, it'll try to download it. From c1e5c8dc86c7d3c921d4b6d87a3e18c34488db63 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 2 May 2023 11:00:57 +0530 Subject: [PATCH 154/291] Update PRIVACY.md --- PRIVACY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PRIVACY.md b/PRIVACY.md index 9d813724..bda8a2a8 100644 --- a/PRIVACY.md +++ b/PRIVACY.md @@ -3,6 +3,6 @@ This is a summary of how Easy Diffusion uses your data or tracks you: * The short answer is - Easy Diffusion does *not* use your data, and does *not* track you. * Easy Diffusion does not send your prompts or usage or analytics to anyone. There is no tracking. We don't even know how many people use Easy Diffusion, let alone their prompts. -* Easy Diffusion fetches updates to the code whenever it starts up. It does this by contacting GitHub directly, via SSL (secure connection). Only your computer and GitHub and [this repository](https://github.com/cmdr2/stable-diffusion-ui) are involved, and no third party is involved. Some countries intercepts SSL connections, that's not something we can do much about. +* Easy Diffusion fetches updates to the code whenever it starts up. It does this by contacting GitHub directly, via SSL (secure connection). Only your computer and GitHub and [this repository](https://github.com/cmdr2/stable-diffusion-ui) are involved, and no third party is involved. Some countries intercepts SSL connections, that's not something we can do much about. GitHub does *not* share statistics (even with me) about how many people fetched code updates. * Easy Diffusion fetches the models from huggingface.co and github.com, if they don't exist on your PC. For e.g. if the safety checker (NSFW) model doesn't exist, it'll try to download it. * Occasionally, antivirus software are known to *incorrectly* flag and delete some model files, which will result in Easy Diffusion re-downloading `pytorch_model.bin`. This *incorrect deletion* affects other Stable Diffusion UIs as well, like Invoke AI - https://itch.io/post/7509488 From 49599dc3baef01f32d89dae809abaac2f17cb15e Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 2 May 2023 11:03:09 +0530 Subject: [PATCH 155/291] Update PRIVACY.md --- PRIVACY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PRIVACY.md b/PRIVACY.md index bda8a2a8..ce1b4e1f 100644 --- a/PRIVACY.md +++ b/PRIVACY.md @@ -1,6 +1,6 @@ // placeholder until a more formal and legal-sounding privacy policy document is written. but the information below is true. -This is a summary of how Easy Diffusion uses your data or tracks you: +This is a summary of whether Easy Diffusion uses your data or tracks you: * The short answer is - Easy Diffusion does *not* use your data, and does *not* track you. * Easy Diffusion does not send your prompts or usage or analytics to anyone. There is no tracking. We don't even know how many people use Easy Diffusion, let alone their prompts. * Easy Diffusion fetches updates to the code whenever it starts up. It does this by contacting GitHub directly, via SSL (secure connection). Only your computer and GitHub and [this repository](https://github.com/cmdr2/stable-diffusion-ui) are involved, and no third party is involved. Some countries intercepts SSL connections, that's not something we can do much about. GitHub does *not* share statistics (even with me) about how many people fetched code updates. From 5a9e74cef765afe80e68ffc3800d91dac8c385c5 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 2 May 2023 11:05:58 +0530 Subject: [PATCH 156/291] Update PRIVACY.md --- PRIVACY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/PRIVACY.md b/PRIVACY.md index ce1b4e1f..6c997997 100644 --- a/PRIVACY.md +++ b/PRIVACY.md @@ -5,4 +5,5 @@ This is a summary of whether Easy Diffusion uses your data or tracks you: * Easy Diffusion does not send your prompts or usage or analytics to anyone. There is no tracking. We don't even know how many people use Easy Diffusion, let alone their prompts. * Easy Diffusion fetches updates to the code whenever it starts up. It does this by contacting GitHub directly, via SSL (secure connection). Only your computer and GitHub and [this repository](https://github.com/cmdr2/stable-diffusion-ui) are involved, and no third party is involved. Some countries intercepts SSL connections, that's not something we can do much about. GitHub does *not* share statistics (even with me) about how many people fetched code updates. * Easy Diffusion fetches the models from huggingface.co and github.com, if they don't exist on your PC. For e.g. if the safety checker (NSFW) model doesn't exist, it'll try to download it. +* Easy Diffusion fetches code packages from pypi.org, which is the standard hosting service for all Python projects. That's where packages installed via `pip install` are stored. * Occasionally, antivirus software are known to *incorrectly* flag and delete some model files, which will result in Easy Diffusion re-downloading `pytorch_model.bin`. This *incorrect deletion* affects other Stable Diffusion UIs as well, like Invoke AI - https://itch.io/post/7509488 From d231c533ae4c9b4f1aa14c76ca9b66f0eee9999f Mon Sep 17 00:00:00 2001 From: JeLuF Date: Tue, 2 May 2023 12:29:02 +0200 Subject: [PATCH 157/291] "Please restart" note for network settings (#1233) * "Please restart" note for network changes https://discord.com/channels/1014774730907209781/1101629831839494344 * typo --- ui/media/js/parameters.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/media/js/parameters.js b/ui/media/js/parameters.js index 9fc906e4..75abecd7 100644 --- a/ui/media/js/parameters.js +++ b/ui/media/js/parameters.js @@ -181,7 +181,7 @@ var PARAMETERS = [ { id: "listen_to_network", type: ParameterType.checkbox, - label: "Make Stable Diffusion available on your network", + label: "Make Stable Diffusion available on your network. Please restart the program after changing this.", note: "Other devices on your network can access this web page", icon: "fa-network-wired", default: true, @@ -191,7 +191,7 @@ var PARAMETERS = [ id: "listen_port", type: ParameterType.custom, label: "Network port", - note: "Port that this server listens to. The '9000' part in 'http://localhost:9000'", + note: "Port that this server listens to. The '9000' part in 'http://localhost:9000'. Please restart the program after changing this.", icon: "fa-anchor", render: (parameter) => { return `` From 679b828cf50d11639102cad4640fdcebddb67b74 Mon Sep 17 00:00:00 2001 From: patriceac <48073125+patriceac@users.noreply.github.com> Date: Tue, 2 May 2023 03:30:16 -0700 Subject: [PATCH 158/291] Toast notification support (#1228) * Toast notifications for ED Adding support for toast notifications for use in Core and user plugins. * Revert "Toast notifications for ED" This reverts commit dde51c0cefaf36857a54441f68d313ad13ed3e67. * Toast notifications for ED Adding support for toast notifications for use in Core and user plugins. --- ui/media/css/main.css | 51 ++++++++++++++++++++++++++++++++++++++ ui/media/js/utils.js | 57 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) diff --git a/ui/media/css/main.css b/ui/media/css/main.css index 08dea664..ba513237 100644 --- a/ui/media/css/main.css +++ b/ui/media/css/main.css @@ -1302,3 +1302,54 @@ body.wait-pause { .displayNone { display:none !important; } + +/* TOAST NOTIFICATIONS */ +.toast-notification { + position: fixed; + bottom: 10px; + right: -300px; + width: 300px; + background-color: #333; + color: #fff; + padding: 10px 20px; + border-radius: 5px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); + z-index: 9999; + animation: slideInRight 0.5s ease forwards; + transition: bottom 0.5s ease; // Add a transition to smoothly reposition the toasts +} + +.toast-notification-error { + color: red; +} + +@keyframes slideInRight { + from { + right: -300px; + } + to { + right: 10px; + } +} + +.toast-notification.hide { + animation: slideOutRight 0.5s ease forwards; +} + +@keyframes slideOutRight { + from { + right: 10px; + } + to { + right: -300px; + } +} + +@keyframes slideDown { + from { + bottom: 10px; + } + to { + bottom: 0; + } +} diff --git a/ui/media/js/utils.js b/ui/media/js/utils.js index 6558f7d2..d1578d8e 100644 --- a/ui/media/js/utils.js +++ b/ui/media/js/utils.js @@ -840,3 +840,60 @@ function createTab(request) { } }) } + +/* TOAST NOTIFICATIONS */ +function showToast(message, duration = 5000, error = false) { + const toast = document.createElement("div"); + toast.classList.add("toast-notification"); + if (error === true) { + toast.classList.add("toast-notification-error"); + } + toast.innerHTML = message; + document.body.appendChild(toast); + + // Set the position of the toast on the screen + const toastCount = document.querySelectorAll(".toast-notification").length; + const toastHeight = toast.offsetHeight; + const previousToastsHeight = Array.from(document.querySelectorAll(".toast-notification")) + .slice(0, -1) // exclude current toast + .reduce((totalHeight, toast) => totalHeight + toast.offsetHeight + 10, 0); // add 10 pixels for spacing + toast.style.bottom = `${10 + previousToastsHeight}px`; + toast.style.right = "10px"; + + // Delay the removal of the toast until animation has completed + const removeToast = () => { + toast.classList.add("hide"); + const removeTimeoutId = setTimeout(() => { + toast.remove(); + // Adjust the position of remaining toasts + const remainingToasts = document.querySelectorAll(".toast-notification"); + const removedToastBottom = toast.getBoundingClientRect().bottom; + + remainingToasts.forEach((toast) => { + if (toast.getBoundingClientRect().bottom < removedToastBottom) { + toast.classList.add("slide-down"); + } + }); + + // Wait for the slide-down animation to complete + setTimeout(() => { + // Remove the slide-down class after the animation has completed + const slidingToasts = document.querySelectorAll(".slide-down"); + slidingToasts.forEach((toast) => { + toast.classList.remove("slide-down"); + }); + + // Adjust the position of remaining toasts again, in case there are multiple toasts being removed at once + const remainingToastsDown = document.querySelectorAll(".toast-notification"); + let heightSoFar = 0; + remainingToastsDown.forEach((toast) => { + toast.style.bottom = `${10 + heightSoFar}px`; + heightSoFar += toast.offsetHeight + 10; // add 10 pixels for spacing + }); + }, 0); // The duration of the slide-down animation (in milliseconds) + }, 500); + }; + + // Remove the toast after specified duration + setTimeout(removeToast, duration); +} From eaba64a64ac958cfb07b0d925867f057d5fad91a Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 2 May 2023 18:26:29 +0530 Subject: [PATCH 159/291] Log device usage stats during thread startup --- ui/easydiffusion/renderer.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ui/easydiffusion/renderer.py b/ui/easydiffusion/renderer.py index 6c10dca1..6b110468 100644 --- a/ui/easydiffusion/renderer.py +++ b/ui/easydiffusion/renderer.py @@ -17,6 +17,7 @@ from sdkit.utils import ( img_to_base64_str, img_to_buffer, latent_samples_to_images, + get_device_usage, ) context = Context() # thread-local @@ -40,6 +41,9 @@ def init(device): app_config.get("test_diffusers", False) and app_config.get("update_branch", "main") != "main" ) + log.info("Device usage during initialization:") + get_device_usage(device, log_info=True) + device_manager.device_init(context, device) From 843d22d0d42b5f5c64789b5016bad7b41310e37d Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 3 May 2023 14:53:18 +0530 Subject: [PATCH 160/291] Update README.md --- README.md | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 3cb0bf8e..54d66221 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,11 @@ Click the download button for your operating system:

**Hardware requirements:** -- **Windows:** NVIDIA graphics card, or run on your CPU -- **Linux:** NVIDIA or AMD graphics card, or run on your CPU -- **Mac:** M1 or M2, or run on your CPU +- **Windows:** NVIDIA graphics card, or run on your CPU. +- **Linux:** NVIDIA or AMD graphics card, or run on your CPU. +- **Mac:** M1 or M2, or run on your CPU. +- Minimum 8 GB of system RAM. +- Atleast 25 GB of space on the hard disk. The installer will take care of whatever is needed. If you face any problems, you can join the friendly [Discord community](https://discord.com/invite/u9yhsFmEkB) and ask for assistance. @@ -113,14 +115,6 @@ Useful for judging (and stopping) an image quickly, without waiting for it to fi ![Screenshot of task queue](https://user-images.githubusercontent.com/844287/217043984-0b35f73b-1318-47cb-9eed-a2a91b430490.png) - -# System Requirements -1. Windows 10/11, or Linux. Experimental support for Mac is coming soon. -2. An NVIDIA graphics card, preferably with 4GB or more of VRAM. If you don't have a compatible graphics card, it'll automatically run in the slower "CPU Mode". -3. Minimum 8 GB of RAM and 25GB of disk space. - -You don't need to install or struggle with Python, Anaconda, Docker etc. The installer will take care of whatever is needed. - ---- # How to use? From 75f0780bd106e588a103a7f2b95bd4714d304f8c Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 3 May 2023 16:12:11 +0530 Subject: [PATCH 161/291] sdkit 1.0.84 - VRAM optimizations for the diffusers version --- scripts/check_modules.py | 2 +- ui/easydiffusion/renderer.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index c8955e0a..b1ec44d6 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.83", + "sdkit": "1.0.84", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", diff --git a/ui/easydiffusion/renderer.py b/ui/easydiffusion/renderer.py index 6b110468..f685d038 100644 --- a/ui/easydiffusion/renderer.py +++ b/ui/easydiffusion/renderer.py @@ -42,7 +42,7 @@ def init(device): ) log.info("Device usage during initialization:") - get_device_usage(device, log_info=True) + get_device_usage(device, log_info=True, process_usage_only=False) device_manager.device_init(context, device) From 5c95bcc65d857760f1fe91670b08ed90b1c0ab0d Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 3 May 2023 16:13:57 +0530 Subject: [PATCH 162/291] changelog --- CHANGES.md | 1 + ui/index.html | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 6b4faf95..d34aaddc 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -21,6 +21,7 @@ Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. ### Detailed changelog +* 2.5.35 - 3 May 2023 - (beta-only) VRAM Optimizations for the "Test Diffusers" version. This change significantly reduces the amount of VRAM used by the diffusers version during image generation. * 2.5.34 - 22 Apr 2023 - Don't start the browser in an incognito new profile (on Windows). Thanks @JeLuf. * 2.5.33 - 21 Apr 2023 - Install PyTorch 2.0 on new installations (on Windows and Linux). * 2.5.32 - 19 Apr 2023 - Automatically check for black images, and set full-precision if necessary (for attn). This means custom models based on Stable Diffusion v2.1 will just work, without needing special command-line arguments or editing of yaml config files. diff --git a/ui/index.html b/ui/index.html index 3db86593..be522689 100644 --- a/ui/index.html +++ b/ui/index.html @@ -30,7 +30,7 @@

Easy Diffusion - v2.5.34 + v2.5.35

From c18bf3e41304a33c03961343ed531cf5e30e5998 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 3 May 2023 18:18:18 +0530 Subject: [PATCH 163/291] Update CHANGES.md --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index d34aaddc..9fe2cff0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -21,7 +21,7 @@ Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. ### Detailed changelog -* 2.5.35 - 3 May 2023 - (beta-only) VRAM Optimizations for the "Test Diffusers" version. This change significantly reduces the amount of VRAM used by the diffusers version during image generation. +* 2.5.35 - 3 May 2023 - (beta-only) First round of VRAM Optimizations for the "Test Diffusers" version. This change significantly reduces the amount of VRAM used by the diffusers version during image generation. The VRAM usage is still not equal to the "non-diffusers" version, but more optimizations are coming soon. * 2.5.34 - 22 Apr 2023 - Don't start the browser in an incognito new profile (on Windows). Thanks @JeLuf. * 2.5.33 - 21 Apr 2023 - Install PyTorch 2.0 on new installations (on Windows and Linux). * 2.5.32 - 19 Apr 2023 - Automatically check for black images, and set full-precision if necessary (for attn). This means custom models based on Stable Diffusion v2.1 will just work, without needing special command-line arguments or editing of yaml config files. From 01c77129617bc718d31ba0a585ec4d0bacce1c3c Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 3 May 2023 18:47:09 +0530 Subject: [PATCH 164/291] Increase task timeout from 15 mins to 30 mins --- ui/easydiffusion/task_manager.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/easydiffusion/task_manager.py b/ui/easydiffusion/task_manager.py index 450636cf..c11acbec 100644 --- a/ui/easydiffusion/task_manager.py +++ b/ui/easydiffusion/task_manager.py @@ -7,7 +7,7 @@ Notes: import json import traceback -TASK_TTL = 15 * 60 # seconds, Discard last session's task timeout +TASK_TTL = 30 * 60 # seconds, Discard last session's task timeout import queue import threading @@ -398,8 +398,8 @@ def get_devices(): return {"name": device_manager.get_processor_name()} mem_free, mem_total = torch.cuda.mem_get_info(device) - mem_free /= float(10 ** 9) - mem_total /= float(10 ** 9) + mem_free /= float(10**9) + mem_total /= float(10**9) return { "name": torch.cuda.get_device_name(device), From 06c990e94dc4457f76986776b7f57ef41e3106b3 Mon Sep 17 00:00:00 2001 From: JeLuF Date: Thu, 4 May 2023 00:05:11 +0200 Subject: [PATCH 165/291] Default value for hypernetworkStrength Don't fail when the Hypernetwork Strength text input field is empty --- ui/media/js/main.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ui/media/js/main.js b/ui/media/js/main.js index 08a73024..a54f6ecb 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -692,6 +692,9 @@ function makeImage() { if (guidanceScaleField.value == "") { guidanceScaleField.value = guidanceScaleSlider.value / 10 } + if (hypernetworkStrengthField.value == "") { + hypernetworkStrengthField.value = hypernetworkStrengthSlider.value / 100 + } const taskTemplate = getCurrentUserRequest() const newTaskRequests = getPrompts().map((prompt) => Object.assign({}, taskTemplate, { From 8e416cef25fc1c9b715276fdfb87243fcfb9f65f Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Thu, 4 May 2023 10:12:06 +0530 Subject: [PATCH 166/291] Disable self test link --- ui/plugins/ui/{selftest.plugin.js => selftest.plugin_.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ui/plugins/ui/{selftest.plugin.js => selftest.plugin_.js} (100%) diff --git a/ui/plugins/ui/selftest.plugin.js b/ui/plugins/ui/selftest.plugin_.js similarity index 100% rename from ui/plugins/ui/selftest.plugin.js rename to ui/plugins/ui/selftest.plugin_.js From b27a14b1b444be5d4add6e03d816f35d75138c8c Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Thu, 4 May 2023 16:04:45 +0530 Subject: [PATCH 167/291] sdkit 1.0.85 - torch.Generator fix for mps/mac --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index b1ec44d6..ede9eeda 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.84", + "sdkit": "1.0.85", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", From f9cfe1da459afb7543530eee9a247c5b602ece0c Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Thu, 4 May 2023 16:09:28 +0530 Subject: [PATCH 168/291] sdkit 1.0.86 - don't use cpu offload for mps/mac, doesn't make sense since the memory is shared between GPU/CPU --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index ede9eeda..209a2049 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.85", + "sdkit": "1.0.86", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", From 64cfd5506504cc68f24505c73857744a1c626f58 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Thu, 4 May 2023 16:31:40 +0530 Subject: [PATCH 169/291] sdkit 1.0.87 - typo --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 209a2049..031f7d66 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.86", + "sdkit": "1.0.87", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", From 3f9ec378a0094d7f3d25114a7d5c73a36206fdb1 Mon Sep 17 00:00:00 2001 From: patriceac <48073125+patriceac@users.noreply.github.com> Date: Thu, 4 May 2023 09:16:19 -0700 Subject: [PATCH 170/291] Fix restoration of inactive image modifiers --- ui/media/js/image-modifiers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/media/js/image-modifiers.js b/ui/media/js/image-modifiers.js index fd4ecaf1..69f31ab1 100644 --- a/ui/media/js/image-modifiers.js +++ b/ui/media/js/image-modifiers.js @@ -246,7 +246,7 @@ function refreshInactiveTags(inactiveTags) { overlays.forEach((i) => { let modifierName = i.parentElement.getElementsByClassName("modifier-card-label")[0].getElementsByTagName("p")[0] .dataset.fullName - if (inactiveTags?.find((element) => element === modifierName) !== undefined) { + if (inactiveTags?.find((element) => trimModifiers(element) === modifierName) !== undefined) { i.parentElement.classList.add("modifier-toggle-inactive") } }) From fec21408962b451c693cae271c57289ca350538f Mon Sep 17 00:00:00 2001 From: Olivia Godone-Maresca Date: Thu, 4 May 2023 19:36:10 -0400 Subject: [PATCH 171/291] Allow grabbing the image to scroll zoomed in images --- ui/media/css/image-modal.css | 8 +++++ ui/media/js/image-modal.js | 63 ++++++++++++++++++++++++++++++++++-- 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/ui/media/css/image-modal.css b/ui/media/css/image-modal.css index 1001807c..64096003 100644 --- a/ui/media/css/image-modal.css +++ b/ui/media/css/image-modal.css @@ -70,6 +70,14 @@ max-height: calc(100vh - (var(--popup-padding) * 2) - 4px); } +#viewFullSizeImgModal img:not(.natural-zoom) { + cursor: grab; +} + +#viewFullSizeImgModal .grabbing img:not(.natural-zoom) { + cursor: grabbing; +} + #viewFullSizeImgModal .content > div::-webkit-scrollbar-track, #viewFullSizeImgModal .content > div::-webkit-scrollbar-corner { background: rgba(0, 0, 0, .5) } diff --git a/ui/media/js/image-modal.js b/ui/media/js/image-modal.js index 3a97c4d8..28c1eaf2 100644 --- a/ui/media/js/image-modal.js +++ b/ui/media/js/image-modal.js @@ -63,15 +63,73 @@ const imageModal = (function() { setZoomLevel(imageContainer.querySelector("img")?.classList?.contains("natural-zoom")) ) - const state = { + const initialState = () => ({ previous: undefined, next: undefined, + + start: { + x: 0, + y: 0, + }, + + scroll: { + x: 0, + y: 0, + }, + }) + + const state = initialState() + + // Allow grabbing the image to scroll + const stopGrabbing = (e) => { + if(imageContainer.classList.contains("grabbing")) { + imageContainer.classList.remove("grabbing") + e?.preventDefault() + console.log(`stopGrabbing()`, e) + } + } + + const addImageGrabbing = (image) => { + image?.addEventListener('mousedown', (e) => { + if (!image.classList.contains("natural-zoom")) { + e.stopPropagation() + e.stopImmediatePropagation() + e.preventDefault() + + imageContainer.classList.add("grabbing") + state.start.x = e.pageX - imageContainer.offsetLeft + state.scroll.x = imageContainer.scrollLeft + state.start.y = e.pageY - imageContainer.offsetTop + state.scroll.y = imageContainer.scrollTop + } + }) + + image?.addEventListener('mouseup', stopGrabbing) + image?.addEventListener('mouseleave', stopGrabbing) + image?.addEventListener('mousemove', (e) => { + if(imageContainer.classList.contains("grabbing")) { + e.stopPropagation() + e.stopImmediatePropagation() + e.preventDefault() + + // Might need to increase this multiplier based on the image size to window size ratio + // The default 1:1 is pretty slow + const multiplier = 1.0 + + const deltaX = e.pageX - imageContainer.offsetLeft - state.start.x + imageContainer.scrollLeft = state.scroll.x - (deltaX * multiplier) + const deltaY = e.pageY - imageContainer.offsetTop - state.start.y + imageContainer.scrollTop = state.scroll.y - (deltaY * multiplier) + } + }) } const clear = () => { imageContainer.innerHTML = "" - Object.keys(state).forEach((key) => delete state[key]) + Object.entries(initialState()).forEach(([key, value]) => state[key] = value) + + stopGrabbing() } const close = () => { @@ -95,6 +153,7 @@ const imageModal = (function() { const src = typeof options === "string" ? options : options.src const imgElem = createElement("img", { src }, "natural-zoom") + addImageGrabbing(imgElem) imageContainer.appendChild(imgElem) modalElem.classList.add("active") document.body.style.overflow = "hidden" From 2d1be6186e7037b146fe8062145e8593990db719 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 10 May 2023 20:19:17 +0530 Subject: [PATCH 172/291] sdkit 1.0.88 - Fix LoRA in low VRAM mode --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 031f7d66..3fcfce14 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.87", + "sdkit": "1.0.88", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", From 08f44472f8bd0052fcb04cb443180b13cc961a6e Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 10 May 2023 20:20:59 +0530 Subject: [PATCH 173/291] changelog --- CHANGES.md | 1 + ui/index.html | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 9fe2cff0..570d0c72 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -21,6 +21,7 @@ Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. ### Detailed changelog +* 2.5.36 - 10 May 2023 - (beta-only) Bug fix for "meta" error when using a LoRA in 'low' VRAM usage mode. * 2.5.35 - 3 May 2023 - (beta-only) First round of VRAM Optimizations for the "Test Diffusers" version. This change significantly reduces the amount of VRAM used by the diffusers version during image generation. The VRAM usage is still not equal to the "non-diffusers" version, but more optimizations are coming soon. * 2.5.34 - 22 Apr 2023 - Don't start the browser in an incognito new profile (on Windows). Thanks @JeLuf. * 2.5.33 - 21 Apr 2023 - Install PyTorch 2.0 on new installations (on Windows and Linux). diff --git a/ui/index.html b/ui/index.html index be522689..d196b99d 100644 --- a/ui/index.html +++ b/ui/index.html @@ -30,7 +30,7 @@

Easy Diffusion - v2.5.35 + v2.5.36

From 566a83ce3f14af592903ef0417575229e3b5277a Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Thu, 11 May 2023 14:49:15 +0530 Subject: [PATCH 174/291] sdkit 1.0.89 - use half precision in test diffusers for low vram usage mode' --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 3fcfce14..b9797515 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.88", + "sdkit": "1.0.89", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", From 4bca739b3d64f3a5827475bd50c52a9025d6e31d Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Thu, 11 May 2023 14:52:30 +0530 Subject: [PATCH 175/291] changelog --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 570d0c72..d6d93bd7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -21,6 +21,7 @@ Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. ### Detailed changelog +* 2.5.36 - 11 May 2023 - (beta-only) Another VRAM optimization for "low" VRAM usage mode. * 2.5.36 - 10 May 2023 - (beta-only) Bug fix for "meta" error when using a LoRA in 'low' VRAM usage mode. * 2.5.35 - 3 May 2023 - (beta-only) First round of VRAM Optimizations for the "Test Diffusers" version. This change significantly reduces the amount of VRAM used by the diffusers version during image generation. The VRAM usage is still not equal to the "non-diffusers" version, but more optimizations are coming soon. * 2.5.34 - 22 Apr 2023 - Don't start the browser in an incognito new profile (on Windows). Thanks @JeLuf. From add05228bdb34278e0393cfb47ccbcd79392b9fc Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Thu, 11 May 2023 16:30:06 +0530 Subject: [PATCH 176/291] sdkit 1.0.91 - use slice size 1 for low vram usage mode, to reduce VRAM usage --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index b9797515..8da111e0 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.89", + "sdkit": "1.0.91", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", From 1cba62af242c9ebfd1109b18bc0d8fafeda463a2 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Thu, 11 May 2023 16:30:32 +0530 Subject: [PATCH 177/291] changelog --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index d6d93bd7..6168fcc3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -21,7 +21,7 @@ Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. ### Detailed changelog -* 2.5.36 - 11 May 2023 - (beta-only) Another VRAM optimization for "low" VRAM usage mode. +* 2.5.36 - 11 May 2023 - (beta-only) More VRAM optimizations for "low" VRAM usage mode. * 2.5.36 - 10 May 2023 - (beta-only) Bug fix for "meta" error when using a LoRA in 'low' VRAM usage mode. * 2.5.35 - 3 May 2023 - (beta-only) First round of VRAM Optimizations for the "Test Diffusers" version. This change significantly reduces the amount of VRAM used by the diffusers version during image generation. The VRAM usage is still not equal to the "non-diffusers" version, but more optimizations are coming soon. * 2.5.34 - 22 Apr 2023 - Don't start the browser in an incognito new profile (on Windows). Thanks @JeLuf. From 7240c91db722b0c92803f62131f20a887276c5c7 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 12 May 2023 14:48:48 +0530 Subject: [PATCH 178/291] Update CHANGES.md --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 6168fcc3..f2432505 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -23,6 +23,7 @@ Our focus continues to remain on an easy installation experience, and an easy us ### Detailed changelog * 2.5.36 - 11 May 2023 - (beta-only) More VRAM optimizations for "low" VRAM usage mode. * 2.5.36 - 10 May 2023 - (beta-only) Bug fix for "meta" error when using a LoRA in 'low' VRAM usage mode. +* 2.5.35 - 8 May 2023 - Allow dragging a zoomed-in image (after opening an image with the "expand" button). Thanks @ogmaresca. * 2.5.35 - 3 May 2023 - (beta-only) First round of VRAM Optimizations for the "Test Diffusers" version. This change significantly reduces the amount of VRAM used by the diffusers version during image generation. The VRAM usage is still not equal to the "non-diffusers" version, but more optimizations are coming soon. * 2.5.34 - 22 Apr 2023 - Don't start the browser in an incognito new profile (on Windows). Thanks @JeLuf. * 2.5.33 - 21 Apr 2023 - Install PyTorch 2.0 on new installations (on Windows and Linux). From 8142fd0701d63a35dd8782ebd0b340d4b2d93aeb Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 12 May 2023 14:50:04 +0530 Subject: [PATCH 179/291] Update CHANGES.md --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index f2432505..d6ac5cda 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,7 @@ ### Major Changes - **Nearly twice as fast** - significantly faster speed of image generation. Code contributions are welcome to make our project even faster: https://github.com/easydiffusion/sdkit/#is-it-fast - **Mac M1/M2 support** - Experimental support for Mac M1/M2. Thanks @michaelgallacher, @JeLuf and vishae. +- **AMD support for Linux** - Experimental support for AMD GPUs on Linux. Thanks @DianaNites and @JeLuf. - **Full support for Stable Diffusion 2.1 (including CPU)** - supports loading v1.4 or v2.0 or v2.1 models seamlessly. No need to enable "Test SD2", and no need to add `sd2_` to your SD 2.0 model file names. Works on CPU as well. - **Memory optimized Stable Diffusion 2.1** - you can now use Stable Diffusion 2.1 models, with the same low VRAM optimizations that we've always had for SD 1.4. Please note, the SD 2.0 and 2.1 models require more GPU and System RAM, as compared to the SD 1.4 and 1.5 models. - **11 new samplers!** - explore the new samplers, some of which can generate great images in less than 10 inference steps! We've added the Karras and UniPC samplers. Thanks @Schorny for the UniPC samplers. From 45db4bb0367e237244ccf675c1f2b9c3522e5255 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 12 May 2023 16:49:13 +0530 Subject: [PATCH 180/291] sdkit 1.0.92 - more vram optimizations for low,balanced,high - reduces VRAM usage by 20% (especially with larger images) --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 8da111e0..21392011 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.91", + "sdkit": "1.0.92", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", From 366bc7275981f1b4d3eb76a40d0d1faa11764419 Mon Sep 17 00:00:00 2001 From: JeLuF Date: Mon, 15 May 2023 17:01:21 +0200 Subject: [PATCH 181/291] Add GTX1630 to list of FP32 GPUs https://discord.com/channels/1014774730907209781/1014774732018683926/1107677076233912340 --- ui/easydiffusion/device_manager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/easydiffusion/device_manager.py b/ui/easydiffusion/device_manager.py index 59c07ea3..5dd244b7 100644 --- a/ui/easydiffusion/device_manager.py +++ b/ui/easydiffusion/device_manager.py @@ -165,6 +165,7 @@ def needs_to_force_full_precision(context): and ( " 1660" in device_name or " 1650" in device_name + or " 1630" in device_name or " t400" in device_name or " t550" in device_name or " t600" in device_name From 7562a882f4699084c3e07a6ad69b04b513ca2ab9 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 16 May 2023 16:02:20 +0530 Subject: [PATCH 182/291] sdkit 1.0.93 - lower vram usage for balanced mode, by using attention slice of 1 --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 21392011..18549217 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.92", + "sdkit": "1.0.93", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", From 1605c5fbccb46f66dae672bd8fb2f41e3e7989c6 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 16 May 2023 16:03:12 +0530 Subject: [PATCH 183/291] changelog --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index d6ac5cda..3b910ae6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -22,6 +22,7 @@ Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. ### Detailed changelog +* 2.5.36 - 16 May 2023 - (beta-only) More VRAM optimizations for "balanced" VRAM usage mode. * 2.5.36 - 11 May 2023 - (beta-only) More VRAM optimizations for "low" VRAM usage mode. * 2.5.36 - 10 May 2023 - (beta-only) Bug fix for "meta" error when using a LoRA in 'low' VRAM usage mode. * 2.5.35 - 8 May 2023 - Allow dragging a zoomed-in image (after opening an image with the "expand" button). Thanks @ogmaresca. From 9410879b73cbac56ed23624c71fe74e9e642b985 Mon Sep 17 00:00:00 2001 From: patriceac <48073125+patriceac@users.noreply.github.com> Date: Tue, 16 May 2023 17:43:14 -0700 Subject: [PATCH 184/291] Fix error when removing image Error report: https://discord.com/channels/1014774730907209781/1085803885500825600/1108150298289115187 --- ui/plugins/ui/Autoscroll.plugin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/plugins/ui/Autoscroll.plugin.js b/ui/plugins/ui/Autoscroll.plugin.js index 26969365..336e8b50 100644 --- a/ui/plugins/ui/Autoscroll.plugin.js +++ b/ui/plugins/ui/Autoscroll.plugin.js @@ -23,7 +23,7 @@ img.addEventListener( "load", function() { - img.closest(".imageTaskContainer").scrollIntoView() + img?.closest(".imageTaskContainer").scrollIntoView() }, { once: true } ) From a25364732b3f74dbfd5083cc4d7a0f84ccc5d9d5 Mon Sep 17 00:00:00 2001 From: patriceac <48073125+patriceac@users.noreply.github.com> Date: Wed, 17 May 2023 02:04:20 -0700 Subject: [PATCH 185/291] Support for CodeFormer Depends on https://github.com/easydiffusion/sdkit/pull/34. --- ui/easydiffusion/model_manager.py | 3 ++- ui/easydiffusion/renderer.py | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ui/easydiffusion/model_manager.py b/ui/easydiffusion/model_manager.py index 7bf56575..2bb4852d 100644 --- a/ui/easydiffusion/model_manager.py +++ b/ui/easydiffusion/model_manager.py @@ -107,11 +107,12 @@ def resolve_model_to_use(model_name: str = None, model_type: str = None): def reload_models_if_necessary(context: Context, task_data: TaskData): + face_correction_model = "codeformer" if "codeformer" in task_data.use_face_correction.lower() else "gfpgan" model_paths_in_req = { "stable-diffusion": task_data.use_stable_diffusion_model, "vae": task_data.use_vae_model, "hypernetwork": task_data.use_hypernetwork_model, - "gfpgan": task_data.use_face_correction, + face_correction_model: task_data.use_face_correction, "realesrgan": task_data.use_upscale, "nsfw_checker": True if task_data.block_nsfw else None, "lora": task_data.use_lora_model, diff --git a/ui/easydiffusion/renderer.py b/ui/easydiffusion/renderer.py index f685d038..e1176c8b 100644 --- a/ui/easydiffusion/renderer.py +++ b/ui/easydiffusion/renderer.py @@ -158,7 +158,9 @@ def filter_images(task_data: TaskData, images: list, user_stopped): filters_to_apply = [] if task_data.block_nsfw: filters_to_apply.append("nsfw_checker") - if task_data.use_face_correction and "gfpgan" in task_data.use_face_correction.lower(): + if task_data.use_face_correction and "codeformer" in task_data.use_face_correction.lower(): + filters_to_apply.append("codeformer") + elif task_data.use_face_correction and "gfpgan" in task_data.use_face_correction.lower(): filters_to_apply.append("gfpgan") if task_data.use_upscale and "realesrgan" in task_data.use_upscale.lower(): filters_to_apply.append("realesrgan") From 9d408a62bfd4c895807ef1b5e154550cf93ca661 Mon Sep 17 00:00:00 2001 From: Olivia Godone-Maresca Date: Wed, 17 May 2023 21:13:06 -0400 Subject: [PATCH 186/291] Add DDPM and DEIS samplers for diffusers These new samplers will be hidden when diffusers is disabled. Also, samplers that aren't implemented in diffusers yet will be disabled when using diffusers --- README.md | 2 +- ui/index.html | 18 ++++++++++-------- ui/media/js/parameters.js | 21 +++++++++++++++------ 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 3cb0bf8e..2d7fcf5a 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ Just delete the `EasyDiffusion` folder to uninstall all the downloaded packages. ### Image generation - **Supports**: "*Text to Image*" and "*Image to Image*". -- **19 Samplers**: `ddim`, `plms`, `heun`, `euler`, `euler_a`, `dpm2`, `dpm2_a`, `lms`, `dpm_solver_stability`, `dpmpp_2s_a`, `dpmpp_2m`, `dpmpp_sde`, `dpm_fast`, `dpm_adaptive`, `unipc_snr`, `unipc_tu`, `unipc_tq`, `unipc_snr_2`, `unipc_tu_2`. +- **21 Samplers**: `ddim`, `plms`, `heun`, `euler`, `euler_a`, `dpm2`, `dpm2_a`, `lms`, `dpm_solver_stability`, `dpmpp_2s_a`, `dpmpp_2m`, `dpmpp_sde`, `dpm_fast`, `dpm_adaptive`, `ddpm`, `deis`, `unipc_snr`, `unipc_tu`, `unipc_tq`, `unipc_snr_2`, `unipc_tu_2`. - **In-Painting**: Specify areas of your image to paint into. - **Simple Drawing Tool**: Draw basic images to guide the AI, without needing an external drawing program. - **Face Correction (GFPGAN)** diff --git a/ui/index.html b/ui/index.html index d196b99d..53632610 100644 --- a/ui/index.html +++ b/ui/index.html @@ -154,16 +154,18 @@ - + - - - - - - + + + + + + + + - + Click to learn more about samplers diff --git a/ui/media/js/parameters.js b/ui/media/js/parameters.js index 75abecd7..8c38f2f5 100644 --- a/ui/media/js/parameters.js +++ b/ui/media/js/parameters.js @@ -388,15 +388,24 @@ async function getAppConfig() { if (config.net && config.net.listen_port !== undefined) { listenPortField.value = config.net.listen_port } - if (config.test_diffusers === undefined || config.update_branch === "main") { - testDiffusers.checked = false + + const testDiffusersEnabled = config.test_diffusers && config.update_branch !== "main" + testDiffusers.checked = testDiffusersEnabled + + if (!testDiffusersEnabled) { document.querySelector("#lora_model_container").style.display = "none" document.querySelector("#lora_alpha_container").style.display = "none" + + document.querySelectorAll("#sampler_name option.diffusers-only").forEach(option => { + option.style.display = "none" + }) } else { - testDiffusers.checked = config.test_diffusers && config.update_branch !== "main" - document.querySelector("#lora_model_container").style.display = testDiffusers.checked ? "" : "none" - document.querySelector("#lora_alpha_container").style.display = - testDiffusers.checked && loraModelField.value !== "" ? "" : "none" + document.querySelector("#lora_model_container").style.display = "" + document.querySelector("#lora_alpha_container").style.display = loraModelField.value ? "" : "none" + + document.querySelectorAll("#sampler_name option.k_diffusion-only").forEach(option => { + option.disabled = true + }) } console.log("get config status response", config) From d3dd15eb63f0f1de3c5654ca224508d4e3350830 Mon Sep 17 00:00:00 2001 From: Olivia Godone-Maresca Date: Wed, 17 May 2023 21:44:28 -0400 Subject: [PATCH 187/291] Fix unipc_tu --- ui/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/index.html b/ui/index.html index 53632610..32d31b34 100644 --- a/ui/index.html +++ b/ui/index.html @@ -162,7 +162,7 @@ - + From 00603ce124d6a137e7e6001cd01fb2007737cd82 Mon Sep 17 00:00:00 2001 From: JeLuF Date: Thu, 18 May 2023 13:55:45 +0200 Subject: [PATCH 188/291] Add Clip Skip support --- ui/easydiffusion/model_manager.py | 12 +++++++++++- ui/easydiffusion/types.py | 1 + ui/index.html | 11 +++++++---- ui/media/js/auto-save.js | 1 + ui/media/js/dnd.js | 8 ++++++++ ui/media/js/engine.js | 2 ++ ui/media/js/main.js | 7 +++++++ ui/media/js/parameters.js | 1 + 8 files changed, 38 insertions(+), 5 deletions(-) diff --git a/ui/easydiffusion/model_manager.py b/ui/easydiffusion/model_manager.py index 7bf56575..324dcec9 100644 --- a/ui/easydiffusion/model_manager.py +++ b/ui/easydiffusion/model_manager.py @@ -122,7 +122,7 @@ def reload_models_if_necessary(context: Context, task_data: TaskData): if context.model_paths.get(model_type) != path } - if set_vram_optimizations(context): # reload SD + if set_vram_optimizations(context) or set_clip_skip(context, task_data): # reload SD models_to_reload["stable-diffusion"] = model_paths_in_req["stable-diffusion"] for model_type, model_path_in_req in models_to_reload.items(): @@ -157,6 +157,16 @@ def set_vram_optimizations(context: Context): return False +def set_clip_skip(context: Context, task_data: TaskData): + clip_skip = task_data.clip_skip + + if clip_skip != context.clip_skip: + context.clip_skip = clip_skip + return True + + return False + + def make_model_folders(): for model_type in KNOWN_MODEL_TYPES: model_dir_path = os.path.join(app.MODELS_DIR, model_type) diff --git a/ui/easydiffusion/types.py b/ui/easydiffusion/types.py index 7462355f..7a5201ab 100644 --- a/ui/easydiffusion/types.py +++ b/ui/easydiffusion/types.py @@ -48,6 +48,7 @@ class TaskData(BaseModel): metadata_output_format: str = "txt" # or "json" stream_image_progress: bool = False stream_image_progress_interval: int = 5 + clip_skip: bool = False class MergeRequest(BaseModel): diff --git a/ui/index.html b/ui/index.html index d196b99d..45263e4b 100644 --- a/ui/index.html +++ b/ui/index.html @@ -135,10 +135,13 @@ Click to learn more about custom models - + + + + + Click to learn more about Clip Skip + + Click to learn more about VAEs diff --git a/ui/media/js/auto-save.js b/ui/media/js/auto-save.js index 1e536247..ee01ba98 100644 --- a/ui/media/js/auto-save.js +++ b/ui/media/js/auto-save.js @@ -13,6 +13,7 @@ const SETTINGS_IDS_LIST = [ "num_outputs_total", "num_outputs_parallel", "stable_diffusion_model", + "clip_skip", "vae_model", "hypernetwork_model", "lora_model", diff --git a/ui/media/js/dnd.js b/ui/media/js/dnd.js index 548b06ad..02848266 100644 --- a/ui/media/js/dnd.js +++ b/ui/media/js/dnd.js @@ -240,6 +240,14 @@ const TASK_MAPPING = { readUI: () => stableDiffusionModelField.value, parse: (val) => val, }, + clip_skip: { + name: "Clip Skip", + setUI: (value) => { + clip_skip.checked = value + }, + readUI: () => clip_skip.checked, + parse: (val) => Boolean(val), + }, use_vae_model: { name: "VAE model", setUI: (use_vae_model) => { diff --git a/ui/media/js/engine.js b/ui/media/js/engine.js index f396d951..eccae6ac 100644 --- a/ui/media/js/engine.js +++ b/ui/media/js/engine.js @@ -750,6 +750,7 @@ sampler_name: "string", use_stable_diffusion_model: "string", + clip_skip: "boolean", num_inference_steps: "number", guidance_scale: "number", @@ -763,6 +764,7 @@ const TASK_DEFAULTS = { sampler_name: "plms", use_stable_diffusion_model: "sd-v1-4", + clip_skip: false, num_inference_steps: 50, guidance_scale: 7.5, negative_prompt: "", diff --git a/ui/media/js/main.js b/ui/media/js/main.js index a54f6ecb..c69535df 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -13,6 +13,11 @@ const taskConfigSetup = { num_inference_steps: "Inference Steps", guidance_scale: "Guidance Scale", use_stable_diffusion_model: "Model", + clip_skip: { + label: "Clip Skip", + visible: ({ reqBody }) => reqBody?.clip_skip, + value: ({ reqBody }) => "yes", + }, use_vae_model: { label: "VAE", visible: ({ reqBody }) => reqBody?.use_vae_model !== undefined && reqBody?.use_vae_model.trim() !== "", @@ -82,6 +87,7 @@ let useUpscalingField = document.querySelector("#use_upscale") let upscaleModelField = document.querySelector("#upscale_model") let upscaleAmountField = document.querySelector("#upscale_amount") let stableDiffusionModelField = new ModelDropdown(document.querySelector("#stable_diffusion_model"), "stable-diffusion") +let clipSkipField = document.querySelector("#clip_skip") let vaeModelField = new ModelDropdown(document.querySelector("#vae_model"), "vae", "None") let hypernetworkModelField = new ModelDropdown(document.querySelector("#hypernetwork_model"), "hypernetwork", "None") let hypernetworkStrengthSlider = document.querySelector("#hypernetwork_strength_slider") @@ -1224,6 +1230,7 @@ function getCurrentUserRequest() { sampler_name: samplerField.value, //render_device: undefined, // Set device affinity. Prefer this device, but wont activate. use_stable_diffusion_model: stableDiffusionModelField.value, + clip_skip: clipSkipField.checked, use_vae_model: vaeModelField.value, stream_progress_updates: true, stream_image_progress: numOutputsTotal > 50 ? false : streamImageProgressField.checked, diff --git a/ui/media/js/parameters.js b/ui/media/js/parameters.js index 75abecd7..746dc00e 100644 --- a/ui/media/js/parameters.js +++ b/ui/media/js/parameters.js @@ -397,6 +397,7 @@ async function getAppConfig() { document.querySelector("#lora_model_container").style.display = testDiffusers.checked ? "" : "none" document.querySelector("#lora_alpha_container").style.display = testDiffusers.checked && loraModelField.value !== "" ? "" : "none" + document.querySelector("#clip_skip_config").classList.remove("displayNone") } console.log("get config status response", config) From b77036443ffc8175b21675ad49874c996ca40071 Mon Sep 17 00:00:00 2001 From: JeLuF Date: Thu, 18 May 2023 16:04:28 +0200 Subject: [PATCH 189/291] Fail gracefully if proc access isn't possible --- scripts/check_modules.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 18549217..df89f2b2 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -130,10 +130,13 @@ def include_cuda_versions(module_versions: tuple) -> tuple: def is_amd_on_linux(): if os_name == "Linux": - with open("/proc/bus/pci/devices", "r") as f: - device_info = f.read() - if "amdgpu" in device_info and "nvidia" not in device_info: - return True + try: + with open("/proc/bus/pci/devices", "r") as f: + device_info = f.read() + if "amdgpu" in device_info and "nvidia" not in device_info: + return True + except: + return False return False From 063d14d2ac00128ec3096faf09155ae4ce558404 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 19 May 2023 17:25:53 +0530 Subject: [PATCH 190/291] Allow GPUs with less than 2 GB, instead of restricting to 3 GB --- ui/easydiffusion/device_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/easydiffusion/device_manager.py b/ui/easydiffusion/device_manager.py index 59c07ea3..47297900 100644 --- a/ui/easydiffusion/device_manager.py +++ b/ui/easydiffusion/device_manager.py @@ -224,9 +224,9 @@ def is_device_compatible(device): try: _, mem_total = torch.cuda.mem_get_info(device) mem_total /= float(10 ** 9) - if mem_total < 3.0: + if mem_total < 2.0: if is_device_compatible.history.get(device) == None: - log.warn(f"GPU {device} with less than 3 GB of VRAM is not compatible with Stable Diffusion") + log.warn(f"GPU {device} with less than 2 GB of VRAM is not compatible with Stable Diffusion") is_device_compatible.history[device] = 1 return False except RuntimeError as e: From 53b23756a4d97502e03d0ef261c4054ac8726f41 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 19 May 2023 17:26:04 +0530 Subject: [PATCH 191/291] formatting --- ui/easydiffusion/device_manager.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/easydiffusion/device_manager.py b/ui/easydiffusion/device_manager.py index 47297900..4b92ac6e 100644 --- a/ui/easydiffusion/device_manager.py +++ b/ui/easydiffusion/device_manager.py @@ -98,8 +98,8 @@ def auto_pick_devices(currently_active_devices): continue mem_free, mem_total = torch.cuda.mem_get_info(device) - mem_free /= float(10 ** 9) - mem_total /= float(10 ** 9) + mem_free /= float(10**9) + mem_total /= float(10**9) device_name = torch.cuda.get_device_name(device) log.debug( f"{device} detected: {device_name} - Memory (free/total): {round(mem_free, 2)}Gb / {round(mem_total, 2)}Gb" @@ -181,7 +181,7 @@ def get_max_vram_usage_level(device): else: return "high" - mem_total /= float(10 ** 9) + mem_total /= float(10**9) if mem_total < 4.5: return "low" elif mem_total < 6.5: @@ -223,7 +223,7 @@ def is_device_compatible(device): # Memory check try: _, mem_total = torch.cuda.mem_get_info(device) - mem_total /= float(10 ** 9) + mem_total /= float(10**9) if mem_total < 2.0: if is_device_compatible.history.get(device) == None: log.warn(f"GPU {device} with less than 2 GB of VRAM is not compatible with Stable Diffusion") From 415213878dfb985c45fce946bd111f4951716e72 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 19 May 2023 17:28:54 +0530 Subject: [PATCH 192/291] sdkit 1.0.94 - vram optimizations - perform softmax in half precision --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 18549217..d6e0b251 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.93", + "sdkit": "1.0.94", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", From b08e9b79827e6034dba0361e746cad9b36d3e966 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 19 May 2023 17:29:10 +0530 Subject: [PATCH 193/291] changelog --- CHANGES.md | 1 + ui/index.html | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 3b910ae6..e29f75a0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -22,6 +22,7 @@ Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. ### Detailed changelog +* 2.5.37 - 19 May 2023 - (beta-only) More VRAM optimizations for all modes in diffusers. The VRAM usage for diffusers in "low" and "balanced" should now be equal or less than the non-diffusers version. Performs softmax in half precision, like sdkit does. * 2.5.36 - 16 May 2023 - (beta-only) More VRAM optimizations for "balanced" VRAM usage mode. * 2.5.36 - 11 May 2023 - (beta-only) More VRAM optimizations for "low" VRAM usage mode. * 2.5.36 - 10 May 2023 - (beta-only) Bug fix for "meta" error when using a LoRA in 'low' VRAM usage mode. diff --git a/ui/index.html b/ui/index.html index d196b99d..d4b8c7bd 100644 --- a/ui/index.html +++ b/ui/index.html @@ -30,7 +30,7 @@

Easy Diffusion - v2.5.36 + v2.5.37

From 107323d8e7dfe26383c59af7759aacc1f73b81e6 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 19 May 2023 17:42:47 +0530 Subject: [PATCH 194/291] sdkit 1.0.95 - lower vram usage for high mode --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 4b88cd1c..98748967 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.94", + "sdkit": "1.0.95", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", From 11e1436e2efb92b41aae84289addc4ab3b1b9171 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 19 May 2023 17:56:20 +0530 Subject: [PATCH 195/291] 2 GB cards aren't exactly 2 GB --- ui/easydiffusion/device_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/easydiffusion/device_manager.py b/ui/easydiffusion/device_manager.py index 3a820116..dc705927 100644 --- a/ui/easydiffusion/device_manager.py +++ b/ui/easydiffusion/device_manager.py @@ -225,7 +225,7 @@ def is_device_compatible(device): try: _, mem_total = torch.cuda.mem_get_info(device) mem_total /= float(10**9) - if mem_total < 2.0: + if mem_total < 1.9: if is_device_compatible.history.get(device) == None: log.warn(f"GPU {device} with less than 2 GB of VRAM is not compatible with Stable Diffusion") is_device_compatible.history[device] = 1 From 760909f4958765f2496079b52ac7baef8db2eb92 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 19 May 2023 18:23:10 +0530 Subject: [PATCH 196/291] Update CHANGES.md --- CHANGES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index e29f75a0..9b2b72c1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -22,6 +22,8 @@ Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. ### Detailed changelog +* 2.5.37 - 19 May 2023 - (beta-only) Two more samplers: DDPM and DEIS. Also disables the samplers that aren't working yet in the Diffusers version. Thanks @ogmaresca. +* 2.5.37 - 19 May 2023 - (beta-only) Support CLIP-Skip. You can set this option under the models dropdown. Thanks @JeLuf. * 2.5.37 - 19 May 2023 - (beta-only) More VRAM optimizations for all modes in diffusers. The VRAM usage for diffusers in "low" and "balanced" should now be equal or less than the non-diffusers version. Performs softmax in half precision, like sdkit does. * 2.5.36 - 16 May 2023 - (beta-only) More VRAM optimizations for "balanced" VRAM usage mode. * 2.5.36 - 11 May 2023 - (beta-only) More VRAM optimizations for "low" VRAM usage mode. From bdf36a8dabff7f2dbd2dfe92c8c82d2dbc96e10f Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 19 May 2023 18:36:37 +0530 Subject: [PATCH 197/291] sdkit 1.0.96 - missing xformers import --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 98748967..1590f569 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.95", + "sdkit": "1.0.96", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", From a6dbdf664b2efa965926aa38c81a278817fb48c5 Mon Sep 17 00:00:00 2001 From: Olivia Godone-Maresca Date: Fri, 19 May 2023 19:05:32 -0400 Subject: [PATCH 198/291] Add Clip Skip to metadata files Also, force the properties to be in a consistent order so that, for example, LoRA strength will always be the line below LoRA model. I've rearranged the properties so that they are saved in the same order that the properties are laid out in the UI --- ui/easydiffusion/renderer.py | 2 +- ui/easydiffusion/utils/save_utils.py | 72 ++++++++++++++-------------- ui/media/js/dnd.js | 1 + 3 files changed, 39 insertions(+), 36 deletions(-) diff --git a/ui/easydiffusion/renderer.py b/ui/easydiffusion/renderer.py index f685d038..e26b4389 100644 --- a/ui/easydiffusion/renderer.py +++ b/ui/easydiffusion/renderer.py @@ -72,7 +72,7 @@ def make_images( def print_task_info(req: GenerateImageRequest, task_data: TaskData): - req_str = pprint.pformat(get_printable_request(req)).replace("[", "\[") + req_str = pprint.pformat(get_printable_request(req, task_data)).replace("[", "\[") task_str = pprint.pformat(task_data.dict()).replace("[", "\[") log.info(f"request: {req_str}") log.info(f"task data: {task_str}") diff --git a/ui/easydiffusion/utils/save_utils.py b/ui/easydiffusion/utils/save_utils.py index a7043f27..24b2198c 100644 --- a/ui/easydiffusion/utils/save_utils.py +++ b/ui/easydiffusion/utils/save_utils.py @@ -15,23 +15,24 @@ img_number_regex = re.compile("([0-9]{5,})") # keep in sync with `ui/media/js/dnd.js` TASK_TEXT_MAPPING = { "prompt": "Prompt", + "negative_prompt": "Negative Prompt", + "seed": "Seed", + "use_stable_diffusion_model": "Stable Diffusion model", + "clip_skip": "Clip Skip", + "use_vae_model": "VAE model", + "sampler_name": "Sampler", "width": "Width", "height": "Height", - "seed": "Seed", "num_inference_steps": "Steps", "guidance_scale": "Guidance Scale", "prompt_strength": "Prompt Strength", + "use_lora_model": "LoRA model", + "lora_alpha": "LoRA Strength", + "use_hypernetwork_model": "Hypernetwork model", + "hypernetwork_strength": "Hypernetwork Strength", "use_face_correction": "Use Face Correction", "use_upscale": "Use Upscaling", "upscale_amount": "Upscale By", - "sampler_name": "Sampler", - "negative_prompt": "Negative Prompt", - "use_stable_diffusion_model": "Stable Diffusion model", - "use_vae_model": "VAE model", - "use_hypernetwork_model": "Hypernetwork model", - "hypernetwork_strength": "Hypernetwork Strength", - "use_lora_model": "LoRA model", - "lora_alpha": "LoRA Strength", } time_placeholders = { @@ -179,27 +180,7 @@ def save_images_to_disk(images: list, filtered_images: list, req: GenerateImageR def get_metadata_entries_for_request(req: GenerateImageRequest, task_data: TaskData): - metadata = get_printable_request(req) - metadata.update( - { - "use_stable_diffusion_model": task_data.use_stable_diffusion_model, - "use_vae_model": task_data.use_vae_model, - "use_hypernetwork_model": task_data.use_hypernetwork_model, - "use_lora_model": task_data.use_lora_model, - "use_face_correction": task_data.use_face_correction, - "use_upscale": task_data.use_upscale, - } - ) - if metadata["use_upscale"] is not None: - metadata["upscale_amount"] = task_data.upscale_amount - if task_data.use_hypernetwork_model is None: - del metadata["hypernetwork_strength"] - if task_data.use_lora_model is None: - if "lora_alpha" in metadata: - del metadata["lora_alpha"] - app_config = app.getConfig() - if not app_config.get("test_diffusers", False) and "use_lora_model" in metadata: - del metadata["use_lora_model"] + metadata = get_printable_request(req, task_data) # if text, format it in the text format expected by the UI is_txt_format = task_data.metadata_output_format.lower() == "txt" @@ -213,12 +194,33 @@ def get_metadata_entries_for_request(req: GenerateImageRequest, task_data: TaskD return entries -def get_printable_request(req: GenerateImageRequest): - metadata = req.dict() - del metadata["init_image"] - del metadata["init_image_mask"] - if req.init_image is None: +def get_printable_request(req: GenerateImageRequest, task_data: TaskData): + req_metadata = req.dict() + task_data_metadata = task_data.dict() + + # Save the metadata in the order defined in TASK_TEXT_MAPPING + metadata = {} + for key in TASK_TEXT_MAPPING.keys(): + if key in req_metadata: + metadata[key] = req_metadata[key] + elif key in task_data_metadata: + metadata[key] = task_data_metadata[key] + + # Clean up the metadata + if req.init_image is None and "prompt_strength" in metadata: del metadata["prompt_strength"] + if task_data.use_upscale is None and "upscale_amount" in metadata: + del metadata["upscale_amount"] + if task_data.use_hypernetwork_model is None and "hypernetwork_strength" in metadata: + del metadata["hypernetwork_strength"] + if task_data.use_lora_model is None and "lora_alpha" in metadata: + del metadata["lora_alpha"] + + app_config = app.getConfig() + if not app_config.get("test_diffusers", False): + for key in (x for x in ["use_lora_model", "lora_alpha", "clip_skip"] if x in metadata): + del metadata[key] + return metadata diff --git a/ui/media/js/dnd.js b/ui/media/js/dnd.js index 02848266..8b66a3a4 100644 --- a/ui/media/js/dnd.js +++ b/ui/media/js/dnd.js @@ -37,6 +37,7 @@ function parseBoolean(stringValue) { } } +// keep in sync with `ui/easydiffusion/utils/save_utils.py` const TASK_MAPPING = { prompt: { name: "Prompt", From 70a3beeaa2c44c21b7f43c58ba98548a5e485535 Mon Sep 17 00:00:00 2001 From: JeLuF Date: Sun, 21 May 2023 17:42:47 +0200 Subject: [PATCH 199/291] Automatically use 'Low' when VRAM<4.5GB --- ui/media/js/auto-save.js | 16 ++++++++++++++++ ui/media/js/main.js | 2 +- ui/media/js/parameters.js | 4 +++- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/ui/media/js/auto-save.js b/ui/media/js/auto-save.js index ee01ba98..49d3c751 100644 --- a/ui/media/js/auto-save.js +++ b/ui/media/js/auto-save.js @@ -169,6 +169,22 @@ function loadSettings() { } }) CURRENTLY_LOADING_SETTINGS = false + } else if (localStorage.length < 2) { + // localStorage is too short for OldSettings + // So this is likely the first time Easy Diffusion is running. + // Initialize vram_usage_level based on the available VRAM + function initGPUProfile(event) { + if ( "detail" in event + && "active" in event.detail + && "cuda:0" in event.detail.active + && event.detail.active["cuda:0"].mem_total <4.5 ) + { + vramUsageLevelField.value = "low" + vramUsageLevelField.dispatchEvent(new Event("change")) + } + document.removeEventListener("system_info_update", initGPUProfile) + } + document.addEventListener("system_info_update", initGPUProfile) } else { CURRENTLY_LOADING_SETTINGS = true tryLoadOldSettings() diff --git a/ui/media/js/main.js b/ui/media/js/main.js index c69535df..0ce32f2b 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -239,7 +239,7 @@ function setServerStatus(event) { break } if (SD.serverState.devices) { - setDeviceInfo(SD.serverState.devices) + document.dispatchEvent(new CustomEvent("system_info_update", { detail: SD.serverState.devices})) } } diff --git a/ui/media/js/parameters.js b/ui/media/js/parameters.js index e48e2e04..30cdda78 100644 --- a/ui/media/js/parameters.js +++ b/ui/media/js/parameters.js @@ -586,7 +586,7 @@ async function getSystemInfo() { $("#use_gpus").val(activeDeviceIds) } - setDeviceInfo(devices) + document.dispatchEvent(new CustomEvent("system_info_update", { detail: devices})) setHostInfo(res["hosts"]) let force = false if (res["enforce_output_dir"] !== undefined) { @@ -657,3 +657,5 @@ saveSettingsBtn.addEventListener("click", function() { saveSettingsBtn.classList.add("active") Promise.all([savePromise, asyncDelay(300)]).then(() => saveSettingsBtn.classList.remove("active")) }) + +document.addEventListener("system_info_update", (e) => setDeviceInfo(e.detail)) From cac4bd11d2337b2bcb5108cec87ad51078eaf6ef Mon Sep 17 00:00:00 2001 From: JeLuF Date: Sun, 21 May 2023 17:59:06 +0200 Subject: [PATCH 200/291] Network settings - Fix typo. The 'Please restart' text should be part of the note, not the label --- ui/media/js/parameters.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/media/js/parameters.js b/ui/media/js/parameters.js index e48e2e04..ac011263 100644 --- a/ui/media/js/parameters.js +++ b/ui/media/js/parameters.js @@ -181,8 +181,8 @@ var PARAMETERS = [ { id: "listen_to_network", type: ParameterType.checkbox, - label: "Make Stable Diffusion available on your network. Please restart the program after changing this.", - note: "Other devices on your network can access this web page", + label: "Make Stable Diffusion available on your network", + note: "Other devices on your network can access this web page. Please restart the program after changing this.", icon: "fa-network-wired", default: true, saveInAppConfig: true, From cca6dd9230352e776afde748da95df62c3f1516e Mon Sep 17 00:00:00 2001 From: JeLuF Date: Sun, 21 May 2023 21:30:29 +0200 Subject: [PATCH 201/291] 2.5.37 updates --- ui/index.html | 17 +++++++++-------- ui/media/css/main.css | 25 +++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/ui/index.html b/ui/index.html index 296e9230..56d4e254 100644 --- a/ui/index.html +++ b/ui/index.html @@ -411,7 +411,7 @@

Diffusers Tech Preview

The Diffusers Tech Preview allows early access to the new features based on Diffusers.

The Preview is under active development. It is experimental! It does still have bugs and missing features!

-

New upcoming features in our new engine include:

+

New upcoming features in our new engine

  • LORA support - Place LORA files in the models/lora folder.
  • Compel Prompt Parser - New, more powerful parser. In short: @@ -434,19 +434,20 @@
  • More choices for img2img samplers
  • Support for inpainting models
  • +
  • Clip Skip support allows to skip the last CLIP layer (recommended by some LORA models)
  • +
  • New samplers: DDPM and DEIS
  • +
  • Memory optimizations that allow the use of 2GB GPUs

Known issues

  • Some LoRA consistently fail to load in EasyDiffusion
  • Some LoRA are far more sensitive to alpha (compared to a11)
  • -
  • hangs sometimes on "compel is ready", while making the token.
  • +
  • Hangs sometimes on "compel is ready", while making the token.
  • Some custom inpainting models don't work
  • - -
  • these samplers don't work yet: Unipc SNR, Unipc TQ, Unipc SNR2, DPM++ 2s Ancestral, DPM++ SDE, DPM Fast, DPM Adaptive, DPM2
  • -
  • hypernetwork doesn't work
  • - -
  • multi GPU - cuda:1 and cuda:0 conflict
  • -
  • the time remaining in browser differs from the one in the console
  • +
  • These samplers don't work yet: Unipc SNR, Unipc TQ, Unipc SNR2, DPM++ 2s Ancestral, DPM++ SDE, DPM Fast, DPM Adaptive, DPM2
  • +
  • Hypernetwork doesn't work
  • +
  • Multi GPU - cuda:1 and cuda:0 conflict
  • +
  • The time remaining in browser differs from the one in the console
diff --git a/ui/media/css/main.css b/ui/media/css/main.css index d2b1fc55..7d45c3ca 100644 --- a/ui/media/css/main.css +++ b/ui/media/css/main.css @@ -1310,6 +1310,31 @@ body.wait-pause { border-radius: 5px; } +#splash-screen li { + margin-bottom: 6px; +} + +#splash-screen a +{ + color: #ccf; + text-decoration: none; + font-weight: bold; +} + +#splash-screen a[href^="http"]::after, +#splash-screen a[href^="https://"]::after +{ + content: ""; + width: 11px; + height: 11px; + margin-left: 4px; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='lightblue' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M8.636 3.5a.5.5 0 0 0-.5-.5H1.5A1.5 1.5 0 0 0 0 4.5v10A1.5 1.5 0 0 0 1.5 16h10a1.5 1.5 0 0 0 1.5-1.5V7.864a.5.5 0 0 0-1 0V14.5a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h6.636a.5.5 0 0 0 .5-.5z'/%3E%3Cpath fill-rule='evenodd' d='M16 .5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0 0 1h3.793L6.146 9.146a.5.5 0 1 0 .708.708L15 1.707V5.5a.5.5 0 0 0 1 0v-5z'/%3E%3C/svg%3E"); + background-position: center; + background-repeat: no-repeat; + background-size: contain; + display: inline-block; +} + .jconfirm.jconfirm-modern .jconfirm-box div.jconfirm-title-c { color: var(--button-text-color); } From a5a1d3358918626e2860db1effcabea3787fee2b Mon Sep 17 00:00:00 2001 From: patriceac <48073125+patriceac@users.noreply.github.com> Date: Sun, 21 May 2023 18:32:48 -0700 Subject: [PATCH 202/291] Fix face restoration model selection --- ui/easydiffusion/model_manager.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ui/easydiffusion/model_manager.py b/ui/easydiffusion/model_manager.py index 2bb4852d..a0b2489a 100644 --- a/ui/easydiffusion/model_manager.py +++ b/ui/easydiffusion/model_manager.py @@ -107,12 +107,18 @@ def resolve_model_to_use(model_name: str = None, model_type: str = None): def reload_models_if_necessary(context: Context, task_data: TaskData): - face_correction_model = "codeformer" if "codeformer" in task_data.use_face_correction.lower() else "gfpgan" + if hasattr(task_data, 'use_face_correction') and task_data.use_face_correction: + face_correction_model = "codeformer" if "codeformer" in task_data.use_face_correction.lower() else "gfpgan" + face_correction_value = task_data.use_face_correction + else: + face_correction_model = "gfpgan" + face_correction_value = None + model_paths_in_req = { "stable-diffusion": task_data.use_stable_diffusion_model, "vae": task_data.use_vae_model, "hypernetwork": task_data.use_hypernetwork_model, - face_correction_model: task_data.use_face_correction, + face_correction_model: face_correction_value, "realesrgan": task_data.use_upscale, "nsfw_checker": True if task_data.block_nsfw else None, "lora": task_data.use_lora_model, From 0f6caaec3319c82427e1bf2274a58091974b8c9c Mon Sep 17 00:00:00 2001 From: JeLuF Date: Mon, 22 May 2023 10:21:19 +0200 Subject: [PATCH 203/291] get_config: return default value if conf file is corrupted --- scripts/get_config.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/get_config.py b/scripts/get_config.py index 02523364..9cdfb2fe 100644 --- a/scripts/get_config.py +++ b/scripts/get_config.py @@ -1,5 +1,6 @@ import os import argparse +import sys # The config file is in the same directory as this script config_directory = os.path.dirname(__file__) @@ -21,16 +22,16 @@ if os.path.isfile(config_yaml): try: config = yaml.safe_load(configfile) except Exception as e: - print(e) - exit() + print(e, file=sys.stderr) + config = {} elif os.path.isfile(config_json): import json with open(config_json, 'r') as configfile: try: config = json.load(configfile) except Exception as e: - print(e) - exit() + print(e, file=sys.stderr) + config = {} else: config = {} From ea9861d180ed4c91749324b8685fd8335570de5b Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Mon, 22 May 2023 16:07:50 +0530 Subject: [PATCH 204/291] Less min VRAM requirement --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c6b047e4..6a629e57 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ Click the download button for your operating system:

**Hardware requirements:** -- **Windows:** NVIDIA graphics card, or run on your CPU. -- **Linux:** NVIDIA or AMD graphics card, or run on your CPU. +- **Windows:** NVIDIA graphics card (minimum 2 GB RAM), or run on your CPU. +- **Linux:** NVIDIA or AMD graphics card (minimum 2 GB RAM), or run on your CPU. - **Mac:** M1 or M2, or run on your CPU. - Minimum 8 GB of system RAM. - Atleast 25 GB of space on the hard disk. @@ -86,7 +86,7 @@ Just delete the `EasyDiffusion` folder to uninstall all the downloaded packages. ### Performance and security - **Fast**: Creates a 512x512 image with euler_a in 5 seconds, on an NVIDIA 3060 12GB. -- **Low Memory Usage**: Create 512x512 images with less than 3 GB of GPU RAM, and 768x768 images with less than 4 GB of GPU RAM! +- **Low Memory Usage**: Create 512x512 images with less than 2 GB of GPU RAM, and 768x768 images with less than 3 GB of GPU RAM! - **Use CPU setting**: If you don't have a compatible graphics card, but still want to run it on your CPU. - **Multi-GPU support**: Automatically spreads your tasks across multiple GPUs (if available), for faster performance! - **Auto scan for malicious models**: Uses picklescan to prevent malicious models. From d60cb61e585ea89928c47403a0a5af7cc1c0a7f6 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Mon, 22 May 2023 18:06:38 +0530 Subject: [PATCH 205/291] sdkit 1.0.97 - flatten arguments sent to latent upscaler --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 1590f569..8747d173 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.96", + "sdkit": "1.0.97", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", From 0127714929c9eb5567a4dd4a0360df2d771db48f Mon Sep 17 00:00:00 2001 From: JeLuF Date: Mon, 22 May 2023 21:19:31 +0200 Subject: [PATCH 206/291] Add 'ED is ready, go to localhost:9000' msg to log Sometimes the browser window does not open (esp. on Linux and Mac). Show a prominent message to the log so that users don't wait for hours. --- ui/easydiffusion/app.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ui/easydiffusion/app.py b/ui/easydiffusion/app.py index b6318f01..3064e151 100644 --- a/ui/easydiffusion/app.py +++ b/ui/easydiffusion/app.py @@ -10,6 +10,8 @@ import warnings from easydiffusion import task_manager from easydiffusion.utils import log from rich.logging import RichHandler +from rich.console import Console +from rich.panel import Panel from sdkit.utils import log as sdkit_log # hack, so we can overwrite the log config # Remove all handlers associated with the root logger object. @@ -213,11 +215,19 @@ def open_browser(): ui = config.get("ui", {}) net = config.get("net", {}) port = net.get("listen_port", 9000) + if ui.get("open_browser_on_start", True): import webbrowser webbrowser.open(f"http://localhost:{port}") + Console().print(Panel( + "\n" + + "[white]Easy Diffusion is ready to serve requests.\n\n" + + "A new browser tab should have been opened by now.\n" + + f"If not, please open your web browser and navigate to [bold yellow underline]http://localhost:{port}/\n", + title="Easy Diffusion is ready", style="bold yellow on blue")) + def get_image_modifiers(): modifiers_json_path = os.path.join(SD_UI_DIR, "modifiers.json") From 2bab4341a3d658e1d4516b68f66449467265329e Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 23 May 2023 16:53:53 +0530 Subject: [PATCH 207/291] Add 'Latent Upscaler' as an option in the upscaling dropdown --- ui/easydiffusion/model_manager.py | 7 +++-- ui/easydiffusion/renderer.py | 24 +++++++++++++---- ui/easydiffusion/types.py | 3 ++- ui/index.html | 8 ++++-- ui/media/css/main.css | 6 +++++ ui/media/js/main.js | 44 ++++++++++++++++++++++++++++++- 6 files changed, 81 insertions(+), 11 deletions(-) diff --git a/ui/easydiffusion/model_manager.py b/ui/easydiffusion/model_manager.py index 324dcec9..d6a227be 100644 --- a/ui/easydiffusion/model_manager.py +++ b/ui/easydiffusion/model_manager.py @@ -107,12 +107,15 @@ def resolve_model_to_use(model_name: str = None, model_type: str = None): def reload_models_if_necessary(context: Context, task_data: TaskData): + use_upscale_lower = task_data.use_upscale.lower() if task_data.use_upscale else "" + model_paths_in_req = { "stable-diffusion": task_data.use_stable_diffusion_model, "vae": task_data.use_vae_model, "hypernetwork": task_data.use_hypernetwork_model, "gfpgan": task_data.use_face_correction, - "realesrgan": task_data.use_upscale, + "realesrgan": task_data.use_upscale if "realesrgan" in use_upscale_lower else None, + "latent_upscaler": True if task_data.use_upscale == "latent_upscaler" else None, "nsfw_checker": True if task_data.block_nsfw else None, "lora": task_data.use_lora_model, } @@ -142,7 +145,7 @@ def resolve_model_paths(task_data: TaskData): if task_data.use_face_correction: task_data.use_face_correction = resolve_model_to_use(task_data.use_face_correction, "gfpgan") - if task_data.use_upscale: + if task_data.use_upscale and "realesrgan" in task_data.use_upscale.lower(): task_data.use_upscale = resolve_model_to_use(task_data.use_upscale, "realesrgan") diff --git a/ui/easydiffusion/renderer.py b/ui/easydiffusion/renderer.py index e26b4389..c60c42df 100644 --- a/ui/easydiffusion/renderer.py +++ b/ui/easydiffusion/renderer.py @@ -95,7 +95,7 @@ def make_images_internal( task_data.stream_image_progress_interval, ) gc(context) - filtered_images = filter_images(task_data, images, user_stopped) + filtered_images = filter_images(req, task_data, images, user_stopped) if task_data.save_to_disk_path is not None: save_images_to_disk(images, filtered_images, req, task_data) @@ -151,22 +151,36 @@ def generate_images_internal( return images, user_stopped -def filter_images(task_data: TaskData, images: list, user_stopped): +def filter_images(req: GenerateImageRequest, task_data: TaskData, images: list, user_stopped): if user_stopped: return images filters_to_apply = [] + filter_params = {} if task_data.block_nsfw: filters_to_apply.append("nsfw_checker") if task_data.use_face_correction and "gfpgan" in task_data.use_face_correction.lower(): filters_to_apply.append("gfpgan") - if task_data.use_upscale and "realesrgan" in task_data.use_upscale.lower(): - filters_to_apply.append("realesrgan") + if task_data.use_upscale: + if "realesrgan" in task_data.use_upscale.lower(): + filters_to_apply.append("realesrgan") + elif task_data.use_upscale == "latent_upscaler": + filters_to_apply.append("latent_upscaler") + + filter_params["latent_upscaler_options"] = { + "prompt": req.prompt, + "negative_prompt": req.negative_prompt, + "seed": req.seed, + "num_inference_steps": task_data.latent_upscaler_steps, + "guidance_scale": 0, + } + + filter_params["scale"] = task_data.upscale_amount if len(filters_to_apply) == 0: return images - return apply_filters(context, filters_to_apply, images, scale=task_data.upscale_amount) + return apply_filters(context, filters_to_apply, images, **filter_params) def construct_response(images: list, seeds: list, task_data: TaskData, base_seed: int): diff --git a/ui/easydiffusion/types.py b/ui/easydiffusion/types.py index 7a5201ab..a76f489a 100644 --- a/ui/easydiffusion/types.py +++ b/ui/easydiffusion/types.py @@ -32,8 +32,9 @@ class TaskData(BaseModel): vram_usage_level: str = "balanced" # or "low" or "medium" use_face_correction: str = None # or "GFPGANv1.3" - use_upscale: str = None # or "RealESRGAN_x4plus" or "RealESRGAN_x4plus_anime_6B" + use_upscale: str = None # or "RealESRGAN_x4plus" or "RealESRGAN_x4plus_anime_6B" or "latent_upscaler" upscale_amount: int = 4 # or 2 + latent_upscaler_steps: int = 10 use_stable_diffusion_model: str = "sd-v1-4" # use_stable_diffusion_config: str = "v1-inference" use_vae_model: str = None diff --git a/ui/index.html b/ui/index.html index 99087eec..5097d84a 100644 --- a/ui/index.html +++ b/ui/index.html @@ -258,14 +258,18 @@
  • with +
    + +
  • diff --git a/ui/media/css/main.css b/ui/media/css/main.css index ba513237..8f4f49fa 100644 --- a/ui/media/css/main.css +++ b/ui/media/css/main.css @@ -1303,6 +1303,12 @@ body.wait-pause { display:none !important; } +#latent_upscaler_settings { + padding-top: 3pt; + padding-bottom: 3pt; + padding-left: 5pt; +} + /* TOAST NOTIFICATIONS */ .toast-notification { position: fixed; diff --git a/ui/media/js/main.js b/ui/media/js/main.js index 0ce32f2b..23ed5f46 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -86,6 +86,9 @@ let gfpganModelField = new ModelDropdown(document.querySelector("#gfpgan_model") let useUpscalingField = document.querySelector("#use_upscale") let upscaleModelField = document.querySelector("#upscale_model") let upscaleAmountField = document.querySelector("#upscale_amount") +let latentUpscalerSettings = document.querySelector("#latent_upscaler_settings") +let latentUpscalerStepsSlider = document.querySelector("#latent_upscaler_steps_slider") +let latentUpscalerStepsField = document.querySelector("#latent_upscaler_steps") let stableDiffusionModelField = new ModelDropdown(document.querySelector("#stable_diffusion_model"), "stable-diffusion") let clipSkipField = document.querySelector("#clip_skip") let vaeModelField = new ModelDropdown(document.querySelector("#vae_model"), "vae", "None") @@ -239,7 +242,7 @@ function setServerStatus(event) { break } if (SD.serverState.devices) { - document.dispatchEvent(new CustomEvent("system_info_update", { detail: SD.serverState.devices})) + document.dispatchEvent(new CustomEvent("system_info_update", { detail: SD.serverState.devices })) } } @@ -1268,6 +1271,10 @@ function getCurrentUserRequest() { if (useUpscalingField.checked) { newTask.reqBody.use_upscale = upscaleModelField.value newTask.reqBody.upscale_amount = upscaleAmountField.value + if (upscaleModelField.value === "latent_upscaler") { + newTask.reqBody.upscale_amount = "2" + newTask.reqBody.latent_upscaler_steps = latentUpscalerStepsField.value + } } if (hypernetworkModelField.value) { newTask.reqBody.use_hypernetwork_model = hypernetworkModelField.value @@ -1582,6 +1589,20 @@ useUpscalingField.addEventListener("change", function(e) { upscaleAmountField.disabled = !this.checked }) +function onUpscaleModelChange() { + let upscale4x = document.querySelector("#upscale_amount_4x") + if (upscaleModelField.value === "latent_upscaler") { + upscale4x.disabled = true + upscaleAmountField.value = "2" + latentUpscalerSettings.classList.remove("displayNone") + } else { + upscale4x.disabled = false + latentUpscalerSettings.classList.add("displayNone") + } +} +upscaleModelField.addEventListener("change", onUpscaleModelChange) +onUpscaleModelChange() + makeImageBtn.addEventListener("click", makeImage) document.onkeydown = function(e) { @@ -1591,6 +1612,27 @@ document.onkeydown = function(e) { } } +/********************* Latent Upscaler Steps **************************/ +function updateLatentUpscalerSteps() { + latentUpscalerStepsField.value = latentUpscalerStepsSlider.value + latentUpscalerStepsField.dispatchEvent(new Event("change")) +} + +function updateLatentUpscalerStepsSlider() { + if (latentUpscalerStepsField.value < 1) { + latentUpscalerStepsField.value = 1 + } else if (latentUpscalerStepsField.value > 50) { + latentUpscalerStepsField.value = 50 + } + + latentUpscalerStepsSlider.value = latentUpscalerStepsField.value + latentUpscalerStepsSlider.dispatchEvent(new Event("change")) +} + +latentUpscalerStepsSlider.addEventListener("input", updateLatentUpscalerSteps) +latentUpscalerStepsField.addEventListener("input", updateLatentUpscalerStepsSlider) +updateLatentUpscalerSteps() + /********************* Guidance **************************/ function updateGuidanceScale() { guidanceScaleField.value = guidanceScaleSlider.value / 10 From a87dca1ef4b0c2bd6a0380866b1ca76acbc26c89 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 23 May 2023 16:55:42 +0530 Subject: [PATCH 208/291] changelog --- CHANGES.md | 1 + ui/index.html | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 9b2b72c1..d8e5c3f0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -22,6 +22,7 @@ Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. ### Detailed changelog +* 2.5.38 - 23 May 2023 - Add Latent Upscaler as another option for upscaling images. Thanks @JeLuf for the implementation of the Latent Upscaler model. * 2.5.37 - 19 May 2023 - (beta-only) Two more samplers: DDPM and DEIS. Also disables the samplers that aren't working yet in the Diffusers version. Thanks @ogmaresca. * 2.5.37 - 19 May 2023 - (beta-only) Support CLIP-Skip. You can set this option under the models dropdown. Thanks @JeLuf. * 2.5.37 - 19 May 2023 - (beta-only) More VRAM optimizations for all modes in diffusers. The VRAM usage for diffusers in "low" and "balanced" should now be equal or less than the non-diffusers version. Performs softmax in half precision, like sdkit does. diff --git a/ui/index.html b/ui/index.html index 5097d84a..dc4eb7f0 100644 --- a/ui/index.html +++ b/ui/index.html @@ -30,7 +30,7 @@

    Easy Diffusion - v2.5.37 + v2.5.38

    From eba83386c1e69ac2d02c8e11c7b59251a518664b Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 24 May 2023 10:08:00 +0530 Subject: [PATCH 209/291] make a note about a flood fill library --- ui/media/js/image-editor.js | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/media/js/image-editor.js b/ui/media/js/image-editor.js index af19daeb..e7de9f2b 100644 --- a/ui/media/js/image-editor.js +++ b/ui/media/js/image-editor.js @@ -834,6 +834,7 @@ function pixelCompare(int1, int2) { } // adapted from https://ben.akrin.com/canvas_fill/fill_04.html +// May 2023 - look at using a library instead of custom code: https://github.com/shaneosullivan/example-canvas-fill function flood_fill(editor, the_canvas_context, x, y, color) { pixel_stack = [{ x: x, y: y }] pixels = the_canvas_context.getImageData(0, 0, editor.width, editor.height) From 30c07eab6b5fd131fa0858d7582d690fbdb7b76a Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 24 May 2023 15:30:55 +0530 Subject: [PATCH 210/291] Cleaner reporting of errors in the UI; Suggest increasing the page size if that's the error --- ui/media/js/main.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/ui/media/js/main.js b/ui/media/js/main.js index 23ed5f46..473ed780 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -296,6 +296,7 @@ function logError(msg, res, outputMsg) { logMsg(msg, "error", outputMsg) console.log("request error", res) + console.trace() setStatus("request", "error", "error") } @@ -787,11 +788,6 @@ function getTaskUpdater(task, reqBody, outputContainer) { } msg += "" logError(msg, event, outputMsg) - } else { - let msg = `Unexpected Read Error:
    Error:${
    -                            this.exception
    -                        }
    EventInfo: ${JSON.stringify(event, undefined, 4)}
    ` - logError(msg, event, outputMsg) } break } @@ -888,15 +884,15 @@ function onTaskCompleted(task, reqBody, instance, outputContainer, stepUpdate) { 1. If you have set an initial image, please try reducing its dimension to ${MAX_INIT_IMAGE_DIMENSION}x${MAX_INIT_IMAGE_DIMENSION} or smaller.
    2. Try picking a lower level in the 'GPU Memory Usage' setting (in the 'Settings' tab).
    3. Try generating a smaller image.
    ` - } else if (msg.toLowerCase().includes("DefaultCPUAllocator: not enough memory")) { + } else if (msg.includes("DefaultCPUAllocator: not enough memory")) { msg += `

    Reason: Your computer is running out of system RAM! -
    +

    Suggestions:
    1. Try closing unnecessary programs and browser tabs.
    2. If that doesn't help, please increase your computer's virtual memory by following these steps for - Windows, or + Windows or Linux.
    3. Try restarting your computer.
    ` } From 8554b0eab2a355c5472c42ccad039b01c7f18d67 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 24 May 2023 16:02:53 +0530 Subject: [PATCH 211/291] Better reporting of model load errors - sends the report to the browser UI during the next image rendering task --- ui/easydiffusion/model_manager.py | 23 ++++++++++++++++++++++- ui/easydiffusion/renderer.py | 1 + ui/easydiffusion/task_manager.py | 1 + 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/ui/easydiffusion/model_manager.py b/ui/easydiffusion/model_manager.py index d6a227be..0a1f1b5c 100644 --- a/ui/easydiffusion/model_manager.py +++ b/ui/easydiffusion/model_manager.py @@ -53,15 +53,21 @@ def load_default_models(context: Context): scan_model=context.model_paths[model_type] != None and not context.model_paths[model_type].endswith(".safetensors"), ) + if model_type in context.model_load_errors: + del context.model_load_errors[model_type] except Exception as e: log.error(f"[red]Error while loading {model_type} model: {context.model_paths[model_type]}[/red]") log.exception(e) del context.model_paths[model_type] + context.model_load_errors[model_type] = str(e) # storing the entire Exception can lead to memory leaks + def unload_all(context: Context): for model_type in KNOWN_MODEL_TYPES: unload_model(context, model_type) + if model_type in context.model_load_errors: + del context.model_load_errors[model_type] def resolve_model_to_use(model_name: str = None, model_type: str = None): @@ -132,7 +138,14 @@ def reload_models_if_necessary(context: Context, task_data: TaskData): context.model_paths[model_type] = model_path_in_req action_fn = unload_model if context.model_paths[model_type] is None else load_model - action_fn(context, model_type, scan_model=False) # we've scanned them already + try: + action_fn(context, model_type, scan_model=False) # we've scanned them already + if model_type in context.model_load_errors: + del context.model_load_errors[model_type] + except Exception as e: + log.exception(e) + if action_fn == load_model: + context.model_load_errors[model_type] = str(e) # storing the entire Exception can lead to memory leaks def resolve_model_paths(task_data: TaskData): @@ -149,6 +162,14 @@ def resolve_model_paths(task_data: TaskData): task_data.use_upscale = resolve_model_to_use(task_data.use_upscale, "realesrgan") +def fail_if_models_did_not_load(context: Context): + for model_type in KNOWN_MODEL_TYPES: + if model_type in context.model_load_errors: + e = context.model_load_errors[model_type] + raise Exception(f"Could not load the {model_type} model! Reason: " + e) + # concat 'e', don't use in format string (injection attack) + + def set_vram_optimizations(context: Context): config = app.getConfig() vram_usage_level = config.get("vram_usage_level", "balanced") diff --git a/ui/easydiffusion/renderer.py b/ui/easydiffusion/renderer.py index c60c42df..e2dae34f 100644 --- a/ui/easydiffusion/renderer.py +++ b/ui/easydiffusion/renderer.py @@ -33,6 +33,7 @@ def init(device): context.stop_processing = False context.temp_images = {} context.partial_x_samples = None + context.model_load_errors = {} from easydiffusion import app diff --git a/ui/easydiffusion/task_manager.py b/ui/easydiffusion/task_manager.py index c11acbec..a91cd9c6 100644 --- a/ui/easydiffusion/task_manager.py +++ b/ui/easydiffusion/task_manager.py @@ -336,6 +336,7 @@ def thread_render(device): current_state = ServerStates.LoadingModel model_manager.resolve_model_paths(task.task_data) model_manager.reload_models_if_necessary(renderer.context, task.task_data) + model_manager.fail_if_models_did_not_load(renderer.context) current_state = ServerStates.Rendering task.response = renderer.make_images( From db265309a57d576f2ec08433c56c33b8f8bb27a4 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 24 May 2023 16:24:29 +0530 Subject: [PATCH 212/291] Show an explanation for why the CPU toggle is disabled; utility class for alert() and confirm() that matches the ED theme; code formatting --- ui/media/js/main.js | 19 +++------ ui/media/js/parameters.js | 19 +++++++-- ui/media/js/utils.js | 90 +++++++++++++++++++++++++-------------- 3 files changed, 79 insertions(+), 49 deletions(-) diff --git a/ui/media/js/main.js b/ui/media/js/main.js index 473ed780..ecd8ad73 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -261,20 +261,11 @@ function shiftOrConfirm(e, prompt, fn) { if (e.shiftKey || !confirmDangerousActionsField.checked) { fn(e) } else { - $.confirm({ - theme: "modern", - title: prompt, - useBootstrap: false, - animateFromElement: false, - content: - 'Tip: To skip this dialog, use shift-click or disable the "Confirm dangerous actions" setting in the Settings tab.', - buttons: { - yes: () => { - fn(e) - }, - cancel: () => {}, - }, - }) + confirm( + 'Tip: To skip this dialog, use shift-click or disable the "Confirm dangerous actions" setting in the Settings tab.', + prompt, + fn + ) } } diff --git a/ui/media/js/parameters.js b/ui/media/js/parameters.js index f51b5290..4c7240eb 100644 --- a/ui/media/js/parameters.js +++ b/ui/media/js/parameters.js @@ -191,7 +191,8 @@ var PARAMETERS = [ id: "listen_port", type: ParameterType.custom, label: "Network port", - note: "Port that this server listens to. The '9000' part in 'http://localhost:9000'. Please restart the program after changing this.", + note: + "Port that this server listens to. The '9000' part in 'http://localhost:9000'. Please restart the program after changing this.", icon: "fa-anchor", render: (parameter) => { return `` @@ -396,14 +397,14 @@ async function getAppConfig() { document.querySelector("#lora_model_container").style.display = "none" document.querySelector("#lora_alpha_container").style.display = "none" - document.querySelectorAll("#sampler_name option.diffusers-only").forEach(option => { + document.querySelectorAll("#sampler_name option.diffusers-only").forEach((option) => { option.style.display = "none" }) } else { document.querySelector("#lora_model_container").style.display = "" document.querySelector("#lora_alpha_container").style.display = loraModelField.value ? "" : "none" - document.querySelectorAll("#sampler_name option.k_diffusion-only").forEach(option => { + document.querySelectorAll("#sampler_name option.k_diffusion-only").forEach((option) => { option.disabled = true }) document.querySelector("#clip_skip_config").classList.remove("displayNone") @@ -568,6 +569,16 @@ async function getSystemInfo() { if (allDeviceIds.length === 0) { useCPUField.checked = true useCPUField.disabled = true // no compatible GPUs, so make the CPU mandatory + + getParameterSettingsEntry("use_cpu").addEventListener("click", function() { + alert( + "Sorry, we could not find a compatible graphics card! Easy Diffusion supports graphics cards with minimum 2 GB of RAM. " + + "Only NVIDIA cards are supported on Windows. NVIDIA and AMD cards are supported on Linux.

    " + + "If you have a compatible graphics card, please try updating to the latest drivers.

    " + + "Only the CPU can be used for generating images, without a compatible graphics card.", + "No compatible graphics card found!" + ) + }) } autoPickGPUsField.checked = devices["config"] === "auto" @@ -586,7 +597,7 @@ async function getSystemInfo() { $("#use_gpus").val(activeDeviceIds) } - document.dispatchEvent(new CustomEvent("system_info_update", { detail: devices})) + document.dispatchEvent(new CustomEvent("system_info_update", { detail: devices })) setHostInfo(res["hosts"]) let force = false if (res["enforce_output_dir"] !== undefined) { diff --git a/ui/media/js/utils.js b/ui/media/js/utils.js index d1578d8e..16778b2d 100644 --- a/ui/media/js/utils.js +++ b/ui/media/js/utils.js @@ -843,57 +843,85 @@ function createTab(request) { /* TOAST NOTIFICATIONS */ function showToast(message, duration = 5000, error = false) { - const toast = document.createElement("div"); - toast.classList.add("toast-notification"); + const toast = document.createElement("div") + toast.classList.add("toast-notification") if (error === true) { - toast.classList.add("toast-notification-error"); + toast.classList.add("toast-notification-error") } - toast.innerHTML = message; - document.body.appendChild(toast); + toast.innerHTML = message + document.body.appendChild(toast) // Set the position of the toast on the screen - const toastCount = document.querySelectorAll(".toast-notification").length; - const toastHeight = toast.offsetHeight; + const toastCount = document.querySelectorAll(".toast-notification").length + const toastHeight = toast.offsetHeight const previousToastsHeight = Array.from(document.querySelectorAll(".toast-notification")) .slice(0, -1) // exclude current toast - .reduce((totalHeight, toast) => totalHeight + toast.offsetHeight + 10, 0); // add 10 pixels for spacing - toast.style.bottom = `${10 + previousToastsHeight}px`; - toast.style.right = "10px"; + .reduce((totalHeight, toast) => totalHeight + toast.offsetHeight + 10, 0) // add 10 pixels for spacing + toast.style.bottom = `${10 + previousToastsHeight}px` + toast.style.right = "10px" // Delay the removal of the toast until animation has completed const removeToast = () => { - toast.classList.add("hide"); + toast.classList.add("hide") const removeTimeoutId = setTimeout(() => { - toast.remove(); + toast.remove() // Adjust the position of remaining toasts - const remainingToasts = document.querySelectorAll(".toast-notification"); - const removedToastBottom = toast.getBoundingClientRect().bottom; - + const remainingToasts = document.querySelectorAll(".toast-notification") + const removedToastBottom = toast.getBoundingClientRect().bottom + remainingToasts.forEach((toast) => { if (toast.getBoundingClientRect().bottom < removedToastBottom) { - toast.classList.add("slide-down"); + toast.classList.add("slide-down") } - }); - + }) + // Wait for the slide-down animation to complete setTimeout(() => { // Remove the slide-down class after the animation has completed - const slidingToasts = document.querySelectorAll(".slide-down"); + const slidingToasts = document.querySelectorAll(".slide-down") slidingToasts.forEach((toast) => { - toast.classList.remove("slide-down"); - }); - + toast.classList.remove("slide-down") + }) + // Adjust the position of remaining toasts again, in case there are multiple toasts being removed at once - const remainingToastsDown = document.querySelectorAll(".toast-notification"); - let heightSoFar = 0; + const remainingToastsDown = document.querySelectorAll(".toast-notification") + let heightSoFar = 0 remainingToastsDown.forEach((toast) => { - toast.style.bottom = `${10 + heightSoFar}px`; - heightSoFar += toast.offsetHeight + 10; // add 10 pixels for spacing - }); - }, 0); // The duration of the slide-down animation (in milliseconds) - }, 500); - }; + toast.style.bottom = `${10 + heightSoFar}px` + heightSoFar += toast.offsetHeight + 10 // add 10 pixels for spacing + }) + }, 0) // The duration of the slide-down animation (in milliseconds) + }, 500) + } // Remove the toast after specified duration - setTimeout(removeToast, duration); + setTimeout(removeToast, duration) +} + +function alert(msg, title) { + title = title || "" + $.alert({ + theme: "modern", + title: title, + useBootstrap: false, + animateFromElement: false, + content: msg, + }) +} + +function confirm(msg, title, fn) { + title = title || "" + $.confirm({ + theme: "modern", + title: title, + useBootstrap: false, + animateFromElement: false, + content: msg, + buttons: { + yes: () => { + fn(e) + }, + cancel: () => {}, + }, + }) } From 3d7e16cfd944540a0e5b9a0f0c8c10b760f6cf7d Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 24 May 2023 16:29:58 +0530 Subject: [PATCH 213/291] changelog --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index d8e5c3f0..a1e49336 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -22,6 +22,7 @@ Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. ### Detailed changelog +* 2.5.38 - 24 May 2023 - Better reporting of errors, and show an explanation if the user cannot disable the "Use CPU" setting. * 2.5.38 - 23 May 2023 - Add Latent Upscaler as another option for upscaling images. Thanks @JeLuf for the implementation of the Latent Upscaler model. * 2.5.37 - 19 May 2023 - (beta-only) Two more samplers: DDPM and DEIS. Also disables the samplers that aren't working yet in the Diffusers version. Thanks @ogmaresca. * 2.5.37 - 19 May 2023 - (beta-only) Support CLIP-Skip. You can set this option under the models dropdown. Thanks @JeLuf. From 3ea74af76d32f005eaed7df6bc9245e99f99ffd6 Mon Sep 17 00:00:00 2001 From: JeLuF Date: Wed, 24 May 2023 19:29:54 +0200 Subject: [PATCH 214/291] Fix confirmation dialog By splitting the confirmation function into two halves, the closure was lost --- ui/media/js/main.js | 2 +- ui/media/js/utils.js | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/ui/media/js/main.js b/ui/media/js/main.js index ecd8ad73..fa37600a 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -264,7 +264,7 @@ function shiftOrConfirm(e, prompt, fn) { confirm( 'Tip: To skip this dialog, use shift-click or disable the "Confirm dangerous actions" setting in the Settings tab.', prompt, - fn + () => { fn(e) } ) } } diff --git a/ui/media/js/utils.js b/ui/media/js/utils.js index 16778b2d..6ddb0ae6 100644 --- a/ui/media/js/utils.js +++ b/ui/media/js/utils.js @@ -918,9 +918,7 @@ function confirm(msg, title, fn) { animateFromElement: false, content: msg, buttons: { - yes: () => { - fn(e) - }, + yes: fn, cancel: () => {}, }, }) From 9dfa300083eba3b5f059c889f7c55e6468a27271 Mon Sep 17 00:00:00 2001 From: JeLuF Date: Thu, 25 May 2023 00:16:14 +0200 Subject: [PATCH 215/291] Add seamless tiling support --- ui/easydiffusion/types.py | 1 + ui/index.html | 9 +++++++++ ui/media/js/auto-save.js | 1 + ui/media/js/dnd.js | 8 ++++++++ ui/media/js/engine.js | 3 ++- ui/media/js/main.js | 7 +++++++ 6 files changed, 28 insertions(+), 1 deletion(-) diff --git a/ui/easydiffusion/types.py b/ui/easydiffusion/types.py index a76f489a..e4426714 100644 --- a/ui/easydiffusion/types.py +++ b/ui/easydiffusion/types.py @@ -23,6 +23,7 @@ class GenerateImageRequest(BaseModel): sampler_name: str = None # "ddim", "plms", "heun", "euler", "euler_a", "dpm2", "dpm2_a", "lms" hypernetwork_strength: float = 0 lora_alpha: float = 0 + tiling: str = "none" # "none", "x", "y", "xy" class TaskData(BaseModel): diff --git a/ui/index.html b/ui/index.html index dc4eb7f0..4814446b 100644 --- a/ui/index.html +++ b/ui/index.html @@ -236,6 +236,15 @@
    + + + Click to learn more about Seamless Tiling + `) +const pluginFilter = document.getElementById("plugin-filter") // search box + +// Add the debounced function to the keyup event listener +pluginFilter.addEventListener('keyup', debouncedFilterPlugins); + +// select the text on focus +pluginFilter.addEventListener('focus', function (event) { + pluginFilter.select() +}); + +// empty the searchbox on escape +pluginFilter.addEventListener('keydown', function (event) { + if (event.key === 'Escape') { + pluginFilter.value = ''; + filterPlugins(); + } +}); + +// focus on the search box upon tab selection +document.addEventListener("tabClick", (e) => { + if (e.detail.name == 'plugin') { + pluginFilter.focus() + } +}) + +// refresh link +pluginsTable.insertAdjacentHTML('afterend', `

    Refresh plugins

    +

    (Plugin developers, add your plugins to plugins.json)

    `) +const refreshPlugins = document.getElementById("refresh-plugins") +refreshPlugins.addEventListener("click", async function (event) { + event.preventDefault() + await initPlugins(true) +}) + +function showPluginToast(message, duration = 5000, error = false, addNotification = true) { + if (addNotification === true) { + addPluginNotification(pluginNotifications, message, error) + } + try { + showToast(message, duration, error) + } catch (error) { + console.error('Error while trying to show toast:', error); + } +} + +function matchPluginFileNames(fileName1, fileName2) { + const regex = /^(.+?)(?:-\d+(\.\d+)*)?\.plugin\.js$/; + const match1 = fileName1.match(regex); + const match2 = fileName2.match(regex); + + if (match1 && match2 && match1[1] === match2[1]) { + return true; // the two file names match + } else { + return false; // the two file names do not match + } +} + +function extractFilename(filepath) { + // Normalize the path separators to forward slashes and make the file names lowercase + const normalizedFilePath = filepath.replace(/\\/g, "/").toLowerCase(); + + // Strip off the path from the file name + const fileName = normalizedFilePath.substring(normalizedFilePath.lastIndexOf("/") + 1); + + return fileName +} + +function checkFileNameInArray(paths, filePath) { + // Strip off the path from the file name + const fileName = extractFilename(filePath); + + // Check if the file name exists in the array of paths + return paths.some(path => { + // Strip off the path from the file name + const baseName = extractFilename(path); + + // Check if the file names match and return the result as a boolean + return matchPluginFileNames(fileName, baseName); + }); +} + +function isGitHub(url) { + return url.startsWith("https://raw.githubusercontent.com/") === true +} + +/* fill in the plugins table */ +function getIncompatiblePlugins(pluginId) { + const enabledPlugins = plugins.filter(plugin => plugin.enabled && plugin.id !== pluginId); + const incompatiblePlugins = enabledPlugins.filter(plugin => plugin.compatIssueIds?.includes(pluginId)); + const pluginNames = incompatiblePlugins.map(plugin => plugin.name); + if (pluginNames.length === 0) { + return null; + } + const pluginNamesList = pluginNames.map(name => `
  • ${name}
  • `).join(''); + return `
      ${pluginNamesList}
    `; +} + +async function initPluginTable(plugins) { + pluginsTable.innerHTML = '' + plugins.sort((a, b) => a.name.localeCompare(b.name, undefined, { sensitivity: 'base' })) + plugins.forEach(plugin => { + const name = plugin.name + const author = plugin.author ? ', by ' + plugin.author : '' + const version = plugin.version ? ' (version: ' + plugin.version + ')' : '' + const warning = getIncompatiblePlugins(plugin.id) ? `This plugin might conflict with:${getIncompatiblePlugins(plugin.id)}` : '' + const note = plugin.description ? `${plugin.description.replaceAll('\n', '
    ')}
    ` : `No description`; + const icon = plugin.icon ? `` : ''; + const newRow = document.createElement('div') + const localPluginFound = checkFileNameInArray(localPlugins, plugin.url) + + newRow.innerHTML = ` +
    ${icon}
    +
    ${warning}${note}Source: ${extractFilename(plugin.url)}
    +
    + ${localPluginFound ? "Installed locally" : + (plugin.localInstallOnly ? 'Download and
    install manually
    ' : + (isGitHub(plugin.url) ? + '' : + '' + ) + ) + } +
    `; + newRow.classList.add('plugin-container') + //console.log(plugin.id, plugin.localInstallOnly) + pluginsTable.appendChild(newRow) + const pluginManualInstall = pluginsTable.querySelector('#plugin-' + plugin.id + '-install') + updateManualInstallButtonCaption() + + // checkbox event handler + const pluginToggle = pluginsTable.querySelector('#plugin-' + plugin.id) + if (pluginToggle !== null) { + pluginToggle.checked = plugin.enabled // set initial state of checkbox + pluginToggle.addEventListener('change', async () => { + const container = pluginToggle.closest(".plugin-container"); + const warningElement = container.querySelector(".plugin-warning"); + + // if the plugin got enabled, download the plugin's code + plugin.enabled = pluginToggle.checked + if (plugin.enabled) { + const pluginSource = await getDocument(plugin.url); + if (pluginSource !== null) { + // Store the current scroll position before navigating away + const currentPosition = window.pageYOffset; + initPluginTable(plugins) + // When returning to the page, set the scroll position to the stored value + window.scrollTo(0, currentPosition); + warningElement?.classList.remove("hide"); + plugin.code = pluginSource + loadPlugins([plugin]) + console.log(`Plugin ${plugin.name} installed`); + showPluginToast("Plugin " + plugin.name + " installed"); + } + else { + plugin.enabled = false + pluginToggle.checked = false + console.error(`Couldn't download plugin ${plugin.name}`); + showPluginToast("Failed to install " + plugin.name + " (Couldn't fetch " + extractFilename(plugin.url) + ")", 5000, true); + } + } else { + warningElement?.classList.add("hide"); + // Store the current scroll position before navigating away + const currentPosition = window.pageYOffset; + initPluginTable(plugins) + // When returning to the page, set the scroll position to the stored value + window.scrollTo(0, currentPosition); + console.log(`Plugin ${plugin.name} uninstalled`); + showPluginToast("Plugin " + plugin.name + " uninstalled"); + } + await setStorageData('plugins', JSON.stringify(plugins)) + }) + } + + // manual install event handler + if (pluginManualInstall !== null) { + pluginManualInstall.addEventListener('click', async () => { + pluginDialogOpenDialog(inputOK, inputCancel) + pluginDialogTextarea.value = plugin.code ? plugin.code : '' + pluginDialogTextarea.select() + pluginDialogTextarea.focus() + }) + } + // Dialog OK + async function inputOK() { + let pluginSource = pluginDialogTextarea.value + // remove empty lines and trim existing lines + plugin.code = pluginSource + if (pluginSource.trim() !== '') { + plugin.enabled = true + console.log(`Plugin ${plugin.name} installed`); + showPluginToast("Plugin " + plugin.name + " installed"); + } + else { + plugin.enabled = false + console.log(`No code provided for plugin ${plugin.name}, disabling the plugin`); + showPluginToast("No code provided for plugin " + plugin.name + ", disabling the plugin"); + } + updateManualInstallButtonCaption() + await setStorageData('plugins', JSON.stringify(plugins)) + } + // Dialog Cancel + async function inputCancel() { + plugin.enabled = false + console.log(`Installation of plugin ${plugin.name} cancelled`); + showPluginToast("Cancelled installation of " + plugin.name); + } + // update button caption + function updateManualInstallButtonCaption() { + if (pluginManualInstall !== null) { + pluginManualInstall.innerHTML = plugin.code === undefined || plugin.code.trim() === '' ? 'Install' : 'Edit' + } + } + }) + prettifyInputs(pluginsTable) + filterPlugins() +} + +/* version management. Thanks Madrang! */ +const parseVersion = function (versionString, options = {}) { + if (typeof versionString === "undefined") { + throw new Error("versionString is undefined."); + } + if (typeof versionString !== "string") { + throw new Error("versionString is not a string."); + } + const lexicographical = options && options.lexicographical; + const zeroExtend = options && options.zeroExtend; + let versionParts = versionString.split('.'); + function isValidPart(x) { + const re = (lexicographical ? /^\d+[A-Za-z]*$/ : /^\d+$/); + return re.test(x); + } + + if (!versionParts.every(isValidPart)) { + throw new Error("Version string is invalid."); + } + + if (zeroExtend) { + while (versionParts.length < 4) { + versionParts.push("0"); + } + } + if (!lexicographical) { + versionParts = versionParts.map(Number); + } + return versionParts; +}; + +const versionCompare = function (v1, v2, options = {}) { + if (typeof v1 == "undefined") { + throw new Error("vi is undefined."); + } + if (typeof v2 === "undefined") { + throw new Error("v2 is undefined."); + } + + let v1parts; + if (typeof v1 === "string") { + v1parts = parseVersion(v1, options); + } else if (Array.isArray(v1)) { + v1parts = [...v1]; + if (!v1parts.every(p => typeof p === "number" && p !== NaN)) { + throw new Error("v1 part array does not only contains numbers."); + } + } else { + throw new Error("v1 is of an unexpected type: " + typeof v1); + } + + let v2parts; + if (typeof v2 === "string") { + v2parts = parseVersion(v2, options); + } else if (Array.isArray(v2)) { + v2parts = [...v2]; + if (!v2parts.every(p => typeof p === "number" && p !== NaN)) { + throw new Error("v2 part array does not only contains numbers."); + } + } else { + throw new Error("v2 is of an unexpected type: " + typeof v2); + } + + while (v1parts.length < v2parts.length) { + v1parts.push("0"); + } + while (v2parts.length < v1parts.length) { + v2parts.push("0"); + } + + for (let i = 0; i < v1parts.length; ++i) { + if (v2parts.length == i) { + return 1; + } + if (v1parts[i] == v2parts[i]) { + continue; + } else if (v1parts[i] > v2parts[i]) { + return 1; + } else { + return -1; + } + } + return 0; +}; + +function filterPluginsByMinEDVersion(plugins, EDVersion) { + const filteredPlugins = plugins.filter(plugin => { + if (plugin.minEDVersion) { + return versionCompare(plugin.minEDVersion, EDVersion) <= 0; + } + return true; + }); + + return filteredPlugins; +} + +function extractVersionNumber(elem) { + const versionStr = elem.innerHTML; + const regex = /v(\d+\.\d+\.\d+)/; + const matches = regex.exec(versionStr); + if (matches && matches.length > 1) { + return matches[1]; + } else { + return null; + } +} +const EasyDiffusionVersion = extractVersionNumber(document.querySelector('#top-nav > #logo')) + +/* PLUGIN MANAGEMENT */ +let plugins +let localPlugins +let initPluginsInProgress = false + +async function initPlugins(refreshPlugins = false) { + let pluginsLoaded + if (initPluginsInProgress === true) { + return + } + initPluginsInProgress = true + + const res = await fetch('/get/ui_plugins') + if (!res.ok) { + console.error(`Error HTTP${res.status} while loading plugins list. - ${res.statusText}`) + } + else { + localPlugins = await res.json() + } + + if (refreshPlugins === false) { + // load the notifications + pluginNotifications = await getStorageData('notifications') + if (typeof pluginNotifications === "string") { + try { + pluginNotifications = JSON.parse(pluginNotifications) + } catch (e) { + console.error("Failed to parse pluginNotifications", e); + pluginNotifications = {}; + pluginNotifications.entries = []; + } + } + if (pluginNotifications !== undefined) { + if (pluginNotifications.entries && pluginNotifications.entries.length > 0 && pluginNotifications.entries[0].date && pluginNotifications.lastUpdated <= pluginNotifications.entries[0].date) { + notificationPill.style.display = "block"; + } + } else { + pluginNotifications = {}; + pluginNotifications.entries = []; + } + + // try and load plugins from local cache + plugins = await getStorageData('plugins') + if (plugins !== undefined) { + plugins = JSON.parse(plugins) + + // remove duplicate entries if any (should not happen) + plugins = deduplicatePluginsById(plugins) + + // remove plugins that don't meet the min ED version requirement + plugins = filterPluginsByMinEDVersion(plugins, EasyDiffusionVersion) + + // remove from plugins the entries that don't have mandatory fields (id, name, url) + plugins = plugins.filter((plugin) => { return plugin.id !== '' && plugin.name !== '' && plugin.url !== ''; }); + + // populate the table + initPluginTable(plugins) + await loadPlugins(plugins) + pluginsLoaded = true + } + else { + plugins = [] + pluginsLoaded = false + } + } + + // update plugins asynchronously (updated versions will be available next time the UI is loaded) + if (refreshAllowed()) { + let pluginCatalog = await getDocument(PLUGIN_CATALOG) + if (pluginCatalog !== null) { + let parseError = false; + try { + pluginCatalog = JSON.parse(pluginCatalog); + console.log('Plugin catalog successfully downloaded'); + } catch (error) { + console.error('Error parsing plugin catalog:', error); + parseError = true; + } + + if (!parseError) { + await downloadPlugins(pluginCatalog, plugins, refreshPlugins) + + // update compatIssueIds + updateCompatIssueIds() + + // remove plugins that don't meet the min ED version requirement + plugins = filterPluginsByMinEDVersion(plugins, EasyDiffusionVersion) + + // remove from plugins the entries that don't have mandatory fields (id, name, url) + plugins = plugins.filter((plugin) => { return plugin.id !== '' && plugin.name !== '' && plugin.url !== ''; }); + + // remove from plugins the entries that no longer exist in the catalog + plugins = plugins.filter((plugin) => { return pluginCatalog.some((p) => p.id === plugin.id) }); + + if (pluginCatalog.length > plugins.length) { + const newPlugins = pluginCatalog.filter((plugin) => { + return !plugins.some((p) => p.id === plugin.id); + }); + + newPlugins.forEach((plugin, index) => { + setTimeout(() => { + showPluginToast(`New plugin "${plugin.name}" is available.`); + }, (index + 1) * 1000); + }); + } + + let pluginsJson; + try { + pluginsJson = JSON.stringify(plugins); // attempt to parse plugins to JSON + } catch (error) { + console.error('Error converting plugins to JSON:', error); + } + + if (pluginsJson) { // only store the data if pluginsJson is not null or undefined + await setStorageData('plugins', pluginsJson) + } + + // refresh the display of the plugins table + initPluginTable(plugins) + if (pluginsLoaded && pluginsLoaded === false) { + loadPlugins(plugins) + } + } + } + } + else { + if (refreshPlugins) { + showPluginToast('Plugins have been refreshed recently, refresh will be available in ' + convertSeconds(getTimeUntilNextRefresh()), 5000, true, false) + } + } + initPluginsInProgress = false +} + +function updateMetaTagPlugins(plugin) { + // Update the meta tag with the list of loaded plugins + let metaTag = document.querySelector('meta[name="plugins"]'); + if (metaTag === null) { + metaTag = document.createElement('meta'); + metaTag.name = 'plugins'; + document.head.appendChild(metaTag); + } + const pluginArray = [...(metaTag.content ? metaTag.content.split(',') : []), plugin.id]; + metaTag.content = pluginArray.join(','); +} + +function updateCompatIssueIds() { + // Loop through each plugin + plugins.forEach(plugin => { + // Check if the plugin has `compatIssueIds` property + if (plugin.compatIssueIds !== undefined) { + // Loop through each of the `compatIssueIds` + plugin.compatIssueIds.forEach(issueId => { + // Find the plugin with the corresponding `issueId` + const issuePlugin = plugins.find(p => p.id === issueId); + // If the corresponding plugin is found, initialize its `compatIssueIds` property with an empty array if it's undefined + if (issuePlugin) { + if (issuePlugin.compatIssueIds === undefined) { + issuePlugin.compatIssueIds = []; + } + // If the current plugin's ID is not already in the `compatIssueIds` array, add it + if (!issuePlugin.compatIssueIds.includes(plugin.id)) { + issuePlugin.compatIssueIds.push(plugin.id); + } + } + }); + } else { + // If the plugin doesn't have `compatIssueIds` property, initialize it with an empty array + plugin.compatIssueIds = []; + } + }); +} + +function deduplicatePluginsById(plugins) { + const seenIds = new Set(); + const deduplicatedPlugins = []; + + for (const plugin of plugins) { + if (!seenIds.has(plugin.id)) { + seenIds.add(plugin.id); + deduplicatedPlugins.push(plugin); + } else { + // favor dupes that have enabled == true + const index = deduplicatedPlugins.findIndex(p => p.id === plugin.id); + if (index >= 0) { + if (plugin.enabled) { + deduplicatedPlugins[index] = plugin; + } + } + } + } + + return deduplicatedPlugins; +} + +async function loadPlugins(plugins) { + for (let i = 0; i < plugins.length; i++) { + const plugin = plugins[i]; + if (plugin.enabled === true && plugin.localInstallOnly !== true) { + const localPluginFound = checkFileNameInArray(localPlugins, plugin.url); + if (!localPluginFound) { + try { + // Indirect eval to work around sloppy plugin implementations + const indirectEval = { eval }; + console.log("Loading plugin " + plugin.name); + indirectEval.eval(plugin.code); + console.log("Plugin " + plugin.name + " loaded"); + await updateMetaTagPlugins(plugin); // add plugin to the meta tag + } catch (err) { + showPluginToast("Error loading plugin " + plugin.name + " (" + err.message + ")", null, true); + console.error("Error loading plugin " + plugin.name + ": " + err.message); + } + } else { + console.log("Skipping plugin " + plugin.name + " (installed locally)"); + } + } + } +} + +async function getFileHash(url) { + const regex = /^https:\/\/raw\.githubusercontent\.com\/(?[^/]+)\/(?[^/]+)\/(?[^/]+)\/(?.+)$/; + const match = url.match(regex); + if (!match) { + console.error('Invalid GitHub repository URL.'); + return Promise.resolve(null); + } + const owner = match.groups.owner; + const repo = match.groups.repo; + const branch = match.groups.branch; + const filePath = match.groups.filePath; + const apiUrl = `https://api.github.com/repos/${owner}/${repo}/contents/${filePath}?ref=${branch}`; + + try { + const response = await fetch(apiUrl); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}, url: ${apiUrl}`); + } + const data = await response.json(); + return data.sha; + } catch (error) { + console.error('Error fetching data from url:', apiUrl, 'Error:', error); + return null; + } +} + +// only allow two refresh per hour +function getTimeUntilNextRefresh() { + const lastRuns = JSON.parse(localStorage.getItem('lastRuns') || '[]'); + const currentTime = new Date().getTime(); + const numRunsLast60Min = lastRuns.filter(run => currentTime - run <= 60 * 60 * 1000).length; + + if (numRunsLast60Min >= 2) { + return 3600 - Math.round((currentTime - lastRuns[lastRuns.length - 1]) / 1000); + } + + return 0; +} + +function refreshAllowed() { + const timeUntilNextRefresh = getTimeUntilNextRefresh(); + + if (timeUntilNextRefresh > 0) { + console.log(`Next refresh available in ${timeUntilNextRefresh} seconds`); + return false; + } + + const lastRuns = JSON.parse(localStorage.getItem('lastRuns') || '[]'); + const currentTime = new Date().getTime(); + lastRuns.push(currentTime); + localStorage.setItem('lastRuns', JSON.stringify(lastRuns)); + return true; +} + +async function downloadPlugins(pluginCatalog, plugins, refreshPlugins) { + // download the plugins as needed + for (const plugin of pluginCatalog) { + //console.log(plugin.id, plugin.url) + const existingPlugin = plugins.find(p => p.id === plugin.id); + // get the file hash in the GitHub repo + let sha + if (isGitHub(plugin.url) && existingPlugin?.enabled === true) { + sha = await getFileHash(plugin.url) + } + if (plugin.localInstallOnly !== true && isGitHub(plugin.url) && existingPlugin?.enabled === true && (refreshPlugins || (existingPlugin.sha !== undefined && existingPlugin.sha !== null && existingPlugin.sha !== sha) || existingPlugin?.code === undefined)) { + const pluginSource = await getDocument(plugin.url); + if (pluginSource !== null && pluginSource !== existingPlugin.code) { + console.log(`Plugin ${plugin.name} updated`); + showPluginToast("Plugin " + plugin.name + " updated", 5000); + // Update the corresponding plugin + const updatedPlugin = { + ...existingPlugin, + icon: plugin.icon ? plugin.icon : "fa-puzzle-piece", + id: plugin.id, + name: plugin.name, + description: plugin.description, + url: plugin.url, + localInstallOnly: Boolean(plugin.localInstallOnly), + version: plugin.version, + code: pluginSource, + author: plugin.author, + sha: sha, + compatIssueIds: plugin.compatIssueIds + }; + // Replace the old plugin in the plugins array + const pluginIndex = plugins.indexOf(existingPlugin); + if (pluginIndex >= 0) { + plugins.splice(pluginIndex, 1, updatedPlugin); + } else { + plugins.push(updatedPlugin); + } + } + } + else if (existingPlugin !== undefined) { + // Update the corresponding plugin's metadata + const updatedPlugin = { + ...existingPlugin, + icon: plugin.icon ? plugin.icon : "fa-puzzle-piece", + id: plugin.id, + name: plugin.name, + description: plugin.description, + url: plugin.url, + localInstallOnly: Boolean(plugin.localInstallOnly), + version: plugin.version, + author: plugin.author, + compatIssueIds: plugin.compatIssueIds + }; + // Replace the old plugin in the plugins array + const pluginIndex = plugins.indexOf(existingPlugin); + plugins.splice(pluginIndex, 1, updatedPlugin); + } + else { + plugins.push(plugin); + } + } +} + +async function getDocument(url) { + try { + let response = await fetch(url === PLUGIN_CATALOG ? PLUGIN_CATALOG : url, { cache: "no-cache" }); + if (!response.ok) { + throw new Error(`Response error: ${response.status} ${response.statusText}`); + } + let document = await response.text(); + return document; + } catch (error) { + showPluginToast("Couldn't fetch " + extractFilename(url) + " (" + error + ")", null, true); + console.error(error); + return null; + } +} + +/* MODAL DIALOG */ +const pluginDialogDialog = document.createElement("div"); +pluginDialogDialog.id = "pluginDialog-input-dialog"; +pluginDialogDialog.style.display = "none"; + +pluginDialogDialog.innerHTML = ` +
    +
    +
    +

    Paste the plugin's code here

    + +
    +
    + +
    +
    + + +
    +
    +`; + +document.body.appendChild(pluginDialogDialog); + +const pluginDialogOverlay = document.querySelector(".pluginDialog-dialog-overlay"); +const pluginDialogOkButton = document.getElementById("pluginDialog-input-ok"); +const pluginDialogCancelButton = document.getElementById("pluginDialog-input-cancel"); +const pluginDialogCloseButton = document.querySelector(".pluginDialog-dialog-close-button"); +const pluginDialogTextarea = document.getElementById("pluginDialog-input-textarea"); +let callbackOK +let callbackCancel + +function pluginDialogOpenDialog(inputOK, inputCancel) { + pluginDialogDialog.style.display = "block"; + callbackOK = inputOK + callbackCancel = inputCancel +} + +function pluginDialogCloseDialog() { + pluginDialogDialog.style.display = "none"; +} + +function pluginDialogHandleOkClick() { + const userInput = pluginDialogTextarea.value; + // Do something with the user input + callbackOK() + pluginDialogCloseDialog(); +} + +function pluginDialogHandleCancelClick() { + callbackCancel() + pluginDialogCloseDialog(); +} + +function pluginDialogHandleOverlayClick(event) { + if (event.target === pluginDialogOverlay) { + pluginDialogCloseDialog(); + } +} + +function pluginDialogHandleKeyDown(event) { + if ((event.key === "Enter" && event.ctrlKey) || event.key === "Escape") { + event.preventDefault(); + if (event.key === "Enter" && event.ctrlKey) { + pluginDialogHandleOkClick(); + } else { + pluginDialogCloseDialog(); + } + } +} + +pluginDialogTextarea.addEventListener("keydown", pluginDialogHandleKeyDown); +pluginDialogOkButton.addEventListener("click", pluginDialogHandleOkClick); +pluginDialogCancelButton.addEventListener("click", pluginDialogHandleCancelClick); +pluginDialogCloseButton.addEventListener("click", pluginDialogCloseDialog); +pluginDialogOverlay.addEventListener("click", pluginDialogHandleOverlayClick); diff --git a/ui/media/js/utils.js b/ui/media/js/utils.js index d1578d8e..324600f2 100644 --- a/ui/media/js/utils.js +++ b/ui/media/js/utils.js @@ -841,6 +841,7 @@ function createTab(request) { }) } + /* TOAST NOTIFICATIONS */ function showToast(message, duration = 5000, error = false) { const toast = document.createElement("div"); @@ -897,3 +898,130 @@ function showToast(message, duration = 5000, error = false) { // Remove the toast after specified duration setTimeout(removeToast, duration); } + + +/* STORAGE MANAGEMENT */ +// Request persistent storage +async function requestPersistentStorage() { + if (navigator.storage && navigator.storage.persist) { + const isPersisted = await navigator.storage.persist(); + console.log(`Persisted storage granted: ${isPersisted}`); + } +} +requestPersistentStorage() + +// Open a database +async function openDB() { + return new Promise((resolve, reject) => { + let request = indexedDB.open("EasyDiffusionSettingsDatabase", 1); + request.addEventListener("upgradeneeded", function () { + let db = request.result; + db.createObjectStore("EasyDiffusionSettings", { keyPath: "id" }); + }); + request.addEventListener("success", function () { + resolve(request.result); + }); + request.addEventListener("error", function () { + reject(request.error); + }); + }); +} + +// Function to write data to the object store +async function setStorageData(key, value) { + return openDB().then(db => { + let tx = db.transaction("EasyDiffusionSettings", "readwrite"); + let store = tx.objectStore("EasyDiffusionSettings"); + let data = { id: key, value: value }; + return new Promise((resolve, reject) => { + let request = store.put(data); + request.addEventListener("success", function () { + resolve(request.result); + }); + request.addEventListener("error", function () { + reject(request.error); + }); + }); + }); +} + +// Function to retrieve data from the object store +async function getStorageData(key) { + return openDB().then(db => { + let tx = db.transaction("EasyDiffusionSettings", "readonly"); + let store = tx.objectStore("EasyDiffusionSettings"); + return new Promise((resolve, reject) => { + let request = store.get(key); + request.addEventListener("success", function () { + if (request.result) { + resolve(request.result.value); + } else { + // entry not found + resolve(); + } + }); + request.addEventListener("error", function () { + reject(request.error); + }); + }); + }); +} + +// indexedDB debug functions +async function getAllKeys() { + return openDB().then(db => { + let tx = db.transaction("EasyDiffusionSettings", "readonly"); + let store = tx.objectStore("EasyDiffusionSettings"); + let keys = []; + return new Promise((resolve, reject) => { + store.openCursor().onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + keys.push(cursor.key); + cursor.continue(); + } else { + resolve(keys); + } + }; + }); + }); +} + +async function logAllStorageKeys() { + try { + let keys = await getAllKeys(); + console.log("All keys:", keys); + for (const k of keys) { + console.log(k, await getStorageData(k)) + } + } catch (error) { + console.error("Error retrieving keys:", error); + } +} + +// USE WITH CARE - THIS MAY DELETE ALL ENTRIES +async function deleteKeys(keyToDelete) { + let confirmationMessage = keyToDelete + ? `This will delete the template with key "${keyToDelete}". Continue?` + : "This will delete ALL templates. Continue?"; + if (confirm(confirmationMessage)) { + return openDB().then(db => { + let tx = db.transaction("EasyDiffusionSettings", "readwrite"); + let store = tx.objectStore("EasyDiffusionSettings"); + return new Promise((resolve, reject) => { + store.openCursor().onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + if (!keyToDelete || cursor.key === keyToDelete) { + cursor.delete(); + } + cursor.continue(); + } else { + // refresh the dropdown and resolve + resolve(); + } + }; + }); + }); + } +} From a548c026b1fa01822ae9518d0de10a0d811003bf Mon Sep 17 00:00:00 2001 From: patriceac <48073125+patriceac@users.noreply.github.com> Date: Thu, 25 May 2023 00:32:48 -0700 Subject: [PATCH 217/291] Revert "Fix face restoration model selection" This reverts commit a5a1d3358918626e2860db1effcabea3787fee2b. --- ui/easydiffusion/model_manager.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/ui/easydiffusion/model_manager.py b/ui/easydiffusion/model_manager.py index a0b2489a..2bb4852d 100644 --- a/ui/easydiffusion/model_manager.py +++ b/ui/easydiffusion/model_manager.py @@ -107,18 +107,12 @@ def resolve_model_to_use(model_name: str = None, model_type: str = None): def reload_models_if_necessary(context: Context, task_data: TaskData): - if hasattr(task_data, 'use_face_correction') and task_data.use_face_correction: - face_correction_model = "codeformer" if "codeformer" in task_data.use_face_correction.lower() else "gfpgan" - face_correction_value = task_data.use_face_correction - else: - face_correction_model = "gfpgan" - face_correction_value = None - + face_correction_model = "codeformer" if "codeformer" in task_data.use_face_correction.lower() else "gfpgan" model_paths_in_req = { "stable-diffusion": task_data.use_stable_diffusion_model, "vae": task_data.use_vae_model, "hypernetwork": task_data.use_hypernetwork_model, - face_correction_model: face_correction_value, + face_correction_model: task_data.use_face_correction, "realesrgan": task_data.use_upscale, "nsfw_checker": True if task_data.block_nsfw else None, "lora": task_data.use_lora_model, From 3a059bb919b63b77fbddf383d9124d492bdc7fec Mon Sep 17 00:00:00 2001 From: patriceac <48073125+patriceac@users.noreply.github.com> Date: Thu, 25 May 2023 00:32:57 -0700 Subject: [PATCH 218/291] Revert "Support for CodeFormer" This reverts commit a25364732b3f74dbfd5083cc4d7a0f84ccc5d9d5. --- ui/easydiffusion/model_manager.py | 3 +-- ui/easydiffusion/renderer.py | 4 +--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/ui/easydiffusion/model_manager.py b/ui/easydiffusion/model_manager.py index 2bb4852d..7bf56575 100644 --- a/ui/easydiffusion/model_manager.py +++ b/ui/easydiffusion/model_manager.py @@ -107,12 +107,11 @@ def resolve_model_to_use(model_name: str = None, model_type: str = None): def reload_models_if_necessary(context: Context, task_data: TaskData): - face_correction_model = "codeformer" if "codeformer" in task_data.use_face_correction.lower() else "gfpgan" model_paths_in_req = { "stable-diffusion": task_data.use_stable_diffusion_model, "vae": task_data.use_vae_model, "hypernetwork": task_data.use_hypernetwork_model, - face_correction_model: task_data.use_face_correction, + "gfpgan": task_data.use_face_correction, "realesrgan": task_data.use_upscale, "nsfw_checker": True if task_data.block_nsfw else None, "lora": task_data.use_lora_model, diff --git a/ui/easydiffusion/renderer.py b/ui/easydiffusion/renderer.py index e1176c8b..f685d038 100644 --- a/ui/easydiffusion/renderer.py +++ b/ui/easydiffusion/renderer.py @@ -158,9 +158,7 @@ def filter_images(task_data: TaskData, images: list, user_stopped): filters_to_apply = [] if task_data.block_nsfw: filters_to_apply.append("nsfw_checker") - if task_data.use_face_correction and "codeformer" in task_data.use_face_correction.lower(): - filters_to_apply.append("codeformer") - elif task_data.use_face_correction and "gfpgan" in task_data.use_face_correction.lower(): + if task_data.use_face_correction and "gfpgan" in task_data.use_face_correction.lower(): filters_to_apply.append("gfpgan") if task_data.use_upscale and "realesrgan" in task_data.use_upscale.lower(): filters_to_apply.append("realesrgan") From a0b3b5af53be1c5e5cdf772091bb989160ecc657 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Thu, 25 May 2023 15:36:27 +0530 Subject: [PATCH 219/291] sdkit 1.0.98 - seamless tiling --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 8747d173..3686ca00 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.97", + "sdkit": "1.0.98", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", From 5c8965b3abadc038fb1690d7b972cb13afdb6527 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Thu, 25 May 2023 15:38:49 +0530 Subject: [PATCH 220/291] changelog --- CHANGES.md | 1 + ui/index.html | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index a1e49336..19a19bb8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -22,6 +22,7 @@ Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. ### Detailed changelog +* 2.5.39 - 25 May 2023 - Seamless Tiling - make seamlessly tiled images, e.g. rock and grass textures. Thanks @JeLuf. * 2.5.38 - 24 May 2023 - Better reporting of errors, and show an explanation if the user cannot disable the "Use CPU" setting. * 2.5.38 - 23 May 2023 - Add Latent Upscaler as another option for upscaling images. Thanks @JeLuf for the implementation of the Latent Upscaler model. * 2.5.37 - 19 May 2023 - (beta-only) Two more samplers: DDPM and DEIS. Also disables the samplers that aren't working yet in the Diffusers version. Thanks @ogmaresca. diff --git a/ui/index.html b/ui/index.html index dc4eb7f0..76a9f9e6 100644 --- a/ui/index.html +++ b/ui/index.html @@ -30,7 +30,7 @@

    Easy Diffusion - v2.5.38 + v2.5.39

    From 59c322dc3b44b25819290dad6cd255c7d32faff2 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Thu, 25 May 2023 15:41:41 +0530 Subject: [PATCH 221/291] Show seamless tiling only in diffusers mode --- ui/index.html | 2 +- ui/media/js/parameters.js | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/index.html b/ui/index.html index 81012c14..375dae8c 100644 --- a/ui/index.html +++ b/ui/index.html @@ -236,7 +236,7 @@
    - +
    - + Click to learn more about samplers From 69d937e0b1e1eb94855fa2b3b155e8b3b5df3ac4 Mon Sep 17 00:00:00 2001 From: Olivia Godone-Maresca Date: Fri, 26 May 2023 19:51:30 -0400 Subject: [PATCH 226/291] Add tiling and latent upscaler steps to metadata Also fix txt metadata labels when also embedding metadata --- ui/easydiffusion/utils/save_utils.py | 26 ++++++++++++++++---------- ui/media/js/auto-save.js | 1 + ui/media/js/dnd.js | 8 ++++++++ 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/ui/easydiffusion/utils/save_utils.py b/ui/easydiffusion/utils/save_utils.py index 24b2198c..6a6f7239 100644 --- a/ui/easydiffusion/utils/save_utils.py +++ b/ui/easydiffusion/utils/save_utils.py @@ -30,9 +30,11 @@ TASK_TEXT_MAPPING = { "lora_alpha": "LoRA Strength", "use_hypernetwork_model": "Hypernetwork model", "hypernetwork_strength": "Hypernetwork Strength", + "tiling": "Seamless Tiling", "use_face_correction": "Use Face Correction", "use_upscale": "Use Upscaling", "upscale_amount": "Upscale By", + "latent_upscaler_steps": "Latent Upscaler Steps" } time_placeholders = { @@ -169,21 +171,23 @@ def save_images_to_disk(images: list, filtered_images: list, req: GenerateImageR output_quality=task_data.output_quality, output_lossless=task_data.output_lossless, ) - if task_data.metadata_output_format.lower() in ["json", "txt", "embed"]: - save_dicts( - metadata_entries, - save_dir_path, - file_name=make_filter_filename, - output_format=task_data.metadata_output_format, - file_format=task_data.output_format, - ) + if task_data.metadata_output_format: + for metadata_output_format in task_data.metadata_output_format.split(","): + if metadata_output_format.lower() in ["json", "txt", "embed"]: + save_dicts( + metadata_entries, + save_dir_path, + file_name=make_filter_filename, + output_format=task_data.metadata_output_format, + file_format=task_data.output_format, + ) def get_metadata_entries_for_request(req: GenerateImageRequest, task_data: TaskData): metadata = get_printable_request(req, task_data) # if text, format it in the text format expected by the UI - is_txt_format = task_data.metadata_output_format.lower() == "txt" + is_txt_format = task_data.metadata_output_format and "txt" in task_data.metadata_output_format.lower().split(",") if is_txt_format: metadata = {TASK_TEXT_MAPPING[key]: val for key, val in metadata.items() if key in TASK_TEXT_MAPPING} @@ -215,10 +219,12 @@ def get_printable_request(req: GenerateImageRequest, task_data: TaskData): del metadata["hypernetwork_strength"] if task_data.use_lora_model is None and "lora_alpha" in metadata: del metadata["lora_alpha"] + if task_data.use_upscale is not "latent_upscaler" and "latent_upscaler_steps" in metadata: + del metadata["latent_upscaler_steps"] app_config = app.getConfig() if not app_config.get("test_diffusers", False): - for key in (x for x in ["use_lora_model", "lora_alpha", "clip_skip"] if x in metadata): + for key in (x for x in ["use_lora_model", "lora_alpha", "clip_skip", "tiling", "latent_upscaler_steps"] if x in metadata): del metadata[key] return metadata diff --git a/ui/media/js/auto-save.js b/ui/media/js/auto-save.js index 1424a301..bbcbf9a5 100644 --- a/ui/media/js/auto-save.js +++ b/ui/media/js/auto-save.js @@ -35,6 +35,7 @@ const SETTINGS_IDS_LIST = [ "gfpgan_model", "use_upscale", "upscale_amount", + "latent_upscaler_steps", "block_nsfw", "show_only_filtered_image", "upscale_model", diff --git a/ui/media/js/dnd.js b/ui/media/js/dnd.js index a303301e..09def0b8 100644 --- a/ui/media/js/dnd.js +++ b/ui/media/js/dnd.js @@ -221,6 +221,14 @@ const TASK_MAPPING = { readUI: () => upscaleAmountField.value, parse: (val) => val, }, + latent_upscaler_steps: { + name: "Latent Upscaler Steps", + setUI: (latent_upscaler_steps) => { + latentUpscalerStepsField.value = latent_upscaler_steps + }, + readUI: () => latentUpscalerStepsField.value, + parse: (val) => val, + }, sampler_name: { name: "Sampler", setUI: (sampler_name) => { From 6826435046421e350c2b92608e61f0e3694a702c Mon Sep 17 00:00:00 2001 From: patriceac <48073125+patriceac@users.noreply.github.com> Date: Sat, 27 May 2023 00:26:25 -0700 Subject: [PATCH 227/291] Fix restore task to UI flow Fixes a regression introduced by https://github.com/cmdr2/stable-diffusion-ui/pull/1304 --- ui/media/js/dnd.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/media/js/dnd.js b/ui/media/js/dnd.js index a303301e..bac15bba 100644 --- a/ui/media/js/dnd.js +++ b/ui/media/js/dnd.js @@ -43,7 +43,6 @@ const TASK_MAPPING = { name: "Prompt", setUI: (prompt) => { promptField.value = prompt - promptField.dispatchEvent(new Event("input")) }, readUI: () => promptField.value, parse: (val) => val, @@ -422,6 +421,7 @@ function restoreTaskToUI(task, fieldsToSkip) { if (!("original_prompt" in task.reqBody)) { promptField.value = task.reqBody.prompt } + promptField.dispatchEvent(new Event("input")) // properly reset checkboxes if (!("use_face_correction" in task.reqBody)) { From 2080d6e27b66ebffa1b0ff112112d498e6caca68 Mon Sep 17 00:00:00 2001 From: JeLuF Date: Sun, 28 May 2023 00:50:23 +0200 Subject: [PATCH 228/291] Share ED via Cloudflare's ArgoTunnel Shares the Easy Diffusion instance via https://try.cloudflare.com/ --- scripts/check_modules.py | 1 + ui/easydiffusion/server.py | 54 ++++++++++++++++++++++++++++++++++++++ ui/index.html | 11 ++++++-- ui/media/css/auto-save.css | 8 +++--- ui/media/css/main.css | 9 +++++++ ui/media/js/engine.js | 4 +++ ui/media/js/main.js | 32 ++++++++++++++++++++++ ui/media/js/parameters.js | 46 +++++++++++++++++++++++++++++--- 8 files changed, 157 insertions(+), 8 deletions(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 8747d173..534e1e58 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -23,6 +23,7 @@ modules_to_check = { "rich": "12.6.0", "uvicorn": "0.19.0", "fastapi": "0.85.1", + "pycloudflared": "0.2.0", # "xformers": "0.0.16", } diff --git a/ui/easydiffusion/server.py b/ui/easydiffusion/server.py index a1aab6c0..b9ffe5f9 100644 --- a/ui/easydiffusion/server.py +++ b/ui/easydiffusion/server.py @@ -15,6 +15,7 @@ from fastapi import FastAPI, HTTPException from fastapi.staticfiles import StaticFiles from pydantic import BaseModel, Extra from starlette.responses import FileResponse, JSONResponse, StreamingResponse +from pycloudflared import try_cloudflare log.info(f"started in {app.SD_DIR}") log.info(f"started at {datetime.datetime.now():%x %X}") @@ -113,6 +114,14 @@ def init(): def get_image(task_id: int, img_id: int): return get_image_internal(task_id, img_id) + @server_api.post("/tunnel/cloudflare/start") + def start_cloudflare_tunnel(req: dict): + return start_cloudflare_tunnel_internal(req) + + @server_api.post("/tunnel/cloudflare/stop") + def stop_cloudflare_tunnel(req: dict): + return stop_cloudflare_tunnel_internal(req) + @server_api.get("/") def read_root(): return FileResponse(os.path.join(app.SD_UI_DIR, "index.html"), headers=NOCACHE_HEADERS) @@ -211,6 +220,8 @@ def ping_internal(session_id: str = None): session = task_manager.get_cached_session(session_id, update_ttl=True) response["tasks"] = {id(t): t.status for t in session.tasks} response["devices"] = task_manager.get_devices() + if cloudflare.address != None: + response["cloudflare"] = cloudflare.address return JSONResponse(response, headers=NOCACHE_HEADERS) @@ -322,3 +333,46 @@ def get_image_internal(task_id: int, img_id: int): return StreamingResponse(img_data, media_type="image/jpeg") except KeyError as e: raise HTTPException(status_code=500, detail=str(e)) + +#---- Cloudflare Tunnel ---- +class CloudflareTunnel: + def __init__(self): + config = app.getConfig() + self.Urls = None + self.port = config["net"]["listen_port"] + + def start(self): + self.Urls = try_cloudflare(self.port) + + def stop(self): + if self.Urls != None: + try_cloudflare.terminate(self.port) + self.Urls = None + + @property + def address(self): + if self.Urls != None: + return self.Urls.tunnel + else: + return None + +cloudflare = CloudflareTunnel() + +def start_cloudflare_tunnel_internal(req: dict): + try: + cloudflare.start() + log.info(f"- Started cloudflare tunnel. Using address: {cloudflare.address}") + return JSONResponse({"address":cloudflare.address}) + except Exception as e: + log.error(str(e)) + log.error(traceback.format_exc()) + return HTTPException(status_code=500, detail=str(e)) + +def stop_cloudflare_tunnel_internal(req: dict): + try: + cloudflare.stop() + except Exception as e: + log.error(str(e)) + log.error(traceback.format_exc()) + return HTTPException(status_code=500, detail=str(e)) + diff --git a/ui/index.html b/ui/index.html index dc4eb7f0..18837b59 100644 --- a/ui/index.html +++ b/ui/index.html @@ -347,10 +347,16 @@

    System Settings

    -
    +



    +
    +

    Share Easy Diffusion

    +
    +
    +
    +

    System Info

    @@ -525,7 +531,8 @@ async function init() { SD.init({ events: { statusChange: setServerStatus, - idle: onIdle + idle: onIdle, + ping: tunnelUpdate } }) diff --git a/ui/media/css/auto-save.css b/ui/media/css/auto-save.css index 80aa48d8..119a7e10 100644 --- a/ui/media/css/auto-save.css +++ b/ui/media/css/auto-save.css @@ -69,13 +69,15 @@ } .parameters-table > div:first-child { - border-radius: 12px 12px 0px 0px; + border-top-left-radius: 12px; + border-top-right-radius: 12px; } .parameters-table > div:last-child { - border-radius: 0px 0px 12px 12px; + border-bottom-left-radius: 12px; + border-bottom-right-radius: 12px; } .parameters-table .fa-fire { color: #F7630C; -} \ No newline at end of file +} diff --git a/ui/media/css/main.css b/ui/media/css/main.css index 8f4f49fa..47a88601 100644 --- a/ui/media/css/main.css +++ b/ui/media/css/main.css @@ -1309,6 +1309,15 @@ body.wait-pause { padding-left: 5pt; } +#cloudflare-address { + background-color: var(--background-color3); + padding: 6px; + border-radius: var(--input-border-radius); + border: var(--input-border-size) solid var(--input-border-color); + margin-top: 0.2em; + margin-bottom: 0.2em; +} + /* TOAST NOTIFICATIONS */ .toast-notification { position: fixed; diff --git a/ui/media/js/engine.js b/ui/media/js/engine.js index eccae6ac..506451d6 100644 --- a/ui/media/js/engine.js +++ b/ui/media/js/engine.js @@ -186,6 +186,7 @@ const EVENT_TASK_START = "taskStart" const EVENT_TASK_END = "taskEnd" const EVENT_TASK_ERROR = "task_error" + const EVENT_PING = "ping" const EVENT_UNEXPECTED_RESPONSE = "unexpectedResponse" const EVENTS_TYPES = [ EVENT_IDLE, @@ -196,6 +197,7 @@ EVENT_TASK_START, EVENT_TASK_END, EVENT_TASK_ERROR, + EVENT_PING, EVENT_UNEXPECTED_RESPONSE, ] @@ -240,6 +242,7 @@ setServerStatus("error", "offline") return false } + // Set status switch (serverState.status) { case ServerStates.init: @@ -261,6 +264,7 @@ break } serverState.time = Date.now() + await eventSource.fireEvent(EVENT_PING, serverState) return true } catch (e) { console.error(e) diff --git a/ui/media/js/main.js b/ui/media/js/main.js index ecd8ad73..d08d7c4a 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -1951,6 +1951,38 @@ resumeBtn.addEventListener("click", function() { document.body.classList.remove("wait-pause") }) +function tunnelUpdate(event) { + if ("cloudflare" in event) { + document.getElementById('cloudflare-off').classList.add("displayNone") + document.getElementById('cloudflare-on').classList.remove("displayNone") + document.getElementById('cloudflare-address').innerHTML = event.cloudflare + document.getElementById('toggle-cloudflare-tunnel').innerHTML = "Stop" + } else { + document.getElementById('cloudflare-on').classList.add("displayNone") + document.getElementById('cloudflare-off').classList.remove("displayNone") + document.getElementById('toggle-cloudflare-tunnel').innerHTML = "Start" + } +} + +document.getElementById('toggle-cloudflare-tunnel').addEventListener("click", async function() { + let command = "stop" + if (document.getElementById('toggle-cloudflare-tunnel').innerHTML == "Start") { + command = "start" + } + showToast(`Cloudflare tunnel ${command} initiated. Please wait.`) + + let res = await fetch("/tunnel/cloudflare/"+command, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({}), + }) + res = await res.json() + + console.log(`Cloudflare tunnel ${command} result:`, res) +}) + /* Pause function */ document.querySelectorAll(".tab").forEach(linkTabContents) diff --git a/ui/media/js/parameters.js b/ui/media/js/parameters.js index 4c7240eb..cab32cc8 100644 --- a/ui/media/js/parameters.js +++ b/ui/media/js/parameters.js @@ -11,6 +11,12 @@ var ParameterType = { custom: "custom", } +/** + * Element shortcuts + */ +let parametersTable = document.querySelector("#system-settings-table") +let networkParametersTable = document.querySelector("#system-settings-network-table") + /** * JSDoc style * @typedef {object} Parameter @@ -186,6 +192,7 @@ var PARAMETERS = [ icon: "fa-network-wired", default: true, saveInAppConfig: true, + table: networkParametersTable, }, { id: "listen_port", @@ -198,6 +205,7 @@ var PARAMETERS = [ return `` }, saveInAppConfig: true, + table: networkParametersTable, }, { id: "use_beta_channel", @@ -218,6 +226,21 @@ var PARAMETERS = [ default: false, saveInAppConfig: true, }, + { + id: "cloudflare", + type: ParameterType.custom, + label: "Cloudflare tunnel", + note: `Create a VPN tunnel to share your Easy Diffusion instance with your friends. This will + generate a web server address on the public Internet for your Easy Diffusion instance. +
    This Easy Diffusion server is available on the Internet using the + address:
    + Anyone knowing this address can access your server. The address of your server will change each time + you share a session.
    + Uses Cloudflare services.`, + icon: ["fa-brands", "fa-cloudflare"], + render: () => '', + table: networkParametersTable, + } ] function getParameterSettingsEntry(id) { @@ -266,7 +289,6 @@ function getParameterElement(parameter) { } } -let parametersTable = document.querySelector("#system-settings .parameters-table") /** * fill in the system settings popup table * @param {Array | undefined} parameters @@ -293,7 +315,10 @@ function initParameters(parameters) { noteElements.push(noteElement) } - const icon = parameter.icon ? [createElement("i", undefined, ["fa", parameter.icon])] : [] + if (typeof(parameter.icon) == "string") { + parameter.icon = [parameter.icon] + } + const icon = parameter.icon ? [createElement("i", undefined, ["fa", ...parameter.icon])] : [] const label = typeof parameter.label === "function" ? parameter.label(parameter) : parameter.label const labelElement = createElement("label", { for: parameter.id }) @@ -313,7 +338,13 @@ function initParameters(parameters) { elementWrapper, ] ) - parametersTable.appendChild(newrow) + + let p = parametersTable + if (parameter.table) { + p = parameter.table + } + p.appendChild(newrow) + parameter.settingsEntry = newrow }) } @@ -665,8 +696,17 @@ saveSettingsBtn.addEventListener("click", function() { }) const savePromise = changeAppConfig(updateAppConfigRequest) + showToast("Settings saved") saveSettingsBtn.classList.add("active") Promise.all([savePromise, asyncDelay(300)]).then(() => saveSettingsBtn.classList.remove("active")) }) +listenToNetworkField.addEventListener("change", debounce( ()=>{ + saveSettingsBtn.click() +}, 1000)) + +listenPortField.addEventListener("change", debounce( ()=>{ + saveSettingsBtn.click() +}, 1000)) + document.addEventListener("system_info_update", (e) => setDeviceInfo(e.detail)) From 9ce076eb0d0df21395a54f2d66538dec83fa9b1f Mon Sep 17 00:00:00 2001 From: JeLuF Date: Sun, 28 May 2023 01:18:39 +0200 Subject: [PATCH 229/291] Copy address button --- ui/media/css/main.css | 6 ++++++ ui/media/js/main.js | 2 +- ui/media/js/parameters.js | 10 +++++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/ui/media/css/main.css b/ui/media/css/main.css index 47a88601..cdb67703 100644 --- a/ui/media/css/main.css +++ b/ui/media/css/main.css @@ -1316,6 +1316,12 @@ body.wait-pause { border: var(--input-border-size) solid var(--input-border-color); margin-top: 0.2em; margin-bottom: 0.2em; + display: inline-block; +} + +#copy-cloudflare-address { + padding: 4px 8px; + margin-left: 0.5em; } /* TOAST NOTIFICATIONS */ diff --git a/ui/media/js/main.js b/ui/media/js/main.js index d08d7c4a..c3f0c016 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -1955,7 +1955,7 @@ function tunnelUpdate(event) { if ("cloudflare" in event) { document.getElementById('cloudflare-off').classList.add("displayNone") document.getElementById('cloudflare-on').classList.remove("displayNone") - document.getElementById('cloudflare-address').innerHTML = event.cloudflare + cloudflareAddressField.innerHTML = event.cloudflare document.getElementById('toggle-cloudflare-tunnel').innerHTML = "Stop" } else { document.getElementById('cloudflare-on').classList.add("displayNone") diff --git a/ui/media/js/parameters.js b/ui/media/js/parameters.js index cab32cc8..cb66383c 100644 --- a/ui/media/js/parameters.js +++ b/ui/media/js/parameters.js @@ -233,7 +233,7 @@ var PARAMETERS = [ note: `Create a VPN tunnel to share your Easy Diffusion instance with your friends. This will generate a web server address on the public Internet for your Easy Diffusion instance.
    This Easy Diffusion server is available on the Internet using the - address:
    + address:
    Anyone knowing this address can access your server. The address of your server will change each time you share a session.
    Uses Cloudflare services.`, @@ -709,4 +709,12 @@ listenPortField.addEventListener("change", debounce( ()=>{ saveSettingsBtn.click() }, 1000)) +let copyCloudflareAddressBtn = document.querySelector("#copy-cloudflare-address") +let cloudflareAddressField = document.getElementById("cloudflare-address") + +copyCloudflareAddressBtn.addEventListener("click", (e) => { + navigator.clipboard.writeText(cloudflareAddressField.innerHTML) + showToast("Copied server address to clipboard") +}) + document.addEventListener("system_info_update", (e) => setDeviceInfo(e.detail)) From 30dcc7477f12d22d2dc8b17b9c61fc80e3e56bc7 Mon Sep 17 00:00:00 2001 From: JeLuF Date: Sun, 28 May 2023 01:43:58 +0200 Subject: [PATCH 230/291] Fix GFPGAN settings import The word None which many txt metadata files contain as value for the GFPGAN field should not be considered to be a model name. If the value is None, disable the checkbox --- ui/media/js/dnd.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/ui/media/js/dnd.js b/ui/media/js/dnd.js index 8b66a3a4..f8fd8ccc 100644 --- a/ui/media/js/dnd.js +++ b/ui/media/js/dnd.js @@ -172,16 +172,22 @@ const TASK_MAPPING = { name: "Use Face Correction", setUI: (use_face_correction) => { const oldVal = gfpganModelField.value - gfpganModelField.value = getModelPath(use_face_correction, [".pth"]) - if (gfpganModelField.value) { - // Is a valid value for the field. - useFaceCorrectionField.checked = true - gfpganModelField.disabled = false - } else { - // Not a valid value, restore the old value and disable the filter. + console.log("use face correction", use_face_correction) + if (use_face_correction == null || use_face_correction == "None") { gfpganModelField.disabled = true - gfpganModelField.value = oldVal useFaceCorrectionField.checked = false + } else { + gfpganModelField.value = getModelPath(use_face_correction, [".pth"]) + if (gfpganModelField.value) { + // Is a valid value for the field. + useFaceCorrectionField.checked = true + gfpganModelField.disabled = false + } else { + // Not a valid value, restore the old value and disable the filter. + gfpganModelField.disabled = true + gfpganModelField.value = oldVal + useFaceCorrectionField.checked = false + } } //useFaceCorrectionField.checked = parseBoolean(use_face_correction) From 7202ffba6e9645cbbd4e668e475ca3bbfb1a7d41 Mon Sep 17 00:00:00 2001 From: JeLuF Date: Sun, 28 May 2023 02:36:56 +0200 Subject: [PATCH 231/291] Fix #1312 - invert model A and B ratio in merge --- ui/plugins/ui/merge.plugin.js | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/ui/plugins/ui/merge.plugin.js b/ui/plugins/ui/merge.plugin.js index 5ce97b2d..d3ddedbf 100644 --- a/ui/plugins/ui/merge.plugin.js +++ b/ui/plugins/ui/merge.plugin.js @@ -403,16 +403,19 @@ // Batch main loop for (let i = 0; i < iterations; i++) { let alpha = (start + i * step) / 100 - switch (document.querySelector("#merge-interpolation").value) { - case "SmoothStep": - alpha = smoothstep(alpha) - break - case "SmootherStep": - alpha = smootherstep(alpha) - break - case "SmoothestStep": - alpha = smootheststep(alpha) - break + + if (isTabActive(tabSettingsBatch)) { + switch (document.querySelector("#merge-interpolation").value) { + case "SmoothStep": + alpha = smoothstep(alpha) + break + case "SmootherStep": + alpha = smootherstep(alpha) + break + case "SmoothestStep": + alpha = smootheststep(alpha) + break + } } addLogMessage(`merging batch job ${i + 1}/${iterations}, alpha = ${alpha.toFixed(5)}...`) @@ -420,7 +423,8 @@ request["out_path"] += "-" + alpha.toFixed(5) + "." + document.querySelector("#merge-format").value addLogMessage(`  filename: ${request["out_path"]}`) - request["ratio"] = alpha + // sdkit documentation: "ratio - the ratio of the second model. 1 means only the second model will be used." + request["ratio"] = 1-alpha let res = await fetch("/model/merge", { method: "POST", headers: { "Content-Type": "application/json" }, From 7830ec7ca2b9d4715cd9d2cd9238d2d40f07d619 Mon Sep 17 00:00:00 2001 From: Olivia Godone-Maresca Date: Sun, 28 May 2023 14:39:36 -0400 Subject: [PATCH 232/291] Fix SyntaxWarning on startup Fixes ``` /ssd2/easydiffusion/ui/easydiffusion/utils/save_utils.py:222: SyntaxWarning: "is not" with a literal. Did you mean "!="? if task_data.use_upscale is not "latent_upscaler" and "latent_upscaler_steps" in metadata: ``` --- ui/easydiffusion/utils/save_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/easydiffusion/utils/save_utils.py b/ui/easydiffusion/utils/save_utils.py index 6a6f7239..ff2906a6 100644 --- a/ui/easydiffusion/utils/save_utils.py +++ b/ui/easydiffusion/utils/save_utils.py @@ -219,7 +219,7 @@ def get_printable_request(req: GenerateImageRequest, task_data: TaskData): del metadata["hypernetwork_strength"] if task_data.use_lora_model is None and "lora_alpha" in metadata: del metadata["lora_alpha"] - if task_data.use_upscale is not "latent_upscaler" and "latent_upscaler_steps" in metadata: + if task_data.use_upscale != "latent_upscaler" and "latent_upscaler_steps" in metadata: del metadata["latent_upscaler_steps"] app_config = app.getConfig() From 0860e35d17e15c3cf879b657fd5788b06192c6e4 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Thu, 1 Jun 2023 16:50:01 +0530 Subject: [PATCH 233/291] sdkit 1.0.101 - CodeFormer as an option to improve faces --- scripts/check_models.py | 4 ++++ scripts/check_modules.py | 2 +- ui/easydiffusion/model_manager.py | 20 +++++++++++++------- ui/easydiffusion/renderer.py | 3 +++ ui/easydiffusion/types.py | 3 ++- ui/index.html | 9 +++++++-- ui/media/css/main.css | 4 ++-- ui/media/js/main.js | 27 ++++++++++++++++++++++++--- ui/media/js/searchable-models.js | 13 ++++++++++++- 9 files changed, 68 insertions(+), 17 deletions(-) diff --git a/scripts/check_models.py b/scripts/check_models.py index 4b8d68c6..a2186727 100644 --- a/scripts/check_models.py +++ b/scripts/check_models.py @@ -24,6 +24,9 @@ models_to_check = { "vae": [ {"file_name": "vae-ft-mse-840000-ema-pruned.ckpt", "model_id": "vae-ft-mse-840000-ema-pruned"}, ], + "codeformer": [ + {"file_name": "codeformer.pth", "model_id": "codeformer-0.1.0"}, + ], } MODEL_EXTENSIONS = { # copied from easydiffusion/model_manager.py "stable-diffusion": [".ckpt", ".safetensors"], @@ -32,6 +35,7 @@ MODEL_EXTENSIONS = { # copied from easydiffusion/model_manager.py "gfpgan": [".pth"], "realesrgan": [".pth"], "lora": [".ckpt", ".safetensors"], + "codeformer": [".pth"], } diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 3686ca00..416c3cc2 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.98", + "sdkit": "1.0.101", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", diff --git a/ui/easydiffusion/model_manager.py b/ui/easydiffusion/model_manager.py index 2a8b57fd..458dae7f 100644 --- a/ui/easydiffusion/model_manager.py +++ b/ui/easydiffusion/model_manager.py @@ -13,6 +13,7 @@ KNOWN_MODEL_TYPES = [ "gfpgan", "realesrgan", "lora", + "codeformer", ] MODEL_EXTENSIONS = { "stable-diffusion": [".ckpt", ".safetensors"], @@ -21,6 +22,7 @@ MODEL_EXTENSIONS = { "gfpgan": [".pth"], "realesrgan": [".pth"], "lora": [".ckpt", ".safetensors"], + "codeformer": [".pth"], } DEFAULT_MODELS = { "stable-diffusion": [ # needed to support the legacy installations @@ -133,6 +135,9 @@ def reload_models_if_necessary(context: Context, task_data: TaskData): if context.model_paths.get(model_type) != path } + if task_data.codeformer_upscale_faces and "realesrgan" not in models_to_reload.keys(): + models_to_reload["realesrgan"] = resolve_model_to_use(DEFAULT_MODELS["realesrgan"][0], "realesrgan") + if set_vram_optimizations(context) or set_clip_skip(context, task_data): # reload SD models_to_reload["stable-diffusion"] = model_paths_in_req["stable-diffusion"] @@ -159,7 +164,12 @@ def resolve_model_paths(task_data: TaskData): task_data.use_lora_model = resolve_model_to_use(task_data.use_lora_model, model_type="lora") if task_data.use_face_correction: - task_data.use_face_correction = resolve_model_to_use(task_data.use_face_correction, "gfpgan") + if "gfpgan" in task_data.use_face_correction.lower(): + model_type = "gfpgan" + elif "codeformer" in task_data.use_face_correction.lower(): + model_type = "codeformer" + + task_data.use_face_correction = resolve_model_to_use(task_data.use_face_correction, model_type) if task_data.use_upscale and "realesrgan" in task_data.use_upscale.lower(): task_data.use_upscale = resolve_model_to_use(task_data.use_upscale, "realesrgan") @@ -240,17 +250,12 @@ def is_malicious_model(file_path): def getModels(): models = { - "active": { - "stable-diffusion": "sd-v1-4", - "vae": "", - "hypernetwork": "", - "lora": "", - }, "options": { "stable-diffusion": ["sd-v1-4"], "vae": [], "hypernetwork": [], "lora": [], + "codeformer": [], }, } @@ -307,6 +312,7 @@ def getModels(): listModels(model_type="hypernetwork") listModels(model_type="gfpgan") listModels(model_type="lora") + listModels(model_type="codeformer") if models_scanned > 0: log.info(f"[green]Scanned {models_scanned} models. Nothing infected[/]") diff --git a/ui/easydiffusion/renderer.py b/ui/easydiffusion/renderer.py index 1ebd05ec..4be6e6bf 100644 --- a/ui/easydiffusion/renderer.py +++ b/ui/easydiffusion/renderer.py @@ -34,6 +34,7 @@ def init(device): context.temp_images = {} context.partial_x_samples = None context.model_load_errors = {} + context.enable_codeformer = True from easydiffusion import app @@ -162,6 +163,8 @@ def filter_images(req: GenerateImageRequest, task_data: TaskData, images: list, filters_to_apply.append("nsfw_checker") if task_data.use_face_correction and "codeformer" in task_data.use_face_correction.lower(): filters_to_apply.append("codeformer") + + filter_params["upscale_faces"] = task_data.codeformer_upscale_faces elif task_data.use_face_correction and "gfpgan" in task_data.use_face_correction.lower(): filters_to_apply.append("gfpgan") if task_data.use_upscale: diff --git a/ui/easydiffusion/types.py b/ui/easydiffusion/types.py index e4426714..4559a23a 100644 --- a/ui/easydiffusion/types.py +++ b/ui/easydiffusion/types.py @@ -23,7 +23,7 @@ class GenerateImageRequest(BaseModel): sampler_name: str = None # "ddim", "plms", "heun", "euler", "euler_a", "dpm2", "dpm2_a", "lms" hypernetwork_strength: float = 0 lora_alpha: float = 0 - tiling: str = "none" # "none", "x", "y", "xy" + tiling: str = "none" # "none", "x", "y", "xy" class TaskData(BaseModel): @@ -51,6 +51,7 @@ class TaskData(BaseModel): stream_image_progress: bool = False stream_image_progress_interval: int = 5 clip_skip: bool = False + codeformer_upscale_faces: bool = False class MergeRequest(BaseModel): diff --git a/ui/index.html b/ui/index.html index 21ec2550..4167a873 100644 --- a/ui/index.html +++ b/ui/index.html @@ -263,7 +263,12 @@
    • Render Settings
    • -
    • +
    • +
      +
      + +
      +
    • -
      +
    • diff --git a/ui/media/css/main.css b/ui/media/css/main.css index 8f4f49fa..e2d1f79a 100644 --- a/ui/media/css/main.css +++ b/ui/media/css/main.css @@ -1303,7 +1303,7 @@ body.wait-pause { display:none !important; } -#latent_upscaler_settings { +.sub-settings { padding-top: 3pt; padding-bottom: 3pt; padding-left: 5pt; @@ -1322,7 +1322,7 @@ body.wait-pause { box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); z-index: 9999; animation: slideInRight 0.5s ease forwards; - transition: bottom 0.5s ease; // Add a transition to smoothly reposition the toasts + transition: bottom 0.5s ease; /* Add a transition to smoothly reposition the toasts */ } .toast-notification-error { diff --git a/ui/media/js/main.js b/ui/media/js/main.js index 8628732b..ef27cf1b 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -87,7 +87,7 @@ let promptStrengthField = document.querySelector("#prompt_strength") let samplerField = document.querySelector("#sampler_name") let samplerSelectionContainer = document.querySelector("#samplerSelection") let useFaceCorrectionField = document.querySelector("#use_face_correction") -let gfpganModelField = new ModelDropdown(document.querySelector("#gfpgan_model"), "gfpgan") +let gfpganModelField = new ModelDropdown(document.querySelector("#gfpgan_model"), ["codeformer", "gfpgan"]) let useUpscalingField = document.querySelector("#use_upscale") let upscaleModelField = document.querySelector("#upscale_model") let upscaleAmountField = document.querySelector("#upscale_amount") @@ -270,7 +270,9 @@ function shiftOrConfirm(e, prompt, fn) { confirm( 'Tip: To skip this dialog, use shift-click or disable the "Confirm dangerous actions" setting in the Settings tab.', prompt, - () => { fn(e) } + () => { + fn(e) + } ) } } @@ -1261,6 +1263,10 @@ function getCurrentUserRequest() { } if (useFaceCorrectionField.checked) { newTask.reqBody.use_face_correction = gfpganModelField.value + + if (gfpganModelField.value.includes("codeformer")) { + newTask.reqBody.codeformer_upscale_faces = document.querySelector("#codeformer_upscale_faces").checked + } } if (useUpscalingField.checked) { newTask.reqBody.use_upscale = upscaleModelField.value @@ -1574,18 +1580,33 @@ metadataOutputFormatField.disabled = !saveToDiskField.checked gfpganModelField.disabled = !useFaceCorrectionField.checked useFaceCorrectionField.addEventListener("change", function(e) { gfpganModelField.disabled = !this.checked + + onFixFaceModelChange() }) +function onFixFaceModelChange() { + let codeformerSettings = document.querySelector("#codeformer_settings") + if (gfpganModelField.value === "codeformer" && !gfpganModelField.disabled) { + codeformerSettings.classList.remove("displayNone") + } else { + codeformerSettings.classList.add("displayNone") + } +} +gfpganModelField.addEventListener("change", onFixFaceModelChange) +onFixFaceModelChange() + upscaleModelField.disabled = !useUpscalingField.checked upscaleAmountField.disabled = !useUpscalingField.checked useUpscalingField.addEventListener("change", function(e) { upscaleModelField.disabled = !this.checked upscaleAmountField.disabled = !this.checked + + onUpscaleModelChange() }) function onUpscaleModelChange() { let upscale4x = document.querySelector("#upscale_amount_4x") - if (upscaleModelField.value === "latent_upscaler") { + if (upscaleModelField.value === "latent_upscaler" && !upscaleModelField.disabled) { upscale4x.disabled = true upscaleAmountField.value = "2" latentUpscalerSettings.classList.remove("displayNone") diff --git a/ui/media/js/searchable-models.js b/ui/media/js/searchable-models.js index 9a723d15..f0d06b80 100644 --- a/ui/media/js/searchable-models.js +++ b/ui/media/js/searchable-models.js @@ -90,7 +90,12 @@ class ModelDropdown { if (modelsOptions !== undefined) { // reuse models from cache (only useful for plugins, which are loaded after models) - this.inputModels = modelsOptions[this.modelKey] + this.inputModels = [] + let modelKeys = Array.isArray(this.modelKey) ? this.modelKey : [this.modelKey] + for (let i = 0; i < modelKeys.length; i++) { + let key = modelKeys[i] + this.inputModels.push(...modelsOptions[key]) + } this.populateModels() } document.addEventListener( @@ -98,6 +103,12 @@ class ModelDropdown { this.bind(function(e) { // reload the models this.inputModels = modelsOptions[this.modelKey] + this.inputModels = [] + let modelKeys = Array.isArray(this.modelKey) ? this.modelKey : [this.modelKey] + for (let i = 0; i < modelKeys.length; i++) { + let key = modelKeys[i] + this.inputModels.push(...modelsOptions[key]) + } this.populateModels() }, this) ) From dd95df8f022a51bb8e5b097b94fca34e9fd5105a Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 2 Jun 2023 16:34:29 +0530 Subject: [PATCH 234/291] Refactor the default model download code, remove check_models.py, don't check in legacy paths since that's already migrated during initialization; Download CodeFormer's model only when it's used for the first time --- scripts/check_models.py | 105 -------------------------- scripts/on_sd_start.bat | 7 -- scripts/on_sd_start.sh | 6 -- ui/easydiffusion/app.py | 45 +++++++++-- ui/easydiffusion/model_manager.py | 120 +++++++++++++++++++++--------- 5 files changed, 121 insertions(+), 162 deletions(-) delete mode 100644 scripts/check_models.py diff --git a/scripts/check_models.py b/scripts/check_models.py deleted file mode 100644 index a2186727..00000000 --- a/scripts/check_models.py +++ /dev/null @@ -1,105 +0,0 @@ -# this script runs inside the legacy "stable-diffusion" folder - -from sdkit.models import download_model, get_model_info_from_db -from sdkit.utils import hash_file_quick - -import os -import shutil -from glob import glob -import traceback - -models_base_dir = os.path.abspath(os.path.join("..", "models")) - -models_to_check = { - "stable-diffusion": [ - {"file_name": "sd-v1-4.ckpt", "model_id": "1.4"}, - ], - "gfpgan": [ - {"file_name": "GFPGANv1.4.pth", "model_id": "1.4"}, - ], - "realesrgan": [ - {"file_name": "RealESRGAN_x4plus.pth", "model_id": "x4plus"}, - {"file_name": "RealESRGAN_x4plus_anime_6B.pth", "model_id": "x4plus_anime_6"}, - ], - "vae": [ - {"file_name": "vae-ft-mse-840000-ema-pruned.ckpt", "model_id": "vae-ft-mse-840000-ema-pruned"}, - ], - "codeformer": [ - {"file_name": "codeformer.pth", "model_id": "codeformer-0.1.0"}, - ], -} -MODEL_EXTENSIONS = { # copied from easydiffusion/model_manager.py - "stable-diffusion": [".ckpt", ".safetensors"], - "vae": [".vae.pt", ".ckpt", ".safetensors"], - "hypernetwork": [".pt", ".safetensors"], - "gfpgan": [".pth"], - "realesrgan": [".pth"], - "lora": [".ckpt", ".safetensors"], - "codeformer": [".pth"], -} - - -def download_if_necessary(model_type: str, file_name: str, model_id: str): - model_path = os.path.join(models_base_dir, model_type, file_name) - expected_hash = get_model_info_from_db(model_type=model_type, model_id=model_id)["quick_hash"] - - other_models_exist = any_model_exists(model_type) - known_model_exists = os.path.exists(model_path) - known_model_is_corrupt = known_model_exists and hash_file_quick(model_path) != expected_hash - - if known_model_is_corrupt or (not other_models_exist and not known_model_exists): - print("> download", model_type, model_id) - download_model(model_type, model_id, download_base_dir=models_base_dir) - - -def init(): - migrate_legacy_model_location() - - for model_type, models in models_to_check.items(): - for model in models: - try: - download_if_necessary(model_type, model["file_name"], model["model_id"]) - except: - traceback.print_exc() - fail(model_type) - - print(model_type, "model(s) found.") - - -### utilities -def any_model_exists(model_type: str) -> bool: - extensions = MODEL_EXTENSIONS.get(model_type, []) - for ext in extensions: - if any(glob(f"{models_base_dir}/{model_type}/**/*{ext}", recursive=True)): - return True - - return False - - -def migrate_legacy_model_location(): - 'Move the models inside the legacy "stable-diffusion" folder, to their respective folders' - - for model_type, models in models_to_check.items(): - for model in models: - file_name = model["file_name"] - if os.path.exists(file_name): - dest_dir = os.path.join(models_base_dir, model_type) - os.makedirs(dest_dir, exist_ok=True) - shutil.move(file_name, os.path.join(dest_dir, file_name)) - - -def fail(model_name): - print( - f"""Error downloading the {model_name} model. Sorry about that, please try to: -1. Run this installer again. -2. If that doesn't fix it, please try to download the file manually. The address to download from, and the destination to save to are printed above this message. -3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB -4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues -Thanks!""" - ) - exit(1) - - -### start - -init() diff --git a/scripts/on_sd_start.bat b/scripts/on_sd_start.bat index ba205c9e..d2ef7321 100644 --- a/scripts/on_sd_start.bat +++ b/scripts/on_sd_start.bat @@ -79,13 +79,6 @@ call WHERE uvicorn > .tmp @echo conda_sd_ui_deps_installed >> ..\scripts\install_status.txt ) -@rem Download the required models -call python ..\scripts\check_models.py -if "%ERRORLEVEL%" NEQ "0" ( - pause - exit /b -) - @>nul findstr /m "sd_install_complete" ..\scripts\install_status.txt @if "%ERRORLEVEL%" NEQ "0" ( @echo sd_weights_downloaded >> ..\scripts\install_status.txt diff --git a/scripts/on_sd_start.sh b/scripts/on_sd_start.sh index 820c36ed..55f4da25 100755 --- a/scripts/on_sd_start.sh +++ b/scripts/on_sd_start.sh @@ -51,12 +51,6 @@ if ! command -v uvicorn &> /dev/null; then fail "UI packages not found!" fi -# Download the required models -if ! python ../scripts/check_models.py; then - read -p "Press any key to continue" - exit 1 -fi - if [ `grep -c sd_install_complete ../scripts/install_status.txt` -gt "0" ]; then echo sd_weights_downloaded >> ../scripts/install_status.txt echo sd_install_complete >> ../scripts/install_status.txt diff --git a/ui/easydiffusion/app.py b/ui/easydiffusion/app.py index 3064e151..38e3392c 100644 --- a/ui/easydiffusion/app.py +++ b/ui/easydiffusion/app.py @@ -90,8 +90,8 @@ def init(): os.makedirs(USER_SERVER_PLUGINS_DIR, exist_ok=True) # https://pytorch.org/docs/stable/storage.html - warnings.filterwarnings('ignore', category=UserWarning, message='TypedStorage is deprecated') - + warnings.filterwarnings("ignore", category=UserWarning, message="TypedStorage is deprecated") + load_server_plugins() update_render_threads() @@ -221,12 +221,41 @@ def open_browser(): webbrowser.open(f"http://localhost:{port}") - Console().print(Panel( - "\n" + - "[white]Easy Diffusion is ready to serve requests.\n\n" + - "A new browser tab should have been opened by now.\n" + - f"If not, please open your web browser and navigate to [bold yellow underline]http://localhost:{port}/\n", - title="Easy Diffusion is ready", style="bold yellow on blue")) + Console().print( + Panel( + "\n" + + "[white]Easy Diffusion is ready to serve requests.\n\n" + + "A new browser tab should have been opened by now.\n" + + f"If not, please open your web browser and navigate to [bold yellow underline]http://localhost:{port}/\n", + title="Easy Diffusion is ready", + style="bold yellow on blue", + ) + ) + + +def fail_and_die(fail_type: str, data: str): + suggestions = [ + "Run this installer again.", + "If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB", + "If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues", + ] + + if fail_type == "model_download": + fail_label = f"Error downloading the {data} model" + suggestions.insert( + 1, + "If that doesn't fix it, please try to download the file manually. The address to download from, and the destination to save to are printed above this message.", + ) + else: + fail_label = "Error while installing Easy Diffusion" + + msg = [f"{fail_label}. Sorry about that, please try to:"] + for i, suggestion in enumerate(suggestions): + msg.append(f"{i+1}. {suggestion}") + msg.append("Thanks!") + + print("\n".join(msg)) + exit(1) def get_image_modifiers(): diff --git a/ui/easydiffusion/model_manager.py b/ui/easydiffusion/model_manager.py index 458dae7f..c4447033 100644 --- a/ui/easydiffusion/model_manager.py +++ b/ui/easydiffusion/model_manager.py @@ -1,10 +1,14 @@ import os +import shutil +from glob import glob +import traceback from easydiffusion import app from easydiffusion.types import TaskData from easydiffusion.utils import log from sdkit import Context -from sdkit.models import load_model, scan_model, unload_model +from sdkit.models import load_model, scan_model, unload_model, download_model, get_model_info_from_db +from sdkit.utils import hash_file_quick KNOWN_MODEL_TYPES = [ "stable-diffusion", @@ -25,12 +29,19 @@ MODEL_EXTENSIONS = { "codeformer": [".pth"], } DEFAULT_MODELS = { - "stable-diffusion": [ # needed to support the legacy installations - "custom-model", # only one custom model file was supported initially, creatively named 'custom-model' - "sd-v1-4", # Default fallback. + "stable-diffusion": [ + {"file_name": "sd-v1-4.ckpt", "model_id": "1.4"}, + ], + "gfpgan": [ + {"file_name": "GFPGANv1.4.pth", "model_id": "1.4"}, + ], + "realesrgan": [ + {"file_name": "RealESRGAN_x4plus.pth", "model_id": "x4plus"}, + {"file_name": "RealESRGAN_x4plus_anime_6B.pth", "model_id": "x4plus_anime_6"}, + ], + "vae": [ + {"file_name": "vae-ft-mse-840000-ema-pruned.ckpt", "model_id": "vae-ft-mse-840000-ema-pruned"}, ], - "gfpgan": ["GFPGANv1.3"], - "realesrgan": ["RealESRGAN_x4plus"], } MODELS_TO_LOAD_ON_START = ["stable-diffusion", "vae", "hypernetwork", "lora"] @@ -39,6 +50,8 @@ known_models = {} def init(): make_model_folders() + migrate_legacy_model_location() # if necessary + download_default_models_if_necessary() getModels() # run this once, to cache the picklescan results @@ -77,7 +90,7 @@ def resolve_model_to_use(model_name: str = None, model_type: str = None): default_models = DEFAULT_MODELS.get(model_type, []) config = app.getConfig() - model_dirs = [os.path.join(app.MODELS_DIR, model_type), app.SD_DIR] + model_dir = os.path.join(app.MODELS_DIR, model_type) if not model_name: # When None try user configured model. # config = getConfig() if "model" in config and model_type in config["model"]: @@ -85,31 +98,25 @@ def resolve_model_to_use(model_name: str = None, model_type: str = None): if model_name: # Check models directory - models_dir_path = os.path.join(app.MODELS_DIR, model_type, model_name) + model_path = os.path.join(model_dir, model_name) + if os.path.exists(model_path): + return model_path for model_extension in model_extensions: - if os.path.exists(models_dir_path + model_extension): - return models_dir_path + model_extension + if os.path.exists(model_path + model_extension): + return model_path + model_extension if os.path.exists(model_name + model_extension): return os.path.abspath(model_name + model_extension) - # Default locations - if model_name in default_models: - default_model_path = os.path.join(app.SD_DIR, model_name) - for model_extension in model_extensions: - if os.path.exists(default_model_path + model_extension): - return default_model_path + model_extension - # Can't find requested model, check the default paths. - for default_model in default_models: - for model_dir in model_dirs: - default_model_path = os.path.join(model_dir, default_model) - for model_extension in model_extensions: - if os.path.exists(default_model_path + model_extension): - if model_name is not None: - log.warn( - f"Could not find the configured custom model {model_name}{model_extension}. Using the default one: {default_model_path}{model_extension}" - ) - return default_model_path + model_extension + if model_type == "stable-diffusion": + for default_model in default_models: + default_model_path = os.path.join(model_dir, default_model["file_name"]) + if os.path.exists(default_model_path): + if model_name is not None: + log.warn( + f"Could not find the configured custom model {model_name}. Using the default one: {default_model_path}" + ) + return default_model_path return None @@ -136,7 +143,9 @@ def reload_models_if_necessary(context: Context, task_data: TaskData): } if task_data.codeformer_upscale_faces and "realesrgan" not in models_to_reload.keys(): - models_to_reload["realesrgan"] = resolve_model_to_use(DEFAULT_MODELS["realesrgan"][0], "realesrgan") + models_to_reload["realesrgan"] = resolve_model_to_use( + DEFAULT_MODELS["realesrgan"][0]["file_name"], "realesrgan" + ) if set_vram_optimizations(context) or set_clip_skip(context, task_data): # reload SD models_to_reload["stable-diffusion"] = model_paths_in_req["stable-diffusion"] @@ -168,6 +177,7 @@ def resolve_model_paths(task_data: TaskData): model_type = "gfpgan" elif "codeformer" in task_data.use_face_correction.lower(): model_type = "codeformer" + download_if_necessary("codeformer", "codeformer.pth", "codeformer-0.1.0") task_data.use_face_correction = resolve_model_to_use(task_data.use_face_correction, model_type) if task_data.use_upscale and "realesrgan" in task_data.use_upscale.lower(): @@ -179,7 +189,31 @@ def fail_if_models_did_not_load(context: Context): if model_type in context.model_load_errors: e = context.model_load_errors[model_type] raise Exception(f"Could not load the {model_type} model! Reason: " + e) - # concat 'e', don't use in format string (injection attack) + + +def download_default_models_if_necessary(): + for model_type, models in DEFAULT_MODELS.items(): + for model in models: + try: + download_if_necessary(model_type, model["file_name"], model["model_id"]) + except: + traceback.print_exc() + app.fail_and_die(fail_type="model_download", data=model_type) + + print(model_type, "model(s) found.") + + +def download_if_necessary(model_type: str, file_name: str, model_id: str): + model_path = os.path.join(app.MODELS_DIR, model_type, file_name) + expected_hash = get_model_info_from_db(model_type=model_type, model_id=model_id)["quick_hash"] + + other_models_exist = any_model_exists(model_type) + known_model_exists = os.path.exists(model_path) + known_model_is_corrupt = known_model_exists and hash_file_quick(model_path) != expected_hash + + if known_model_is_corrupt or (not other_models_exist and not known_model_exists): + print("> download", model_type, model_id) + download_model(model_type, model_id, download_base_dir=app.MODELS_DIR) def set_vram_optimizations(context: Context): @@ -193,6 +227,26 @@ def set_vram_optimizations(context: Context): return False +def migrate_legacy_model_location(): + 'Move the models inside the legacy "stable-diffusion" folder, to their respective folders' + + for model_type, models in DEFAULT_MODELS.items(): + for model in models: + file_name = model["file_name"] + legacy_path = os.path.join(app.SD_DIR, file_name) + if os.path.exists(legacy_path): + shutil.move(legacy_path, os.path.join(app.MODELS_DIR, model_type, file_name)) + + +def any_model_exists(model_type: str) -> bool: + extensions = MODEL_EXTENSIONS.get(model_type, []) + for ext in extensions: + if any(glob(f"{app.MODELS_DIR}/{model_type}/**/*{ext}", recursive=True)): + return True + + return False + + def set_clip_skip(context: Context, task_data: TaskData): clip_skip = task_data.clip_skip @@ -255,7 +309,7 @@ def getModels(): "vae": [], "hypernetwork": [], "lora": [], - "codeformer": [], + "codeformer": ["codeformer"], }, } @@ -312,14 +366,8 @@ def getModels(): listModels(model_type="hypernetwork") listModels(model_type="gfpgan") listModels(model_type="lora") - listModels(model_type="codeformer") if models_scanned > 0: log.info(f"[green]Scanned {models_scanned} models. Nothing infected[/]") - # legacy - custom_weight_path = os.path.join(app.SD_DIR, "custom-model.ckpt") - if os.path.exists(custom_weight_path): - models["options"]["stable-diffusion"].append("custom-model") - return models From 51d52d3a0795d59986420db5e40e512d9508dd1c Mon Sep 17 00:00:00 2001 From: JeLuF Date: Fri, 2 Jun 2023 23:41:53 +0200 Subject: [PATCH 235/291] Tiled image download plugin --- ui/plugins/ui/tiled-image-download.plugin.js | 325 +++++++++++++++++++ 1 file changed, 325 insertions(+) create mode 100644 ui/plugins/ui/tiled-image-download.plugin.js diff --git a/ui/plugins/ui/tiled-image-download.plugin.js b/ui/plugins/ui/tiled-image-download.plugin.js new file mode 100644 index 00000000..aaceab46 --- /dev/null +++ b/ui/plugins/ui/tiled-image-download.plugin.js @@ -0,0 +1,325 @@ +;(function(){ + "use strict"; + const PAPERSIZE = [ + {id: "a3p", width: 297, height: 420, unit: "mm"}, + {id: "a3l", width: 420, height: 297, unit: "mm"}, + {id: "a4p", width: 210, height: 297, unit: "mm"}, + {id: "a4l", width: 297, height: 210, unit: "mm"}, + {id: "ll", width: 279, height: 216, unit: "mm"}, + {id: "lp", width: 216, height: 279, unit: "mm"}, + {id: "hd", width: 1920, height: 1080, unit: "pixels"}, + {id: "4k", width: 3840, height: 2160, unit: "pixels"}, + ] + + // ---- Register plugin + PLUGINS['IMAGE_INFO_BUTTONS'].push({ + html: ' Download tiled image', + on_click: onDownloadTiledImage, + filter: (req, img) => req.tiling != "none", + }) + + var thisImage + + function onDownloadTiledImage(req, img) { + document.getElementById("download-tiled-image-dialog").showModal() + thisImage = new Image() + thisImage.src = img.src + thisImage.dataset["prompt"] = img.dataset["prompt"] + } + + // ---- Add HTML + document.getElementById('container').lastElementChild.insertAdjacentHTML("afterend", + ` +

      Download tiled image

      +
      +
      +
      + + Number of tiles + + + Image dimensions + +
      +
      +
      +
      + + +
      +
      +
      +
      +
      + + + +
      +
      + +
      +
      + Some standard sizes:
      +
      +
      +
      + +
      +
      +
      +
      +
      +
      +
      + + Tile placement + +
      +
      +
      +
      + +
      + +
      + +
      +
      +
      +
      +
      +
      + +
      +
      + +
      +
      + +
      +
      +
      `) + + let downloadTiledImageDialog = document.getElementById("download-tiled-image-dialog") + let dtim1_width = document.getElementById("dtim1-width") + let dtim1_height = document.getElementById("dtim1-height") + let dtim2_width = document.getElementById("dtim2-width") + let dtim2_height = document.getElementById("dtim2-height") + let dtim2_unit = document.getElementById("dtim2-unit") + let dtim2_dpi = document.getElementById("dtim2-dpi") + let tabTiledTilesOptions = document.getElementById("tab-image-tiles") + let tabTiledSizeOptions = document.getElementById("tab-image-size") + + linkTabContents(tabTiledTilesOptions) + linkTabContents(tabTiledSizeOptions) + + prettifyInputs(downloadTiledImageDialog) + + // ---- Predefined image dimensions + PAPERSIZE.forEach( function(p) { + document.getElementById("dtim2-" + p.id).addEventListener("click", (e) => { + dtim2_unit.value = p.unit + dtim2_width.value = p.width + dtim2_height.value = p.height + }) + }) + + // ---- Close popup + document.getElementById("dti-cancel").addEventListener("click", (e) => downloadTiledImageDialog.close()) + downloadTiledImageDialog.addEventListener('click', function (event) { + var rect = downloadTiledImageDialog.getBoundingClientRect(); + var isInDialog=(rect.top <= event.clientY && event.clientY <= rect.top + rect.height + && rect.left <= event.clientX && event.clientX <= rect.left + rect.width); + if (!isInDialog) { + downloadTiledImageDialog.close(); + } + }); + + // ---- Stylesheet + const styleSheet = document.createElement("style") + styleSheet.textContent = ` + dialog { + background: var(--background-color2); + color: var(--text-color); + border-radius: 7px; + border: 1px solid var(--background-color3); + } + + dialog::backdrop { + background: rgba(0, 0, 0, 0.5); + } + + + button[disabled] { + opacity: 0.5; + } + + .method-2-dpi { + margin-top: 1em; + margin-bottom: 1em; + } + + .method-2-paper button { + width: 10em; + padding: 4px; + margin: 4px; + } + + .download-tiled-image .tab-content { + background: var(--background-color1); + border-radius: 3pt; + } + + .dtim-container { display: grid; + grid-template-columns: auto auto; + grid-template-rows: auto auto; + gap: 1em 0px; + grid-auto-flow: row; + grid-template-areas: + "dtim-tab dtim-tab dtim-plc" + "dtim-ok dtim-newtab dtim-cancel"; + } + + .download-tiled-image-top { + justify-self: center; + grid-area: dtim-tab; + } + + .download-tiled-image-placement { + justify-self: center; + grid-area: dtim-plc; + margin-left: 1em; + } + + .dtim-ok { + justify-self: center; + align-self: start; + grid-area: dtim-ok; + } + + .dtim-newtab { + justify-self: center; + align-self: start; + grid-area: dtim-newtab; + } + + .dtim-cancel { + justify-self: center; + align-self: start; + grid-area: dtim-cancel; + } + + #tab-content-image-placement img { + margin: 4px; + opacity: 0.3; + border: solid 2px var(--background-color1); + } + + #tab-content-image-placement img:hover { + margin: 4px; + opacity: 1; + border: solid 2px var(--accent-color); + filter: brightness(2); + } + + #tab-content-image-placement img.active { + margin: 4px; + opacity: 1; + border: solid 2px var(--background-color1); + } + + ` + document.head.appendChild(styleSheet) + + // ---- Placement widget + + function updatePlacementWidget(event) { + document.querySelector("#tab-content-image-placement img.active").classList.remove("active") + event.target.classList.add("active") + } + + document.querySelectorAll("#tab-content-image-placement img").forEach( + (i) => i.addEventListener("click", updatePlacementWidget) + ) + + function getPlacement() { + return document.querySelector("#tab-content-image-placement img.active").id.substr(5) + } + + // ---- Make the image + function downloadTiledImage(image, width, height, offsetX=0, offsetY=0, new_tab=false) { + + const canvas = document.createElement('canvas') + canvas.width = width + canvas.height = height + const context = canvas.getContext('2d') + + const w = image.width + const h = image.height + + for (var x = offsetX; x < width; x += w) { + for (var y = offsetY; y < height; y += h) { + context.drawImage(image, x, y, w, h) + } + } + if (new_tab) { + var newTab = window.open("") + newTab.document.write(`${width}×${height}, "${image.dataset["prompt"]}"`) + } else { + const link = document.createElement('a') + link.href = canvas.toDataURL() + link.download = image.dataset["prompt"].replace(/[^a-zA-Z0-9]+/g, "-").substr(0,22)+crypto.randomUUID()+".png" + link.click() + } + } + + function onDownloadTiledImageClick(e, newtab=false) { + var width, height, offsetX, offsetY + + if (isTabActive(tabTiledTilesOptions)) { + width = thisImage.width * dtim1_width.value + height = thisImage.height * dtim1_height.value + } else { + if ( dtim2_unit.value == "pixels" ) { + width = dtim2_width.value + height= dtim2_height.value + } else if ( dtim2_unit.value == "mm" ) { + width = Math.floor( dtim2_width.value * dtim2_dpi.value / 25.4 ) + height = Math.floor( dtim2_height.value * dtim2_dpi.value / 25.4 ) + } else { // inch + width = Math.floor( dtim2_width.value * dtim2_dpi.value ) + height = Math.floor( dtim2_height.value * dtim2_dpi.value ) + } + } + + var placement = getPlacement() + if (placement == "1tl") { + offsetX = 0 + offsetY = 0 + } else if (placement == "1tr") { + offsetX = width - thisImage.width * Math.ceil( width / thisImage.width ) + offsetY = 0 + } else if (placement == "1bl") { + offsetX = 0 + offsetY = height - thisImage.height * Math.ceil( height / thisImage.height ) + } else if (placement == "1br") { + offsetX = width - thisImage.width * Math.ceil( width / thisImage.width ) + offsetY = height - thisImage.height * Math.ceil( height / thisImage.height ) + } else if (placement == "4center") { + offsetX = width/2 - thisImage.width * Math.ceil( width/2 / thisImage.width ) + offsetY = height/2 - thisImage.height * Math.ceil( height/2 / thisImage.height ) + } else if (placement == "1center") { + offsetX = width/2 - thisImage.width/2 - thisImage.width * Math.ceil( (width/2 - thisImage.width/2) / thisImage.width ) + offsetY = height/2 - thisImage.height/2 - thisImage.height * Math.ceil( (height/2 - thisImage.height/2) / thisImage.height ) + } + downloadTiledImage(thisImage, width, height, offsetX, offsetY, newtab) + } + + document.getElementById("dti-ok").addEventListener("click", onDownloadTiledImageClick) + document.getElementById("dti-newtab").addEventListener("click", (e) => onDownloadTiledImageClick(e,true)) + +})() From 6dcf7539bb9b92bc70c0d3af85bd4fc7282e30bb Mon Sep 17 00:00:00 2001 From: JeLuF Date: Sat, 3 Jun 2023 00:04:13 +0200 Subject: [PATCH 236/291] close window --- ui/plugins/ui/tiled-image-download.plugin.js | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/plugins/ui/tiled-image-download.plugin.js b/ui/plugins/ui/tiled-image-download.plugin.js index aaceab46..0fe00c2e 100644 --- a/ui/plugins/ui/tiled-image-download.plugin.js +++ b/ui/plugins/ui/tiled-image-download.plugin.js @@ -317,6 +317,7 @@ offsetY = height/2 - thisImage.height/2 - thisImage.height * Math.ceil( (height/2 - thisImage.height/2) / thisImage.height ) } downloadTiledImage(thisImage, width, height, offsetX, offsetY, newtab) + downloadTiledImageDialog.close() } document.getElementById("dti-ok").addEventListener("click", onDownloadTiledImageClick) From 1d5309decbfb2a2f2cac491eae53ccc32c179463 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Sat, 3 Jun 2023 10:02:34 +0530 Subject: [PATCH 237/291] changelog --- CHANGES.md | 1 + ui/index.html | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 2e45c279..88ded3b5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -22,6 +22,7 @@ Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. ### Detailed changelog +* 2.5.40 - 3 Jun 2023 - Added CodeFormer as another option for fixing faces and eyes. CodeFormer tends to perform better than GFPGAN for many images. Thanks @patriceac for the implementation, and for contacting the CodeFormer team (who were supportive of it being integrated into Easy Diffusion). * 2.5.39 - 25 May 2023 - (beta-only) Seamless Tiling - make seamlessly tiled images, e.g. rock and grass textures. Thanks @JeLuf. * 2.5.38 - 24 May 2023 - Better reporting of errors, and show an explanation if the user cannot disable the "Use CPU" setting. * 2.5.38 - 23 May 2023 - Add Latent Upscaler as another option for upscaling images. Thanks @JeLuf for the implementation of the Latent Upscaler model. diff --git a/ui/index.html b/ui/index.html index 4167a873..eaa007f6 100644 --- a/ui/index.html +++ b/ui/index.html @@ -30,7 +30,7 @@

      Easy Diffusion - v2.5.39 + v2.5.40

    From 6ca7247c027327552e05f8e27423c3e72495caa8 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Sat, 3 Jun 2023 10:11:03 +0530 Subject: [PATCH 238/291] Enable face upscaling by default --- ui/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/index.html b/ui/index.html index eaa007f6..88917b68 100644 --- a/ui/index.html +++ b/ui/index.html @@ -266,7 +266,7 @@
  • - +
  • From 401fc30617302c347ad5dba1413a212d55bdd0e4 Mon Sep 17 00:00:00 2001 From: Olivia Godone-Maresca Date: Sat, 3 Jun 2023 14:54:17 -0400 Subject: [PATCH 239/291] Allow LoRA strengths between -2 and 2 --- ui/index.html | 2 +- ui/media/js/main.js | 8 ++++---- ui/media/js/utils.js | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ui/index.html b/ui/index.html index 88917b68..6cc6978c 100644 --- a/ui/index.html +++ b/ui/index.html @@ -227,7 +227,7 @@ -
    +
    diff --git a/ui/media/js/main.js b/ui/media/js/main.js index ef27cf1b..e4043fa0 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -1725,10 +1725,10 @@ function updateLoraAlpha() { } function updateLoraAlphaSlider() { - if (loraAlphaField.value < 0) { - loraAlphaField.value = 0 - } else if (loraAlphaField.value > 1) { - loraAlphaField.value = 1 + if (loraAlphaField.value < -2) { + loraAlphaField.value = -2 + } else if (loraAlphaField.value > 2) { + loraAlphaField.value = 2 } loraAlphaSlider.value = loraAlphaField.value * 100 diff --git a/ui/media/js/utils.js b/ui/media/js/utils.js index 6ddb0ae6..871ba714 100644 --- a/ui/media/js/utils.js +++ b/ui/media/js/utils.js @@ -402,12 +402,12 @@ function debounce(func, wait, immediate) { function preventNonNumericalInput(e) { e = e || window.event - let charCode = typeof e.which == "undefined" ? e.keyCode : e.which - let charStr = String.fromCharCode(charCode) - let re = e.target.getAttribute("pattern") || "^[0-9]+$" - re = new RegExp(re) + const charCode = typeof e.which == "undefined" ? e.keyCode : e.which + const charStr = String.fromCharCode(charCode) + const newInputValue = `${e.target.value}${charStr}` + const re = new RegExp(e.target.getAttribute("pattern") || "^[0-9]+$") - if (!charStr.match(re)) { + if (!re.test(charStr) && !re.test(newInputValue)) { e.preventDefault() } } From 8a2c09c6de4a6a421472146f6609463b5ef6e219 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Mon, 5 Jun 2023 09:00:50 +0530 Subject: [PATCH 240/291] Fix for rabbit hole plugin --- ui/media/js/searchable-models.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ui/media/js/searchable-models.js b/ui/media/js/searchable-models.js index f0d06b80..855cfc61 100644 --- a/ui/media/js/searchable-models.js +++ b/ui/media/js/searchable-models.js @@ -94,7 +94,8 @@ class ModelDropdown { let modelKeys = Array.isArray(this.modelKey) ? this.modelKey : [this.modelKey] for (let i = 0; i < modelKeys.length; i++) { let key = modelKeys[i] - this.inputModels.push(...modelsOptions[key]) + let k = Array.isArray(modelsOptions[key]) ? modelsOptions[key] : [modelsOptions[key]] + this.inputModels.push(...k) } this.populateModels() } @@ -107,7 +108,8 @@ class ModelDropdown { let modelKeys = Array.isArray(this.modelKey) ? this.modelKey : [this.modelKey] for (let i = 0; i < modelKeys.length; i++) { let key = modelKeys[i] - this.inputModels.push(...modelsOptions[key]) + let k = Array.isArray(modelsOptions[key]) ? modelsOptions[key] : [modelsOptions[key]] + this.inputModels.push(...k) } this.populateModels() }, this) From a10aa92634f5743319a7ad1e2faf3f68c875fdf8 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Mon, 5 Jun 2023 15:08:57 +0530 Subject: [PATCH 241/291] Fix a bug where the realesrgan model would get unloaded after the first request in a batch while using Codeformer with upscaling of faces --- scripts/check_modules.py | 2 +- ui/easydiffusion/model_manager.py | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 416c3cc2..cbd6d0c1 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.101", + "sdkit": "1.0.102", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", diff --git a/ui/easydiffusion/model_manager.py b/ui/easydiffusion/model_manager.py index c4447033..29cf529b 100644 --- a/ui/easydiffusion/model_manager.py +++ b/ui/easydiffusion/model_manager.py @@ -142,10 +142,12 @@ def reload_models_if_necessary(context: Context, task_data: TaskData): if context.model_paths.get(model_type) != path } - if task_data.codeformer_upscale_faces and "realesrgan" not in models_to_reload.keys(): - models_to_reload["realesrgan"] = resolve_model_to_use( - DEFAULT_MODELS["realesrgan"][0]["file_name"], "realesrgan" - ) + if task_data.codeformer_upscale_faces: + if "realesrgan" not in models_to_reload and "realesrgan" not in context.models: + default_realesrgan = DEFAULT_MODELS["realesrgan"][0]["file_name"] + models_to_reload["realesrgan"] = resolve_model_to_use(default_realesrgan, "realesrgan") + elif "realesrgan" in models_to_reload and models_to_reload["realesrgan"] is None: + del models_to_reload["realesrgan"] # don't unload realesrgan if set_vram_optimizations(context) or set_clip_skip(context, task_data): # reload SD models_to_reload["stable-diffusion"] = model_paths_in_req["stable-diffusion"] From c72b287c823c573e6361f040f733ec3f6c055bf3 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Mon, 5 Jun 2023 15:22:37 +0530 Subject: [PATCH 242/291] Show a more helpful error message in the logs when the system runs out of RAM --- ui/easydiffusion/model_manager.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ui/easydiffusion/model_manager.py b/ui/easydiffusion/model_manager.py index 29cf529b..9953fd74 100644 --- a/ui/easydiffusion/model_manager.py +++ b/ui/easydiffusion/model_manager.py @@ -72,7 +72,12 @@ def load_default_models(context: Context): del context.model_load_errors[model_type] except Exception as e: log.error(f"[red]Error while loading {model_type} model: {context.model_paths[model_type]}[/red]") - log.exception(e) + if "DefaultCPUAllocator: not enough memory" in str(e): + log.error( + f"[red]Your PC is low on system RAM. Please add some virtual memory (or swap space) by following the instructions at this link: https://www.ibm.com/docs/en/opw/8.2.0?topic=tuning-optional-increasing-paging-file-size-windows-computers[/red]" + ) + else: + log.exception(e) del context.model_paths[model_type] context.model_load_errors[model_type] = str(e) # storing the entire Exception can lead to memory leaks From b14653cb9e47ad5a6a1c924d7edd597bbd8cb427 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Mon, 5 Jun 2023 16:11:48 +0530 Subject: [PATCH 243/291] sdkit 1.0.103 - Pin the versions of diffusers models used; Use cpu offloading for balanced and low while upscaling using latent upscaler --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index cbd6d0c1..f8ca8ce3 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.102", + "sdkit": "1.0.103", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", From 046c00d844be9908d10c38d40dfc6a7e45984757 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Mon, 5 Jun 2023 16:13:41 +0530 Subject: [PATCH 244/291] changelog --- CHANGES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 88ded3b5..86b4dce1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -22,6 +22,8 @@ Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. ### Detailed changelog +* 2.5.40 - 4 Jun 2023 - Reduce the VRAM usage of Latent Upscaling when using "balanced" VRAM usage mode. +* 2.5.40 - 4 Jun 2023 - Fix the "realesrgan" key error when using CodeFormer with more than 1 image in a batch. * 2.5.40 - 3 Jun 2023 - Added CodeFormer as another option for fixing faces and eyes. CodeFormer tends to perform better than GFPGAN for many images. Thanks @patriceac for the implementation, and for contacting the CodeFormer team (who were supportive of it being integrated into Easy Diffusion). * 2.5.39 - 25 May 2023 - (beta-only) Seamless Tiling - make seamlessly tiled images, e.g. rock and grass textures. Thanks @JeLuf. * 2.5.38 - 24 May 2023 - Better reporting of errors, and show an explanation if the user cannot disable the "Use CPU" setting. From 047390873c2280f1f22725539259049f0899c2cc Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Mon, 5 Jun 2023 16:53:18 +0530 Subject: [PATCH 245/291] changelog; show labels next to the lora strength slider --- CHANGES.md | 3 +++ ui/index.html | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 86b4dce1..be96e000 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -22,6 +22,9 @@ Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. ### Detailed changelog +* 2.5.41 - 4 Jun 2023 - Allow sharing an Easy Diffusion instance via https://try.cloudflare.com/ . You can find this option at the bottom of the Settings tab. Thanks @JeLuf. +* 2.5.41 - 4 Jun 2023 - Show an option to download for tiled images. Shows a button on the generated image. Creates larger images by tiling them with the image generated by Easy Diffusion. Thanks @JeLuf. +* 2.5.41 - 4 Jun 2023 - Allow LoRA strengths between -2 and 2. Thanks @ogmaresca. * 2.5.40 - 4 Jun 2023 - Reduce the VRAM usage of Latent Upscaling when using "balanced" VRAM usage mode. * 2.5.40 - 4 Jun 2023 - Fix the "realesrgan" key error when using CodeFormer with more than 1 image in a batch. * 2.5.40 - 3 Jun 2023 - Added CodeFormer as another option for fixing faces and eyes. CodeFormer tends to perform better than GFPGAN for many images. Thanks @patriceac for the implementation, and for contacting the CodeFormer team (who were supportive of it being integrated into Easy Diffusion). diff --git a/ui/index.html b/ui/index.html index 8558cc2c..30cd1453 100644 --- a/ui/index.html +++ b/ui/index.html @@ -30,7 +30,7 @@

    Easy Diffusion - v2.5.40 + v2.5.41

  • @@ -227,7 +227,10 @@ -
    + + -2 2   +
    + From 79d6ab9915571ecb394cb3f1d60576e079a54af5 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 6 Jun 2023 15:22:27 +0530 Subject: [PATCH 246/291] Update CHANGES.md --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index be96e000..36e0dde4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -24,7 +24,7 @@ Our focus continues to remain on an easy installation experience, and an easy us ### Detailed changelog * 2.5.41 - 4 Jun 2023 - Allow sharing an Easy Diffusion instance via https://try.cloudflare.com/ . You can find this option at the bottom of the Settings tab. Thanks @JeLuf. * 2.5.41 - 4 Jun 2023 - Show an option to download for tiled images. Shows a button on the generated image. Creates larger images by tiling them with the image generated by Easy Diffusion. Thanks @JeLuf. -* 2.5.41 - 4 Jun 2023 - Allow LoRA strengths between -2 and 2. Thanks @ogmaresca. +* 2.5.41 - 4 Jun 2023 - (beta-only) Allow LoRA strengths between -2 and 2. Thanks @ogmaresca. * 2.5.40 - 4 Jun 2023 - Reduce the VRAM usage of Latent Upscaling when using "balanced" VRAM usage mode. * 2.5.40 - 4 Jun 2023 - Fix the "realesrgan" key error when using CodeFormer with more than 1 image in a batch. * 2.5.40 - 3 Jun 2023 - Added CodeFormer as another option for fixing faces and eyes. CodeFormer tends to perform better than GFPGAN for many images. Thanks @patriceac for the implementation, and for contacting the CodeFormer team (who were supportive of it being integrated into Easy Diffusion). From d0184a15989f8df8cfb752002e4231839bb81a35 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 6 Jun 2023 16:16:21 +0530 Subject: [PATCH 247/291] Allow changing the strength of the codeformer model (1 - fidelity); Improve the styling of the sub-settings --- CHANGES.md | 11 +++++---- ui/easydiffusion/renderer.py | 1 + ui/easydiffusion/types.py | 1 + ui/index.html | 15 +++++------ ui/media/css/main.css | 8 ++++++ ui/media/js/main.js | 48 ++++++++++++++++++++++++++++-------- 6 files changed, 62 insertions(+), 22 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index be96e000..6ae532db 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -22,11 +22,12 @@ Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. ### Detailed changelog -* 2.5.41 - 4 Jun 2023 - Allow sharing an Easy Diffusion instance via https://try.cloudflare.com/ . You can find this option at the bottom of the Settings tab. Thanks @JeLuf. -* 2.5.41 - 4 Jun 2023 - Show an option to download for tiled images. Shows a button on the generated image. Creates larger images by tiling them with the image generated by Easy Diffusion. Thanks @JeLuf. -* 2.5.41 - 4 Jun 2023 - Allow LoRA strengths between -2 and 2. Thanks @ogmaresca. -* 2.5.40 - 4 Jun 2023 - Reduce the VRAM usage of Latent Upscaling when using "balanced" VRAM usage mode. -* 2.5.40 - 4 Jun 2023 - Fix the "realesrgan" key error when using CodeFormer with more than 1 image in a batch. +* 2.5.41 - 6 Jun 2023 - Allow changing the strength of CodeFormer, and slightly improved styling of the CodeFormer options. +* 2.5.41 - 5 Jun 2023 - Allow sharing an Easy Diffusion instance via https://try.cloudflare.com/ . You can find this option at the bottom of the Settings tab. Thanks @JeLuf. +* 2.5.41 - 5 Jun 2023 - Show an option to download for tiled images. Shows a button on the generated image. Creates larger images by tiling them with the image generated by Easy Diffusion. Thanks @JeLuf. +* 2.5.41 - 5 Jun 2023 - Allow LoRA strengths between -2 and 2. Thanks @ogmaresca. +* 2.5.40 - 5 Jun 2023 - Reduce the VRAM usage of Latent Upscaling when using "balanced" VRAM usage mode. +* 2.5.40 - 5 Jun 2023 - Fix the "realesrgan" key error when using CodeFormer with more than 1 image in a batch. * 2.5.40 - 3 Jun 2023 - Added CodeFormer as another option for fixing faces and eyes. CodeFormer tends to perform better than GFPGAN for many images. Thanks @patriceac for the implementation, and for contacting the CodeFormer team (who were supportive of it being integrated into Easy Diffusion). * 2.5.39 - 25 May 2023 - (beta-only) Seamless Tiling - make seamlessly tiled images, e.g. rock and grass textures. Thanks @JeLuf. * 2.5.38 - 24 May 2023 - Better reporting of errors, and show an explanation if the user cannot disable the "Use CPU" setting. diff --git a/ui/easydiffusion/renderer.py b/ui/easydiffusion/renderer.py index 4be6e6bf..ef038715 100644 --- a/ui/easydiffusion/renderer.py +++ b/ui/easydiffusion/renderer.py @@ -165,6 +165,7 @@ def filter_images(req: GenerateImageRequest, task_data: TaskData, images: list, filters_to_apply.append("codeformer") filter_params["upscale_faces"] = task_data.codeformer_upscale_faces + filter_params["codeformer_fidelity"] = task_data.codeformer_fidelity elif task_data.use_face_correction and "gfpgan" in task_data.use_face_correction.lower(): filters_to_apply.append("gfpgan") if task_data.use_upscale: diff --git a/ui/easydiffusion/types.py b/ui/easydiffusion/types.py index 4559a23a..abf8db29 100644 --- a/ui/easydiffusion/types.py +++ b/ui/easydiffusion/types.py @@ -52,6 +52,7 @@ class TaskData(BaseModel): stream_image_progress_interval: int = 5 clip_skip: bool = False codeformer_upscale_faces: bool = False + codeformer_fidelity: float = 0.5 class MergeRequest(BaseModel): diff --git a/ui/index.html b/ui/index.html index 30cd1453..c8f11b4d 100644 --- a/ui/index.html +++ b/ui/index.html @@ -266,11 +266,12 @@
    • Render Settings
    • -
    • +
    • -
      - -
      + + + +
    • @@ -284,9 +285,9 @@ -
      - -
      + + +
    diff --git a/ui/media/css/main.css b/ui/media/css/main.css index bd3cdbe6..9df38f9a 100644 --- a/ui/media/css/main.css +++ b/ui/media/css/main.css @@ -1324,6 +1324,14 @@ body.wait-pause { margin-left: 0.5em; } +.expandedSettingRow { + background: var(--background-color1); + width: 95%; + border-radius: 4pt; + margin-top: 5pt; + margin-bottom: 3pt; +} + /* TOAST NOTIFICATIONS */ .toast-notification { position: fixed; diff --git a/ui/media/js/main.js b/ui/media/js/main.js index 1684046f..5909fdb1 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -94,6 +94,8 @@ let upscaleAmountField = document.querySelector("#upscale_amount") let latentUpscalerSettings = document.querySelector("#latent_upscaler_settings") let latentUpscalerStepsSlider = document.querySelector("#latent_upscaler_steps_slider") let latentUpscalerStepsField = document.querySelector("#latent_upscaler_steps") +let codeformerFidelitySlider = document.querySelector("#codeformer_fidelity_slider") +let codeformerFidelityField = document.querySelector("#codeformer_fidelity") let stableDiffusionModelField = new ModelDropdown(document.querySelector("#stable_diffusion_model"), "stable-diffusion") let clipSkipField = document.querySelector("#clip_skip") let tilingField = document.querySelector("#tiling") @@ -1266,6 +1268,7 @@ function getCurrentUserRequest() { if (gfpganModelField.value.includes("codeformer")) { newTask.reqBody.codeformer_upscale_faces = document.querySelector("#codeformer_upscale_faces").checked + newTask.reqBody.codeformer_fidelity = 1 - parseFloat(codeformerFidelityField.value) } } if (useUpscalingField.checked) { @@ -1588,8 +1591,10 @@ function onFixFaceModelChange() { let codeformerSettings = document.querySelector("#codeformer_settings") if (gfpganModelField.value === "codeformer" && !gfpganModelField.disabled) { codeformerSettings.classList.remove("displayNone") + codeformerSettings.classList.add("expandedSettingRow") } else { codeformerSettings.classList.add("displayNone") + codeformerSettings.classList.remove("expandedSettingRow") } } gfpganModelField.addEventListener("change", onFixFaceModelChange) @@ -1610,9 +1615,11 @@ function onUpscaleModelChange() { upscale4x.disabled = true upscaleAmountField.value = "2" latentUpscalerSettings.classList.remove("displayNone") + latentUpscalerSettings.classList.add("expandedSettingRow") } else { upscale4x.disabled = false latentUpscalerSettings.classList.add("displayNone") + latentUpscalerSettings.classList.remove("expandedSettingRow") } } upscaleModelField.addEventListener("change", onUpscaleModelChange) @@ -1627,6 +1634,27 @@ document.onkeydown = function(e) { } } +/********************* CodeFormer Fidelity **************************/ +function updateCodeformerFidelity() { + codeformerFidelityField.value = codeformerFidelitySlider.value / 10 + codeformerFidelityField.dispatchEvent(new Event("change")) +} + +function updateCodeformerFidelitySlider() { + if (codeformerFidelityField.value < 0) { + codeformerFidelityField.value = 0 + } else if (codeformerFidelityField.value > 1) { + codeformerFidelityField.value = 1 + } + + codeformerFidelitySlider.value = codeformerFidelityField.value * 10 + codeformerFidelitySlider.dispatchEvent(new Event("change")) +} + +codeformerFidelitySlider.addEventListener("input", updateCodeformerFidelity) +codeformerFidelityField.addEventListener("input", updateCodeformerFidelitySlider) +updateCodeformerFidelity() + /********************* Latent Upscaler Steps **************************/ function updateLatentUpscalerSteps() { latentUpscalerStepsField.value = latentUpscalerStepsSlider.value @@ -1981,25 +2009,25 @@ resumeBtn.addEventListener("click", function() { function tunnelUpdate(event) { if ("cloudflare" in event) { - document.getElementById('cloudflare-off').classList.add("displayNone") - document.getElementById('cloudflare-on').classList.remove("displayNone") + document.getElementById("cloudflare-off").classList.add("displayNone") + document.getElementById("cloudflare-on").classList.remove("displayNone") cloudflareAddressField.innerHTML = event.cloudflare - document.getElementById('toggle-cloudflare-tunnel').innerHTML = "Stop" + document.getElementById("toggle-cloudflare-tunnel").innerHTML = "Stop" } else { - document.getElementById('cloudflare-on').classList.add("displayNone") - document.getElementById('cloudflare-off').classList.remove("displayNone") - document.getElementById('toggle-cloudflare-tunnel').innerHTML = "Start" + document.getElementById("cloudflare-on").classList.add("displayNone") + document.getElementById("cloudflare-off").classList.remove("displayNone") + document.getElementById("toggle-cloudflare-tunnel").innerHTML = "Start" } } -document.getElementById('toggle-cloudflare-tunnel').addEventListener("click", async function() { +document.getElementById("toggle-cloudflare-tunnel").addEventListener("click", async function() { let command = "stop" - if (document.getElementById('toggle-cloudflare-tunnel').innerHTML == "Start") { - command = "start" + if (document.getElementById("toggle-cloudflare-tunnel").innerHTML == "Start") { + command = "start" } showToast(`Cloudflare tunnel ${command} initiated. Please wait.`) - let res = await fetch("/tunnel/cloudflare/"+command, { + let res = await fetch("/tunnel/cloudflare/" + command, { method: "POST", headers: { "Content-Type": "application/json", From 6ae5cb28cfcb55a9d0a6507100cd9f4205231982 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 6 Jun 2023 16:37:17 +0530 Subject: [PATCH 248/291] Set the default codeformer strength to 0.5 --- ui/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/index.html b/ui/index.html index c8f11b4d..0c4386de 100644 --- a/ui/index.html +++ b/ui/index.html @@ -269,7 +269,7 @@
  • - +
  • From 05c2de94506057101535ffd56a6583a60bf38e72 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 6 Jun 2023 16:56:37 +0530 Subject: [PATCH 249/291] Fail with an error if the desired model (non-Stable Diffusion) wasn't found --- ui/easydiffusion/model_manager.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ui/easydiffusion/model_manager.py b/ui/easydiffusion/model_manager.py index 9953fd74..ef5166df 100644 --- a/ui/easydiffusion/model_manager.py +++ b/ui/easydiffusion/model_manager.py @@ -123,6 +123,9 @@ def resolve_model_to_use(model_name: str = None, model_type: str = None): ) return default_model_path + if model_name: + raise Exception(f"Could not find the desired model {model_name}! Is it present in the {model_dir} folder?") + return None From c09512bf12bc75df4e0f3240e509bb0983c5ebec Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 6 Jun 2023 16:57:25 +0530 Subject: [PATCH 250/291] dead code --- ui/easydiffusion/model_manager.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ui/easydiffusion/model_manager.py b/ui/easydiffusion/model_manager.py index ef5166df..4d58953a 100644 --- a/ui/easydiffusion/model_manager.py +++ b/ui/easydiffusion/model_manager.py @@ -126,8 +126,6 @@ def resolve_model_to_use(model_name: str = None, model_type: str = None): if model_name: raise Exception(f"Could not find the desired model {model_name}! Is it present in the {model_dir} folder?") - return None - def reload_models_if_necessary(context: Context, task_data: TaskData): face_fix_lower = task_data.use_face_correction.lower() if task_data.use_face_correction else "" From 9486c03a89350d3b7e915f1645ddc38c19e6d70e Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 6 Jun 2023 17:10:38 +0530 Subject: [PATCH 251/291] Don't use the default SD model (if the desired model was not found), unless the UI is starting up --- ui/easydiffusion/model_manager.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/easydiffusion/model_manager.py b/ui/easydiffusion/model_manager.py index 4d58953a..de2c10ac 100644 --- a/ui/easydiffusion/model_manager.py +++ b/ui/easydiffusion/model_manager.py @@ -60,7 +60,7 @@ def load_default_models(context: Context): # init default model paths for model_type in MODELS_TO_LOAD_ON_START: - context.model_paths[model_type] = resolve_model_to_use(model_type=model_type) + context.model_paths[model_type] = resolve_model_to_use(model_type=model_type, fail_if_not_found=False) try: load_model( context, @@ -90,7 +90,7 @@ def unload_all(context: Context): del context.model_load_errors[model_type] -def resolve_model_to_use(model_name: str = None, model_type: str = None): +def resolve_model_to_use(model_name: str = None, model_type: str = None, fail_if_not_found: bool = True): model_extensions = MODEL_EXTENSIONS.get(model_type, []) default_models = DEFAULT_MODELS.get(model_type, []) config = app.getConfig() @@ -113,7 +113,7 @@ def resolve_model_to_use(model_name: str = None, model_type: str = None): return os.path.abspath(model_name + model_extension) # Can't find requested model, check the default paths. - if model_type == "stable-diffusion": + if model_type == "stable-diffusion" and not fail_if_not_found: for default_model in default_models: default_model_path = os.path.join(model_dir, default_model["file_name"]) if os.path.exists(default_model_path): @@ -123,7 +123,7 @@ def resolve_model_to_use(model_name: str = None, model_type: str = None): ) return default_model_path - if model_name: + if model_name and fail_if_not_found: raise Exception(f"Could not find the desired model {model_name}! Is it present in the {model_dir} folder?") From 0d8e73b206e8fb1cfe6a59768b5db7b1c3b1f3c2 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 7 Jun 2023 15:10:57 +0530 Subject: [PATCH 252/291] sdkit 1.0.104 - Not all pipelines have vae slicing --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 527410c5..5264ec08 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.103", + "sdkit": "1.0.104", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", From 9a0031c47b6b84d20d763949e6bba7aab0df6c01 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 7 Jun 2023 15:21:16 +0530 Subject: [PATCH 253/291] Don't copy check_models.py, it doesn't exist anymore --- scripts/on_env_start.bat | 1 - scripts/on_env_start.sh | 1 - scripts/on_sd_start.bat | 1 - scripts/on_sd_start.sh | 1 - 4 files changed, 4 deletions(-) diff --git a/scripts/on_env_start.bat b/scripts/on_env_start.bat index 44144cfa..bc92d0e9 100644 --- a/scripts/on_env_start.bat +++ b/scripts/on_env_start.bat @@ -67,7 +67,6 @@ if "%update_branch%"=="" ( @xcopy sd-ui-files\ui ui /s /i /Y /q @copy sd-ui-files\scripts\on_sd_start.bat scripts\ /Y @copy sd-ui-files\scripts\check_modules.py scripts\ /Y -@copy sd-ui-files\scripts\check_models.py scripts\ /Y @copy sd-ui-files\scripts\get_config.py scripts\ /Y @copy "sd-ui-files\scripts\Start Stable Diffusion UI.cmd" . /Y @copy "sd-ui-files\scripts\Developer Console.cmd" . /Y diff --git a/scripts/on_env_start.sh b/scripts/on_env_start.sh index 30465975..366b5dd1 100755 --- a/scripts/on_env_start.sh +++ b/scripts/on_env_start.sh @@ -50,7 +50,6 @@ cp -Rf sd-ui-files/ui . cp sd-ui-files/scripts/on_sd_start.sh scripts/ cp sd-ui-files/scripts/bootstrap.sh scripts/ cp sd-ui-files/scripts/check_modules.py scripts/ -cp sd-ui-files/scripts/check_models.py scripts/ cp sd-ui-files/scripts/get_config.py scripts/ cp sd-ui-files/scripts/start.sh . cp sd-ui-files/scripts/developer_console.sh . diff --git a/scripts/on_sd_start.bat b/scripts/on_sd_start.bat index d2ef7321..f92b9f6f 100644 --- a/scripts/on_sd_start.bat +++ b/scripts/on_sd_start.bat @@ -5,7 +5,6 @@ @copy sd-ui-files\scripts\on_env_start.bat scripts\ /Y @copy sd-ui-files\scripts\check_modules.py scripts\ /Y -@copy sd-ui-files\scripts\check_models.py scripts\ /Y @copy sd-ui-files\scripts\get_config.py scripts\ /Y if exist "%cd%\profile" ( diff --git a/scripts/on_sd_start.sh b/scripts/on_sd_start.sh index 55f4da25..be5161d4 100755 --- a/scripts/on_sd_start.sh +++ b/scripts/on_sd_start.sh @@ -4,7 +4,6 @@ cp sd-ui-files/scripts/functions.sh scripts/ cp sd-ui-files/scripts/on_env_start.sh scripts/ cp sd-ui-files/scripts/bootstrap.sh scripts/ cp sd-ui-files/scripts/check_modules.py scripts/ -cp sd-ui-files/scripts/check_models.py scripts/ cp sd-ui-files/scripts/get_config.py scripts/ source ./scripts/functions.sh From e23f66a697f1afab2310a36f73b0d76adc31cf7c Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 7 Jun 2023 15:45:21 +0530 Subject: [PATCH 254/291] Fix #1333 - listen_port isn't always present in the config file --- ui/easydiffusion/server.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/ui/easydiffusion/server.py b/ui/easydiffusion/server.py index b9ffe5f9..d8940bb5 100644 --- a/ui/easydiffusion/server.py +++ b/ui/easydiffusion/server.py @@ -338,21 +338,22 @@ def get_image_internal(task_id: int, img_id: int): class CloudflareTunnel: def __init__(self): config = app.getConfig() - self.Urls = None - self.port = config["net"]["listen_port"] + self.urls = None + self.port = config.get("net", {}).get("listen_port") def start(self): - self.Urls = try_cloudflare(self.port) + if self.port: + self.urls = try_cloudflare(self.port) def stop(self): - if self.Urls != None: + if self.urls: try_cloudflare.terminate(self.port) - self.Urls = None + self.urls = None @property def address(self): - if self.Urls != None: - return self.Urls.tunnel + if self.urls: + return self.urls.tunnel else: return None From 267c7b85eab7fba34030cadfdb550f68ea81aa06 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 7 Jun 2023 16:37:44 +0530 Subject: [PATCH 255/291] Use only realesrgan_x4 (not anime) for upscaling in codeformer --- ui/easydiffusion/renderer.py | 64 ++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 24 deletions(-) diff --git a/ui/easydiffusion/renderer.py b/ui/easydiffusion/renderer.py index ef038715..a57dfc6c 100644 --- a/ui/easydiffusion/renderer.py +++ b/ui/easydiffusion/renderer.py @@ -7,10 +7,12 @@ from easydiffusion import device_manager from easydiffusion.types import GenerateImageRequest from easydiffusion.types import Image as ResponseImage from easydiffusion.types import Response, TaskData, UserInitiatedStop +from easydiffusion.model_manager import DEFAULT_MODELS, resolve_model_to_use from easydiffusion.utils import get_printable_request, log, save_images_to_disk from sdkit import Context from sdkit.filter import apply_filters from sdkit.generate import generate_images +from sdkit.models import load_model from sdkit.utils import ( diffusers_latent_samples_to_images, gc, @@ -157,37 +159,51 @@ def filter_images(req: GenerateImageRequest, task_data: TaskData, images: list, if user_stopped: return images - filters_to_apply = [] - filter_params = {} if task_data.block_nsfw: - filters_to_apply.append("nsfw_checker") - if task_data.use_face_correction and "codeformer" in task_data.use_face_correction.lower(): - filters_to_apply.append("codeformer") + images = apply_filters(context, "nsfw_checker", images) - filter_params["upscale_faces"] = task_data.codeformer_upscale_faces - filter_params["codeformer_fidelity"] = task_data.codeformer_fidelity + if task_data.use_face_correction and "codeformer" in task_data.use_face_correction.lower(): + default_realesrgan = DEFAULT_MODELS["realesrgan"][0]["file_name"] + prev_realesrgan_path = None + if task_data.codeformer_upscale_faces and default_realesrgan not in context.model_paths["realesrgan"]: + prev_realesrgan_path = context.model_paths["realesrgan"] + context.model_paths["realesrgan"] = resolve_model_to_use(default_realesrgan, "realesrgan") + load_model(context, "realesrgan") + + try: + images = apply_filters( + context, + "codeformer", + images, + upscale_faces=task_data.codeformer_upscale_faces, + codeformer_fidelity=task_data.codeformer_fidelity, + ) + finally: + if prev_realesrgan_path: + context.model_paths["realesrgan"] = prev_realesrgan_path + load_model(context, "realesrgan") elif task_data.use_face_correction and "gfpgan" in task_data.use_face_correction.lower(): - filters_to_apply.append("gfpgan") + images = apply_filters(context, "gfpgan", images) + if task_data.use_upscale: if "realesrgan" in task_data.use_upscale.lower(): - filters_to_apply.append("realesrgan") + images = apply_filters(context, "realesrgan", images, scale=task_data.upscale_amount) elif task_data.use_upscale == "latent_upscaler": - filters_to_apply.append("latent_upscaler") + images = apply_filters( + context, + "latent_upscaler", + images, + scale=task_data.upscale_amount, + latent_upscaler_options={ + "prompt": req.prompt, + "negative_prompt": req.negative_prompt, + "seed": req.seed, + "num_inference_steps": task_data.latent_upscaler_steps, + "guidance_scale": 0, + }, + ) - filter_params["latent_upscaler_options"] = { - "prompt": req.prompt, - "negative_prompt": req.negative_prompt, - "seed": req.seed, - "num_inference_steps": task_data.latent_upscaler_steps, - "guidance_scale": 0, - } - - filter_params["scale"] = task_data.upscale_amount - - if len(filters_to_apply) == 0: - return images - - return apply_filters(context, filters_to_apply, images, **filter_params) + return images def construct_response(images: list, seeds: list, task_data: TaskData, base_seed: int): From 48edce72a96d08f646221f79b19d26a6842e20d6 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 7 Jun 2023 16:38:15 +0530 Subject: [PATCH 256/291] Log the version numbers of only a few important modules --- scripts/check_modules.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 5264ec08..dc3733e9 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -26,6 +26,7 @@ modules_to_check = { "pycloudflared": "0.2.0", # "xformers": "0.0.16", } +modules_to_log = ["torch", "torchvision", "sdkit", "stable-diffusion-sdkit"] def version(module_name: str) -> str: @@ -90,7 +91,8 @@ def init(): traceback.print_exc() fail(module_name) - print(f"{module_name}: {version(module_name)}") + if module_name in modules_to_log: + print(f"{module_name}: {version(module_name)}") ### utilities From 5398765fd7eddfe81554233df2acf74022371a9b Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Thu, 8 Jun 2023 15:21:16 +0530 Subject: [PATCH 257/291] Tighten the image editor (to reduce unnecessary empty space and reduce mouse travel) - Thanks @fdwr - #1307 --- ui/media/css/image-editor.css | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ui/media/css/image-editor.css b/ui/media/css/image-editor.css index ea8112e3..ba046383 100644 --- a/ui/media/css/image-editor.css +++ b/ui/media/css/image-editor.css @@ -96,7 +96,7 @@ .editor-controls-center { /* background: var(--background-color2); */ - flex: 1; + flex: 0; display: flex; justify-content: center; align-items: center; @@ -105,6 +105,8 @@ .editor-controls-center > div { position: relative; background: black; + margin: 20pt; + margin-top: 40pt; } .editor-controls-center canvas { @@ -164,8 +166,10 @@ margin: var(--popup-margin); padding: var(--popup-padding); min-height: calc(99h - (2 * var(--popup-margin))); - max-width: none; + max-width: fit-content; min-width: fit-content; + margin-left: auto; + margin-right: auto; } .image-editor-popup h1 { From 4f799a2bf077b3c22aa186361fb2f8019d424fc9 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Thu, 8 Jun 2023 15:52:41 +0530 Subject: [PATCH 258/291] Use gfpgan as the default model for face restoration --- ui/media/js/main.js | 7 +++++++ ui/media/js/searchable-models.js | 1 - 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ui/media/js/main.js b/ui/media/js/main.js index 5909fdb1..54e7042a 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -2061,3 +2061,10 @@ prettifyInputs(document) // set the textbox as focused on start promptField.focus() promptField.selectionStart = promptField.value.length + +// use gfpgan as the default model for face restoration +document.addEventListener("refreshModels", function() { + let gfpganIdx = gfpganModelField.inputModels.findIndex((e) => e.toLowerCase().startsWith("gfpgan")) + let gfpganElem = gfpganModelField.modelElements[gfpganIdx] + gfpganModelField.selectModelEntry(gfpganElem) +}) diff --git a/ui/media/js/searchable-models.js b/ui/media/js/searchable-models.js index 855cfc61..2b72aaa6 100644 --- a/ui/media/js/searchable-models.js +++ b/ui/media/js/searchable-models.js @@ -103,7 +103,6 @@ class ModelDropdown { "refreshModels", this.bind(function(e) { // reload the models - this.inputModels = modelsOptions[this.modelKey] this.inputModels = [] let modelKeys = Array.isArray(this.modelKey) ? this.modelKey : [this.modelKey] for (let i = 0; i < modelKeys.length; i++) { From e349fb1a23793c216b5574e3270e97b57a0bd07b Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Thu, 8 Jun 2023 15:55:36 +0530 Subject: [PATCH 259/291] fix --- ui/media/js/main.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ui/media/js/main.js b/ui/media/js/main.js index 54e7042a..00a49d17 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -2063,8 +2063,8 @@ promptField.focus() promptField.selectionStart = promptField.value.length // use gfpgan as the default model for face restoration -document.addEventListener("refreshModels", function() { - let gfpganIdx = gfpganModelField.inputModels.findIndex((e) => e.toLowerCase().startsWith("gfpgan")) - let gfpganElem = gfpganModelField.modelElements[gfpganIdx] - gfpganModelField.selectModelEntry(gfpganElem) -}) +// document.addEventListener("refreshModels", function() { +// let gfpganIdx = gfpganModelField.inputModels.findIndex((e) => e.toLowerCase().startsWith("gfpgan")) +// let gfpganElem = gfpganModelField.modelElements[gfpganIdx] +// gfpganModelField.selectModelEntry(gfpganElem) +// }) From 924fee394ac76ee85df84d5388b24ed9e14ced94 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Thu, 8 Jun 2023 16:09:11 +0530 Subject: [PATCH 260/291] A better way to make gfpgan show up at the top --- ui/media/js/main.js | 9 +-------- ui/media/js/searchable-models.js | 13 ++++++++++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ui/media/js/main.js b/ui/media/js/main.js index 00a49d17..b44af102 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -87,7 +87,7 @@ let promptStrengthField = document.querySelector("#prompt_strength") let samplerField = document.querySelector("#sampler_name") let samplerSelectionContainer = document.querySelector("#samplerSelection") let useFaceCorrectionField = document.querySelector("#use_face_correction") -let gfpganModelField = new ModelDropdown(document.querySelector("#gfpgan_model"), ["codeformer", "gfpgan"]) +let gfpganModelField = new ModelDropdown(document.querySelector("#gfpgan_model"), ["gfpgan", "codeformer"], "", false) let useUpscalingField = document.querySelector("#use_upscale") let upscaleModelField = document.querySelector("#upscale_model") let upscaleAmountField = document.querySelector("#upscale_amount") @@ -2061,10 +2061,3 @@ prettifyInputs(document) // set the textbox as focused on start promptField.focus() promptField.selectionStart = promptField.value.length - -// use gfpgan as the default model for face restoration -// document.addEventListener("refreshModels", function() { -// let gfpganIdx = gfpganModelField.inputModels.findIndex((e) => e.toLowerCase().startsWith("gfpgan")) -// let gfpganElem = gfpganModelField.modelElements[gfpganIdx] -// gfpganModelField.selectModelEntry(gfpganElem) -// }) diff --git a/ui/media/js/searchable-models.js b/ui/media/js/searchable-models.js index 2b72aaa6..7bdb176a 100644 --- a/ui/media/js/searchable-models.js +++ b/ui/media/js/searchable-models.js @@ -38,6 +38,8 @@ class ModelDropdown { noneEntry //= '' modelFilterInitialized //= undefined + sorted //= true + /* MIMIC A REGULAR INPUT FIELD */ get parentElement() { return this.modelFilter.parentElement @@ -83,10 +85,11 @@ class ModelDropdown { /* SEARCHABLE INPUT */ - constructor(input, modelKey, noneEntry = "") { + constructor(input, modelKey, noneEntry = "", sorted = true) { this.modelFilter = input this.noneEntry = noneEntry this.modelKey = modelKey + this.sorted = sorted if (modelsOptions !== undefined) { // reuse models from cache (only useful for plugins, which are loaded after models) @@ -566,11 +569,15 @@ class ModelDropdown { }) const childFolderNames = Array.from(foldersMap.keys()) - this.sortStringArray(childFolderNames) + if (this.sorted) { + this.sortStringArray(childFolderNames) + } const folderElements = childFolderNames.map((name) => foldersMap.get(name)) const modelNames = Array.from(modelsMap.keys()) - this.sortStringArray(modelNames) + if (this.sorted) { + this.sortStringArray(modelNames) + } const modelElements = modelNames.map((name) => modelsMap.get(name)) if (modelElements.length && folderName) { From f83af28e428a3d5f80ab40eb0cc58c2f5f4af0d3 Mon Sep 17 00:00:00 2001 From: JeLuF Date: Sun, 11 Jun 2023 21:12:22 +0200 Subject: [PATCH 261/291] Set PYTHONNOUSERSITE=y in dev console Make behaviour consistent with on_env_start.sh --- scripts/developer_console.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/developer_console.sh b/scripts/developer_console.sh index 73972568..57846eeb 100755 --- a/scripts/developer_console.sh +++ b/scripts/developer_console.sh @@ -39,6 +39,8 @@ if [ "$0" == "bash" ]; then export PYTHONPATH="$(pwd)/stable-diffusion/env/lib/python3.8/site-packages" fi + export PYTHONNOUSERSITE=y + which python python --version From e213f6cb9559a16fa69a227363924cc19ce586a6 Mon Sep 17 00:00:00 2001 From: ManInDark <61268856+ManInDark@users.noreply.github.com> Date: Mon, 12 Jun 2023 12:48:52 +0200 Subject: [PATCH 262/291] Added calculation as to how many images will be generated with given prompts. --- ui/media/js/main.js | 65 ++++++++++++++++++++++++++++++++++++++++++-- ui/media/js/utils.js | 4 +++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/ui/media/js/main.js b/ui/media/js/main.js index b44af102..cb516a37 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -1327,6 +1327,43 @@ function getPrompts(prompts) { return promptsToMake } +function getPromptsNumber(prompts) { + if (typeof prompts === "undefined") { + prompts = promptField.value + } + if (prompts.trim() === "" && activeTags.length === 0) { + return [""] + } + + let promptsToMake = [] + let numberOfPrompts = 0 + if (prompts.trim() !== "") { // this needs to stay sort of the same, as the prompts have to be passed through to the other functions + prompts = prompts.split("\n") + prompts = prompts.map((prompt) => prompt.trim()) + prompts = prompts.filter((prompt) => prompt !== "") + + promptsToMake = applySetOperator(prompts) // switched those around as Set grows in a linear fashion and permute in 2^n, and one has to be computed for the other to be calculated + numberOfPrompts = applyPermuteOperatorNumber(promptsToMake) + } + const newTags = activeTags.filter((tag) => tag.inactive === undefined || tag.inactive === false) + if (newTags.length > 0) { + const promptTags = newTags.map((x) => x.name).join(", ") + if (numberOfPrompts > 0) { + // promptsToMake = promptsToMake.map((prompt) => `${prompt}, ${promptTags}`) + // nothing changes, as all prompts just get modified + } else { + // promptsToMake.push(promptTags) + numberOfPrompts = 1 + } + } + + // Why is this applied twice? It does not do anything here, as everything should have already been done earlier + // promptsToMake = applyPermuteOperator(promptsToMake) + // promptsToMake = applySetOperator(promptsToMake) + + return numberOfPrompts +} + function applySetOperator(prompts) { let promptsToMake = [] let braceExpander = new BraceExpander() @@ -1338,7 +1375,7 @@ function applySetOperator(prompts) { return promptsToMake } -function applyPermuteOperator(prompts) { +function applyPermuteOperator(prompts) { // prompts is array of input, trimmed, filtered and split by \n let promptsToMake = [] prompts.forEach((prompt) => { let promptMatrix = prompt.split("|") @@ -1357,6 +1394,26 @@ function applyPermuteOperator(prompts) { return promptsToMake } +// returns how many prompts would have to be made with the given prompts +function applyPermuteOperatorNumber(prompts) { // prompts is array of input, trimmed, filtered and split by \n + let numberOfPrompts = 0 + prompts.forEach((prompt) => { + let promptCounter = 1 + let promptMatrix = prompt.split("|") + promptMatrix.shift() + + promptMatrix = promptMatrix.map((p) => p.trim()) + promptMatrix = promptMatrix.filter((p) => p !== "") + + if (promptMatrix.length > 0) { + promptCounter *= permutePromptsNumber(promptMatrix) + } + numberOfPrompts += promptCounter + }) + + return numberOfPrompts +} + function permutePrompts(promptBase, promptMatrix) { let prompts = [] let permutations = permute(promptMatrix) @@ -1378,6 +1435,10 @@ function permutePrompts(promptBase, promptMatrix) { return prompts } +function permutePromptsNumber(promptMatrix) { // this should calculate how many different prompts can be made with the prompt matrix + return permuteNumber(promptMatrix) +} + // create a file name with embedded prompt and metadata // for easier cateloging and comparison function createFileName(prompt, seed, steps, guidance, outputFormat) { @@ -1546,7 +1607,7 @@ heightField.addEventListener("change", onDimensionChange) function renameMakeImageButton() { let totalImages = - Math.max(parseInt(numOutputsTotalField.value), parseInt(numOutputsParallelField.value)) * getPrompts().length + Math.max(parseInt(numOutputsTotalField.value), parseInt(numOutputsParallelField.value)) * getPromptsNumber() let imageLabel = "Image" if (totalImages > 1) { imageLabel = totalImages + " Images" diff --git a/ui/media/js/utils.js b/ui/media/js/utils.js index 871ba714..111a12e1 100644 --- a/ui/media/js/utils.js +++ b/ui/media/js/utils.js @@ -153,6 +153,10 @@ function permute(arr) { return permutations } +function permuteNumber(arr) { + return Math.pow(2, arr.length) +} + // https://stackoverflow.com/a/8212878 function millisecondsToStr(milliseconds) { function numberEnding(number) { From 3e34cdc8845fa91fd16018bf70a94569d97db7bb Mon Sep 17 00:00:00 2001 From: ManInDark <61268856+ManInDark@users.noreply.github.com> Date: Mon, 12 Jun 2023 13:25:12 +0200 Subject: [PATCH 263/291] Added sets to the readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6a629e57..5366772f 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,7 @@ Just delete the `EasyDiffusion` folder to uninstall all the downloaded packages. - **Attention/Emphasis**: () in the prompt increases the model's attention to enclosed words, and [] decreases it. - **Weighted Prompts**: Use weights for specific words in your prompt to change their importance, e.g. `red:2.4 dragon:1.2`. - **Prompt Matrix**: Quickly create multiple variations of your prompt, e.g. `a photograph of an astronaut riding a horse | illustration | cinematic lighting`. +- **Prompt Set**: Quickly create multiple variations of your prompt, e.g. `a photograph of an astronaut on the {moon,earth}` - **1-click Upscale/Face Correction**: Upscale or correct an image after it has been generated. - **Make Similar Images**: Click to generate multiple variations of a generated image. - **NSFW Setting**: A setting in the UI to control *NSFW content*. From 9a81d17d33f6c15c23dc26bb92993ed38bac5ab0 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Mon, 12 Jun 2023 16:57:36 +0530 Subject: [PATCH 264/291] Fix for multi-gpu bug in codeformer --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index dc3733e9..1931b4f0 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.104", + "sdkit": "1.0.105", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", From 324ffdefba3537bfcbe2c88a1000055c5d05b83f Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Mon, 12 Jun 2023 16:58:11 +0530 Subject: [PATCH 265/291] changelog --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index cc2cfb04..178ec12f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -22,6 +22,7 @@ Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. ### Detailed changelog +* 2.5.41 - 12 Jun 2023 - Fix multi-gpu bug with CodeFormer. * 2.5.41 - 6 Jun 2023 - Allow changing the strength of CodeFormer, and slightly improved styling of the CodeFormer options. * 2.5.41 - 5 Jun 2023 - Allow sharing an Easy Diffusion instance via https://try.cloudflare.com/ . You can find this option at the bottom of the Settings tab. Thanks @JeLuf. * 2.5.41 - 5 Jun 2023 - Show an option to download for tiled images. Shows a button on the generated image. Creates larger images by tiling them with the image generated by Easy Diffusion. Thanks @JeLuf. From eb96bfe8a49c9c10ed4a6c4fc751a5481a6bcaac Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 13 Jun 2023 13:39:23 +0530 Subject: [PATCH 266/291] sdkit 1.0.106 - fix errors with multi-gpu in low vram mode --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 1931b4f0..6275de45 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.105", + "sdkit": "1.0.106", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", From 41d8847592446af5ed06c28752475d8d7e08f792 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 13 Jun 2023 13:39:58 +0530 Subject: [PATCH 267/291] changelog --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 178ec12f..b53ac141 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -22,6 +22,7 @@ Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. ### Detailed changelog +* 2.5.41 - 13 Jun 2023 - Fix multi-gpu bug with "low" VRAM usage mode while generating images. * 2.5.41 - 12 Jun 2023 - Fix multi-gpu bug with CodeFormer. * 2.5.41 - 6 Jun 2023 - Allow changing the strength of CodeFormer, and slightly improved styling of the CodeFormer options. * 2.5.41 - 5 Jun 2023 - Allow sharing an Easy Diffusion instance via https://try.cloudflare.com/ . You can find this option at the bottom of the Settings tab. Thanks @JeLuf. From 6ae4314b798efe7d3e952e111fd9fc1b6d73c4f4 Mon Sep 17 00:00:00 2001 From: ManInDark <61268856+ManInDark@users.noreply.github.com> Date: Tue, 13 Jun 2023 16:09:54 +0200 Subject: [PATCH 268/291] Incorporated proposal by @JeLuF in #1324 --- ui/media/js/main.js | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/ui/media/js/main.js b/ui/media/js/main.js index cb516a37..a7cccc55 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -1342,6 +1342,16 @@ function getPromptsNumber(prompts) { prompts = prompts.map((prompt) => prompt.trim()) prompts = prompts.filter((prompt) => prompt !== "") + // estimate number of prompts + let estimatedNumberOfPrompts = 0 + prompts.forEach((prompt) => { + estimatedNumberOfPrompts += (prompt.match(/{[^}]*}/g) || []).map((e) => e.match(/,/g).length + 1).reduce( (p,a) => p*a, 1) * (2**(prompt.match(/\|/g) || []).length) + }) + + if (estimatedNumberOfPrompts >= 10000) { + return 10000 + } + promptsToMake = applySetOperator(prompts) // switched those around as Set grows in a linear fashion and permute in 2^n, and one has to be computed for the other to be calculated numberOfPrompts = applyPermuteOperatorNumber(promptsToMake) } @@ -1613,9 +1623,15 @@ function renameMakeImageButton() { imageLabel = totalImages + " Images" } if (SD.activeTasks.size == 0) { - makeImageBtn.innerText = "Make " + imageLabel + if (totalImages >= 10000) + makeImageBtn.innerText = "Make tens of thousands of images" + else + makeImageBtn.innerText = "Make " + imageLabel } else { - makeImageBtn.innerText = "Enqueue Next " + imageLabel + if (totalImages >= 10000) + makeImageBtn.innerText = "Enqueue tens of thousands of images" + else + makeImageBtn.innerText = "Enqueue Next " + imageLabel } } numOutputsTotalField.addEventListener("change", renameMakeImageButton) From ed59972b032b49e497e57985be92926659dc6441 Mon Sep 17 00:00:00 2001 From: ManInDark <61268856+ManInDark@users.noreply.github.com> Date: Wed, 14 Jun 2023 11:57:06 +0200 Subject: [PATCH 269/291] Changed all links as mentioned in #1339 --- CHANGES.md | 12 ++++----- CONTRIBUTING.md | 4 +-- How to install and run.txt | 6 ++--- PRIVACY.md | 2 +- README BEFORE YOU RUN THIS.txt | 2 +- README.md | 14 +++++------ build.bat | 2 +- build.sh | 2 +- scripts/bootstrap.bat | 2 +- scripts/check_modules.py | 4 +-- scripts/functions.sh | 4 +-- scripts/on_env_start.bat | 4 +-- scripts/on_env_start.sh | 2 +- scripts/on_sd_start.bat | 4 +-- ui/easydiffusion/app.py | 2 +- ui/index.html | 36 +++++++++++++-------------- ui/plugins/ui/release-notes.plugin.js | 4 +-- 17 files changed, 53 insertions(+), 53 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index b53ac141..d222d795 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,13 +8,13 @@ - **Full support for Stable Diffusion 2.1 (including CPU)** - supports loading v1.4 or v2.0 or v2.1 models seamlessly. No need to enable "Test SD2", and no need to add `sd2_` to your SD 2.0 model file names. Works on CPU as well. - **Memory optimized Stable Diffusion 2.1** - you can now use Stable Diffusion 2.1 models, with the same low VRAM optimizations that we've always had for SD 1.4. Please note, the SD 2.0 and 2.1 models require more GPU and System RAM, as compared to the SD 1.4 and 1.5 models. - **11 new samplers!** - explore the new samplers, some of which can generate great images in less than 10 inference steps! We've added the Karras and UniPC samplers. Thanks @Schorny for the UniPC samplers. -- **Model Merging** - You can now merge two models (`.ckpt` or `.safetensors`) and output `.ckpt` or `.safetensors` models, optionally in `fp16` precision. Details: https://github.com/cmdr2/stable-diffusion-ui/wiki/Model-Merging . Thanks @JeLuf. +- **Model Merging** - You can now merge two models (`.ckpt` or `.safetensors`) and output `.ckpt` or `.safetensors` models, optionally in `fp16` precision. Details: https://github.com/easydiffusion/easydiffusion/wiki/Model-Merging . Thanks @JeLuf. - **Fast loading/unloading of VAEs** - No longer needs to reload the entire Stable Diffusion model, each time you change the VAE - **Database of known models** - automatically picks the right configuration for known models. E.g. we automatically detect and apply "v" parameterization (required for some SD 2.0 models), and "fp32" attention precision (required for some SD 2.1 models). - **Color correction for img2img** - an option to preserve the color profile (histogram) of the initial image. This is especially useful if you're getting red-tinted images after inpainting/masking. - **Three GPU Memory Usage Settings** - `High` (fastest, maximum VRAM usage), `Balanced` (default - almost as fast, significantly lower VRAM usage), `Low` (slowest, very low VRAM usage). The `Low` setting is applied automatically for GPUs with less than 4 GB of VRAM. - **Find models in sub-folders** - This allows you to organize your models into sub-folders inside `models/stable-diffusion`, instead of keeping them all in a single folder. Thanks @patriceac and @ogmaresca. -- **Custom Modifier Categories** - Ability to create custom modifiers with thumbnails, and custom categories (and hierarchy of categories). Details: https://github.com/cmdr2/stable-diffusion-ui/wiki/Custom-Modifiers . Thanks @ogmaresca. +- **Custom Modifier Categories** - Ability to create custom modifiers with thumbnails, and custom categories (and hierarchy of categories). Details: https://github.com/easydiffusion/easydiffusion/wiki/Custom-Modifiers . Thanks @ogmaresca. - **Embed metadata, or save as TXT/JSON** - You can now embed the metadata directly into the images, or save them as text or json files (choose in the Settings tab). Thanks @patriceac. - **Major rewrite of the code** - Most of the codebase has been reorganized and rewritten, to make it more manageable and easier for new developers to contribute features. We've separated our core engine into a new project called `sdkit`, which allows anyone to easily integrate Stable Diffusion (and related modules like GFPGAN etc) into their programming projects (via a simple `pip install sdkit`): https://github.com/easydiffusion/sdkit/ - **Name change** - Last, and probably the least, the UI is now called "Easy Diffusion". It indicates the focus of this project - an easy way for people to play with Stable Diffusion. @@ -67,7 +67,7 @@ Our focus continues to remain on an easy installation experience, and an easy us * 2.5.24 - 11 Mar 2023 - Button to load an image mask from a file. * 2.5.24 - 10 Mar 2023 - Logo change. Image credit: @lazlo_vii. * 2.5.23 - 8 Mar 2023 - Experimental support for Mac M1/M2. Thanks @michaelgallacher, @JeLuf and vishae! -* 2.5.23 - 8 Mar 2023 - Ability to create custom modifiers with thumbnails, and custom categories (and hierarchy of categories). More details - https://github.com/cmdr2/stable-diffusion-ui/wiki/Custom-Modifiers . Thanks @ogmaresca. +* 2.5.23 - 8 Mar 2023 - Ability to create custom modifiers with thumbnails, and custom categories (and hierarchy of categories). More details - https://github.com/easydiffusion/easydiffusion/wiki/Custom-Modifiers . Thanks @ogmaresca. * 2.5.22 - 28 Feb 2023 - Minor styling changes to UI buttons, and the models dropdown. * 2.5.22 - 28 Feb 2023 - Lots of UI-related bug fixes. Thanks @patriceac. * 2.5.21 - 22 Feb 2023 - An option to control the size of the image thumbnails. You can use the `Display options` in the top-right corner to change this. Thanks @JeLuf. @@ -92,7 +92,7 @@ Our focus continues to remain on an easy installation experience, and an easy us * 2.5.14 - 3 Feb 2023 - Fix the 'Make Similar Images' button, which was producing incorrect images (weren't very similar). * 2.5.13 - 1 Feb 2023 - Fix the remaining GPU memory leaks, including a better fix (more comprehensive) for the change in 2.5.12 (27 Jan). * 2.5.12 - 27 Jan 2023 - Fix a memory leak, which made the UI unresponsive after an out-of-memory error. The allocated memory is now freed-up after an error. -* 2.5.11 - 25 Jan 2023 - UI for Merging Models. Thanks @JeLuf. More info: https://github.com/cmdr2/stable-diffusion-ui/wiki/Model-Merging +* 2.5.11 - 25 Jan 2023 - UI for Merging Models. Thanks @JeLuf. More info: https://github.com/easydiffusion/easydiffusion/wiki/Model-Merging * 2.5.10 - 24 Jan 2023 - Reduce the VRAM usage for img2img in 'balanced' mode (without reducing the rendering speed), to make it similar to v2.4 of this UI. * 2.5.9 - 23 Jan 2023 - Fix a bug where img2img would produce poorer-quality images for the same settings, as compared to version 2.4 of this UI. * 2.5.9 - 23 Jan 2023 - Reduce the VRAM usage for 'balanced' mode (without reducing the rendering speed), to make it similar to v2.4 of the UI. @@ -121,8 +121,8 @@ Our focus continues to remain on an easy installation experience, and an easy us - **Automatic scanning for malicious model files** - using `picklescan`, and support for `safetensor` model format. Thanks @JeLuf - **Image Editor** - for drawing simple images for guiding the AI. Thanks @mdiller - **Use pre-trained hypernetworks** - for improving the quality of images. Thanks @C0bra5 -- **Support for custom VAE models**. You can place your VAE files in the `models/vae` folder, and refresh the browser page to use them. More info: https://github.com/cmdr2/stable-diffusion-ui/wiki/VAE-Variational-Auto-Encoder -- **Experimental support for multiple GPUs!** It should work automatically. Just open one browser tab per GPU, and spread your tasks across your GPUs. For e.g. open our UI in two browser tabs if you have two GPUs. You can customize which GPUs it should use in the "Settings" tab, otherwise let it automatically pick the best GPUs. Thanks @madrang . More info: https://github.com/cmdr2/stable-diffusion-ui/wiki/Run-on-Multiple-GPUs +- **Support for custom VAE models**. You can place your VAE files in the `models/vae` folder, and refresh the browser page to use them. More info: https://github.com/easydiffusion/easydiffusion/wiki/VAE-Variational-Auto-Encoder +- **Experimental support for multiple GPUs!** It should work automatically. Just open one browser tab per GPU, and spread your tasks across your GPUs. For e.g. open our UI in two browser tabs if you have two GPUs. You can customize which GPUs it should use in the "Settings" tab, otherwise let it automatically pick the best GPUs. Thanks @madrang . More info: https://github.com/easydiffusion/easydiffusion/wiki/Run-on-Multiple-GPUs - **Cleaner UI design** - Show settings and help in new tabs, instead of dropdown popups (which were buggy). Thanks @mdiller - **Progress bar.** Thanks @mdiller - **Custom Image Modifiers** - You can now save your custom image modifiers! Your saved modifiers can include special characters like `{}, (), [], |` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c01d489a..bb6408c8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ Hi there, these instructions are meant for the developers of this project. -If you only want to use the Stable Diffusion UI, you've downloaded the wrong file. In that case, please download and follow the instructions at https://github.com/cmdr2/stable-diffusion-ui#installation +If you only want to use the Stable Diffusion UI, you've downloaded the wrong file. In that case, please download and follow the instructions at https://github.com/easydiffusion/easydiffusion#installation Thanks @@ -13,7 +13,7 @@ If you would like to contribute to this project, there is a discord for discussi This is in-flux, but one way to get a development environment running for editing the UI of this project is: (swap `.sh` or `.bat` in instructions depending on your environment, and be sure to adjust any paths to match where you're working) -1) Install the project to a new location using the [usual installation process](https://github.com/cmdr2/stable-diffusion-ui#installation), e.g. to `/projects/stable-diffusion-ui-archive` +1) Install the project to a new location using the [usual installation process](https://github.com/easydiffusion/easydiffusion#installation), e.g. to `/projects/stable-diffusion-ui-archive` 2) Start the newly installed project, and check that you can view and generate images on `localhost:9000` 3) Next, please clone the project repository using `git clone` (e.g. to `/projects/stable-diffusion-ui-repo`) 4) Close the server (started in step 2), and edit `/projects/stable-diffusion-ui-archive/scripts/on_env_start.sh` (or `on_env_start.bat`) diff --git a/How to install and run.txt b/How to install and run.txt index e48d217c..af783b64 100644 --- a/How to install and run.txt +++ b/How to install and run.txt @@ -1,6 +1,6 @@ Congrats on downloading Stable Diffusion UI, version 2! -If you haven't downloaded Stable Diffusion UI yet, please download from https://github.com/cmdr2/stable-diffusion-ui#installation +If you haven't downloaded Stable Diffusion UI yet, please download from https://github.com/easydiffusion/easydiffusion#installation After downloading, to install please follow these instructions: @@ -16,9 +16,9 @@ To start the UI in the future, please run the same command mentioned above. If you have any problems, please: -1. Try the troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting +1. Try the troubleshooting steps at https://github.com/easydiffusion/easydiffusion/wiki/Troubleshooting 2. Or, seek help from the community at https://discord.com/invite/u9yhsFmEkB -3. Or, file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues +3. Or, file an issue at https://github.com/easydiffusion/easydiffusion/issues Thanks cmdr2 (and contributors to the project) \ No newline at end of file diff --git a/PRIVACY.md b/PRIVACY.md index 6c997997..543a167d 100644 --- a/PRIVACY.md +++ b/PRIVACY.md @@ -3,7 +3,7 @@ This is a summary of whether Easy Diffusion uses your data or tracks you: * The short answer is - Easy Diffusion does *not* use your data, and does *not* track you. * Easy Diffusion does not send your prompts or usage or analytics to anyone. There is no tracking. We don't even know how many people use Easy Diffusion, let alone their prompts. -* Easy Diffusion fetches updates to the code whenever it starts up. It does this by contacting GitHub directly, via SSL (secure connection). Only your computer and GitHub and [this repository](https://github.com/cmdr2/stable-diffusion-ui) are involved, and no third party is involved. Some countries intercepts SSL connections, that's not something we can do much about. GitHub does *not* share statistics (even with me) about how many people fetched code updates. +* Easy Diffusion fetches updates to the code whenever it starts up. It does this by contacting GitHub directly, via SSL (secure connection). Only your computer and GitHub and [this repository](https://github.com/easydiffusion/easydiffusion) are involved, and no third party is involved. Some countries intercepts SSL connections, that's not something we can do much about. GitHub does *not* share statistics (even with me) about how many people fetched code updates. * Easy Diffusion fetches the models from huggingface.co and github.com, if they don't exist on your PC. For e.g. if the safety checker (NSFW) model doesn't exist, it'll try to download it. * Easy Diffusion fetches code packages from pypi.org, which is the standard hosting service for all Python projects. That's where packages installed via `pip install` are stored. * Occasionally, antivirus software are known to *incorrectly* flag and delete some model files, which will result in Easy Diffusion re-downloading `pytorch_model.bin`. This *incorrect deletion* affects other Stable Diffusion UIs as well, like Invoke AI - https://itch.io/post/7509488 diff --git a/README BEFORE YOU RUN THIS.txt b/README BEFORE YOU RUN THIS.txt index e9f81544..a989b835 100644 --- a/README BEFORE YOU RUN THIS.txt +++ b/README BEFORE YOU RUN THIS.txt @@ -3,6 +3,6 @@ Hi there, What you have downloaded is meant for the developers of this project, not for users. If you only want to use the Stable Diffusion UI, you've downloaded the wrong file. -Please download and follow the instructions at https://github.com/cmdr2/stable-diffusion-ui#installation +Please download and follow the instructions at https://github.com/easydiffusion/easydiffusion#installation Thanks \ No newline at end of file diff --git a/README.md b/README.md index 5366772f..b97c35d1 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Does not require technical knowledge, does not require pre-installed software. 1-click install, powerful features, friendly community. -[Installation guide](#installation) | [Troubleshooting guide](https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting) | [![Discord Server](https://img.shields.io/discord/1014774730907209781?label=Discord)](https://discord.com/invite/u9yhsFmEkB) (for support queries, and development discussions) +[Installation guide](#installation) | [Troubleshooting guide](https://github.com/easydiffusion/easydiffusion/wiki/Troubleshooting) | [![Discord Server](https://img.shields.io/discord/1014774730907209781?label=Discord)](https://discord.com/invite/u9yhsFmEkB) (for support queries, and development discussions) ![t2i](https://raw.githubusercontent.com/Stability-AI/stablediffusion/main/assets/stable-samples/txt2img/768/merged-0006.png) @@ -11,9 +11,9 @@ Does not require technical knowledge, does not require pre-installed software. 1 Click the download button for your operating system:

    - - - + + +

    **Hardware requirements:** @@ -83,7 +83,7 @@ Just delete the `EasyDiffusion` folder to uninstall all the downloaded packages. - **Use custom VAE models** - **Use pre-trained Hypernetworks** - **Use custom GFPGAN models** -- **UI Plugins**: Choose from a growing list of [community-generated UI plugins](https://github.com/cmdr2/stable-diffusion-ui/wiki/UI-Plugins), or write your own plugin to add features to the project! +- **UI Plugins**: Choose from a growing list of [community-generated UI plugins](https://github.com/easydiffusion/easydiffusion/wiki/UI-Plugins), or write your own plugin to add features to the project! ### Performance and security - **Fast**: Creates a 512x512 image with euler_a in 5 seconds, on an NVIDIA 3060 12GB. @@ -119,10 +119,10 @@ Useful for judging (and stopping) an image quickly, without waiting for it to fi ---- # How to use? -Please refer to our [guide](https://github.com/cmdr2/stable-diffusion-ui/wiki/How-to-Use) to understand how to use the features in this UI. +Please refer to our [guide](https://github.com/easydiffusion/easydiffusion/wiki/How-to-Use) to understand how to use the features in this UI. # Bugs reports and code contributions welcome -If there are any problems or suggestions, please feel free to ask on the [discord server](https://discord.com/invite/u9yhsFmEkB) or [file an issue](https://github.com/cmdr2/stable-diffusion-ui/issues). +If there are any problems or suggestions, please feel free to ask on the [discord server](https://discord.com/invite/u9yhsFmEkB) or [file an issue](https://github.com/easydiffusion/easydiffusion/issues). We could really use help on these aspects (click to view tasks that need your help): * [User Interface](https://github.com/users/cmdr2/projects/1/views/1) diff --git a/build.bat b/build.bat index 6e3f3f81..dc9e622f 100644 --- a/build.bat +++ b/build.bat @@ -2,7 +2,7 @@ @echo "Hi there, what you are running is meant for the developers of this project, not for users." & echo. @echo "If you only want to use the Stable Diffusion UI, you've downloaded the wrong file." -@echo "Please download and follow the instructions at https://github.com/cmdr2/stable-diffusion-ui#installation" & echo. +@echo "Please download and follow the instructions at https://github.com/easydiffusion/easydiffusion#installation" & echo. @echo "If you are actually a developer of this project, please type Y and press enter" & echo. set /p answer=Are you a developer of this project (Y/N)? diff --git a/build.sh b/build.sh index f4538e5c..bddf3c49 100755 --- a/build.sh +++ b/build.sh @@ -2,7 +2,7 @@ printf "Hi there, what you are running is meant for the developers of this project, not for users.\n\n" printf "If you only want to use the Stable Diffusion UI, you've downloaded the wrong file.\n" -printf "Please download and follow the instructions at https://github.com/cmdr2/stable-diffusion-ui#installation\n\n" +printf "Please download and follow the instructions at https://github.com/easydiffusion/easydiffusion#installation \n\n" printf "If you are actually a developer of this project, please type Y and press enter\n\n" read -p "Are you a developer of this project (Y/N) " yn diff --git a/scripts/bootstrap.bat b/scripts/bootstrap.bat index d3cdd19f..8c1069c8 100644 --- a/scripts/bootstrap.bat +++ b/scripts/bootstrap.bat @@ -11,7 +11,7 @@ setlocal enabledelayedexpansion set MAMBA_ROOT_PREFIX=%cd%\installer_files\mamba set INSTALL_ENV_DIR=%cd%\installer_files\env set LEGACY_INSTALL_ENV_DIR=%cd%\installer -set MICROMAMBA_DOWNLOAD_URL=https://github.com/cmdr2/stable-diffusion-ui/releases/download/v1.1/micromamba.exe +set MICROMAMBA_DOWNLOAD_URL=https://github.com/easydiffusion/easydiffusion/releases/download/v1.1/micromamba.exe set umamba_exists=F set OLD_APPDATA=%APPDATA% diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 6275de45..5f1a9bb8 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -148,9 +148,9 @@ def fail(module_name): print( f"""Error installing {module_name}. Sorry about that, please try to: 1. Run this installer again. -2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting +2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/easydiffusion/easydiffusion/wiki/Troubleshooting 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB -4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues +4. If that doesn't solve the problem, please file an issue at https://github.com/easydiffusion/easydiffusion/issues Thanks!""" ) exit(1) diff --git a/scripts/functions.sh b/scripts/functions.sh index 495e9950..477b7743 100644 --- a/scripts/functions.sh +++ b/scripts/functions.sh @@ -15,9 +15,9 @@ fail() { Error downloading Stable Diffusion UI. Sorry about that, please try to: 1. Run this installer again. - 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting + 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/easydiffusion/easydiffusion/wiki/Troubleshooting 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB - 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues + 4. If that doesn't solve the problem, please file an issue at https://github.com/easydiffusion/easydiffusion/issues Thanks! diff --git a/scripts/on_env_start.bat b/scripts/on_env_start.bat index bc92d0e9..0871973f 100644 --- a/scripts/on_env_start.bat +++ b/scripts/on_env_start.bat @@ -55,10 +55,10 @@ if "%update_branch%"=="" ( @echo. & echo "Downloading Easy Diffusion..." & echo. @echo "Using the %update_branch% channel" & echo. - @call git clone -b "%update_branch%" https://github.com/cmdr2/stable-diffusion-ui.git sd-ui-files && ( + @call git clone -b "%update_branch%" https://github.com/easydiffusion/easydiffusion.git sd-ui-files && ( @echo sd_ui_git_cloned >> scripts\install_status.txt ) || ( - @echo "Error downloading Easy Diffusion. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" + @echo "Error downloading Easy Diffusion. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/easydiffusion/easydiffusion/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/easydiffusion/easydiffusion/issues" & echo "Thanks!" pause @exit /b ) diff --git a/scripts/on_env_start.sh b/scripts/on_env_start.sh index 366b5dd1..d936924e 100755 --- a/scripts/on_env_start.sh +++ b/scripts/on_env_start.sh @@ -38,7 +38,7 @@ else printf "\n\nDownloading Easy Diffusion..\n\n" printf "Using the $update_branch channel\n\n" - if git clone -b "$update_branch" https://github.com/cmdr2/stable-diffusion-ui.git sd-ui-files ; then + if git clone -b "$update_branch" https://github.com/easydiffusion/easydiffusion.git sd-ui-files ; then echo sd_ui_git_cloned >> scripts/install_status.txt else fail "git clone failed" diff --git a/scripts/on_sd_start.bat b/scripts/on_sd_start.bat index f92b9f6f..860361d4 100644 --- a/scripts/on_sd_start.bat +++ b/scripts/on_sd_start.bat @@ -26,7 +26,7 @@ if exist "%cd%\stable-diffusion\env" ( @rem activate the installer env call conda activate @if "%ERRORLEVEL%" NEQ "0" ( - @echo. & echo "Error activating conda for Easy Diffusion. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo. + @echo. & echo "Error activating conda for Easy Diffusion. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/easydiffusion/easydiffusion/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/easydiffusion/easydiffusion/issues" & echo "Thanks!" & echo. pause exit /b ) @@ -68,7 +68,7 @@ if "%ERRORLEVEL%" NEQ "0" ( call WHERE uvicorn > .tmp @>nul findstr /m "uvicorn" .tmp @if "%ERRORLEVEL%" NEQ "0" ( - @echo. & echo "UI packages not found! Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo. + @echo. & echo "UI packages not found! Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/easydiffusion/easydiffusion/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/easydiffusion/easydiffusion/issues" & echo "Thanks!" & echo. pause exit /b ) diff --git a/ui/easydiffusion/app.py b/ui/easydiffusion/app.py index 38e3392c..99810e75 100644 --- a/ui/easydiffusion/app.py +++ b/ui/easydiffusion/app.py @@ -237,7 +237,7 @@ def fail_and_die(fail_type: str, data: str): suggestions = [ "Run this installer again.", "If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB", - "If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues", + "If that doesn't solve the problem, please file an issue at https://github.com/easydiffusion/easydiffusion/issues", ] if fail_type == "model_download": diff --git a/ui/index.html b/ui/index.html index 0c4386de..2afdfd84 100644 --- a/ui/index.html +++ b/ui/index.html @@ -60,7 +60,7 @@
    @@ -133,18 +133,18 @@ - Click to learn more about custom models + Click to learn more about custom models - Click to learn more about Clip Skip + Click to learn more about Clip Skip - Click to learn more about VAEs + Click to learn more about VAEs - Click to learn more about samplers + Click to learn more about samplers - Click to learn more about Seamless Tiling + Click to learn more about Seamless Tiling
    +
    +
    +
    +

    Image Modifiers

    + (drawing style, camera, etc.) +
    +
    + + + Add Custom Modifiers + + + + +
    +
    +
    +
    + + + Expand Categories + +
    +
    +
    +
    @@ -102,7 +132,7 @@
    - +
    @@ -293,29 +323,6 @@
    - -
    -

    - Image Modifiers (art styles, tags etc) - - - Add Custom Modifiers - - -

    -
    -
    - - -   - - -
    -
    -
    @@ -475,6 +482,16 @@

    Set your custom modifiers (one per line)

    Tip: You can include special characters like {} () [] and |. You can also put multiple comma-separated phrases in a single line, to make a single modifier that combines all of those.

    +
    + + +   + + +
    diff --git a/ui/media/css/main.css b/ui/media/css/main.css index 9df38f9a..af4c152e 100644 --- a/ui/media/css/main.css +++ b/ui/media/css/main.css @@ -211,10 +211,6 @@ code { #makeImage { border-radius: 6px; } -#editor-modifiers h5 { - padding: 5pt 0; - margin: 0; -} #makeImage { flex: 0 0 70px; background: var(--accent-color); @@ -284,14 +280,193 @@ button#resume { .collapsible:not(.active) ~ .collapsible-content { display: none !important; } +#image-modifier-dropdown { + margin-left: 1em; + position: relative; + cursor: pointer; +} +#image-modifier-dropdown[data-active="true"]::before { + content: "âž–"; +} +#image-modifier-dropdown[data-active="false"]::before { + content: "âž•"; +} #editor-modifiers { - overflow-y: auto; + max-width: 75vw; + min-width: 50vw; + max-height: fit-content; + overflow-y: hidden; overflow-x: hidden; + display: none; + background: var(--background-color1); + border: solid 1px var(--background-color3); + z-index: 999; + border-radius: 6px; + box-shadow: 0px 0px 30px black; + border: 2px solid rgb(255 255 255 / 10%); +} +#editor-modifiers.active { + display: flex; + flex-direction: column; + position: absolute; + left: 5vw; +} +.modifiers-maximized { + position: fixed !important; + top: 0 !important; + bottom: 0px !important; + left: 0px !important; + right: 0px !important; + max-width: unset !important; + max-height: unset !important; + border: 0px !important; + border-radius: 0px !important; +} +.modifiers-maximized #editor-modifiers-entries { + max-height: 100%; + flex: 1; +} +#editor-modifiers-header { + background-color: var(--background-color4); + padding: 0.5em; + border-bottom: 1px solid rgb(255 255 255 / 10%); + display: flex; + justify-content: space-between; + align-items: center; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} +#editor-modifiers-subheader { + background-color: var(--background-color4); + padding: 0.5em; + border-bottom: 1px solid rgb(255 255 255 / 10%); + display: flex; + align-items: center; + grid-gap: 0.8em; + flex-direction: row; + position: relative; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + transition: all 0.1s ease; +} +#editor-modifiers-subheader::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(255, 255, 255, 0.02); + opacity: 1; + pointer-events: none; +} +#modifiers-header-left { + display: flex; + flex-direction: column; + grid-gap: 0.1em; +} +#modifiers-header-left span { + font-size: 0.8em; + color: rgb(127 127 127); + font-weight: 200; +} +#modifiers-header-right { + display: flex; + align-items: center; + align-content: center; + justify-content: center; + grid-gap: 0.8em; + margin-right: 0.3em; +} + +#editor-modifiers-subheader i, +#modifiers-header-right i { + cursor: pointer; + margin: 0; + padding: 0; +} +#modifiers-header-right .section-button, +#editor-modifiers-subheader .section-button { + margin-top: 0.3em; +} +#modifiers-action-collapsibles-btn { + display: flex; + grid-gap: 0.5em; + cursor: pointer; +} +.modifiers-action-text { + font-size: 0.8em; + color: rgb(192 192 192); +} +#modifiers-expand-btn { + z-index: 2; +} +#modifiers-expand-btn .simple-tooltip { + background-color: var(--background-color3); + border-radius: 50px; +} +.modifier-category .collapsible { + position: relative; +} +.modifier-category .collapsible::after { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(255, 255, 255, 0.1); + opacity: 0; + transition: opacity 0.1s ease; + pointer-events: none; +} +.modifier-category:hover .collapsible::after { + opacity: 1; + pointer-events: none; +} +#editor-modifiers-entries { + overflow: auto scroll; + max-height: 50vh; + height: fit-content; + margin-bottom: 0.1em; + padding-left: 0px; +} +#editor-modifiers-entries .collapsible { + transition: opacity 0.1s ease; + padding-left: 0.5em; +} +#editor-modifiers-entries .modifier-category:nth-child(odd) .collapsible::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(255, 255, 255, 0.02); + opacity: 1; + pointer-events: none; } #editor-modifiers .editor-modifiers-leaf { padding-top: 10pt; padding-bottom: 10pt; } +#editor-modifiers h5 { + padding: 5pt 0; + margin: 0; + position: sticky; + top: -2px; + z-index: 10; + background-color: var(--background-color3); + border-bottom: 1px solid rgb(255 255 255 / 4%); + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} img { box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.15), 0 6px 20px 0 rgba(0, 0, 0, 0.15); } @@ -310,6 +485,9 @@ div.img-preview img { margin-top: 5pt; display: none; } +#editor-inputs-tags-list { + max-height: 30em; +} #server-status { position: absolute; right: 16px; @@ -779,7 +957,6 @@ input::file-selector-button { height: 19px; } - .input-toggle { display: inline-block; position: relative; @@ -1083,6 +1260,7 @@ input::file-selector-button { /* POPUPS */ .popup:not(.active) { visibility: hidden; + overflow-x: hidden; /* fix overflow from body */ opacity: 0; } diff --git a/ui/media/css/modifier-thumbnails.css b/ui/media/css/modifier-thumbnails.css index c6fb8107..02b91fce 100644 --- a/ui/media/css/modifier-thumbnails.css +++ b/ui/media/css/modifier-thumbnails.css @@ -1,14 +1,16 @@ .modifier-card { + position: relative; + box-sizing: content-box; /* fixes border misalignment */ box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2); transition: 0.1s; border-radius: 7px; margin: 3pt 3pt; float: left; - width: 8em; - height: 11.5em; + width: 6em; + height: 9.5em; display: grid; grid-template-columns: 1fr; - grid-template-rows: 8em 3.5em; + grid-template-rows: 6em 3.5em; gap: 0px 0px; grid-auto-flow: row; grid-template-areas: @@ -16,82 +18,71 @@ "modifier-card-container"; border: 2px solid rgba(255, 255, 255, .05); cursor: pointer; -} -.modifier-card-size_5 { - width: 18em; - grid-template-rows: 18em 3.5em; - height: 21.5em; -} -.modifier-card-size_5 .modifier-card-image-overlay { - font-size: 8em; -} -.modifier-card-size_4 { - width: 14em; - grid-template-rows: 14em 3.5em; - height: 17.5em; -} -.modifier-card-size_4 .modifier-card-image-overlay { - font-size: 7em; + z-index: 2; } .modifier-card-size_3 { - width: 11em; - grid-template-rows: 11em 3.5em; - height: 14.5em; -} -.modifier-card-size_3 .modifier-card-image-overlay { - font-size: 6em; -} -.modifier-card-size_2 { width: 10em; grid-template-rows: 10em 3.5em; height: 13.5em; } -.modifier-card-size_2 .modifier-card-image-overlay { +.modifier-card-size_3 .modifier-card-image-overlay { font-size: 6em; } -.modifier-card-size_1 { +.modifier-card-size_3 .modifier-card-label { + font-size: 1.2em; +} +.modifier-card-size_2 { width: 9em; grid-template-rows: 9em 3.5em; height: 12.5em; } -.modifier-card-size_1 .modifier-card-image-overlay { +.modifier-card-size_2 .modifier-card-image-overlay { font-size: 5em; } -.modifier-card-size_-1 { +.modifier-card-size_2 .modifier-card-label { + font-size: 1.1em; +} +.modifier-card-size_1 { width: 7em; grid-template-rows: 7em 3.5em; height: 10.5em; } -.modifier-card-size_-1 .modifier-card-image-overlay { +.modifier-card-size_1 .modifier-card-image-overlay { font-size: 4em; } -.modifier-card-size_-2 { - width: 6em; - grid-template-rows: 6em 3.5em; - height: 9.5em; -} -.modifier-card-size_-2 .modifier-card-image-overlay { - font-size: 3em; -} -.modifier-card-size_-3 { +.modifier-card-size_-1 { width: 5em; grid-template-rows: 5em 3.5em; height: 8.5em; } -.modifier-card-size_-3 .modifier-card-image-overlay { +.modifier-card-size_-1 .modifier-card-image-overlay { font-size: 3em; } -.modifier-card-size_-3 .modifier-card-label { - font-size: 0.8em; +.modifier-card-size_-1 .modifier-card-label { + font-size: 0.9em; +} +.modifier-card-size_-2 { + width: 4em; + grid-template-rows: 3.5em 3em; + height: 6.5em; +} +.modifier-card-size_-2 .modifier-card-image-overlay { + font-size: 2em; +} +.modifier-card-size_-2 .modifier-card-label { + font-size: 0.7em; } .modifier-card-tiny { - width: 6em; - height: 9.5em; - grid-template-rows: 6em 3.5em; + width: 5em; + grid-template-rows: 5em 3.5em; + height: 8.5em; } .modifier-card-tiny .modifier-card-image-overlay { font-size: 4em; } +.modifier-card-tiny .modifier-card-label { + font-size: 0.9em; +} .modifier-card:hover { transform: scale(1.05); box-shadow: 0 5px 16px 5px rgba(0, 0, 0, 0.25); @@ -115,6 +106,7 @@ } .modifier-card-image-container * { position: absolute; + text-align: center; } .modifier-card-container { text-align: center; @@ -131,6 +123,7 @@ .modifier-card-label { padding: 4px; word-break: break-word; + text-transform: capitalize; } .modifier-card-image-overlay { width: inherit; @@ -140,7 +133,7 @@ position: absolute; border-radius: 5px 5px 0 0; opacity: 0; - font-size: 5em; + font-size: 4em; font-weight: 900; color: rgb(255 255 255 / 50%); display: flex; @@ -153,9 +146,8 @@ position: absolute; z-index: 3; } -.modifier-card-overlay:hover ~ .modifier-card-container .modifier-card-label.tooltip .tooltip-text { - visibility: visible; - opacity: 1; +.modifier-card-active .modifier-card-overlay { + background-color: rgb(169 78 241 / 40%); } .modifier-card:hover > .modifier-card-image-container .modifier-card-image-overlay { opacity: 1; @@ -175,53 +167,24 @@ border: 2px solid rgb(179 82 255 / 94%); box-shadow: 0 0px 10px 0 rgb(170 0 229 / 58%); } -.tooltip { - position: relative; - display: inline-block; -} -.tooltip .tooltip-text { - visibility: hidden; - width: 120px; - background: rgb(101,97,181); - background: linear-gradient(180deg, rgba(101,97,181,1) 0%, rgba(47,45,85,1) 100%); - color: #fff; - text-align: center; - border-radius: 6px; - padding: 5px; - position: absolute; - z-index: 1; - top: 105%; - left: 39%; - margin-left: -60px; - opacity: 0; - transition: opacity 0.3s; - border: 2px solid rgb(90 100 177 / 94%); - box-shadow: 0px 10px 20px 5px rgb(11 0 58 / 55%); - width: 10em; -} -.tooltip .tooltip-text::after { - content: ""; - position: absolute; - top: -0.9em; - left: 50%; - margin-left: -5px; - border-width: 5px; - border-style: solid; - border-color: transparent transparent rgb(90 100 177 / 94%) transparent; -} -.tooltip:hover .tooltip-text { - visibility: visible; - opacity: 1; -} #modifier-card-size-slider { width: 6em; margin-bottom: 0.5em; vertical-align: middle; } -#modifier-settings-btn { - float: right; -} #modifier-settings-config textarea { width: 90%; height: 150px; } +.modifier-card .hidden { + display: none; +} +.support-long-label .modifier-card-overlay:hover ~ .modifier-card-container .modifier-card-label { + font-size: 0.7em; +} +.support-long-label .modifier-card-overlay:hover ~ .modifier-card-container .long-label { + display: block; +} +.support-long-label .modifier-card-overlay:hover ~ .modifier-card-container .regular-label { + display: none; +} \ No newline at end of file diff --git a/ui/media/js/image-modifiers.js b/ui/media/js/image-modifiers.js index 69f31ab1..5f3a9014 100644 --- a/ui/media/js/image-modifiers.js +++ b/ui/media/js/image-modifiers.js @@ -1,14 +1,21 @@ let activeTags = [] let modifiers = [] let customModifiersGroupElement = undefined -let customModifiersInitialContent +let customModifiersInitialContent = "" +let modifierPanelFreezed = false +let modifiersMainContainer = document.querySelector("#editor-modifiers") +let modifierDropdown = document.querySelector("#image-modifier-dropdown") +let editorModifiersContainer = document.querySelector("#editor-modifiers") let editorModifierEntries = document.querySelector("#editor-modifiers-entries") let editorModifierTagsList = document.querySelector("#editor-inputs-tags-list") let editorTagsContainer = document.querySelector("#editor-inputs-tags-container") let modifierCardSizeSlider = document.querySelector("#modifier-card-size-slider") let previewImageField = document.querySelector("#preview-image") let modifierSettingsBtn = document.querySelector("#modifier-settings-btn") +let modifiersContainerSizeBtn = document.querySelector("#modifiers-container-size-btn") +let modifiersCloseBtn = document.querySelector("#modifiers-close-button") +let modifiersCollapsiblesBtn = document.querySelector("#modifiers-action-collapsibles-btn") let modifierSettingsOverlay = document.querySelector("#modifier-settings-config") let customModifiersTextBox = document.querySelector("#custom-modifiers-input") let customModifierEntriesToolbar = document.querySelector("#editor-modifiers-entries-toolbar") @@ -18,31 +25,31 @@ const activeCardClass = "modifier-card-active" const CUSTOM_MODIFIERS_KEY = "customModifiers" function createModifierCard(name, previews, removeBy) { - const modifierCard = document.createElement("div") - let style = previewImageField.value - let styleIndex = style == "portrait" ? 0 : 1 + let cardPreviewImageType = previewImageField.value + const modifierCard = document.createElement("div") modifierCard.className = "modifier-card" modifierCard.innerHTML = `
    +
    -

    +

    No Image

    Modifier Image
    -

    +
    + +

    +
    ` const image = modifierCard.querySelector(".modifier-card-image") - const errorText = modifierCard.querySelector(".modifier-card-error-label") - const label = modifierCard.querySelector(".modifier-card-label") - - errorText.innerText = "No Image" + const longLabel = modifierCard.querySelector(".modifier-card-label span.long-label") + const regularLabel = modifierCard.querySelector(".modifier-card-label p.regular-label") if (typeof previews == "object") { - image.src = previews[styleIndex] // portrait - image.setAttribute("preview-type", style) + image.src = previews[cardPreviewImageType == "portrait" ? 0 : 1] // 0 index is portrait, 1 landscape + image.setAttribute("preview-type", cardPreviewImageType) } else { image.remove() } @@ -50,24 +57,32 @@ function createModifierCard(name, previews, removeBy) { const maxLabelLength = 30 const cardLabel = removeBy ? name.replace("by ", "") : name - if (cardLabel.length <= maxLabelLength) { - label.querySelector("p").innerText = cardLabel - } else { - const tooltipText = document.createElement("span") - tooltipText.className = "tooltip-text" - tooltipText.innerText = name - - label.classList.add("tooltip") - label.appendChild(tooltipText) - - label.querySelector("p").innerText = cardLabel.substring(0, maxLabelLength) + "..." + function getFormattedLabel(length) { + if (cardLabel?.length <= length) { + return cardLabel + } else { + return cardLabel.substring(0, length) + "..." + } } - label.querySelector("p").dataset.fullName = name // preserve the full name + modifierCard.dataset.fullName = name // preserve the full name + regularLabel.dataset.fullName = name // preserve the full name, legacy support for older plugins + + longLabel.innerText = getFormattedLabel(maxLabelLength * 2) + regularLabel.innerText = getFormattedLabel(maxLabelLength) + + if (cardLabel.length > maxLabelLength) { + modifierCard.classList.add("support-long-label") + + if (cardLabel.length > maxLabelLength * 2) { + modifierCard.title = `"${name}"` + } + } + return modifierCard } -function createModifierGroup(modifierGroup, initiallyExpanded, removeBy) { +function createModifierGroup(modifierGroup, isInitiallyOpen, removeBy) { const title = modifierGroup.category const modifiers = modifierGroup.modifiers @@ -78,8 +93,8 @@ function createModifierGroup(modifierGroup, initiallyExpanded, removeBy) { const modifiersEl = document.createElement("div") modifiersEl.classList.add("collapsible-content", "editor-modifiers-leaf") - if (initiallyExpanded === true) { - titleEl.className += " active" + if (isInitiallyOpen === true) { + titleEl.classList.add("active") } modifiers.forEach((modObj) => { @@ -126,7 +141,7 @@ function createModifierGroup(modifierGroup, initiallyExpanded, removeBy) { e.appendChild(titleEl) e.appendChild(modifiersEl) - editorModifierEntries.insertBefore(e, customModifierEntriesToolbar.nextSibling) + editorModifierEntries.prepend(e) return e } @@ -149,7 +164,10 @@ async function loadModifiers() { res.reverse() res.forEach((modifierGroup, idx) => { - createModifierGroup(modifierGroup, idx === res.length - 1, modifierGroup === "Artist" ? true : false) // only remove "By " for artists + const isInitiallyOpen = false // idx === res.length - 1 + const removeBy = modifierGroup === "Artist" ? true : false // only remove "By " for artists + + createModifierGroup(modifierGroup, isInitiallyOpen, removeBy) }) createCollapsibles(editorModifierEntries) @@ -169,7 +187,7 @@ function refreshModifiersState(newTags, inactiveTags) { .querySelector("#editor-modifiers") .querySelectorAll(".modifier-card") .forEach((modifierCard) => { - const modifierName = modifierCard.querySelector(".modifier-card-label p").dataset.fullName // pick the full modifier name + const modifierName = modifierCard.dataset.fullName // pick the full modifier name if (activeTags.map((x) => x.name).includes(modifierName)) { modifierCard.classList.remove(activeCardClass) modifierCard.querySelector(".modifier-card-image-overlay").innerText = "+" @@ -184,8 +202,9 @@ function refreshModifiersState(newTags, inactiveTags) { .querySelector("#editor-modifiers") .querySelectorAll(".modifier-card") .forEach((modifierCard) => { - const modifierName = modifierCard.querySelector(".modifier-card-label p").dataset.fullName + const modifierName = modifierCard.dataset.fullName const shortModifierName = modifierCard.querySelector(".modifier-card-label p").innerText + if (trimModifiers(tag) == trimModifiers(modifierName)) { // add modifier to active array if (!activeTags.map((x) => x.name).includes(tag)) { @@ -242,10 +261,10 @@ function refreshInactiveTags(inactiveTags) { } // update cards - let overlays = document.querySelector("#editor-inputs-tags-list").querySelectorAll(".modifier-card-overlay") + let overlays = editorModifierTagsList.querySelectorAll(".modifier-card-overlay") overlays.forEach((i) => { - let modifierName = i.parentElement.getElementsByClassName("modifier-card-label")[0].getElementsByTagName("p")[0] - .dataset.fullName + let modifierName = i.parentElement.dataset.fullName + if (inactiveTags?.find((element) => trimModifiers(element) === modifierName) !== undefined) { i.parentElement.classList.add("modifier-toggle-inactive") } @@ -262,6 +281,12 @@ function refreshTagsList(inactiveTags) { editorTagsContainer.style.display = "block" } + if(activeTags.length > 15) { + editorModifierTagsList.style["overflow-y"] = "auto" + } else { + editorModifierTagsList.style["overflow-y"] = "unset" + } + activeTags.forEach((tag, index) => { tag.element.querySelector(".modifier-card-image-overlay").innerText = "-" tag.element.classList.add("modifier-card-tiny") @@ -285,48 +310,42 @@ function refreshTagsList(inactiveTags) { let brk = document.createElement("br") brk.style.clear = "both" + editorModifierTagsList.appendChild(brk) + refreshInactiveTags(inactiveTags) + document.dispatchEvent(new Event("refreshImageModifiers")) // notify plugins that the image tags have been refreshed } function toggleCardState(modifierName, makeActive) { - document - .querySelector("#editor-modifiers") - .querySelectorAll(".modifier-card") - .forEach((card) => { - const name = card.querySelector(".modifier-card-label").innerText - if ( - trimModifiers(modifierName) == trimModifiers(name) || - trimModifiers(modifierName) == "by " + trimModifiers(name) - ) { - if (makeActive) { - card.classList.add(activeCardClass) - card.querySelector(".modifier-card-image-overlay").innerText = "-" - } else { - card.classList.remove(activeCardClass) - card.querySelector(".modifier-card-image-overlay").innerText = "+" - } - } - }) + const cards = [...document.querySelectorAll("#editor-modifiers .modifier-card")] + .filter(cardElem => trimModifiers(cardElem.dataset.fullName) == trimModifiers(modifierName)) + + const cardExists = typeof cards == "object" && cards?.length > 0 + + if (cardExists) { + const card = cards[0] + + if (makeActive) { + card.classList.add(activeCardClass) + card.querySelector(".modifier-card-image-overlay").innerText = "-" + } else { + card.classList.remove(activeCardClass) + card.querySelector(".modifier-card-image-overlay").innerText = "+" + } + } } function changePreviewImages(val) { const previewImages = document.querySelectorAll(".modifier-card-image-container img") - let previewArr = [] - - modifiers.map((x) => x.modifiers).forEach((x) => previewArr.push(...x.map((m) => m.previews))) - - previewArr = previewArr.map((x) => { - let obj = {} - - x.forEach((preview) => { + const previewArr = modifiers.flatMap((x) => x.modifiers.map((m) => m.previews)) + .map((x) => x.reduce((obj, preview) => { obj[preview.name] = preview.path - }) - return obj - }) + return obj + }, {})) previewImages.forEach((previewImage) => { const currentPreviewType = previewImage.getAttribute("preview-type") @@ -369,17 +388,70 @@ function resizeModifierCards(val) { }) } +function saveCustomModifiers() { + localStorage.setItem(CUSTOM_MODIFIERS_KEY, customModifiersTextBox.value.trim()) + + loadCustomModifiers() +} + +function loadCustomModifiers() { + PLUGINS["MODIFIERS_LOAD"].forEach((fn) => fn.loader.call()) +} + +function showModifierContainer() { + document.addEventListener("click", checkIfClickedOutsideDropdownElem) + + modifierDropdown.dataset.active = true + editorModifiersContainer.classList.add("active") +} + +function hideModifierContainer() { + document.removeEventListener("click", checkIfClickedOutsideDropdownElem) + + modifierDropdown.dataset.active = false + editorModifiersContainer.classList.remove("active") +} + +function checkIfClickedOutsideDropdownElem(e) { + const clickedElement = e.target + + const clickedInsideSpecificElems = [modifierDropdown, editorModifiersContainer, modifierSettingsOverlay].some((div) => + div && (div.contains(clickedElement) || div === clickedElement)) + + if (!clickedInsideSpecificElems && !modifierPanelFreezed) { + hideModifierContainer() + } +} + +function collapseAllModifierCategory() { + const collapsibleElems = editorModifierEntries.querySelectorAll(".modifier-category .collapsible"); // needs to have ";" + + [...collapsibleElems].forEach((elem) => { + const isActive = elem.classList.contains("active") + + if(isActive) { + elem?.click() + } + }) +} + +function expandAllModifierCategory() { + const collapsibleElems = editorModifierEntries.querySelectorAll(".modifier-category .collapsible"); // needs to have ";" + + [...collapsibleElems].forEach((elem) => { + const isActive = elem.classList.contains("active") + + if (!isActive) { + elem?.click() + } + }) +} + +customModifiersTextBox.addEventListener("change", saveCustomModifiers) + modifierCardSizeSlider.onchange = () => resizeModifierCards(modifierCardSizeSlider.value) previewImageField.onchange = () => changePreviewImages(previewImageField.value) -modifierSettingsBtn.addEventListener("click", function(e) { - modifierSettingsOverlay.classList.add("active") - customModifiersTextBox.setSelectionRange(0, 0) - customModifiersTextBox.focus() - customModifiersInitialContent = customModifiersTextBox.value // preserve the initial content - e.stopPropagation() -}) - modifierSettingsOverlay.addEventListener("keydown", function(e) { switch (e.key) { case "Escape": // Escape to cancel @@ -397,14 +469,93 @@ modifierSettingsOverlay.addEventListener("keydown", function(e) { } }) -function saveCustomModifiers() { - localStorage.setItem(CUSTOM_MODIFIERS_KEY, customModifiersTextBox.value.trim()) +modifierDropdown.addEventListener("click", e => { + const targetElem = e.target + const isDropdownActive = targetElem.dataset.active == "true" ? true : false - loadCustomModifiers() -} + if (!isDropdownActive) + showModifierContainer() + else + hideModifierContainer() +}) -function loadCustomModifiers() { - PLUGINS["MODIFIERS_LOAD"].forEach((fn) => fn.loader.call()) -} +let collapsiblesBtnState = false -customModifiersTextBox.addEventListener("change", saveCustomModifiers) +modifiersCollapsiblesBtn.addEventListener("click", (e) => { + const btnElem = modifiersCollapsiblesBtn + + const collapseText = "Collapse Categories" + const expandText = "Expand Categories" + + const collapseIconClasses = ["fa-solid", "fa-square-minus"] + const expandIconClasses = ["fa-solid", "fa-square-plus"] + + const iconElem = btnElem.querySelector(".modifiers-action-icon") + const textElem = btnElem.querySelector(".modifiers-action-text") + + if (collapsiblesBtnState) { + collapseAllModifierCategory() + + collapsiblesBtnState = false + + collapseIconClasses.forEach((c) => iconElem.classList.remove(c)) + expandIconClasses.forEach((c) => iconElem.classList.add(c)) + + textElem.innerText = expandText + } else { + expandAllModifierCategory() + + collapsiblesBtnState = true + + expandIconClasses.forEach((c) => iconElem.classList.remove(c)) + collapseIconClasses.forEach((c) => iconElem.classList.add(c)) + + textElem.innerText = collapseText + } +}) + +let containerSizeBtnState = false + +modifiersContainerSizeBtn.addEventListener("click", (e) => { + const btnElem = modifiersContainerSizeBtn + + const maximizeIconClasses = ["fa-solid", "fa-expand"] + const revertIconClasses = ["fa-solid", "fa-compress"] + + modifiersMainContainer.classList.toggle("modifiers-maximized") + + if(containerSizeBtnState) { + revertIconClasses.forEach((c) => btnElem.classList.remove(c)) + maximizeIconClasses.forEach((c) => btnElem.classList.add(c)) + + containerSizeBtnState = false + } else { + maximizeIconClasses.forEach((c) => btnElem.classList.remove(c)) + revertIconClasses.forEach((c) => btnElem.classList.add(c)) + + containerSizeBtnState = true + } +}) + +modifierSettingsBtn.addEventListener("click", (e) => { + modifierSettingsOverlay.classList.add("active") + customModifiersTextBox.setSelectionRange(0, 0) + customModifiersTextBox.focus() + customModifiersInitialContent = customModifiersTextBox.value // preserve the initial content + e.stopPropagation() +}) + +modifiersCloseBtn.addEventListener("click", (e) => { + hideModifierContainer() +}) + +// prevents the modifier panel closing at the same time as the settings overlay +new MutationObserver(() => { + const isActive = modifierSettingsOverlay.classList.contains("active") + + if (!isActive) { + modifierPanelFreezed = true + + setTimeout(() => modifierPanelFreezed = false, 25) + } +}).observe(modifierSettingsOverlay, { attributes: true }) \ No newline at end of file From 2a5b3040e2e999d1a2254e0a0e9997db8254c924 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Mon, 19 Jun 2023 19:58:17 +0530 Subject: [PATCH 273/291] sdkit 1.0.108 - potential fix for multi-gpu bug while rendering - the sampler instances weren't thread-local --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index c754ec32..e8140839 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.107", + "sdkit": "1.0.108", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", From 4bf78521cee48664c4b08a6aefdf47e2b5311f25 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Mon, 19 Jun 2023 19:58:59 +0530 Subject: [PATCH 274/291] changelog --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index b53ac141..49f056c7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -22,6 +22,7 @@ Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. ### Detailed changelog +* 2.5.41 - 19 Jun 2023 - Another fix for multi-gpu rendering (in all VRAM usage modes). * 2.5.41 - 13 Jun 2023 - Fix multi-gpu bug with "low" VRAM usage mode while generating images. * 2.5.41 - 12 Jun 2023 - Fix multi-gpu bug with CodeFormer. * 2.5.41 - 6 Jun 2023 - Allow changing the strength of CodeFormer, and slightly improved styling of the CodeFormer options. From 5b35c47360e18bfa6e4bfc5fc158f72c97e4182d Mon Sep 17 00:00:00 2001 From: JeLuF Date: Mon, 19 Jun 2023 21:50:56 +0200 Subject: [PATCH 275/291] Fix saving of network settings --- ui/media/js/parameters.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/media/js/parameters.js b/ui/media/js/parameters.js index 2f915eeb..27cfe6fc 100644 --- a/ui/media/js/parameters.js +++ b/ui/media/js/parameters.js @@ -649,6 +649,7 @@ async function getSystemInfo() { } saveSettingsBtn.addEventListener("click", function() { + console.log("listenPortField.value", listenPortField.value) if (listenPortField.value == "") { alert("The network port field must not be empty.") return @@ -664,7 +665,8 @@ saveSettingsBtn.addEventListener("click", function() { update_branch: updateBranch, } - Array.from(parametersTable.children).forEach((parameterRow) => { + //Array.from(parametersTable.children).forEach((parameterRow) => { + document.querySelectorAll('#system-settings [data-setting-id]').forEach((parameterRow) => { if (parameterRow.dataset.saveInAppConfig === "true") { const parameterElement = document.getElementById(parameterRow.dataset.settingId) || From 65bb01892f6286eb5b47dd58511f3f9ca0b9ecd5 Mon Sep 17 00:00:00 2001 From: JeLuF Date: Mon, 19 Jun 2023 21:58:58 +0200 Subject: [PATCH 276/291] remove old code --- ui/media/js/parameters.js | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/media/js/parameters.js b/ui/media/js/parameters.js index 27cfe6fc..50c6682b 100644 --- a/ui/media/js/parameters.js +++ b/ui/media/js/parameters.js @@ -665,7 +665,6 @@ saveSettingsBtn.addEventListener("click", function() { update_branch: updateBranch, } - //Array.from(parametersTable.children).forEach((parameterRow) => { document.querySelectorAll('#system-settings [data-setting-id]').forEach((parameterRow) => { if (parameterRow.dataset.saveInAppConfig === "true") { const parameterElement = From aac9acf068b384bf39b059898be3a563b0c6b4d0 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 20 Jun 2023 10:49:34 +0530 Subject: [PATCH 277/291] sdkit 1.0.109 - auto-set fp32 attention precision in diffusers if required --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index e8140839..fe7521b3 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.108", + "sdkit": "1.0.109", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", From a43bd2fd3b496c4c38c22e09f69b7035baf38722 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Tue, 20 Jun 2023 10:50:28 +0530 Subject: [PATCH 278/291] changelog --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 49f056c7..90f21ae2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -22,6 +22,7 @@ Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. ### Detailed changelog +* 2.5.41 - 20 Jun 2023 - Automatically fix black images if fp32 attention precision is required in diffusers. * 2.5.41 - 19 Jun 2023 - Another fix for multi-gpu rendering (in all VRAM usage modes). * 2.5.41 - 13 Jun 2023 - Fix multi-gpu bug with "low" VRAM usage mode while generating images. * 2.5.41 - 12 Jun 2023 - Fix multi-gpu bug with CodeFormer. From 7811929b5bf7d5b4860607b309b91630aa427681 Mon Sep 17 00:00:00 2001 From: JeLuF Date: Thu, 22 Jun 2023 01:15:07 +0200 Subject: [PATCH 279/291] Run dev console in ED directory --- scripts/Developer Console.cmd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/Developer Console.cmd b/scripts/Developer Console.cmd index 921a9dca..256cd682 100644 --- a/scripts/Developer Console.cmd +++ b/scripts/Developer Console.cmd @@ -2,6 +2,8 @@ echo "Opening Stable Diffusion UI - Developer Console.." & echo. +cd %cd% + set PATH=C:\Windows\System32;%PATH% @rem set legacy and new installer's PATH, if they exist From a5898aaf3b57170ac8c88b327ecf10673d7c9f22 Mon Sep 17 00:00:00 2001 From: JeLuF Date: Thu, 22 Jun 2023 23:54:45 +0200 Subject: [PATCH 280/291] Show COMSPEC variable in logs --- scripts/Developer Console.cmd | 2 ++ scripts/Start Stable Diffusion UI.cmd | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/Developer Console.cmd b/scripts/Developer Console.cmd index 921a9dca..232cb6a1 100644 --- a/scripts/Developer Console.cmd +++ b/scripts/Developer Console.cmd @@ -21,6 +21,8 @@ call git --version call where conda call conda --version +echo. +echo COMSPEC=%COMSPEC% echo. @rem activate the legacy environment (if present) and set PYTHONPATH diff --git a/scripts/Start Stable Diffusion UI.cmd b/scripts/Start Stable Diffusion UI.cmd index 4f8555ea..9a4a6303 100644 --- a/scripts/Start Stable Diffusion UI.cmd +++ b/scripts/Start Stable Diffusion UI.cmd @@ -36,8 +36,9 @@ call git --version call where conda call conda --version +echo . +echo COMSPEC=%COMSPEC% @rem Download the rest of the installer and UI call scripts\on_env_start.bat - @pause From d9bddffc426c850e4c10d836717c2baa5830c82a Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 23 Jun 2023 21:42:11 +0530 Subject: [PATCH 281/291] sdkit 1.0.110 - don't offload latent upscaler to the CPU if not running on a GPU --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index fe7521b3..c1c1febb 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.109", + "sdkit": "1.0.110", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", From eb301a67d4ce2673d0c1095512be1a8dcf5e6715 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Fri, 23 Jun 2023 21:43:36 +0530 Subject: [PATCH 282/291] changelog --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 90f21ae2..0d9640bb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -22,6 +22,7 @@ Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. ### Detailed changelog +* 2.5.41 - 23 Jun 2023 - Fix a regression where latent upscaler stopped working on PCs without a graphics card. * 2.5.41 - 20 Jun 2023 - Automatically fix black images if fp32 attention precision is required in diffusers. * 2.5.41 - 19 Jun 2023 - Another fix for multi-gpu rendering (in all VRAM usage modes). * 2.5.41 - 13 Jun 2023 - Fix multi-gpu bug with "low" VRAM usage mode while generating images. From 4dd1a46efa55533646f16c3f1d989b88defab810 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Sat, 24 Jun 2023 15:21:13 +0530 Subject: [PATCH 283/291] sdkit 1.0.111 - don't apply a negative lora when testing a newly loaded SD model --- scripts/check_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index c1c1febb..a339ed46 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.110", + "sdkit": "1.0.111", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", From 887d871d26eb93155ffb3f173e10acd671cf4ee4 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Sat, 24 Jun 2023 15:22:09 +0530 Subject: [PATCH 284/291] changelog --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 0d9640bb..f147ad0c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -22,6 +22,7 @@ Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. ### Detailed changelog +* 2.5.41 - 24 Jun 2023 - (beta-only) Fix a recent regression where the LoRA would not get applied when changing SD models. * 2.5.41 - 23 Jun 2023 - Fix a regression where latent upscaler stopped working on PCs without a graphics card. * 2.5.41 - 20 Jun 2023 - Automatically fix black images if fp32 attention precision is required in diffusers. * 2.5.41 - 19 Jun 2023 - Another fix for multi-gpu rendering (in all VRAM usage modes). From c74be07c33a883be9532cb5ff0e5f1e33d161ace Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Sat, 24 Jun 2023 15:46:03 +0530 Subject: [PATCH 285/291] sdkit 1.0.112 - fix broken inpainting in low vram mode --- CHANGES.md | 1 + scripts/check_modules.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index f147ad0c..66fae99b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -22,6 +22,7 @@ Our focus continues to remain on an easy installation experience, and an easy user-interface. While still remaining pretty powerful, in terms of features and speed. ### Detailed changelog +* 2.5.41 - 24 Jun 2023 - (beta-only) Fix broken inpainting in low VRAM usage mode. * 2.5.41 - 24 Jun 2023 - (beta-only) Fix a recent regression where the LoRA would not get applied when changing SD models. * 2.5.41 - 23 Jun 2023 - Fix a regression where latent upscaler stopped working on PCs without a graphics card. * 2.5.41 - 20 Jun 2023 - Automatically fix black images if fp32 attention precision is required in diffusers. diff --git a/scripts/check_modules.py b/scripts/check_modules.py index a339ed46..4cbf261f 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -18,7 +18,7 @@ os_name = platform.system() modules_to_check = { "torch": ("1.11.0", "1.13.1", "2.0.0"), "torchvision": ("0.12.0", "0.14.1", "0.15.1"), - "sdkit": "1.0.111", + "sdkit": "1.0.112", "stable-diffusion-sdkit": "2.1.4", "rich": "12.6.0", "uvicorn": "0.19.0", From 881fdc58ec7646d0a1ee76a3f64d002859db3aa0 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Mon, 26 Jun 2023 15:34:25 +0530 Subject: [PATCH 286/291] debug logging --- ui/media/js/parameters.js | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/media/js/parameters.js b/ui/media/js/parameters.js index 50c6682b..cd86be63 100644 --- a/ui/media/js/parameters.js +++ b/ui/media/js/parameters.js @@ -649,7 +649,6 @@ async function getSystemInfo() { } saveSettingsBtn.addEventListener("click", function() { - console.log("listenPortField.value", listenPortField.value) if (listenPortField.value == "") { alert("The network port field must not be empty.") return From c9a5ad9c3a25bc1578f3e920475cef4c053d3bfc Mon Sep 17 00:00:00 2001 From: JeLuF Date: Mon, 26 Jun 2023 12:20:34 +0200 Subject: [PATCH 287/291] Update Developer Console.cmd --- scripts/Developer Console.cmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/Developer Console.cmd b/scripts/Developer Console.cmd index 256cd682..3a91725a 100644 --- a/scripts/Developer Console.cmd +++ b/scripts/Developer Console.cmd @@ -2,7 +2,7 @@ echo "Opening Stable Diffusion UI - Developer Console.." & echo. -cd %cd% +cd /d %~dp0 set PATH=C:\Windows\System32;%PATH% From e1e2a2a249ed414d5e83c4db019b48e2e507204c Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Mon, 26 Jun 2023 16:00:55 +0530 Subject: [PATCH 288/291] Update index.html --- ui/index.html | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/index.html b/ui/index.html index a78a6f06..6f771e62 100644 --- a/ui/index.html +++ b/ui/index.html @@ -450,7 +450,6 @@
  • Some custom inpainting models don't work
  • These samplers don't work yet: Unipc SNR, Unipc TQ, Unipc SNR2, DPM++ 2s Ancestral, DPM++ SDE, DPM Fast, DPM Adaptive, DPM2
  • Hypernetwork doesn't work
  • -
  • Multi GPU - cuda:1 and cuda:0 conflict
  • The time remaining in browser differs from the one in the console
  • From 848ff35e85736466200b5119baead5f05685cbd3 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Mon, 26 Jun 2023 16:04:18 +0530 Subject: [PATCH 289/291] Update index.html --- ui/index.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/index.html b/ui/index.html index 15b44daf..ffe91f37 100644 --- a/ui/index.html +++ b/ui/index.html @@ -461,7 +461,8 @@
  • More choices for img2img samplers
  • -
  • Support for inpainting models
  • +
  • Support for official inpainting models
  • +
  • Generate images that tile seamlessly
  • Clip Skip support allows to skip the last CLIP layer (recommended by some LORA models)
  • New samplers: DDPM and DEIS
  • Memory optimizations that allow the use of 2GB GPUs
  • From df416a6a177044450d28e1e0941dd7087583bc23 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Mon, 26 Jun 2023 16:11:10 +0530 Subject: [PATCH 290/291] link --- ui/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/index.html b/ui/index.html index ffe91f37..555cb691 100644 --- a/ui/index.html +++ b/ui/index.html @@ -438,7 +438,7 @@

    Diffusers Tech Preview

    The Diffusers Tech Preview allows early access to the new features based on Diffusers.

    -

    The Preview is under active development. It is experimental! It does still have bugs and missing features!

    +

    This is under active development, and is missing a few features. It is experimental! Please report any bugs to the #beta channel in our Discord server!

    New upcoming features in our new engine

    • LORA support - Place LORA files in the models/lora folder.
    • From f6bd05bcf1895dcf1b83e0493c1bedb945ed391f Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Mon, 26 Jun 2023 16:35:31 +0530 Subject: [PATCH 291/291] Link to ED-hosted community repo, to provide a stable and continuous home incase a community owner is no longer active --- ui/media/js/plugins.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/media/js/plugins.js b/ui/media/js/plugins.js index ef9c9f33..fb3e8b07 100644 --- a/ui/media/js/plugins.js +++ b/ui/media/js/plugins.js @@ -1,7 +1,7 @@ const PLUGIN_API_VERSION = "1.0" -const PLUGIN_CATALOG = 'https://raw.githubusercontent.com/patriceac/Easy-Diffusion-Plugins/main/plugins.json' -const PLUGIN_CATALOG_GITHUB = 'https://github.com/patriceac/Easy-Diffusion-Plugins/blob/main/plugins.json' +const PLUGIN_CATALOG = 'https://raw.githubusercontent.com/easydiffusion/easydiffusion-plugins/main/plugins.json' +const PLUGIN_CATALOG_GITHUB = 'https://github.com/easydiffusion/easydiffusion-plugins/blob/main/plugins.json' const PLUGINS = { /**