diff --git a/CHANGELOG.md b/CHANGELOG.md index cbd29840d..9733dfa2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log for SD.Next -## Update for 2024-11-24 +## Update for 2024-11-26 - [Flux Tools](https://blackforestlabs.ai/flux-1-tools/): **Redux** is actually a tool, **Fill** is inpaint/outpaint optimized version of *Flux-dev* @@ -29,7 +29,7 @@ - Sampler improvements - update DPM FlowMatch samplers - UI: - - browser->server logging + - browser->server logging framework - Fixes: - update `diffusers` - fix README links @@ -37,6 +37,9 @@ - relax settings validator - improve js progress calls resiliency - fix text-to-video pipeline + - avoid live-preview if vae-decode is running + - allow xyz-grid with multi-axis s&r + - fix xyz-grid with lora ## Update for 2024-11-21 diff --git a/javascript/logger.js b/javascript/logger.js index 5aa8face3..1677fa537 100644 --- a/javascript/logger.js +++ b/javascript/logger.js @@ -1,5 +1,3 @@ -const serverTimeout = 5000; - const log = async (...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')}`; @@ -19,15 +17,15 @@ const error = async (...msg) => { 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')}`; if (window.logger) window.logger.innerHTML += window.logPrettyPrint(...msg); console.error(ts, ...msg); // eslint-disable-line no-console - const txt = msg.join(' '); - if (!txt.includes('asctime') && !txt.includes('xhr.')) xhrPost('/sdapi/v1/log', { error: txt }); // eslint-disable-line no-use-before-define + // const txt = msg.join(' '); + // if (!txt.includes('asctime') && !txt.includes('xhr.')) xhrPost('/sdapi/v1/log', { error: txt }); // eslint-disable-line no-use-before-define }; -const xhrInternal = (xhrObj, data, handler = undefined, errorHandler = undefined, ignore = false) => { +const xhrInternal = (xhrObj, data, handler = undefined, errorHandler = undefined, ignore = false, serverTimeout = 5000) => { const err = (msg) => { if (!ignore) { error(`${msg}: state=${xhrObj.readyState} status=${xhrObj.status} response=${xhrObj.responseText}`); - if (errorHandler) errorHandler(); + if (errorHandler) errorHandler(xhrObj); } }; @@ -54,15 +52,15 @@ const xhrInternal = (xhrObj, data, handler = undefined, errorHandler = undefined xhrObj.send(req); }; -const xhrGet = (url, data, handler = undefined, errorHandler = undefined, ignore = false) => { +const xhrGet = (url, data, handler = undefined, errorHandler = undefined, ignore = false, serverTimeout = 5000) => { const xhr = new XMLHttpRequest(); const args = Object.keys(data).map((k) => `${encodeURIComponent(k)}=${encodeURIComponent(data[k])}`).join('&'); xhr.open('GET', `${url}?${args}`, true); - xhrInternal(xhr, data, handler, errorHandler, ignore); + xhrInternal(xhr, data, handler, errorHandler, ignore, serverTimeout); }; -function xhrPost(url, data, handler = undefined, errorHandler = undefined, ignore = false) { +function xhrPost(url, data, handler = undefined, errorHandler = undefined, ignore = false, serverTimeout = 5000) { const xhr = new XMLHttpRequest(); xhr.open('POST', url, true); - xhrInternal(xhr, data, handler, errorHandler, ignore); + xhrInternal(xhr, data, handler, errorHandler, ignore, serverTimeout); } diff --git a/javascript/notification.js b/javascript/notification.js index 33e8d1c55..c702c90e7 100644 --- a/javascript/notification.js +++ b/javascript/notification.js @@ -4,28 +4,32 @@ let lastHeadImg = null; let notificationButton = null; async function sendNotification() { - if (!notificationButton) { - notificationButton = gradioApp().getElementById('request_notifications'); - if (notificationButton) notificationButton.addEventListener('click', (evt) => Notification.requestPermission(), true); + try { + if (!notificationButton) { + notificationButton = gradioApp().getElementById('request_notifications'); + if (notificationButton) notificationButton.addEventListener('click', (evt) => Notification.requestPermission(), true); + } + if (document.hasFocus()) return; // window is in focus so don't send notifications + let galleryPreviews = gradioApp().querySelectorAll('div[id^="tab_"][style*="display: block"] div[id$="_results"] .thumbnail-item > img'); + if (!galleryPreviews || galleryPreviews.length === 0) galleryPreviews = gradioApp().querySelectorAll('.thumbnail-item > img'); + if (!galleryPreviews || galleryPreviews.length === 0) return; + const headImg = galleryPreviews[0]?.src; + if (!headImg || headImg === lastHeadImg || headImg.includes('logo-bg-')) return; + const audioNotification = gradioApp().querySelector('#audio_notification audio'); + if (audioNotification) audioNotification.play(); + lastHeadImg = headImg; + const imgs = new Set(Array.from(galleryPreviews).map((img) => img.src)); // Multiple copies of the images are in the DOM when one is selected + const notification = new Notification('SD.Next', { + body: `Generated ${imgs.size > 1 ? imgs.size - opts.return_grid : 1} image${imgs.size > 1 ? 's' : ''}`, + icon: headImg, + image: headImg, + }); + notification.onclick = () => { + parent.focus(); + this.close(); + }; + log('sendNotifications'); + } catch (e) { + error(`sendNotification: ${e}`); } - if (document.hasFocus()) return; // window is in focus so don't send notifications - let galleryPreviews = gradioApp().querySelectorAll('div[id^="tab_"][style*="display: block"] div[id$="_results"] .thumbnail-item > img'); - if (!galleryPreviews || galleryPreviews.length === 0) galleryPreviews = gradioApp().querySelectorAll('.thumbnail-item > img'); - if (!galleryPreviews || galleryPreviews.length === 0) return; - const headImg = galleryPreviews[0]?.src; - if (!headImg || headImg === lastHeadImg || headImg.includes('logo-bg-')) return; - const audioNotification = gradioApp().querySelector('#audio_notification audio'); - if (audioNotification) audioNotification.play(); - lastHeadImg = headImg; - const imgs = new Set(Array.from(galleryPreviews).map((img) => img.src)); // Multiple copies of the images are in the DOM when one is selected - const notification = new Notification('SD.Next', { - body: `Generated ${imgs.size > 1 ? imgs.size - opts.return_grid : 1} image${imgs.size > 1 ? 's' : ''}`, - icon: headImg, - image: headImg, - }); - notification.onclick = () => { - parent.focus(); - this.close(); - }; - log('sendNotifications'); } diff --git a/javascript/progressBar.js b/javascript/progressBar.js index 9d897bc87..52f666b3a 100644 --- a/javascript/progressBar.js +++ b/javascript/progressBar.js @@ -12,8 +12,10 @@ function formatTime(secs) { function checkPaused(state) { lastState.paused = state ? !state : !lastState.paused; - document.getElementById('txt2img_pause').innerText = lastState.paused ? 'Resume' : 'Pause'; - document.getElementById('img2img_pause').innerText = lastState.paused ? 'Resume' : 'Pause'; + const t_el = document.getElementById('txt2img_pause'); + const i_el = document.getElementById('img2img_pause'); + if (t_el) t_el.innerText = lastState.paused ? 'Resume' : 'Pause'; + if (i_el) i_el.innerText = lastState.paused ? 'Resume' : 'Pause'; } function setProgress(res) { @@ -87,7 +89,9 @@ function requestProgress(id_task, progressEl, galleryEl, atEnd = null, onProgres debug('taskEnd:', id_task); localStorage.removeItem('task'); setProgress(); - if (parentGallery && livePreview) parentGallery.removeChild(livePreview); + try { + if (parentGallery && livePreview) parentGallery.removeChild(livePreview); + } catch { /* ignore */ } checkPaused(true); sendNotification(); if (atEnd) atEnd(); @@ -118,7 +122,7 @@ function requestProgress(id_task, progressEl, galleryEl, atEnd = null, onProgres done(); }; - xhrPost('./internal/progress', { id_task, id_live_preview }, onProgressHandler, onProgressErrorHandler); + xhrPost('./internal/progress', { id_task, id_live_preview }, onProgressHandler, onProgressErrorHandler, false, 5000); }; start(id_task, 0); } diff --git a/modules/shared_state.py b/modules/shared_state.py index 7def42b8c..51d33f9ed 100644 --- a/modules/shared_state.py +++ b/modules/shared_state.py @@ -28,6 +28,9 @@ class State: oom = False debug_output = os.environ.get('SD_STATE_DEBUG', None) + def __str__(self) -> str: + return f'State: job={self.job} {self.job_no}/{self.job_count} step={self.sampling_step}/{self.sampling_steps} skipped={self.skipped} interrupted={self.interrupted} paused={self.paused} info={self.textinfo}' + def skip(self): log.debug('Requested skip') self.skipped = True @@ -135,6 +138,8 @@ class State: modules.devices.torch_gc() def set_current_image(self): + if self.job == 'VAE': # avoid generating preview while vae is running + return from modules.shared import opts, cmd_opts """sets self.current_image from self.current_latent if enough sampling steps have been made after the last call to this""" if cmd_opts.lowvram or self.api: