diff --git a/.eslintrc.json b/.eslintrc.json
index c8902f517..520fad0b9 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -32,12 +32,15 @@
"no-useless-escape":"off",
"object-curly-newline":"off",
"prefer-rest-params":"off",
+ "prefer-destructuring":"off",
"radix":"off"
},
"globals": {
// asssets
"panzoom": "readonly",
//script.js
+ "log": "readonly",
+ "debug": "readonly",
"gradioApp": "readonly",
"executeCallbacks": "readonly",
"onAfterUiUpdate": "readonly",
@@ -65,7 +68,9 @@
"create_submit_args": "readonly",
"restart_reload": "readonly",
"updateInput": "readonly",
- //extraNetworks.js
+ // settings.js
+ "register_drag_drop": "readonly",
+ //extraNetworks.js
"requestGet": "readonly",
"popup": "readonly",
// from python
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b1a53c3df..a16e00faa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,10 +1,9 @@
# Change Log for SD.Next
-## Update for 2023-08-26
+## Update for 2023-08-29
- general:
- all system and image paths are now relative by default
- - fix extra networks previews
- add settings validation when performing load/save
- settings tab in ui now shows settings that are changed from default values
- settings tab switch to compact view
@@ -20,6 +19,10 @@
enable with `--use-openvino`
thanks @disty0
- add model **precompile** option (when model compile is enbled)
+ - **extra network** folder info caching
+ results in much faster startup when you have large number of extra networks
+ - fix extra networks previews
+ - fix gradio gallery
## Update for 2023-08-20
diff --git a/README.md b/README.md
index 46031e29c..88d65530c 100644
--- a/README.md
+++ b/README.md
@@ -119,7 +119,6 @@ Below is partial list of all available parameters, run `webui --help` for the fu
SD.Next comes with several extensions pre-installed:
-- [Dynamic Thresholding](https://github.com/mcmonkeyprojects/sd-dynamic-thresholding)
- [ControlNet](https://github.com/Mikubill/sd-webui-controlnet)
- [Agent Scheduler](https://github.com/ArtVentureX/sd-webui-agent-scheduler)
- [Multi-Diffusion Tiled Diffusion and VAE](https://github.com/pkuliyi2015/multidiffusion-upscaler-for-automatic1111)
diff --git a/javascript/black-orange.css b/javascript/black-orange.css
index 21334f6fb..bccb966c6 100644
--- a/javascript/black-orange.css
+++ b/javascript/black-orange.css
@@ -103,7 +103,6 @@ svg.feather.feather-image, .feather .feather-image { display: none }
#txt2img_cfg_scale { min-width: 200px; }
#txt2img_checkboxes, #img2img_checkboxes { background-color: transparent; }
#txt2img_checkboxes, #img2img_checkboxes { margin-bottom: 0.2em; }
-#txt2img_gallery, #img2img_gallery, #extras_gallery { padding: 0; margin: 0; object-fit: contain; box-shadow: none; min-height: 0; }
#txt2img_actions_column, #img2img_actions_column { flex-flow: wrap; justify-content: space-between; }
#txt2img_enqueue_wrapper, #img2img_enqueue_wrapper { min-width: unset; width: 48%; }
#txt2img_generate_box, #img2img_generate_box { min-width: unset; width: 48%; }
diff --git a/javascript/contextMenus.js b/javascript/contextMenus.js
index 968ef0824..5aaeba3cd 100644
--- a/javascript/contextMenus.js
+++ b/javascript/contextMenus.js
@@ -62,9 +62,9 @@ const contextMenuInit = () => {
});
}
- function addContextMenuEventListener() {
+ async function addContextMenuEventListener() {
if (eventListenerApplied) return;
- console.log('initContextMenu');
+ log('initContextMenu');
gradioApp().addEventListener('click', (e) => {
if (!e.isTrusted) return;
const oldMenu = gradioApp().querySelector('#context-menu');
diff --git a/javascript/extensions.js b/javascript/extensions.js
index 318cd3f03..1e56a9a36 100644
--- a/javascript/extensions.js
+++ b/javascript/extensions.js
@@ -6,7 +6,7 @@ function extensions_apply(extensions_disabled_list, extensions_update_list, disa
if (x.name.startsWith('update_') && x.checked) update.push(x.name.substring(7));
});
restart_reload();
- console.log('Extensions apply:', { disable, update });
+ log('Extensions apply:', { disable, update });
return [JSON.stringify(disable), JSON.stringify(update), disable_all];
}
@@ -16,7 +16,7 @@ function extensions_check(info, extensions_disabled_list, search_text, sort_colu
if (x.name.startsWith('enable_') && !x.checked) disable.push(x.name.substring(7));
});
const id = randomId();
- console.log('Extensions check:', { disable });
+ log('Extensions check:', { disable });
return [id, JSON.stringify(disable), search_text, sort_column];
}
@@ -27,7 +27,7 @@ function install_extension(button, url) {
const textarea = gradioApp().querySelector('#extension_to_install textarea');
textarea.value = url;
updateInput(textarea);
- console.log('Extension install:', { url });
+ log('Extension install:', { url });
gradioApp().querySelector('#install_extension_button').click();
}
@@ -38,7 +38,7 @@ function uninstall_extension(button, url) {
const textarea = gradioApp().querySelector('#extension_to_install textarea');
textarea.value = url;
updateInput(textarea);
- console.log('Extension uninstall:', { url });
+ log('Extension uninstall:', { url });
gradioApp().querySelector('#uninstall_extension_button').click();
}
@@ -48,6 +48,6 @@ function update_extension(button, url) {
const textarea = gradioApp().querySelector('#extension_to_install textarea');
textarea.value = url;
updateInput(textarea);
- console.log('Extension update:', { url });
+ log('Extension update:', { url });
gradioApp().querySelector('#update_extension_button').click();
}
diff --git a/javascript/generationParams.js b/javascript/generationParams.js
index 6a5c8b524..93cd07bd0 100644
--- a/javascript/generationParams.js
+++ b/javascript/generationParams.js
@@ -4,7 +4,7 @@ function attachGalleryListeners(tab_name) {
const gallery = gradioApp().querySelector(`#${tab_name}_gallery`);
if (!gallery) return null;
gallery.addEventListener('click', () => setTimeout(() => {
- console.log('galleryItemSelected:', tab_name);
+ log('galleryItemSelected:', tab_name);
gradioApp().getElementById(`${tab_name}_generation_info_button`)?.click();
}, 500));
gallery?.addEventListener('keydown', (e) => {
@@ -36,7 +36,7 @@ function initiGenerationParams() {
if (txt2img_gallery && img2img_gallery) generationParamsInitialized = true;
else return;
modalObserver.observe(modal, { attributes: true, attributeFilter: ['style'] });
- console.log('initGenerationParams');
+ log('initGenerationParams');
}
onAfterUiUpdate(initiGenerationParams);
diff --git a/javascript/imageParams.js b/javascript/imageParams.js
index 3ecac0ee5..c93523988 100644
--- a/javascript/imageParams.js
+++ b/javascript/imageParams.js
@@ -3,7 +3,7 @@ let dragDropInitialized = false;
async function initDragDrop() {
if (dragDropInitialized) return;
dragDropInitialized = true;
- console.log('initDragDrop');
+ log('initDragDrop');
window.addEventListener('drop', (e) => {
const target = e.composedPath()[0];
if (!target.placeholder) return;
@@ -18,7 +18,7 @@ async function initDragDrop() {
if (fileInput) {
fileInput.files = files;
fileInput.dispatchEvent(new Event('change'));
- console.log('dropEvent');
+ log('dropEvent');
}
});
}
diff --git a/javascript/imageViewer.js b/javascript/imageViewer.js
index 7ef6d140f..3d2ac2675 100644
--- a/javascript/imageViewer.js
+++ b/javascript/imageViewer.js
@@ -134,7 +134,7 @@ function galleryClickEventHandler(event) {
}
}
-function initImageViewer() {
+async function initImageViewer() {
// Each tab has its own gradio-gallery
const galleryPreviews = gradioApp().querySelectorAll('.gradio-gallery > div.preview');
if (galleryPreviews.length > 0) {
@@ -241,7 +241,7 @@ function initImageViewer() {
modalControls.appendChild(modalClose);
gradioApp().appendChild(modal);
- console.log('initImageViewer');
+ log('initImageViewer');
}
onAfterUiUpdate(initImageViewer);
diff --git a/javascript/loader.js b/javascript/loader.js
index c72a1926c..a280f3cf5 100644
--- a/javascript/loader.js
+++ b/javascript/loader.js
@@ -1,3 +1,5 @@
+const appStartTime = performance.now();
+
async function preloadImages() {
const dark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
const imagePromises = [];
@@ -24,7 +26,7 @@ async function preloadImages() {
async function createSplash() {
const dark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
- console.log('createSplash', { theme: dark ? 'dark' : 'light' });
+ log('createSplash', { theme: dark ? 'dark' : 'light' });
const num = Math.floor(11 * Math.random());
const splash = `
@@ -38,8 +40,8 @@ async function createSplash() {
async function removeSplash() {
const splash = document.getElementById('splash');
if (splash) splash.remove();
- console.log('removeSplash');
- console.log('startupTime', Math.round(performance.now() - appStartTime) / 1000);
+ log('removeSplash');
+ log('startupTime', Math.round(performance.now() - appStartTime) / 1000);
}
window.onload = createSplash;
diff --git a/javascript/logMonitor.js b/javascript/logMonitor.js
index 0bc6051f2..e9b87fa26 100644
--- a/javascript/logMonitor.js
+++ b/javascript/logMonitor.js
@@ -52,7 +52,7 @@ async function initLogMonitor() {
`;
el.style.display = 'none';
logMonitor();
- console.log('initLogMonitor');
+ log('initLogMonitor');
}
onAfterUiUpdate(initLogMonitor);
diff --git a/javascript/notification.js b/javascript/notification.js
index 738698824..e78452b04 100644
--- a/javascript/notification.js
+++ b/javascript/notification.js
@@ -26,7 +26,7 @@ function initNotifications() {
parent.focus();
this.close();
};
- console.log('sendNotification');
+ log('sendNotification');
}
onAfterUiUpdate(initNotifications);
diff --git a/javascript/panZoom.js b/javascript/panZoom.js
index fec42c597..66dd3e92a 100644
--- a/javascript/panZoom.js
+++ b/javascript/panZoom.js
@@ -1017,7 +1017,7 @@ function autoRun() {
return;
}
var options = collectOptions(panzoomScript);
- console.log(options);
+ log(options);
window[globalName] = createPanZoom(el, options);
}
diff --git a/javascript/progressBar.js b/javascript/progressBar.js
index 6e4b74a3c..1305099d6 100644
--- a/javascript/progressBar.js
+++ b/javascript/progressBar.js
@@ -103,7 +103,7 @@ function requestProgress(id_task, progressEl, galleryEl, atEnd = null, onProgres
};
const done = () => {
- console.debug('taskEnd:', id_task);
+ debug('taskEnd:', id_task);
localStorage.removeItem('task');
setProgress();
if (parentGallery && livePreview) parentGallery.removeChild(livePreview);
diff --git a/javascript/script.js b/javascript/script.js
index 339eedc9f..6ad77aa26 100644
--- a/javascript/script.js
+++ b/javascript/script.js
@@ -1,4 +1,14 @@
-const appStartTime = performance.now();
+const log = (...msg) => {
+ const dt = new Date();
+ const ts = `${dt.getHours().toString().padStart(2, '0')}:${dt.getMinutes().toString().padStart(2, '0')}:${dt.getSeconds().toString().padStart(2, '0')}.${dt.getMilliseconds().toString().padStart(3, '0')}`;
+ console.log(ts, ...msg); // eslint-disable-line no-console
+};
+
+const debug = (...msg) => {
+ const dt = new Date();
+ const ts = `${dt.getHours().toString().padStart(2, '0')}:${dt.getMinutes().toString().padStart(2, '0')}:${dt.getSeconds().toString().padStart(2, '0')}.${dt.getMilliseconds().toString().padStart(3, '0')}`;
+ console.debug(ts, ...msg); // eslint-disable-line no-console
+};
function gradioApp() {
const elems = document.getElementsByTagName('gradio-app');
diff --git a/javascript/setHints.js b/javascript/setHints.js
index 7b572a425..d8439ace3 100644
--- a/javascript/setHints.js
+++ b/javascript/setHints.js
@@ -31,19 +31,19 @@ async function tooltipHide(e) {
async function validateHints(elements, data) {
let original = elements.map((e) => e.textContent.toLowerCase().trim()).sort((a, b) => a > b);
original = [...new Set(original)];
- console.log('all hints:', original);
- console.log('hints-differences', { elements: original.length, hints: data.length });
+ log('all hints:', original);
+ log('hints-differences', { elements: original.length, hints: data.length });
const current = data.map((e) => e.label.toLowerCase().trim()).sort((a, b) => a > b);
let missing = [];
for (let i = 0; i < original.length; i++) {
if (!current.includes(original[i])) missing.push(original[i]);
}
- console.log('missing in locale:', missing);
+ log('missing in locale:', missing);
missing = [];
for (let i = 0; i < current.length; i++) {
if (!original.includes(current[i])) missing.push(current[i]);
}
- console.log('in locale but not ui:', missing);
+ log('in locale but not ui:', missing);
}
async function setHints() {
@@ -84,7 +84,7 @@ async function setHints() {
}
}
const t1 = performance.now();
- console.log('setHints', { type: localeData.type, elements: elements.length, localized, hints, data: localeData.data.length, time: t1 - t0 });
+ log('setHints', { type: localeData.type, elements: elements.length, localized, hints, data: localeData.data.length, time: t1 - t0 });
removeSplash();
// validateHints(elements, localeData.data)
}
diff --git a/javascript/settings.js b/javascript/settings.js
index 33411dc0d..0fe0c0bee 100644
--- a/javascript/settings.js
+++ b/javascript/settings.js
@@ -1,10 +1,9 @@
+let opts_metadata = {};
+const opts_tabs = {};
+
const monitoredOpts = [
{ sd_model_checkpoint: null },
- {
- sd_backend: () => {
- gradioApp().getElementById('refresh_sd_model_checkpoint')?.click();
- },
- },
+ { sd_backend: () => gradioApp().getElementById('refresh_sd_model_checkpoint')?.click() },
];
function updateOpts(json_string) {
@@ -13,7 +12,7 @@ function updateOpts(json_string) {
const key = Object.keys(op)[0];
const callback = op[key];
if (opts[key] && opts[key] !== settings_data.values[key]) {
- console.log('updateOpts', key, opts[key], settings_data.values[key]);
+ log('updateOpts', key, opts[key], settings_data.values[key]);
if (callback) callback();
}
}
@@ -31,9 +30,7 @@ function updateOpts(json_string) {
function showAllSettings() {
// Try to ensure that the show all settings tab is opened by clicking on its tab button
const tab_dirty_indicator = gradioApp().getElementById('modification_indicator_show_all_pages');
- if (tab_dirty_indicator && tab_dirty_indicator.nextSibling) {
- tab_dirty_indicator.nextSibling.click();
- }
+ if (tab_dirty_indicator && tab_dirty_indicator.nextSibling) tab_dirty_indicator.nextSibling.click();
gradioApp().querySelectorAll('#settings > .tab-content > .tabitem').forEach((elem) => {
if (elem.id === 'settings_tab_licenses' || elem.id === 'settings_show_all_pages') return;
elem.style.display = 'block';
@@ -42,17 +39,16 @@ function showAllSettings() {
function markIfModified(setting_name, value) {
const elem = gradioApp().getElementById(`modification_indicator_${setting_name}`);
- if (elem == null) return;
- // Use JSON.stringify to compare nested objects (e.g. arrays for checkbox-groups)
- const previous_value_json = JSON.stringify(opts[setting_name]);
- const changed_value = JSON.stringify(value) !== previous_value_json;
- if (changed_value) elem.title = `click to revert to previous value: ${previous_value_json}`;
-
- const is_unsaved = opts_metadata[setting_name].is_stored;
- if (is_unsaved) elem.title = 'custom value';
- elem.disabled = !(is_unsaved || changed_value);
+ if (!elem) return;
+ const previous_value = JSON.stringify(opts[setting_name]);
+ const current_value = JSON.stringify(value);
+ const changed_value = previous_value !== current_value;
+ if (changed_value) elem.title = `click to revert to previous value: ${previous_value}`;
+ const is_stored = opts_metadata[setting_name].is_stored;
+ if (is_stored) elem.title = 'custom value';
+ elem.disabled = !changed_value && !is_stored;
elem.classList.toggle('changed', changed_value);
- elem.classList.toggle('unsaved', is_unsaved);
+ elem.classList.toggle('saved', is_stored);
const { tab_name } = opts_metadata[setting_name];
if (!opts_tabs[tab_name].changed) opts_tabs[tab_name].changed = new Set();
@@ -67,7 +63,7 @@ function markIfModified(setting_name, value) {
tab_nav_indicator.disabled = (changed_items.size === 0) && (unsaved.size === 0);
tab_nav_indicator.title = '';
tab_nav_indicator.classList.toggle('changed', changed_items.size > 0);
- tab_nav_indicator.classList.toggle('unsaved', saved.size > 0);
+ tab_nav_indicator.classList.toggle('saved', saved.size > 0);
if (changed_items.size > 0) tab_nav_indicator.title += `click to reset ${changed_items.size} unapplied changes in this tab\n`;
if (saved.size > 0) tab_nav_indicator.title += `${saved.size} custom values\n${unsaved.size} default values}`;
}
diff --git a/javascript/style.css b/javascript/style.css
index 622ed2c19..b867d7d8b 100644
--- a/javascript/style.css
+++ b/javascript/style.css
@@ -67,8 +67,6 @@ button.custom-button{
.performance { font-size: 0.85em; color: #444; }
.performance p { display: inline-block; color: var(--body-text-color-subdued) !important }
.performance .time { margin-right: 0; }
-@media screen and (min-width: 2500px) { #txt2img_gallery, #img2img_gallery { min-height: 768px; } }
-#txt2img_gallery img, #img2img_gallery img, #extras_gallery img { object-fit: scale-down; width: -webkit-fill-available !important; }
#txt2img_footer, #img2img_footer, #extras_footer { height: fit-content; }
#txt2img_footer, #img2img_footer { height: fit-content; display: none; }
#txt2img_generate_box, #img2img_generate_box { gap: 0.5em; flex-wrap: wrap-reverse; height: fit-content; }
@@ -133,7 +131,7 @@ div#extras_scale_to_tab div.form{ flex-direction: row; }
#settings .dirtyable.hidden { display: none; }
#settings .modification-indicator { height: 1.2em; border-radius: 1em !important; padding: 0; width: 0; margin-right: 0.5em; }
#settings .modification-indicator:disabled { visibility: hidden; }
-#settings .modification-indicator.unsaved { background: var(--color-accent-soft); width: 4px; }
+#settings .modification-indicator.saved { background: var(--color-accent-soft); width: 4px; }
#settings .modification-indicator.changed { background: var(--color-accent); width: 4px; }
#settings .modification-indicator.changed.unsaved { background-image: linear-gradient(var(--color-accent) 25%, var(--color-accent-soft) 75%); width: 4px; }
#settings_result { margin: 0 1.2em; }
diff --git a/javascript/ui.js b/javascript/ui.js
index 5100125e9..c455765ba 100644
--- a/javascript/ui.js
+++ b/javascript/ui.js
@@ -7,8 +7,6 @@ let img2img_textarea;
const wait_time = 800;
const token_timeouts = {};
let uiLoaded = false;
-let opts_metadata = {};
-const opts_tabs = {};
function rememberGallerySelection(name) {
// dummy
@@ -27,7 +25,7 @@ function update_token_counter(button_id) {
function clip_gallery_urls(gallery) {
const files = gallery.map((v) => v.data);
navigator.clipboard.writeText(JSON.stringify(files)).then(
- () => console.log('clipboard:', files),
+ () => log('clipboard:', files),
(err) => console.error('clipboard:', files, err),
);
}
@@ -146,7 +144,7 @@ function clearGallery(tabname) {
}
function submit(...args) {
- console.log('submitTxt');
+ log('submitTxt');
clearGallery('txt2img');
const id = randomId();
requestProgress(id, null, gradioApp().getElementById('txt2img_gallery'));
@@ -156,7 +154,7 @@ function submit(...args) {
}
function submit_img2img(...args) {
- console.log('submitImg');
+ log('submitImg');
clearGallery('img2img');
const id = randomId();
requestProgress(id, null, gradioApp().getElementById('img2img_gallery'));
@@ -167,7 +165,7 @@ function submit_img2img(...args) {
}
function submit_postprocessing(...args) {
- console.log('SubmitExtras');
+ log('SubmitExtras');
clearGallery('extras');
return args;
}
@@ -227,7 +225,7 @@ function register_drag_drop() {
evt.preventDefault();
evt.dataTransfer.dropEffect = 'copy';
for (const f of evt.dataTransfer.files) {
- console.log('QuickSettingsDrop', f);
+ log('QuickSettingsDrop', f);
}
});
}
@@ -259,13 +257,13 @@ function sortUIElements() {
const scriptsImg = gradioApp().getElementById('scripts_alwayson_img2img').children;
for (const el of Array.from(scriptsImg)) el.style.order = find(el, tabsOrder);
- console.log('sortUIElements');
+ log('sortUIElements');
}
onAfterUiUpdate(async () => {
let promptsInitialized = false;
- function registerTextarea(id, id_counter, id_button) {
+ async function registerTextarea(id, id_counter, id_button) {
const prompt = gradioApp().getElementById(id);
if (!prompt) return;
const counter = gradioApp().getElementById(id_counter);
@@ -275,7 +273,7 @@ onAfterUiUpdate(async () => {
prompt.parentElement.style.position = 'relative';
promptTokecountUpdateFuncs[id] = () => { update_token_counter(id_button); };
localTextarea.addEventListener('input', promptTokecountUpdateFuncs[id]);
- if (!promptsInitialized) console.log('initPrompts')
+ if (!promptsInitialized) log('initPrompts');
promptsInitialized = true;
}
@@ -378,16 +376,15 @@ async function preview_theme() {
}
}
-function reconnectUI() {
+async function reconnectUI() {
+ const gallery = gradioApp().getElementById('txt2img_gallery');
+ if (!gallery) return;
+ const task_id = localStorage.getItem('task');
const api_logo = Array.from(gradioApp().querySelectorAll('img')).filter((el) => el?.src?.endsWith('api-logo.svg'));
if (api_logo.length > 0) api_logo[0].remove();
-
- const gallery = gradioApp().getElementById('txt2img_gallery');
- const task_id = localStorage.getItem('task');
- if (!gallery) return;
clearInterval(start_check); // eslint-disable-line no-use-before-define
if (task_id) {
- console.debug('task check:', task_id);
+ debug('task check:', task_id);
requestProgress(task_id, null, gallery, null, null, true);
}
uiLoaded = true;
@@ -411,7 +408,7 @@ function reconnectUI() {
};
const sd_model_observer = new MutationObserver(sd_model_callback);
sd_model_observer.observe(sd_model, { attributes: true, childList: true, subtree: true });
- console.log('reconnectUI');
+ log('reconnectUI');
}
-const start_check = setInterval(reconnectUI, 50);
+const start_check = setInterval(reconnectUI, 100);
diff --git a/modules/images.py b/modules/images.py
index b7332a808..335c55a12 100644
--- a/modules/images.py
+++ b/modules/images.py
@@ -551,9 +551,6 @@ def save_image(image, path, basename, seed=None, prompt=None, extension='jpg', i
file_decoration = shared.opts.samples_filename_pattern
else:
file_decoration = "[seq]-[prompt_words]"
- # add_number = shared.opts.save_images_add_number or file_decoration == ''
- # if file_decoration != "" and add_number:
- # file_decoration = f"-{file_decoration}"
file_decoration = namegen.apply(file_decoration) + suffix
if shared.opts.save_images_add_number:
if '[seq]' not in file_decoration:
@@ -578,10 +575,7 @@ def save_image(image, path, basename, seed=None, prompt=None, extension='jpg', i
params = script_callbacks.ImageSaveParams(image, p, fullfn, pnginfo)
script_callbacks.before_image_saved_callback(params)
exifinfo = params.pnginfo.get('UserComment', '')
- if len(exifinfo) > 0:
- exifinfo = exifinfo + ', ' + params.pnginfo.get(pnginfo_section_name, '')
- else:
- exifinfo = params.pnginfo.get(pnginfo_section_name, '')
+ exifinfo = (exifinfo + ', ' if len(exifinfo) > 0 else '') + params.pnginfo.get(pnginfo_section_name, '')
filename, extension = os.path.splitext(params.filename)
if hasattr(os, 'statvfs'):
max_name_len = os.statvfs(path).f_namemax
@@ -591,7 +585,6 @@ def save_image(image, path, basename, seed=None, prompt=None, extension='jpg', i
save_queue.put((params.image, filename, extension, params, exifinfo, txt_fullfn)) # actual save is executed in a thread that polls data from queue
save_queue.join()
- # atomically_save_image(params.image, filename, extension, params, exifinfo, txt_fullfn)
params.image.already_saved_as = params.filename
script_callbacks.image_saved_callback(params)
diff --git a/modules/processing.py b/modules/processing.py
index a12168031..b20051031 100644
--- a/modules/processing.py
+++ b/modules/processing.py
@@ -498,7 +498,8 @@ def create_infotext(p: StableDiffusionProcessing, all_prompts, all_seeds, all_su
generation_params.update(p.extra_generation_params)
generation_params_text = ", ".join([k if k == v else f'{k}: {generation_parameters_copypaste.quote(v)}' for k, v in generation_params.items() if v is not None])
negative_prompt_text = f"\nNegative prompt: {all_negative_prompts[index]}" if all_negative_prompts[index] else ""
- return f"{all_prompts[index]}{negative_prompt_text}\n{generation_params_text}".strip()
+ infotext = f"{all_prompts[index]}{negative_prompt_text}\n{generation_params_text}".strip()
+ return infotext
"""
@@ -776,19 +777,19 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
p.ops.append('color')
image = apply_color_correction(p.color_corrections[i], image)
image = apply_overlay(image, p.paste_to, i, p.overlay_images)
- if shared.opts.samples_save and not p.do_not_save_samples:
- images.save_image(image, p.outpath_samples, "", p.seeds[i], p.prompts[i], shared.opts.samples_format, info=infotext(i), p=p)
text = infotext(i)
infotexts.append(text)
image.info["parameters"] = text
output_images.append(image)
+ if shared.opts.samples_save and not p.do_not_save_samples:
+ images.save_image(image, p.outpath_samples, "", p.seeds[i], p.prompts[i], shared.opts.samples_format, info=text, p=p)
if hasattr(p, 'mask_for_overlay') and p.mask_for_overlay and any([shared.opts.save_mask, shared.opts.save_mask_composite, shared.opts.return_mask, shared.opts.return_mask_composite]):
image_mask = p.mask_for_overlay.convert('RGB')
image_mask_composite = Image.composite(image.convert('RGBA').convert('RGBa'), Image.new('RGBa', image.size), images.resize_image(3, p.mask_for_overlay, image.width, image.height).convert('L')).convert('RGBA')
if shared.opts.save_mask:
- images.save_image(image_mask, p.outpath_samples, "", p.seeds[i], p.prompts[i], shared.opts.samples_format, info=infotext(i), p=p, suffix="-mask")
+ images.save_image(image_mask, p.outpath_samples, "", p.seeds[i], p.prompts[i], shared.opts.samples_format, info=text, p=p, suffix="-mask")
if shared.opts.save_mask_composite:
- images.save_image(image_mask_composite, p.outpath_samples, "", p.seeds[i], p.prompts[i], shared.opts.samples_format, info=infotext(i), p=p, suffix="-mask-composite")
+ images.save_image(image_mask_composite, p.outpath_samples, "", p.seeds[i], p.prompts[i], shared.opts.samples_format, info=text, p=p, suffix="-mask-composite")
if shared.opts.return_mask:
output_images.append(image_mask)
if shared.opts.return_mask_composite:
diff --git a/modules/sd_models.py b/modules/sd_models.py
index 83d8748cc..fca1b9d6c 100644
--- a/modules/sd_models.py
+++ b/modules/sd_models.py
@@ -215,13 +215,13 @@ def model_hash(filename):
try:
with open(filename, "rb") as file:
import hashlib
- t0 = time.time()
+ # t0 = time.time()
m = hashlib.sha256()
file.seek(0x100000)
m.update(file.read(0x10000))
shorthash = m.hexdigest()[0:8]
- t1 = time.time()
- shared.log.debug(f'Calculating short hash: {filename} hash={shorthash} time={(t1-t0):.2f}')
+ # t1 = time.time()
+ # shared.log.debug(f'Calculating short hash: {filename} hash={shorthash} time={(t1-t0):.2f}')
return shorthash
except FileNotFoundError:
return 'NOFILE'
diff --git a/modules/shared.py b/modules/shared.py
index 75f7d2822..c602d372d 100644
--- a/modules/shared.py
+++ b/modules/shared.py
@@ -280,7 +280,7 @@ def disable_extensions():
else:
opts.data['disabled_extensions'] = [x for x in opts.disabled_extensions if x != 'Lora']
if backend == Backend.DIFFUSERS:
- for ext in ['sd-webui-controlnet', 'sd-dynamic-thresholding', 'multidiffusion-upscaler-for-automatic1111', 'a1111-sd-webui-lycoris']:
+ for ext in ['sd-webui-controlnet', 'multidiffusion-upscaler-for-automatic1111', 'a1111-sd-webui-lycoris']:
if ext not in opts.disabled_extensions:
log.warning(f'Diffusers disabling uncompatible extension: {ext}')
opts.data['disabled_extensions'].append(ext)
@@ -385,7 +385,7 @@ options_templates.update(options_section(('cuda', "Compute Settings"), {
"upcast_sampling": OptionInfo(True if sys.platform == "darwin" else False, "Enable upcast sampling"),
"upcast_attn": OptionInfo(False, "Enable upcast cross attention layer"),
"cuda_cast_unet": OptionInfo(False, "Use fixed UNet precision"),
- "disable_nan_check": OptionInfo(True, "Disable NaN check in produced images/latent spaces"),
+ "disable_nan_check": OptionInfo(True, "Disable NaN check in produced images/latent spaces", gr.Checkbox, {"visible": False}),
"rollback_vae": OptionInfo(False, "Attempt VAE roll back when produced NaN values (experimental)"),
"opt_channelslast": OptionInfo(False, "Use channels last as torch memory format "),
"cudnn_benchmark": OptionInfo(False, "Enable full-depth cuDNN benchmark feature"),
@@ -399,7 +399,7 @@ options_templates.update(options_section(('cuda', "Compute Settings"), {
"cuda_compile_verbose": OptionInfo(False, "Model compile verbose mode"),
"cuda_compile_errors": OptionInfo(True, "Model compile suppress errors"),
"ipex_optimize": OptionInfo(True if devices.backend == "ipex" else False, "Enable IPEX Optimize for Intel GPUs"),
- "directml_memory_provider": OptionInfo(default_memory_provider, '[DirectML] Memory stats provider', gr.Dropdown, lambda: {"choices": memory_providers}),
+ "directml_memory_provider": OptionInfo(default_memory_provider, 'DirectML memory stats provider', gr.Dropdown, lambda: {"choices": memory_providers}),
}))
options_templates.update(options_section(('diffusers', "Diffusers Settings"), {
diff --git a/modules/ui_common.py b/modules/ui_common.py
index 0f1ea52e3..6ef5e8800 100644
--- a/modules/ui_common.py
+++ b/modules/ui_common.py
@@ -82,8 +82,8 @@ def save_files(js_data, images, html_info, index):
self.prompt = getattr(self, 'prompt', None) or getattr(self, 'Prompt', None)
self.all_seeds = getattr(self, 'all_seeds', [self.seed])
self.all_prompts = getattr(self, 'all_prompts', [self.prompt])
- self.infotext = html_info
self.infotexts = getattr(self, 'infotexts', [html_info])
+ self.infotext = self.infotexts[0] if len(self.infotexts) > 0 else html_info
self.index_of_first_image = getattr(self, 'index_of_first_image', 0)
try:
data = json.loads(js_data)
@@ -164,7 +164,8 @@ def create_output_panel(tabname, outdir):
with gr.Column(variant='panel', elem_id=f"{tabname}_results"):
with gr.Group(elem_id=f"{tabname}_gallery_container"):
- result_gallery = gr.Gallery(value=[], label='Output', show_label=False, elem_id=f"{tabname}_gallery", elem_classes="logo").style(preview=False, container=False, columns=[1,2,3,4,5,6]) # <576px, <768px, <992px, <1200px, <1400px, >1400px
+ # columns are for <576px, <768px, <992px, <1200px, <1400px, >1400px
+ result_gallery = gr.Gallery(value=[], label='Output', show_label=False, show_download_button=True, elem_id=f"{tabname}_gallery", container=False, preview=True, columns=[1,2,3,4,5,6], object_fit='scale-down')
with gr.Column(elem_id=f"{tabname}_footer", elem_classes="gallery_footer"):
with gr.Row(elem_id=f"image_buttons_{tabname}", elem_classes="image-buttons"):
diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py
index 0d51fdc73..bbf789112 100644
--- a/modules/ui_extra_networks.py
+++ b/modules/ui_extra_networks.py
@@ -1,4 +1,5 @@
import re
+import time
import json
import html
import os.path
@@ -14,10 +15,23 @@ from modules.ui_components import ToolButton
extra_pages = []
allowed_dirs = set()
+dir_cache = {}
refresh_symbol = '\U0001f504' # 🔄
close_symbol = '\U0000274C' # ❌
+
+def listdir(path):
+ if path in dir_cache and os.path.getmtime(path) == dir_cache[path][0]:
+ return dir_cache[path][1]
+ else:
+ dir_cache[path] = (
+ os.path.getmtime(path),
+ [os.path.join(path, f) for f in os.listdir(path)]
+ )
+ return dir_cache[path][1]
+
+
def register_page(page):
"""registers extra networks page for the UI; recommend doing it in on_before_ui() callback for extensions"""
extra_pages.append(page)
@@ -125,7 +139,7 @@ class ExtraNetworksPage:
return ""
def is_empty(self, folder):
- for f in os.listdir(folder):
+ for f in listdir(folder):
_fn, ext = os.path.splitext(f)
if ext.lower() in ['.ckpt', '.safetensors', '.pt'] or os.path.isdir(os.path.join(folder, f)):
return False
@@ -154,6 +168,7 @@ class ExtraNetworksPage:
self.missing_thumbs.clear()
def create_html(self, tabname, skip = False):
+ t0 = time.time()
self_name_id = self.name.replace(" ", "_")
if skip:
return f""
@@ -199,7 +214,8 @@ class ExtraNetworksPage:
res = f""
else:
return ''
- shared.log.debug(f'Extra networks: {self.name} items={len(self.items)} subdirs={len(subdirs)}')
+ t1 = time.time()
+ shared.log.debug(f'Extra networks: {self.name} items={len(self.items)} subdirs={len(subdirs)} time={round(t1-t0, 2)}')
threading.Thread(target=self.create_thumb).start()
return res
except Exception as e:
@@ -240,7 +256,6 @@ class ExtraNetworksPage:
args['title'] += f'\nAlias: {item["alias"]}'
if item.get("tags", None) is not None:
args['title'] += f'\nTags: {", ".join(tags)}'
- #self.card.format(**args)
return self.card.format(**args)
except Exception as e:
shared.log.error(f'Extra networks item error: page={tabname} item={item["name"]} {e}')
@@ -248,16 +263,20 @@ class ExtraNetworksPage:
def find_preview(self, path):
preview_extensions = ["jpg", "jpeg", "png", "webp", "tiff", "jp2"]
+ files = listdir(os.path.dirname(path))
for file in [f'{path}{mid}{ext}' for ext in preview_extensions for mid in ['.thumb.', '.preview.', '.']]:
- if os.path.exists(file):
+ # if os.path.exists(file):
+ if file in files:
if '.thumb.' not in file:
self.missing_thumbs.append(file)
return self.link_preview(file)
return self.link_preview('html/card-no-preview.png')
def find_description(self, path):
+ files = listdir(os.path.dirname(path))
for file in [f"{path}.txt", f"{path}.description.txt"]:
- if os.path.exists(file):
+ # if os.path.exists(file):
+ if file in files:
try:
with open(file, "r", encoding="utf-8", errors="replace") as f:
txt = f.read()
@@ -269,8 +288,10 @@ class ExtraNetworksPage:
def find_info(self, path):
basename, _ext = os.path.splitext(path)
+ files = listdir(os.path.dirname(path))
for file in [f"{path}.info", f"{path}.civitai.info", f"{basename}.info", f"{basename}.civitai.info"]:
- if os.path.exists(file):
+ # if os.path.exists(file):
+ if file in files:
try:
with open(file, "r", encoding="utf-8", errors="replace") as f:
txt = f.read()
@@ -278,8 +299,7 @@ class ExtraNetworksPage:
return txt
except OSError:
pass
- return None
-
+ return None
def initialize():