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"
Extra network page not ready
Click refresh to try again
" @@ -199,7 +214,8 @@ class ExtraNetworksPage: res = f"
{subdirs_html}
{self.html}
" 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():