mirror of
https://github.com/vladmandic/automatic
synced 2026-09-19 01:04:32 +02:00
Merge branch 'dev' of https://github.com/vladmandic/sdnext into dev
This commit is contained in:
+1
-1
@@ -118,7 +118,7 @@
|
||||
"idbDel": "readonly",
|
||||
"idbAdd": "readonly",
|
||||
"idbCount": "readonly",
|
||||
"idbClean": "readonly",
|
||||
"idbFolderCleanup": "readonly",
|
||||
"initChangelog": "readonly",
|
||||
"sendNotification": "readonly",
|
||||
"monitorConnection": "readonly"
|
||||
|
||||
@@ -93,6 +93,24 @@ TODO: *Prioritize*!
|
||||
- [asyncio.run](https://docs.python.org/3.14/library/asyncio-runner.html#asyncio.run)
|
||||
- [asyncio.Runner](https://docs.python.org/3.14/library/asyncio-runner.html#asyncio.Runner)
|
||||
|
||||
### Shutil
|
||||
|
||||
#### rmtree
|
||||
|
||||
- `onerror` deprecated and replaced with `onexc` in **Python 3.12**
|
||||
``` python
|
||||
def excRemoveReadonly(func, path, exc: BaseException):
|
||||
import stat
|
||||
shared.log.debug(f'Exception during cleanup: {func} {path} {type(exc).__name__}')
|
||||
if func in (os.rmdir, os.remove, os.unlink) and isinstance(exc, PermissionError):
|
||||
shared.log.debug(f'Retrying cleanup: {path}')
|
||||
os.chmod(path, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
|
||||
func(path)
|
||||
# ...
|
||||
try:
|
||||
shutil.rmtree(found.path, ignore_errors=False, onexc=excRemoveReadonly)
|
||||
```
|
||||
|
||||
## Code TODO
|
||||
|
||||
> npm run todo
|
||||
|
||||
@@ -1392,6 +1392,7 @@ def set_environment():
|
||||
os.environ.setdefault('UVICORN_TIMEOUT_KEEP_ALIVE', '60')
|
||||
os.environ.setdefault('RUNAI_STREAMER_CHUNK_BYTESIZE', '2097152')
|
||||
os.environ.setdefault('RUNAI_STREAMER_MEMORY_LIMIT', '-1')
|
||||
os.environ.setdefault('RUNAI_STREAMER_LOG_LEVEL', 'DEBUG' if os.environ.get('SD_LOAD_DEBUG') else 'WARNING')
|
||||
allocator = f'garbage_collection_threshold:{opts.get("torch_gc_threshold", 80)/100:0.2f},max_split_size_mb:512'
|
||||
if opts.get("torch_malloc", "native") == 'cudaMallocAsync':
|
||||
allocator += ',backend:cudaMallocAsync'
|
||||
|
||||
+192
-80
@@ -1,4 +1,5 @@
|
||||
/* eslint-disable max-classes-per-file */
|
||||
/* eslint lines-between-class-members: ["error", "always", { "exceptAfterSingleLine": true }] */
|
||||
let ws;
|
||||
let url;
|
||||
let currentImage;
|
||||
@@ -6,9 +7,10 @@ let pruneImagesTimer;
|
||||
let outstanding = 0;
|
||||
let lastSort = 0;
|
||||
let lastSortName = 'None';
|
||||
let idbIsCleaning = false;
|
||||
let activeGalleryFolder = '';
|
||||
const galleryHashes = new Set();
|
||||
let maintenanceController = new AbortController();
|
||||
const folderStylesheet = new CSSStyleSheet();
|
||||
const fileStylesheet = new CSSStyleSheet();
|
||||
// Store separator states for the session
|
||||
const separatorStates = new Map();
|
||||
const el = {
|
||||
@@ -21,8 +23,103 @@ const el = {
|
||||
|
||||
const SUPPORTED_EXTENSIONS = ['jpg', 'jpeg', 'png', 'webp', 'tiff', 'jp2', 'jxl', 'gif', 'mp4', 'mkv', 'avi', 'mjpeg', 'mpg', 'avr'];
|
||||
|
||||
async function awaitForIDB(num = 0) {
|
||||
while (outstanding > num || idbIsCleaning) await new Promise((resolve) => setTimeout(resolve, 50)); // eslint-disable-line no-promise-executor-return
|
||||
async function awaitForIDB(num = 0, signal = null) {
|
||||
const timeout = AbortSignal.timeout(180000); // Failsafe to ensure no memory leaks
|
||||
const combinedSignals = signal ? AbortSignal.any([timeout, signal]) : timeout;
|
||||
while (outstanding > num && !combinedSignals.aborted) await new Promise((resolve) => { setTimeout(resolve, 50); });
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for gallery to finish populating
|
||||
* @param {number} expectedSize - Expected gallery size
|
||||
* @param {AbortSignal} signal - AbortController signal
|
||||
*/
|
||||
async function awaitForGallery(expectedSize, signal) {
|
||||
const timeout = AbortSignal.timeout(180000); // Failsafe to ensure no memory leaks
|
||||
const combinedSignals = AbortSignal.any([timeout, signal]);
|
||||
while (galleryHashes.size < expectedSize && !combinedSignals.aborted) await new Promise((resolve) => { setTimeout(resolve, 500); }); // longer interval because it's a low priority check
|
||||
}
|
||||
|
||||
function updateGalleryStyles() {
|
||||
if (opts.theme_type?.toLowerCase() === 'modern') {
|
||||
folderStylesheet.replaceSync(`
|
||||
.gallery-folder { cursor: pointer; padding: 8px 6px 8px 6px; background-color: var(--sd-button-normal-color); border-radius: var(--sd-border-radius); text-align: left; min-width: 12em;}
|
||||
.gallery-folder:hover { background-color: var(--button-primary-background-fill-hover); }
|
||||
.gallery-folder-selected { background-color: var(--sd-button-selected-color); color: var(--sd-button-selected-text-color); }
|
||||
.gallery-folder-icon { font-size: 1.2em; color: var(--sd-button-icon-color); margin-right: 1em; filter: drop-shadow(1px 1px 2px black); float: left; }
|
||||
`);
|
||||
} else {
|
||||
folderStylesheet.replaceSync(`
|
||||
.gallery-folder { cursor: pointer; padding: 8px 6px 8px 6px; }
|
||||
.gallery-folder:hover { background-color: var(--button-primary-background-fill-hover); }
|
||||
.gallery-folder-selected { background-color: var(--button-primary-background-fill); }
|
||||
`);
|
||||
}
|
||||
fileStylesheet.replaceSync(`
|
||||
.gallery-file {
|
||||
object-fit: contain;
|
||||
cursor: pointer;
|
||||
height: ${opts.extra_networks_card_size}px;
|
||||
width: ${opts.browser_fixed_width ? `${opts.extra_networks_card_size}px` : 'unset'};
|
||||
}
|
||||
.gallery-file:hover {
|
||||
filter: grayscale(100%);
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
// Classes
|
||||
|
||||
/* This isn't as robust as the Web Locks API, but it will at least work if accessing a remote machine without HTTPS */
|
||||
class SimpleFunctionQueue {
|
||||
#id;
|
||||
#running;
|
||||
#queue;
|
||||
|
||||
constructor(id) {
|
||||
this.#id = id;
|
||||
this.#running = false;
|
||||
this.#queue = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* signal: AbortSignal,
|
||||
* callback: Function
|
||||
* }} config
|
||||
*/
|
||||
enqueue(config) {
|
||||
if (!(config.signal instanceof AbortSignal) || typeof config.callback !== 'function') {
|
||||
throw new Error('Invalid configuration. Object must contain an AbortSignal and a function');
|
||||
}
|
||||
if (config.signal.aborted) {
|
||||
debug(`${this.#id} Queue: Skipping addition to queue due to "${config.signal.reason}"`);
|
||||
return;
|
||||
}
|
||||
this.#queue.push(config);
|
||||
this.#tryRunNext();
|
||||
}
|
||||
|
||||
async #tryRunNext() {
|
||||
if (this.#running || !this.#queue.length) return;
|
||||
try {
|
||||
const { signal, callback } = this.#queue.shift();
|
||||
if (signal.aborted) {
|
||||
return;
|
||||
}
|
||||
this.#running = true;
|
||||
if (callback.constructor.name.toLowerCase() === 'asyncfunction') {
|
||||
await callback();
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
} catch (err) {
|
||||
error(`${this.#id} Queue:`, err);
|
||||
} finally {
|
||||
this.#running = false;
|
||||
this.#tryRunNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HTML Elements
|
||||
@@ -32,32 +129,17 @@ class GalleryFolder extends HTMLElement {
|
||||
super();
|
||||
this.name = decodeURI(name);
|
||||
this.shadow = this.attachShadow({ mode: 'open' });
|
||||
this.shadow.adoptedStyleSheets = [folderStylesheet];
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
const style = document.createElement('style'); // silly but necessasry since we're inside shadowdom
|
||||
if (window.opts.theme_type === 'Modern') {
|
||||
style.textContent = `
|
||||
.gallery-folder { cursor: pointer; padding: 8px 6px 8px 6px; background-color: var(--sd-button-normal-color); border-radius: var(--sd-border-radius); text-align: left; min-width: 12em;}
|
||||
.gallery-folder:hover { background-color: var(--button-primary-background-fill-hover); }
|
||||
.gallery-folder-selected { background-color: var(--sd-button-selected-color); color: var(--sd-button-selected-text-color); }
|
||||
.gallery-folder-icon { font-size: 1.2em; color: var(--sd-button-icon-color); margin-right: 1em; filter: drop-shadow(1px 1px 2px black); float: left; }
|
||||
`;
|
||||
} else {
|
||||
style.textContent = `
|
||||
.gallery-folder { cursor: pointer; padding: 8px 6px 8px 6px; }
|
||||
.gallery-folder:hover { background-color: var(--button-primary-background-fill-hover); }
|
||||
.gallery-folder-selected { background-color: var(--button-primary-background-fill); }
|
||||
`;
|
||||
}
|
||||
this.shadow.appendChild(style);
|
||||
const div = document.createElement('div');
|
||||
div.className = 'gallery-folder';
|
||||
div.innerHTML = `<span class="gallery-folder-icon">\uf03e</span> ${this.name}`;
|
||||
div.addEventListener('click', () => {
|
||||
for (const folder of el.folders.children) {
|
||||
if (folder.name === this.name) folder.shadow.children[1].classList.add('gallery-folder-selected');
|
||||
else folder.shadow.children[1].classList.remove('gallery-folder-selected');
|
||||
if (folder.name === this.name) folder.shadow.firstElementChild.classList.add('gallery-folder-selected');
|
||||
else folder.shadow.firstElementChild.classList.remove('gallery-folder-selected');
|
||||
}
|
||||
});
|
||||
div.addEventListener('click', fetchFilesWS); // eslint-disable-line no-use-before-define
|
||||
@@ -208,10 +290,13 @@ async function delayFetchThumb(fn) {
|
||||
}
|
||||
|
||||
class GalleryFile extends HTMLElement {
|
||||
constructor(folder, file) {
|
||||
#signal;
|
||||
|
||||
constructor(folder, file, signal = undefined) {
|
||||
super();
|
||||
this.folder = folder;
|
||||
this.name = file;
|
||||
this.#signal = signal;
|
||||
this.size = 0;
|
||||
this.mtime = 0;
|
||||
this.hash = undefined;
|
||||
@@ -220,6 +305,7 @@ class GalleryFile extends HTMLElement {
|
||||
this.height = 0;
|
||||
this.src = `${this.folder}/${this.name}`;
|
||||
this.shadow = this.attachShadow({ mode: 'open' });
|
||||
this.shadow.adoptedStyleSheets = [fileStylesheet];
|
||||
}
|
||||
|
||||
async connectedCallback() {
|
||||
@@ -238,21 +324,6 @@ class GalleryFile extends HTMLElement {
|
||||
}
|
||||
|
||||
this.hash = await getHash(`${this.folder}/${this.name}/${this.size}/${this.mtime}`); // eslint-disable-line no-use-before-define
|
||||
galleryHashes.add(this.hash);
|
||||
const style = document.createElement('style');
|
||||
const width = opts.browser_fixed_width ? `${opts.extra_networks_card_size}px` : 'unset';
|
||||
style.textContent = `
|
||||
.gallery-file {
|
||||
object-fit: contain;
|
||||
cursor: pointer;
|
||||
height: ${opts.extra_networks_card_size}px;
|
||||
width: ${width};
|
||||
}
|
||||
.gallery-file:hover {
|
||||
filter: grayscale(100%);
|
||||
}
|
||||
`;
|
||||
|
||||
const cache = (this.hash && opts.browser_cache) ? await idbGet(this.hash) : undefined;
|
||||
const img = document.createElement('img');
|
||||
img.className = 'gallery-file';
|
||||
@@ -306,6 +377,11 @@ class GalleryFile extends HTMLElement {
|
||||
img.src = `file=${this.src}`;
|
||||
}
|
||||
}
|
||||
if (this.#signal && !this.#signal.aborted) {
|
||||
// Guard against accessing external context from a stale initialization
|
||||
galleryHashes.add(this.hash); // Add to hashes Set *after* any database operations
|
||||
this.#signal = null; // Clean up reference to AbortSignal
|
||||
}
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
@@ -325,7 +401,6 @@ class GalleryFile extends HTMLElement {
|
||||
this.style.display = shouldDisplayBasedOnSearch ? 'unset' : 'none';
|
||||
}
|
||||
|
||||
this.shadow.appendChild(style);
|
||||
this.shadow.appendChild(img);
|
||||
}
|
||||
}
|
||||
@@ -554,9 +629,15 @@ async function gallerySort(btn) {
|
||||
updateStatusWithSort(`${arr.length.toLocaleString()} images | ${Math.floor(t1 - t0).toLocaleString()}ms`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function for removing the cleaning overlay
|
||||
* @callback ClearMsgCallback
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Generate and display the overlay to announce cleanup is in progress.
|
||||
* @returns {() => void} Function for clearing the overlay
|
||||
* @returns {ClearMsgCallback}
|
||||
*/
|
||||
function showCleaningMsg() {
|
||||
const parent = el.folders.parentElement;
|
||||
@@ -567,47 +648,75 @@ function showCleaningMsg() {
|
||||
parent.style.position = 'relative';
|
||||
cleaningOverlay.style.cssText = 'position: absolute; height: 100%; width: 100%; background-color: hsl(210 50 20 / 0.8); display: flex; align-items: center; justify-content: center;';
|
||||
msg.style.cssText = 'display: block; background-color: hsl(0 0 10); color: white; padding: 12px; border-radius: 8px; margin-right: 16px;';
|
||||
msg.innerText = 'Running thumbnail cleanup';
|
||||
msg.innerText = 'Thumbnail cleanup...';
|
||||
anim.classList.add('idbBusyAnim');
|
||||
|
||||
cleaningOverlay.append(msg, anim);
|
||||
parent.append(cleaningOverlay);
|
||||
return () => {
|
||||
parent.style.position = '';
|
||||
cleaningOverlay.remove();
|
||||
};
|
||||
return () => { cleaningOverlay.remove(); };
|
||||
}
|
||||
|
||||
async function thumbCacheCleanup() {
|
||||
if (idbIsCleaning) return;
|
||||
await awaitForIDB();
|
||||
idbIsCleaning = true;
|
||||
const maintenanceQueue = new SimpleFunctionQueue('Maintenance');
|
||||
|
||||
const t0 = performance.now();
|
||||
const cachedHashesCount = await idbCount()
|
||||
.catch(() => 0);
|
||||
if (cachedHashesCount < galleryHashes.size + 500) {
|
||||
// Don't run when there aren't many excess entries
|
||||
idbIsCleaning = false;
|
||||
/**
|
||||
* Handles calling the cleanup function for the thumbnail cache
|
||||
* @param {string} folder - Folder to clean
|
||||
* @param {number} imgCount - Expected number of images in gallery
|
||||
* @param {AbortController} controller - AbortController that's handling this task
|
||||
*/
|
||||
async function thumbCacheCleanup(folder, imgCount, controller) {
|
||||
try {
|
||||
if (typeof folder !== 'string' || typeof imgCount !== 'number') {
|
||||
throw new Error('Function called with invalid arguments');
|
||||
}
|
||||
debug('Thumbnail DB cleanup: Waiting for gallery data to settle');
|
||||
await awaitForGallery(imgCount, controller.signal);
|
||||
} catch (err) {
|
||||
debug(`Thumbnail DB cleanup: Skipping cleanup for "${folder}" due to "${controller.signal.aborted ? controller.signal.reason : 'timeout'}"`);
|
||||
return;
|
||||
}
|
||||
|
||||
const removeOverlayFunc = showCleaningMsg();
|
||||
idbClean(galleryHashes, activeGalleryFolder)
|
||||
.then((delcount, folder) => {
|
||||
const t1 = performance.now();
|
||||
log(`Thumbnail DB cleanup: folder=${folder} kept=${galleryHashes.size} deleted=${delcount} time=${Math.floor(t1 - t0)}ms`);
|
||||
})
|
||||
.catch((err) => {
|
||||
error('Thumbnail DB cleanup: Cleanup failed.', err.message);
|
||||
})
|
||||
.finally(() => {
|
||||
removeOverlayFunc();
|
||||
idbIsCleaning = false;
|
||||
});
|
||||
maintenanceQueue.enqueue({
|
||||
signal: controller.signal,
|
||||
callback: async () => {
|
||||
log(`Thumbnail DB cleanup: Checking if "${folder}" needs cleaning`);
|
||||
const t0 = performance.now();
|
||||
const staticGalleryHashes = new Set(galleryHashes); // External context should be safe since this function run is guarded by AbortController/AbortSignal in the SimpleFunctionQueue
|
||||
const cachedHashesCount = await idbCount(folder)
|
||||
.catch((e) => {
|
||||
error(`Thumbnail DB cleanup: Error when getting entry count for "${folder}".`, e);
|
||||
return Infinity; // Forces next check to fail if something went wrong
|
||||
});
|
||||
if (cachedHashesCount < staticGalleryHashes.size + 500) {
|
||||
// Don't run when there aren't many excess entries
|
||||
return;
|
||||
}
|
||||
|
||||
if (controller.signal.aborted) {
|
||||
debug(`Thumbnail DB cleanup: Cancelling "${folder}" cleanup due to "${controller.signal.reason}"`);
|
||||
return;
|
||||
}
|
||||
const cb_clearMsg = showCleaningMsg();
|
||||
await idbFolderCleanup(staticGalleryHashes, folder, controller.signal)
|
||||
.then((delcount) => {
|
||||
const t1 = performance.now();
|
||||
log(`Thumbnail DB cleanup: folder=${folder} kept=${staticGalleryHashes.size} deleted=${delcount} time=${Math.floor(t1 - t0)}ms`);
|
||||
})
|
||||
.catch((reason) => {
|
||||
if (typeof reason === 'string' || (reason instanceof DOMException && reason.name === 'AbortError')) {
|
||||
log('Thumbnail DB cleanup:', reason?.message || reason);
|
||||
} else {
|
||||
error('Thumbnail DB cleanup:', reason.message);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
cb_clearMsg();
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function fetchFilesHT(evt) {
|
||||
async function fetchFilesHT(evt, controller) {
|
||||
const t0 = performance.now();
|
||||
const fragment = document.createDocumentFragment();
|
||||
updateStatusWithSort(`Folder: ${evt.target.name} | in-progress`);
|
||||
@@ -625,7 +734,7 @@ async function fetchFilesHT(evt) {
|
||||
const ext = fileName.split('.').pop().toLowerCase();
|
||||
if (SUPPORTED_EXTENSIONS.includes(ext)) {
|
||||
numFiles++;
|
||||
const f = new GalleryFile(data[0], fileName);
|
||||
const f = new GalleryFile(data[0], fileName, controller.signal);
|
||||
fragment.appendChild(f);
|
||||
}
|
||||
}
|
||||
@@ -633,30 +742,33 @@ async function fetchFilesHT(evt) {
|
||||
el.files.appendChild(fragment);
|
||||
|
||||
const t1 = performance.now();
|
||||
activeGalleryFolder = evt.target.name;
|
||||
log(`gallery: folder=${evt.target.name} num=${numFiles} time=${Math.floor(t1 - t0)}ms`);
|
||||
updateStatusWithSort(`Folder: ${evt.target.name} | ${numFiles.toLocaleString()} images | ${Math.floor(t1 - t0).toLocaleString()}ms`);
|
||||
addSeparators();
|
||||
thumbCacheCleanup();
|
||||
thumbCacheCleanup(evt.target.name, numFiles, controller);
|
||||
}
|
||||
|
||||
async function fetchFilesWS(evt) { // fetch file-by-file list over websockets
|
||||
if (idbIsCleaning) return;
|
||||
galleryHashes.clear(); // Only called here because fetchFilesHT isn't called directly
|
||||
el.files.innerHTML = '';
|
||||
if (!url) return;
|
||||
const controller = new AbortController(); // Only called here because fetchFilesHT isn't called directly
|
||||
maintenanceController.abort('Gallery update'); // Abort previous controller
|
||||
maintenanceController = controller; // Point to new controller for next time
|
||||
galleryHashes.clear(); // Must happen AFTER the AbortController steps
|
||||
|
||||
el.files.innerHTML = '';
|
||||
updateGalleryStyles();
|
||||
if (ws && ws.readyState === WebSocket.OPEN) ws.close(); // abort previous request
|
||||
let wsConnected = false;
|
||||
try {
|
||||
ws = new WebSocket(`${url}/sdapi/v1/browser/files`);
|
||||
wsConnected = await wsConnect(ws);
|
||||
wsConnected = await wsConnect(ws); // Warning. This changes "evt".
|
||||
} catch (err) {
|
||||
log('gallery: ws connect error', err);
|
||||
return;
|
||||
}
|
||||
log(`gallery: connected=${wsConnected} state=${ws?.readyState} url=${ws?.url}`);
|
||||
if (!wsConnected) {
|
||||
await fetchFilesHT(evt); // fallback to http
|
||||
await fetchFilesHT(evt, controller); // fallback to http
|
||||
return;
|
||||
}
|
||||
updateStatusWithSort(`Folder: ${evt.target.name}`);
|
||||
@@ -674,7 +786,7 @@ async function fetchFilesWS(evt) { // fetch file-by-file list over websockets
|
||||
const fileName = data[1];
|
||||
const ext = fileName.split('.').pop().toLowerCase();
|
||||
if (SUPPORTED_EXTENSIONS.includes(ext)) {
|
||||
const file = new GalleryFile(data[0], fileName);
|
||||
const file = new GalleryFile(data[0], fileName, controller.signal);
|
||||
numFiles++;
|
||||
fragment.appendChild(file);
|
||||
if (numFiles % 100 === 0) {
|
||||
@@ -688,11 +800,10 @@ async function fetchFilesWS(evt) { // fetch file-by-file list over websockets
|
||||
ws.onclose = (event) => {
|
||||
el.files.appendChild(fragment);
|
||||
// gallerySort();
|
||||
activeGalleryFolder = evt.target.name;
|
||||
log(`gallery: folder=${evt.target.name} num=${numFiles} time=${Math.floor(t1 - t0)}ms`);
|
||||
updateStatusWithSort(`Folder: ${evt.target.name} | ${numFiles.toLocaleString()} images | ${Math.floor(t1 - t0).toLocaleString()}ms`);
|
||||
addSeparators();
|
||||
thumbCacheCleanup();
|
||||
thumbCacheCleanup(evt.target.name, numFiles, controller);
|
||||
};
|
||||
ws.onerror = (event) => {
|
||||
log('gallery ws error', event);
|
||||
@@ -759,6 +870,7 @@ async function initGallery() { // triggered on gradio change to monitor when ui
|
||||
error('initGallery', 'Missing gallery elements');
|
||||
return;
|
||||
}
|
||||
updateGalleryStyles();
|
||||
setOverlayAnimation();
|
||||
el.search.addEventListener('input', gallerySearch);
|
||||
el.btnSend = gradioApp().getElementById('tab-gallery-send-image');
|
||||
|
||||
+95
-39
@@ -1,9 +1,12 @@
|
||||
let db;
|
||||
/**
|
||||
* @type {?IDBDatabase}
|
||||
*/
|
||||
let db = null;
|
||||
|
||||
async function initIndexDB() {
|
||||
async function createDB() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = indexedDB.open('SDNext');
|
||||
const request = indexedDB.open('SDNext', 2);
|
||||
request.onerror = (evt) => reject(evt);
|
||||
request.onsuccess = (evt) => {
|
||||
db = evt.target.result;
|
||||
@@ -16,9 +19,15 @@ async function initIndexDB() {
|
||||
};
|
||||
request.onupgradeneeded = (evt) => {
|
||||
db = evt.target.result;
|
||||
const store = db.createObjectStore('thumbs', { keyPath: 'hash' });
|
||||
store.createIndex('hash', 'hash', { unique: true });
|
||||
const index = store.index('hash');
|
||||
const oldver = evt.oldVersion;
|
||||
if (oldver < 1) {
|
||||
const store = db.createObjectStore('thumbs', { keyPath: 'hash' });
|
||||
store.createIndex('hash', 'hash', { unique: true });
|
||||
}
|
||||
if (oldver < 2) {
|
||||
const existingStore = request.transaction.objectStore('thumbs');
|
||||
existingStore.createIndex('folder', 'folder', { unique: false });
|
||||
}
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
@@ -75,57 +84,104 @@ async function put(record) {
|
||||
});
|
||||
}
|
||||
|
||||
async function idbGetAllKeys() {
|
||||
async function idbGetAllKeys(index = null, query = null) {
|
||||
if (!db) return null;
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = db
|
||||
.transaction('thumbs', 'readonly')
|
||||
.objectStore('thumbs')
|
||||
.getAllKeys();
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
request.onerror = (evt) => reject(evt);
|
||||
try {
|
||||
let request;
|
||||
const transaction = db.transaction('thumbs', 'readonly');
|
||||
const store = transaction.objectStore('thumbs');
|
||||
if (index) {
|
||||
request = store.index(index).getAllKeys(query);
|
||||
} else {
|
||||
request = store.getAllKeys(query);
|
||||
}
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
request.onerror = (e) => reject(e);
|
||||
transaction.onabort = (e) => reject(e);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function idbCount() {
|
||||
/**
|
||||
* Get the number of entries in the IndexedDB thumbnail cache.
|
||||
* @global
|
||||
* @param {?string} folder - If specified, get the count for this gallery folder. Otherwise get the total count.
|
||||
* @returns {Promise<number>}
|
||||
*/
|
||||
async function idbCount(folder = null) {
|
||||
if (!db) return null;
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = db
|
||||
.transaction('thumbs', 'readonly')
|
||||
.objectStore('thumbs')
|
||||
.count();
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
request.onerror = (evt) => reject(evt);
|
||||
try {
|
||||
let request;
|
||||
const transaction = db.transaction('thumbs', 'readonly');
|
||||
const store = transaction.objectStore('thumbs');
|
||||
if (folder) {
|
||||
request = store.index('folder').count(folder);
|
||||
} else {
|
||||
request = store.count();
|
||||
}
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
request.onerror = (e) => reject(e);
|
||||
transaction.onabort = (e) => reject(e);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function idbClean(keepSet, folder = null) {
|
||||
/**
|
||||
* Cleanup function for IndexedDB thumbnail cache.
|
||||
* @global
|
||||
* @param {Set<string>} keepSet - Set containing the hashes of the current files in the folder
|
||||
* @param {string} folder - Folder name/path
|
||||
* @param {AbortSignal} signal - Signal from the AbortController for thumbCacheCleanup()
|
||||
*/
|
||||
async function idbFolderCleanup(keepSet, folder, signal) {
|
||||
if (!db) return null;
|
||||
if (!(keepSet instanceof Set)) {
|
||||
throw new TypeError('IndexedDB cleaning function must be given a Set() of hashes to keep');
|
||||
throw new TypeError('IndexedDB cleaning function must be given a Set() of the current gallery hashes');
|
||||
}
|
||||
if (folder === null) {
|
||||
if (typeof folder !== 'string') {
|
||||
throw new Error('IndexedDB cleaning function must be told the current active folder');
|
||||
}
|
||||
|
||||
let removals = new Set(await idbGetAllKeys('folder', folder));
|
||||
removals = removals.difference(keepSet); // Don't need to keep full set in memory
|
||||
const totalRemovals = removals.size;
|
||||
if (signal.aborted) {
|
||||
throw `Aborting. ${signal.reason}`; // eslint-disable-line no-throw-literal
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
let counter = 0;
|
||||
const request = db
|
||||
.transaction('thumbs', 'readwrite')
|
||||
.objectStore('thumbs')
|
||||
.openCursor();
|
||||
request.onsuccess = (evt) => {
|
||||
const cursor = evt.target.result;
|
||||
if (cursor) {
|
||||
if (folder === cursor.value.folder && !keepSet.has(cursor.key)) {
|
||||
cursor.delete();
|
||||
counter++;
|
||||
}
|
||||
cursor.continue();
|
||||
} else {
|
||||
resolve(counter, folder);
|
||||
}
|
||||
const transaction = db.transaction('thumbs', 'readwrite');
|
||||
function abortTransaction() {
|
||||
signal.removeEventListener('abort', abortTransaction);
|
||||
transaction.abort();
|
||||
}
|
||||
signal.addEventListener('abort', abortTransaction);
|
||||
|
||||
try {
|
||||
const store = transaction.objectStore('thumbs');
|
||||
removals.forEach((entry) => { store.delete(entry); });
|
||||
} catch (err) {
|
||||
error(err);
|
||||
abortTransaction();
|
||||
}
|
||||
|
||||
transaction.onabort = () => {
|
||||
signal.removeEventListener('abort', abortTransaction);
|
||||
reject(`Aborting. ${signal.reason}`); // eslint-disable-line prefer-promise-reject-errors
|
||||
};
|
||||
transaction.onerror = () => {
|
||||
signal.removeEventListener('abort', abortTransaction);
|
||||
reject(new Error('Database transaction error'));
|
||||
};
|
||||
transaction.oncomplete = async () => {
|
||||
signal.removeEventListener('abort', abortTransaction);
|
||||
resolve(totalRemovals);
|
||||
};
|
||||
request.onerror = (evt) => reject(evt);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@ async function initStartup() {
|
||||
await initAccordions();
|
||||
await initSettings();
|
||||
await initImageViewer();
|
||||
await initGallery();
|
||||
await initiGenerationParams();
|
||||
await initChangelog();
|
||||
await setupControlUI();
|
||||
@@ -47,6 +46,8 @@ async function initStartup() {
|
||||
await reconnectUI();
|
||||
await waitForOpts();
|
||||
|
||||
await initGallery();
|
||||
|
||||
log('mountURL', window.opts.subpath);
|
||||
if (window.opts.subpath?.length > 0) {
|
||||
window.subpath = window.opts.subpath;
|
||||
|
||||
@@ -180,7 +180,7 @@ class Unit(): # mashup of gradio controls and mapping to actual implementation c
|
||||
self.adapter.load(model_id)
|
||||
else:
|
||||
self.controls.append(model_id)
|
||||
model_id.change(fn=self.adapter.load, inputs=[model_id], outputs=[result_txt], show_progress=True)
|
||||
model_id.change(fn=self.adapter.load, inputs=[model_id], outputs=[result_txt], show_progress='full')
|
||||
if extra_controls is not None and len(extra_controls) > 0:
|
||||
extra_controls[0].change(fn=adapter_extra, inputs=extra_controls)
|
||||
elif self.type == 'controlnet':
|
||||
@@ -189,8 +189,8 @@ class Unit(): # mashup of gradio controls and mapping to actual implementation c
|
||||
self.controlnet.load(model_id)
|
||||
else:
|
||||
self.controls.append(model_id)
|
||||
model_id.change(fn=self.controlnet.load, inputs=[model_id], outputs=[result_txt], show_progress=True)
|
||||
model_id.change(fn=control_choices, inputs=[model_id], outputs=[control_mode, control_tile], show_progress=False)
|
||||
model_id.change(fn=self.controlnet.load, inputs=[model_id], outputs=[result_txt], show_progress='full')
|
||||
model_id.change(fn=control_choices, inputs=[model_id], outputs=[control_mode, control_tile], show_progress='hidden')
|
||||
if extra_controls is not None and len(extra_controls) > 0:
|
||||
extra_controls[0].change(fn=controlnet_extra, inputs=extra_controls)
|
||||
elif self.type == 'xs':
|
||||
@@ -199,7 +199,7 @@ class Unit(): # mashup of gradio controls and mapping to actual implementation c
|
||||
self.controlnet.load(model_id)
|
||||
else:
|
||||
self.controls.append(model_id)
|
||||
model_id.change(fn=self.controlnet.load, inputs=[model_id, extra_controls[0]], outputs=[result_txt], show_progress=True)
|
||||
model_id.change(fn=self.controlnet.load, inputs=[model_id, extra_controls[0]], outputs=[result_txt], show_progress='full')
|
||||
if extra_controls is not None and len(extra_controls) > 0:
|
||||
extra_controls[0].change(fn=controlnetxs_extra, inputs=extra_controls)
|
||||
elif self.type == 'lite':
|
||||
@@ -208,7 +208,7 @@ class Unit(): # mashup of gradio controls and mapping to actual implementation c
|
||||
self.controlnet.load(model_id)
|
||||
else:
|
||||
self.controls.append(model_id)
|
||||
model_id.change(fn=self.controlnet.load, inputs=[model_id], outputs=[result_txt], show_progress=True)
|
||||
model_id.change(fn=self.controlnet.load, inputs=[model_id], outputs=[result_txt], show_progress='full')
|
||||
if extra_controls is not None and len(extra_controls) > 0:
|
||||
extra_controls[0].change(fn=controlnetxs_extra, inputs=extra_controls)
|
||||
elif self.type == 'reference':
|
||||
@@ -229,7 +229,7 @@ class Unit(): # mashup of gradio controls and mapping to actual implementation c
|
||||
self.process.load(process_id)
|
||||
else:
|
||||
self.controls.append(process_id)
|
||||
process_id.change(fn=self.process.load, inputs=[process_id], outputs=[result_txt], show_progress=True)
|
||||
process_id.change(fn=self.process.load, inputs=[process_id], outputs=[result_txt], show_progress='full')
|
||||
if reset_btn is not None:
|
||||
reset_btn.click(fn=self.reset, inputs=[], outputs=[enabled_cb, model_id, process_id, model_strength])
|
||||
if preview_btn is not None:
|
||||
|
||||
@@ -115,6 +115,6 @@ def create_ui(prompt, negative, styles, _overrides, init_image, last_image, mp4_
|
||||
_js="submit_framepack",
|
||||
inputs=state_inputs + framepack_inputs,
|
||||
outputs=framepack_outputs,
|
||||
show_progress=False,
|
||||
show_progress='hidden',
|
||||
)
|
||||
generate.click(**framepack_dict)
|
||||
|
||||
@@ -158,7 +158,7 @@ def connect_paste_params_buttons():
|
||||
fn=send_image,
|
||||
inputs=[binding.source_image_component],
|
||||
outputs=[destination_image_component],
|
||||
show_progress=False,
|
||||
show_progress='hidden',
|
||||
)
|
||||
|
||||
override_settings_component = binding.override_settings_component or paste_fields[binding.tabname]["override_settings_component"]
|
||||
@@ -177,7 +177,7 @@ def connect_paste_params_buttons():
|
||||
_js=f"switch_to_{binding.tabname}",
|
||||
inputs=[],
|
||||
outputs=[],
|
||||
show_progress=False,
|
||||
show_progress='hidden',
|
||||
)
|
||||
|
||||
|
||||
@@ -294,12 +294,12 @@ def connect_paste(button, local_paste_fields, input_comp, override_settings_comp
|
||||
fn=paste_func,
|
||||
inputs=[input_comp],
|
||||
outputs=[x[0] for x in local_paste_fields],
|
||||
show_progress=False,
|
||||
show_progress='hidden',
|
||||
)
|
||||
button.click(
|
||||
fn=None,
|
||||
_js=f"recalculate_prompts_{tabname}",
|
||||
inputs=[],
|
||||
outputs=[],
|
||||
show_progress=False,
|
||||
show_progress='hidden',
|
||||
)
|
||||
|
||||
@@ -72,6 +72,6 @@ def create_ui(prompt, negative, styles, overrides, init_image, init_strength, la
|
||||
_js="submit_ltx",
|
||||
inputs=state_inputs + video_inputs,
|
||||
outputs=video_outputs,
|
||||
show_progress=False,
|
||||
show_progress='hidden',
|
||||
)
|
||||
generate.click(**video_dict)
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import io
|
||||
import os
|
||||
import contextlib
|
||||
import safetensors.torch
|
||||
import transformers
|
||||
from installer import install, log
|
||||
@@ -15,21 +12,17 @@ def hijacked_load_file(checkpoint_file, device="cpu"):
|
||||
if not checkpoint_file.endswith('.safetensors'):
|
||||
return orig_load_file(checkpoint_file, device=device)
|
||||
|
||||
install('runai_model_streamer')
|
||||
log.debug(f'Loader: method=runai type=file chunk={os.environ["RUNAI_STREAMER_CHUNK_BYTESIZE"]} limit={os.environ["RUNAI_STREAMER_MEMORY_LIMIT"]} device={device}')
|
||||
install('runai_model_streamer>=0.15.1')
|
||||
state_dict = {}
|
||||
stdout = io.StringIO()
|
||||
from runai_model_streamer import SafetensorsStreamer
|
||||
with contextlib.redirect_stdout(stdout):
|
||||
try:
|
||||
with SafetensorsStreamer() as streamer:
|
||||
streamer.stream_file(checkpoint_file)
|
||||
for key, tensor in streamer.get_tensors():
|
||||
state_dict[key] = tensor.to(device)
|
||||
except Exception as e:
|
||||
log.error(f'Loader: {e}')
|
||||
log.error(stdout.getvalue())
|
||||
errors.display(e, 'runai')
|
||||
try:
|
||||
with SafetensorsStreamer() as streamer:
|
||||
streamer.stream_file(checkpoint_file)
|
||||
for key, tensor in streamer.get_tensors():
|
||||
state_dict[key] = tensor.to(device)
|
||||
except Exception as e:
|
||||
log.error(f'Loader: {e}')
|
||||
errors.display(e, 'runai')
|
||||
return state_dict
|
||||
|
||||
|
||||
@@ -37,21 +30,17 @@ def hijacked_load_state_dict(checkpoint_file, is_quantized: bool = False, map_lo
|
||||
if not checkpoint_file.endswith(".safetensors"):
|
||||
return orig_load_state_dict(checkpoint_file=checkpoint_file, is_quantized=is_quantized, map_location=map_location, weights_only=weights_only)
|
||||
|
||||
install('runai_model_streamer')
|
||||
log.trace(f'Loader: method=runai type=dict chunk={os.environ["RUNAI_STREAMER_CHUNK_BYTESIZE"]} limit={os.environ["RUNAI_STREAMER_MEMORY_LIMIT"]} device={map_location} quantized={is_quantized}')
|
||||
install('runai_model_streamer>=0.15.1')
|
||||
state_dict = {}
|
||||
stdout = io.StringIO()
|
||||
from runai_model_streamer import SafetensorsStreamer
|
||||
with contextlib.redirect_stdout(stdout):
|
||||
try:
|
||||
with SafetensorsStreamer() as streamer:
|
||||
streamer.stream_file(checkpoint_file)
|
||||
for key, tensor in streamer.get_tensors():
|
||||
state_dict[key] = tensor.to(map_location) if map_location != "meta" else tensor
|
||||
except Exception as e:
|
||||
log.error(f'Loader: {e}')
|
||||
log.error(stdout.getvalue())
|
||||
errors.display(e, 'runai')
|
||||
try:
|
||||
with SafetensorsStreamer() as streamer:
|
||||
streamer.stream_file(checkpoint_file)
|
||||
for key, tensor in streamer.get_tensors():
|
||||
state_dict[key] = tensor.to(map_location) if map_location != "meta" else tensor
|
||||
except Exception as e:
|
||||
log.error(f'Loader: {e}')
|
||||
errors.display(e, 'runai')
|
||||
return state_dict
|
||||
|
||||
|
||||
|
||||
@@ -65,6 +65,8 @@ def set_huggingface_options():
|
||||
else:
|
||||
sd_hijack_accelerate.restore_accelerate()
|
||||
if (shared.opts.runai_streamer_diffusers or shared.opts.runai_streamer_transformers) and (sys.platform == 'linux'):
|
||||
import os
|
||||
log.debug(f'Loader: runai enabled chunk={os.environ["RUNAI_STREAMER_CHUNK_BYTESIZE"]} limit={os.environ["RUNAI_STREAMER_MEMORY_LIMIT"]}')
|
||||
sd_hijack_safetensors.hijack_safetensors(shared.opts.runai_streamer_diffusers, shared.opts.runai_streamer_transformers)
|
||||
else:
|
||||
sd_hijack_safetensors.restore_safetensors()
|
||||
@@ -629,8 +631,7 @@ def load_sdnq_model(checkpoint_info, pipeline, diffusers_load_config, op):
|
||||
if shared.opts.runai_streamer_diffusers and (sys.platform == 'linux'):
|
||||
load_method = 'streamer'
|
||||
from installer import install
|
||||
install('runai_model_streamer')
|
||||
shared.log.trace(f'Loader: method={load_method} chunk={os.environ["RUNAI_STREAMER_CHUNK_BYTESIZE"]} limit={os.environ["RUNAI_STREAMER_MEMORY_LIMIT"]}')
|
||||
install('runai_model_streamer>=0.15.1')
|
||||
elif shared.opts.sd_parallel_load:
|
||||
load_method = 'threaded'
|
||||
else:
|
||||
|
||||
@@ -285,23 +285,23 @@ def create_output_panel(tabname, preview=True, prompt=None, height=None, transfe
|
||||
with gr.Group():
|
||||
html_info = gr.HTML(elem_id=f'html_info_{tabname}', elem_classes="infotext", visible=False) # contains raw infotext as returned by wrapped call
|
||||
html_info_formatted = gr.HTML(elem_id=f'html_info_formatted_{tabname}', elem_classes="infotext", visible=True) # contains html formatted infotext
|
||||
html_info.change(fn=infotext_to_html, inputs=[html_info], outputs=[html_info_formatted], show_progress=False)
|
||||
html_info.change(fn=infotext_to_html, inputs=[html_info], outputs=[html_info_formatted], show_progress='hidden')
|
||||
html_log = gr.HTML(elem_id=f'html_log_{tabname}')
|
||||
generation_info = gr.Textbox(visible=False, elem_id=f'generation_info_{tabname}')
|
||||
generation_info_button = gr.Button(visible=False, elem_id=f"{tabname}_generation_info_button")
|
||||
|
||||
result_field = result_info or html_info_formatted
|
||||
generation_info_button.click(fn=update_generation_info, show_progress=False,
|
||||
generation_info_button.click(fn=update_generation_info, show_progress='hidden',
|
||||
_js="(x, y, z) => [x, y, selected_gallery_index()]", # triggered on gallery change from js
|
||||
inputs=[generation_info, html_info, html_info],
|
||||
outputs=[html_info, result_field],
|
||||
)
|
||||
save.click(fn=call_queue.wrap_gradio_call(save_files), show_progress=False,
|
||||
save.click(fn=call_queue.wrap_gradio_call(save_files), show_progress='hidden',
|
||||
_js="(x, y, z, i) => [x, y, z, selected_gallery_index()]",
|
||||
inputs=[generation_info, result_gallery, html_info, html_info],
|
||||
outputs=[download_files, html_log],
|
||||
)
|
||||
delete.click(fn=call_queue.wrap_gradio_call(delete_files), show_progress=False,
|
||||
delete.click(fn=call_queue.wrap_gradio_call(delete_files), show_progress='hidden',
|
||||
_js="(x, y, i, j) => [x, y, ...selected_gallery_files()]",
|
||||
inputs=[generation_info, result_gallery, html_info, html_info],
|
||||
outputs=[result_gallery, html_log],
|
||||
@@ -345,7 +345,7 @@ def create_refresh_button(refresh_component, refresh_method, refreshed_args = No
|
||||
return gr.update(**args)
|
||||
|
||||
refresh_button = ui_components.ToolButton(value=ui_symbols.refresh, elem_id=elem_id, visible=visible)
|
||||
refresh_button.click(fn=refresh, inputs=[], outputs=[refresh_component], show_progress=False)
|
||||
refresh_button.click(fn=refresh, inputs=[], outputs=[refresh_component], show_progress='hidden')
|
||||
return refresh_button
|
||||
|
||||
|
||||
@@ -374,7 +374,7 @@ def reuse_seed(seed_component: gr.Number, reuse_button: gr.Button, subseed:bool=
|
||||
shared.log.debug(f'Reuse seed: index={selected_gallery_index} seed={seed} subseed={subseed}')
|
||||
return seed
|
||||
|
||||
reuse_button.click(fn=reuse_click, _js="selected_gallery_index", inputs=[seed_component], outputs=[seed_component], show_progress=False)
|
||||
reuse_button.click(fn=reuse_click, _js="selected_gallery_index", inputs=[seed_component], outputs=[seed_component], show_progress='hidden')
|
||||
|
||||
|
||||
def connect_reuse_seed(seed: gr.Number, reuse_seed_btn: gr.Button, generation_info: gr.Textbox, is_subseed, subseed_strength=None):
|
||||
@@ -405,9 +405,9 @@ def connect_reuse_seed(seed: gr.Number, reuse_seed_btn: gr.Button, generation_in
|
||||
return [restore_seed, gr_show(False)]
|
||||
dummy_component = gr.Number(visible=False, value=0)
|
||||
if subseed_strength is None:
|
||||
reuse_seed_btn.click(fn=copy_seed, _js="(x, y) => [x, selected_gallery_index()]", show_progress=False, inputs=[generation_info, dummy_component], outputs=[seed, dummy_component])
|
||||
reuse_seed_btn.click(fn=copy_seed, _js="(x, y) => [x, selected_gallery_index()]", show_progress='hidden', inputs=[generation_info, dummy_component], outputs=[seed, dummy_component])
|
||||
else:
|
||||
reuse_seed_btn.click(fn=copy_seed, _js="(x, y) => [x, selected_gallery_index()]", show_progress=False, inputs=[generation_info, dummy_component], outputs=[seed, dummy_component, subseed_strength])
|
||||
reuse_seed_btn.click(fn=copy_seed, _js="(x, y) => [x, selected_gallery_index()]", show_progress='hidden', inputs=[generation_info, dummy_component], outputs=[seed, dummy_component, subseed_strength])
|
||||
|
||||
|
||||
def update_token_counter(text):
|
||||
|
||||
@@ -243,20 +243,20 @@ def create_ui(_blocks: gr.Blocks=None):
|
||||
for u in units:
|
||||
controls.extend(u.controls)
|
||||
btn_update = gr.Button('Update', interactive=True, visible=False, elem_id='control_update')
|
||||
btn_update.click(fn=get_units, inputs=controls, outputs=[], show_progress=False, queue=False)
|
||||
btn_update.click(fn=get_units, inputs=controls, outputs=[], show_progress='hidden', queue=False)
|
||||
|
||||
show_input.change(fn=lambda x: gr.update(visible=x), inputs=[show_input], outputs=[column_input])
|
||||
show_preview.change(fn=lambda x: gr.update(visible=x), inputs=[show_preview], outputs=[column_preview])
|
||||
input_type.change(fn=lambda x: gr.update(visible=x == 2), inputs=[input_type], outputs=[column_init])
|
||||
btn_prompt_counter.click(fn=call_queue.wrap_queued_call(ui_common.update_token_counter), inputs=[prompt], outputs=[prompt_counter], show_progress = False)
|
||||
btn_negative_counter.click(fn=call_queue.wrap_queued_call(ui_common.update_token_counter), inputs=[negative], outputs=[negative_counter], show_progress = False)
|
||||
btn_prompt_counter.click(fn=call_queue.wrap_queued_call(ui_common.update_token_counter), inputs=[prompt], outputs=[prompt_counter], show_progress = 'hidden')
|
||||
btn_negative_counter.click(fn=call_queue.wrap_queued_call(ui_common.update_token_counter), inputs=[negative], outputs=[negative_counter], show_progress = 'hidden')
|
||||
|
||||
select_dict = dict(
|
||||
fn=helpers.select_input,
|
||||
_js="controlInputMode",
|
||||
inputs=[input_mode, input_image, init_image, input_type, input_video, input_batch, input_folder],
|
||||
outputs=[output_tabs, preview_process, result_txt, width_before, height_before],
|
||||
show_progress=False,
|
||||
show_progress='hidden',
|
||||
queue=False,
|
||||
)
|
||||
|
||||
@@ -305,7 +305,7 @@ def create_ui(_blocks: gr.Blocks=None):
|
||||
_js="submit_control",
|
||||
inputs=[tabs_state, state, tabs_state] + input_fields + input_script_args,
|
||||
outputs=output_fields,
|
||||
show_progress=True,
|
||||
show_progress='full',
|
||||
)
|
||||
prompt.submit(**control_dict)
|
||||
negative.submit(**control_dict)
|
||||
|
||||
+8
-8
@@ -242,7 +242,7 @@ def create_ui_logs():
|
||||
_changelog_result = gr.HTML(elem_id="changelog_result")
|
||||
|
||||
changelog_markdown = gr.Markdown('', elem_id="changelog_markdown")
|
||||
get_changelog_btn.click(fn=get_changelog, outputs=[changelog_markdown], show_progress=True)
|
||||
get_changelog_btn.click(fn=get_changelog, outputs=[changelog_markdown], show_progress='full')
|
||||
|
||||
|
||||
def create_ui_github():
|
||||
@@ -254,9 +254,9 @@ def create_ui_github():
|
||||
with gr.Row():
|
||||
github_md_btn = gr.Button(value='html2md', elem_id="github_md_btn", visible=False)
|
||||
github_md = gr.Markdown(elem_id="github_md", value='', elem_classes="github-md")
|
||||
github_search.submit(fn=search_github, inputs=[github_search], outputs=[github_result], show_progress=True)
|
||||
github_search_btn.click(fn=search_github, inputs=[github_search], outputs=[github_result], show_progress=True)
|
||||
github_md_btn.click(fn=get_github_page, _js='getGitHubWikiPage', inputs=[github_search], outputs=[github_md], show_progress=True)
|
||||
github_search.submit(fn=search_github, inputs=[github_search], outputs=[github_result], show_progress='full')
|
||||
github_search_btn.click(fn=search_github, inputs=[github_search], outputs=[github_result], show_progress='full')
|
||||
github_md_btn.click(fn=get_github_page, _js='getGitHubWikiPage', inputs=[github_search], outputs=[github_md], show_progress='full')
|
||||
|
||||
|
||||
def create_ui_docs():
|
||||
@@ -268,10 +268,10 @@ def create_ui_docs():
|
||||
with gr.Row():
|
||||
docs_md_btn = gr.Button(value='html2md', elem_id="docs_md_btn", visible=False)
|
||||
docs_md = gr.Markdown(elem_id="docs_md", value='', elem_classes="docs-md")
|
||||
docs_search.submit(fn=search_docs, inputs=[docs_search], outputs=[docs_result], show_progress=False)
|
||||
docs_search.change(fn=search_docs, inputs=[docs_search], outputs=[docs_result], show_progress=False)
|
||||
docs_search_btn.click(fn=search_docs, inputs=[docs_search], outputs=[docs_result], show_progress=False)
|
||||
docs_md_btn.click(fn=get_docs_page, _js='getDocsPage', inputs=[docs_search], outputs=[docs_md], show_progress=False)
|
||||
docs_search.submit(fn=search_docs, inputs=[docs_search], outputs=[docs_result], show_progress='hidden')
|
||||
docs_search.change(fn=search_docs, inputs=[docs_search], outputs=[docs_result], show_progress='hidden')
|
||||
docs_search_btn.click(fn=search_docs, inputs=[docs_search], outputs=[docs_result], show_progress='hidden')
|
||||
docs_md_btn.click(fn=get_docs_page, _js='getDocsPage', inputs=[docs_search], outputs=[docs_md], show_progress='hidden')
|
||||
|
||||
|
||||
def create_ui():
|
||||
|
||||
@@ -191,10 +191,11 @@ def install_extension(extension_to_install, search_text, sort_column):
|
||||
|
||||
|
||||
def uninstall_extension(extension_path, search_text, sort_column):
|
||||
def excRemoveReadonly(func, path, exc: Exception):
|
||||
def errorRemoveReadonly(func, path, exc):
|
||||
import stat
|
||||
shared.log.debug(f'Exception during cleanup: {func} {path} {type(exc).__name__}')
|
||||
if func in (os.rmdir, os.remove, os.unlink) and isinstance(exc, PermissionError):
|
||||
excvalue = exc[1]
|
||||
shared.log.debug(f'Exception during cleanup: {func} {path} {excvalue.strerror}')
|
||||
if func in (os.rmdir, os.remove, os.unlink) and excvalue.errno == errno.EACCES:
|
||||
shared.log.debug(f'Retrying cleanup: {path}')
|
||||
os.chmod(path, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
|
||||
func(path)
|
||||
@@ -203,7 +204,7 @@ def uninstall_extension(extension_path, search_text, sort_column):
|
||||
if len(found) > 0 and os.path.isdir(extension_path):
|
||||
found = found[0]
|
||||
try:
|
||||
shutil.rmtree(found.path, ignore_errors=False, onexc=excRemoveReadonly)
|
||||
shutil.rmtree(found.path, ignore_errors=False, onerror=errorRemoveReadonly) # pylint: disable=deprecated-argument
|
||||
# extensions.extensions = [extension for extension in extensions.extensions if os.path.abspath(found.path) != os.path.abspath(extension_path)]
|
||||
except Exception as e:
|
||||
shared.log.warning(f'Extension uninstall failed: {found.path} {e}')
|
||||
|
||||
@@ -8,4 +8,4 @@ def create_ui():
|
||||
_history_table = gr.HTML('', elem_id='history_table')
|
||||
with gr.Row():
|
||||
_history_timeline = gr.HTML('', elem_id='history_timeline')
|
||||
btn_refresh.click(_js='refreshHistory', fn=None, inputs=[], outputs=[], show_progress=False)
|
||||
btn_refresh.click(_js='refreshHistory', fn=None, inputs=[], outputs=[], show_progress='hidden')
|
||||
|
||||
@@ -203,7 +203,7 @@ def create_ui():
|
||||
img2img_html_info,
|
||||
img2img_html_log,
|
||||
],
|
||||
show_progress=False,
|
||||
show_progress='hidden',
|
||||
)
|
||||
img2img_prompt.submit(**img2img_dict)
|
||||
img2img_negative_prompt.submit(**img2img_dict)
|
||||
@@ -229,8 +229,8 @@ def create_ui():
|
||||
)
|
||||
interrogate_btn.click(fn=lambda *args: process_interrogate(*args), **interrogate_args)
|
||||
|
||||
img2img_token_button.click(fn=call_queue.wrap_queued_call(ui_common.update_token_counter), inputs=[img2img_prompt], outputs=[img2img_token_counter], show_progress = False)
|
||||
img2img_negative_token_button.click(fn=call_queue.wrap_queued_call(ui_common.update_token_counter), inputs=[img2img_negative_prompt], outputs=[img2img_negative_token_counter], show_progress = False)
|
||||
img2img_token_button.click(fn=call_queue.wrap_queued_call(ui_common.update_token_counter), inputs=[img2img_prompt], outputs=[img2img_token_counter], show_progress = 'hidden')
|
||||
img2img_negative_token_button.click(fn=call_queue.wrap_queued_call(ui_common.update_token_counter), inputs=[img2img_negative_prompt], outputs=[img2img_negative_token_counter], show_progress = 'hidden')
|
||||
|
||||
ui_extra_networks.setup_ui(extra_networks_ui_img2img, img2img_gallery)
|
||||
img2img_paste_fields = [
|
||||
|
||||
@@ -532,7 +532,7 @@ def create_ui():
|
||||
_js="downloadCivitModel",
|
||||
inputs=[_dummy, _dummy, _dummy, civit_folder, civit_token, civitai_models_output],
|
||||
outputs=[civitai_models_output],
|
||||
show_progress=True,
|
||||
show_progress='full',
|
||||
)
|
||||
|
||||
with gr.Tab(label="Huggingface", elem_id="models_huggingface_tab"):
|
||||
|
||||
+12
-12
@@ -45,7 +45,7 @@ def create_toprow(is_img2img: bool = False, id_part: str = None, generate_visibl
|
||||
button_paste = gr.Button(value='Restore', variant='secondary', elem_id=f"{id_part}_paste") # symbols.paste
|
||||
button_clear = gr.Button(value='Clear', variant='secondary', elem_id=f"{id_part}_clear_prompt_btn") # symbols.clear
|
||||
button_extra = gr.Button(value='Networks', variant='secondary', elem_id=f"{id_part}_extra_networks_btn") # symbols.networks
|
||||
button_clear.click(fn=lambda *x: ['', ''], inputs=[prompt, negative_prompt], outputs=[prompt, negative_prompt], show_progress=False)
|
||||
button_clear.click(fn=lambda *x: ['', ''], inputs=[prompt, negative_prompt], outputs=[prompt, negative_prompt], show_progress='hidden')
|
||||
with gr.Row(elem_id=f"{id_part}_counters"):
|
||||
token_counter = gr.HTML(value="<span>0/75</span>", elem_id=f"{id_part}_token_counter", elem_classes=["token-counter"], visible=False)
|
||||
token_button = gr.Button(visible=False, elem_id=f"{id_part}_token_button")
|
||||
@@ -57,9 +57,9 @@ def create_toprow(is_img2img: bool = False, id_part: str = None, generate_visibl
|
||||
styles_btn_select = ToolButton('Select', elem_id=f"{id_part}_styles_select", visible=False)
|
||||
styles_btn_apply = ToolButton(ui_symbols.style_apply, elem_id=f"{id_part}_styles_apply", visible=True)
|
||||
styles_btn_save = ToolButton(ui_symbols.style_save, elem_id=f"{id_part}_styles_save", visible=True)
|
||||
styles_btn_select.click(_js="applyStyles", fn=parse_style, inputs=[styles], outputs=[styles], show_progress=False)
|
||||
styles_btn_apply.click(fn=apply_styles, inputs=[prompt, negative_prompt, styles], outputs=[prompt, negative_prompt, styles], show_progress=False)
|
||||
styles_btn_save.click(fn=lambda: None, _js='() => quickSaveStyle()', inputs=[], outputs=[], show_progress=False)
|
||||
styles_btn_select.click(_js="applyStyles", fn=parse_style, inputs=[styles], outputs=[styles], show_progress='hidden')
|
||||
styles_btn_apply.click(fn=apply_styles, inputs=[prompt, negative_prompt, styles], outputs=[prompt, negative_prompt, styles], show_progress='hidden')
|
||||
styles_btn_save.click(fn=lambda: None, _js='() => quickSaveStyle()', inputs=[], outputs=[], show_progress='hidden')
|
||||
return prompt, styles, negative_prompt, submit, reprocess, button_paste, button_extra, token_counter, token_button, negative_token_counter, negative_token_button
|
||||
|
||||
|
||||
@@ -85,9 +85,9 @@ def create_resolution_inputs(tab, default_width=1024, default_height=1024):
|
||||
ar_list = ['AR'] + [x.strip() for x in shared.opts.aspect_ratios.split(',') if x.strip() != '']
|
||||
ar_dropdown = gr.Dropdown(show_label=False, interactive=True, choices=ar_list, value=ar_list[0], elem_id=f"{tab}_ar", elem_classes=["ar-dropdown"])
|
||||
for c in [ar_dropdown, width, height]:
|
||||
c.change(fn=ar_change, inputs=[ar_dropdown, width, height], outputs=[width, height], show_progress=False)
|
||||
c.change(fn=ar_change, inputs=[ar_dropdown, width, height], outputs=[width, height], show_progress='hidden')
|
||||
res_switch_btn = ToolButton(value=ui_symbols.switch, elem_id=f"{tab}_res_btn_swap")
|
||||
res_switch_btn.click(lambda w, h: (h, w), inputs=[width, height], outputs=[width, height], show_progress=False)
|
||||
res_switch_btn.click(lambda w, h: (h, w), inputs=[width, height], outputs=[width, height], show_progress='hidden')
|
||||
return width, height
|
||||
|
||||
|
||||
@@ -120,8 +120,8 @@ def create_seed_inputs(tab, reuse_visible=True, accordion=True, subseed_visible=
|
||||
with gr.Row(visible=seed_resize_visible):
|
||||
seed_resize_from_w = gr.Slider(minimum=0, maximum=4096, step=8, label="Resize seed from width", value=0, elem_id=f"{tab}_seed_resize_from_w")
|
||||
seed_resize_from_h = gr.Slider(minimum=0, maximum=4096, step=8, label="Resize seed from height", value=0, elem_id=f"{tab}_seed_resize_from_h")
|
||||
random_seed.click(fn=lambda: -1, show_progress=False, inputs=[], outputs=[seed])
|
||||
random_subseed.click(fn=lambda: -1, show_progress=False, inputs=[], outputs=[subseed])
|
||||
random_seed.click(fn=lambda: -1, show_progress='hidden', inputs=[], outputs=[seed])
|
||||
random_subseed.click(fn=lambda: -1, show_progress='hidden', inputs=[], outputs=[subseed])
|
||||
return seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w
|
||||
|
||||
|
||||
@@ -351,17 +351,17 @@ def create_resize_inputs(tab, images, accordion=True, latent=False, non_zero=Tru
|
||||
ar_list = ['AR'] + [x.strip() for x in shared.opts.aspect_ratios.split(',') if x.strip() != '']
|
||||
ar_dropdown = gr.Dropdown(show_label=False, interactive=True, choices=ar_list, value=ar_list[0], elem_id=f"{tab}_resize_ar", elem_classes=["ar-dropdown"])
|
||||
for c in [ar_dropdown, width, height]:
|
||||
c.change(fn=ar_change, inputs=[ar_dropdown, width, height], outputs=[width, height], show_progress=False)
|
||||
c.change(fn=ar_change, inputs=[ar_dropdown, width, height], outputs=[width, height], show_progress='hidden')
|
||||
res_switch_btn = ToolButton(value=ui_symbols.switch, elem_id=f"{tab}_resize_size_swap")
|
||||
res_switch_btn.click(lambda w, h: (h, w), inputs=[width, height], outputs=[width, height], show_progress=False)
|
||||
res_switch_btn.click(lambda w, h: (h, w), inputs=[width, height], outputs=[width, height], show_progress='hidden')
|
||||
detect_image_size_btn = ToolButton(value=ui_symbols.detect, elem_id=f"{tab}_resize_detect_size")
|
||||
el = tab.split('_')[0]
|
||||
detect_image_size_btn.click(fn=lambda w, h, _: (w or gr.update(), h or gr.update()), _js=f'currentImageResolution{el}', inputs=[dummy_component, dummy_component, dummy_component], outputs=[width, height], show_progress=False)
|
||||
detect_image_size_btn.click(fn=lambda w, h, _: (w or gr.update(), h or gr.update()), _js=f'currentImageResolution{el}', inputs=[dummy_component, dummy_component, dummy_component], outputs=[width, height], show_progress='hidden')
|
||||
with gr.Tab(label="Scale", id=1, elem_id=f"{tab}_scale_tab_scale") as tab_scale_by:
|
||||
scale_by = gr.Slider(minimum=0.05, maximum=8.0, step=0.05, label=f"Scale{prefix}" if non_zero else "Resize scale", value=1.0, elem_id=f"{tab}_scale")
|
||||
if images is not None:
|
||||
for component in images:
|
||||
component.change(fn=lambda: None, _js="updateImg2imgResizeToTextAfterChangingImage", inputs=[], outputs=[], show_progress=False)
|
||||
component.change(fn=lambda: None, _js="updateImg2imgResizeToTextAfterChangingImage", inputs=[], outputs=[], show_progress='hidden')
|
||||
tab_scale_to.select(fn=lambda: 0, inputs=[], outputs=[selected_scale_tab])
|
||||
tab_scale_by.select(fn=lambda: 1, inputs=[], outputs=[selected_scale_tab])
|
||||
# resize_mode.change(fn=lambda x: gr.update(visible=x != 0), inputs=[resize_mode], outputs=[_resize_group])
|
||||
|
||||
@@ -100,7 +100,7 @@ def create_setting_component(key, is_quicksettings=False):
|
||||
except Exception as e:
|
||||
shared.log.error(f'Quicksetting: component={res} {e}')
|
||||
if dirty_indicator is not None:
|
||||
dirty_indicator.click(fn=lambda: shared.opts.get_default(key), outputs=[res], show_progress=False)
|
||||
dirty_indicator.click(fn=lambda: shared.opts.get_default(key), outputs=[res], show_progress='hidden')
|
||||
dirtyable_setting.__exit__()
|
||||
|
||||
return res
|
||||
@@ -113,7 +113,7 @@ def create_dirty_indicator(key, keys_to_reset, **kwargs):
|
||||
|
||||
elements_to_reset = [shared.settings_components[_key] for _key in keys_to_reset if shared.settings_components[_key] is not None]
|
||||
indicator = gr.Button('', elem_classes="modification-indicator", elem_id=f"modification_indicator_{key}", **kwargs)
|
||||
indicator.click(fn=get_default_values, outputs=elements_to_reset, show_progress=True)
|
||||
indicator.click(fn=get_default_values, outputs=elements_to_reset, show_progress='full')
|
||||
return indicator
|
||||
|
||||
|
||||
@@ -369,7 +369,7 @@ def create_quicksettings(interfaces):
|
||||
fn=lambda value, k=k, progress=info.refresh is not None: run_settings_single(value, key=k, progress=progress),
|
||||
inputs=[component],
|
||||
outputs=[component, text_settings],
|
||||
show_progress=info.refresh is not None,
|
||||
show_progress='full' if info.refresh is not None else 'hidden',
|
||||
)
|
||||
|
||||
button_set_checkpoint = gr.Button('Change model', elem_id='change_checkpoint', visible=False)
|
||||
|
||||
@@ -77,7 +77,7 @@ def create_ui():
|
||||
txt2img_html_info,
|
||||
txt2img_html_log,
|
||||
],
|
||||
show_progress=False,
|
||||
show_progress='hidden',
|
||||
)
|
||||
|
||||
txt2img_prompt.submit(**txt2img_dict)
|
||||
@@ -160,7 +160,7 @@ def create_ui():
|
||||
txt2img_bindings = generation_parameters_copypaste.ParamBinding(paste_button=txt2img_paste, tabname="txt2img", source_text_component=txt2img_prompt, source_image_component=None)
|
||||
generation_parameters_copypaste.register_paste_params_button(txt2img_bindings)
|
||||
|
||||
txt2img_token_button.click(fn=call_queue.wrap_queued_call(ui_common.update_token_counter), inputs=[txt2img_prompt], outputs=[txt2img_token_counter], show_progress = False)
|
||||
txt2img_negative_token_button.click(fn=call_queue.wrap_queued_call(ui_common.update_token_counter), inputs=[txt2img_negative_prompt], outputs=[txt2img_negative_token_counter], show_progress = False)
|
||||
txt2img_token_button.click(fn=call_queue.wrap_queued_call(ui_common.update_token_counter), inputs=[txt2img_prompt], outputs=[txt2img_token_counter], show_progress = 'hidden')
|
||||
txt2img_negative_token_button.click(fn=call_queue.wrap_queued_call(ui_common.update_token_counter), inputs=[txt2img_negative_prompt], outputs=[txt2img_negative_token_counter], show_progress = 'hidden')
|
||||
|
||||
ui_extra_networks.setup_ui(extra_networks_ui, txt2img_gallery)
|
||||
|
||||
@@ -65,6 +65,6 @@ def create_ui(prompt_element:gr.Textbox, image_element:gr.Image):
|
||||
fn=enhance_prompt,
|
||||
inputs=[enable, model, image_element, prompt_element, system_prompt, nsfw],
|
||||
outputs=prompt_element,
|
||||
show_progress=True,
|
||||
show_progress='full',
|
||||
)
|
||||
return enable, model, system_prompt
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import os
|
||||
import sys
|
||||
import copy
|
||||
import time
|
||||
import transformers # pylint: disable=unused-import
|
||||
@@ -7,6 +8,15 @@ from modules import shared, errors, sd_models, sd_checkpoint, model_quant, devic
|
||||
from modules.video_models import models_def, video_utils, video_overrides, video_cache
|
||||
|
||||
|
||||
def _loader(component):
|
||||
"""Return loader type for log messages."""
|
||||
if sys.platform != 'linux':
|
||||
return 'default'
|
||||
if component == 'diffusers':
|
||||
return 'runai' if shared.opts.runai_streamer_diffusers else 'default'
|
||||
return 'runai' if shared.opts.runai_streamer_transformers else 'default'
|
||||
|
||||
|
||||
loaded_model = None
|
||||
|
||||
|
||||
@@ -60,7 +70,7 @@ def load_model(selected: models_def.Model):
|
||||
selected.te_folder = 'text_encoder'
|
||||
selected.te_revision = None
|
||||
|
||||
shared.log.debug(f'Video load: module=te repo="{selected.te or selected.repo}" folder="{selected.te_folder}" cls={selected.te_cls.__name__} quant={model_quant.get_quant_type(quant_args)}')
|
||||
shared.log.debug(f'Video load: module=te repo="{selected.te or selected.repo}" folder="{selected.te_folder}" cls={selected.te_cls.__name__} quant={model_quant.get_quant_type(quant_args)} loader={_loader("transformers")}')
|
||||
kwargs["text_encoder"] = selected.te_cls.from_pretrained(
|
||||
pretrained_model_name_or_path=selected.te or selected.repo,
|
||||
subfolder=selected.te_folder,
|
||||
@@ -80,7 +90,7 @@ def load_model(selected: models_def.Model):
|
||||
if dit_folder is not None and dit_folder not in kwargs:
|
||||
# get a new quant arg on every loop to prevent the quant config classes getting entangled
|
||||
load_args, quant_args = model_quant.get_dit_args({}, module='Model', device_map=True)
|
||||
shared.log.debug(f'Video load: module=transformer repo="{selected.dit or selected.repo}" module="{dit_folder}" folder="{dit_folder}" cls={selected.dit_cls.__name__} quant={model_quant.get_quant_type(quant_args)}')
|
||||
shared.log.debug(f'Video load: module=transformer repo="{selected.dit or selected.repo}" module="{dit_folder}" folder="{dit_folder}" cls={selected.dit_cls.__name__} quant={model_quant.get_quant_type(quant_args)} loader={_loader("diffusers")}')
|
||||
kwargs[dit_folder] = selected.dit_cls.from_pretrained(
|
||||
pretrained_model_name_or_path=selected.dit or selected.repo,
|
||||
subfolder=dit_folder,
|
||||
@@ -91,7 +101,7 @@ def load_model(selected: models_def.Model):
|
||||
**offline_args,
|
||||
)
|
||||
else:
|
||||
shared.log.debug(f'Video load: module=transformer repo="{selected.dit or selected.repo}" module="{dit_folder}" folder="{dit_folder}" cls={selected.dit_cls.__name__} skip')
|
||||
shared.log.debug(f'Video load: module=transformer repo="{selected.dit or selected.repo}" module="{dit_folder}" folder="{dit_folder}" cls={selected.dit_cls.__name__} loader={_loader("diffusers")} skip')
|
||||
|
||||
if selected.dit_folder is None:
|
||||
selected.dit_folder = ['transformer']
|
||||
|
||||
@@ -116,7 +116,7 @@ def create_ui_size():
|
||||
seed = gr.Number(label='Initial seed', value=-1, elem_id="video_seed", container=True)
|
||||
random_seed = ToolButton(ui_symbols.random, elem_id="video_seed_random")
|
||||
reuse_seed = ToolButton(ui_symbols.reuse, elem_id="video_seed_reuse")
|
||||
random_seed.click(fn=lambda: -1, show_progress=False, inputs=[], outputs=[seed])
|
||||
random_seed.click(fn=lambda: -1, show_progress='hidden', inputs=[], outputs=[seed])
|
||||
return width, height, frames, seed, reuse_seed
|
||||
|
||||
|
||||
@@ -194,7 +194,7 @@ def create_ui(prompt, negative, styles, overrides, init_image, init_strength, la
|
||||
_js="submit_video",
|
||||
inputs=state_inputs + video_inputs,
|
||||
outputs=video_outputs,
|
||||
show_progress=False,
|
||||
show_progress='hidden',
|
||||
)
|
||||
generate.click(**video_dict)
|
||||
return [engine, model, steps, sampler_index]
|
||||
|
||||
+20
-10
@@ -1,4 +1,5 @@
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import diffusers
|
||||
import transformers
|
||||
@@ -8,6 +9,15 @@ from modules import shared, devices, errors, sd_models, model_quant
|
||||
debug = os.environ.get('SD_LOAD_DEBUG', None) is not None
|
||||
|
||||
|
||||
def _loader(component):
|
||||
"""Return loader type for log messages."""
|
||||
if sys.platform != 'linux':
|
||||
return 'default'
|
||||
if component == 'diffusers':
|
||||
return 'runai' if shared.opts.runai_streamer_diffusers else 'default'
|
||||
return 'runai' if shared.opts.runai_streamer_transformers else 'default'
|
||||
|
||||
|
||||
def load_transformer(repo_id, cls_name, load_config=None, subfolder="transformer", allow_quant=True, variant=None, dtype=None, modules_to_not_convert=None, modules_dtype_dict=None):
|
||||
transformer = None
|
||||
if load_config is None:
|
||||
@@ -31,7 +41,7 @@ def load_transformer(repo_id, cls_name, load_config=None, subfolder="transformer
|
||||
local_file = sd_unet.unet_dict[shared.opts.sd_unet]
|
||||
|
||||
if local_file is not None and local_file.lower().endswith('.gguf'):
|
||||
shared.log.debug(f'Load model: transformer="{local_file}" cls={cls_name.__name__} quant="{quant_type}" args={load_args}')
|
||||
shared.log.debug(f'Load model: transformer="{local_file}" cls={cls_name.__name__} quant="{quant_type}" loader={_loader("diffusers")} args={load_args}')
|
||||
from modules import ggml
|
||||
ggml.install_gguf()
|
||||
loader = cls_name.from_single_file if hasattr(cls_name, 'from_single_file') else cls_name.from_pretrained
|
||||
@@ -43,7 +53,7 @@ def load_transformer(repo_id, cls_name, load_config=None, subfolder="transformer
|
||||
)
|
||||
transformer = model_quant.do_post_load_quant(transformer, allow=quant_type is not None)
|
||||
elif local_file is not None and local_file.lower().endswith('.safetensors'):
|
||||
shared.log.debug(f'Load model: transformer="{local_file}" cls={cls_name.__name__} quant="{quant_type}" args={load_args}')
|
||||
shared.log.debug(f'Load model: transformer="{local_file}" cls={cls_name.__name__} quant="{quant_type}" loader={_loader("diffusers")} args={load_args}')
|
||||
if dtype is not None:
|
||||
load_args['torch_dtype'] = dtype
|
||||
loader = cls_name.from_single_file if hasattr(cls_name, 'from_single_file') else cls_name.from_pretrained
|
||||
@@ -54,7 +64,7 @@ def load_transformer(repo_id, cls_name, load_config=None, subfolder="transformer
|
||||
**quant_args,
|
||||
)
|
||||
else:
|
||||
shared.log.debug(f'Load model: transformer="{repo_id}" cls={cls_name.__name__} subfolder={subfolder} quant="{quant_type}" args={load_args}')
|
||||
shared.log.debug(f'Load model: transformer="{repo_id}" cls={cls_name.__name__} subfolder={subfolder} quant="{quant_type}" loader={_loader("diffusers")} args={load_args}')
|
||||
if 'sdnq-' in repo_id.lower():
|
||||
quant_args = {}
|
||||
if dtype is not None:
|
||||
@@ -115,7 +125,7 @@ def load_text_encoder(repo_id, cls_name, load_config=None, subfolder="text_encod
|
||||
|
||||
# load from local file gguf
|
||||
if local_file is not None and local_file.lower().endswith('.gguf'):
|
||||
shared.log.debug(f'Load model: text_encoder="{local_file}" cls={cls_name.__name__} quant="{quant_type}"')
|
||||
shared.log.debug(f'Load model: text_encoder="{local_file}" cls={cls_name.__name__} quant="{quant_type}" loader={_loader("transformers")}')
|
||||
"""
|
||||
from modules import ggml
|
||||
ggml.install_gguf()
|
||||
@@ -132,7 +142,7 @@ def load_text_encoder(repo_id, cls_name, load_config=None, subfolder="text_encod
|
||||
|
||||
# load from local file safetensors
|
||||
elif local_file is not None and local_file.lower().endswith('.safetensors'):
|
||||
shared.log.debug(f'Load model: text_encoder="{local_file}" cls={cls_name.__name__} quant="{quant_type}"')
|
||||
shared.log.debug(f'Load model: text_encoder="{local_file}" cls={cls_name.__name__} quant="{quant_type}" loader={_loader("transformers")}')
|
||||
from modules import model_te
|
||||
text_encoder = model_te.load_t5(local_file)
|
||||
text_encoder = model_quant.do_post_load_quant(text_encoder, allow=quant_type is not None)
|
||||
@@ -143,7 +153,7 @@ def load_text_encoder(repo_id, cls_name, load_config=None, subfolder="text_encod
|
||||
import nunchaku
|
||||
repo_id = 'nunchaku-tech/nunchaku-t5/awq-int4-flux.1-t5xxl.safetensors'
|
||||
cls_name = nunchaku.NunchakuT5EncoderModel
|
||||
shared.log.debug(f'Load model: text_encoder="{repo_id}" cls={cls_name.__name__} quant="SVDQuant"')
|
||||
shared.log.debug(f'Load model: text_encoder="{repo_id}" cls={cls_name.__name__} quant="SVDQuant" loader={_loader("transformers")}')
|
||||
text_encoder = nunchaku.NunchakuT5EncoderModel.from_pretrained(
|
||||
repo_id,
|
||||
torch_dtype=dtype,
|
||||
@@ -157,7 +167,7 @@ def load_text_encoder(repo_id, cls_name, load_config=None, subfolder="text_encod
|
||||
repo_id = 'Disty0/t5-xxl'
|
||||
with open(os.path.join('configs', 'flux', 'text_encoder_2', 'config.json'), encoding='utf8') as f:
|
||||
load_args['config'] = transformers.T5Config(**json.load(f))
|
||||
shared.log.debug(f'Load model: text_encoder="{repo_id}" cls={cls_name.__name__} quant="{quant_type}" shared={shared.opts.te_shared_t5}')
|
||||
shared.log.debug(f'Load model: text_encoder="{repo_id}" cls={cls_name.__name__} quant="{quant_type}" loader={_loader("transformers")} shared={shared.opts.te_shared_t5}')
|
||||
text_encoder = cls_name.from_pretrained(
|
||||
repo_id,
|
||||
cache_dir=shared.opts.hfcache_dir,
|
||||
@@ -170,7 +180,7 @@ def load_text_encoder(repo_id, cls_name, load_config=None, subfolder="text_encod
|
||||
else:
|
||||
repo_id = 'Wan-AI/Wan2.1-T2V-1.3B-Diffusers'
|
||||
subfolder = 'text_encoder'
|
||||
shared.log.debug(f'Load model: text_encoder="{repo_id}" cls={cls_name.__name__} quant="{quant_type}" shared={shared.opts.te_shared_t5}')
|
||||
shared.log.debug(f'Load model: text_encoder="{repo_id}" cls={cls_name.__name__} quant="{quant_type}" loader={_loader("transformers")} shared={shared.opts.te_shared_t5}')
|
||||
text_encoder = cls_name.from_pretrained(
|
||||
repo_id,
|
||||
cache_dir=shared.opts.hfcache_dir,
|
||||
@@ -181,7 +191,7 @@ def load_text_encoder(repo_id, cls_name, load_config=None, subfolder="text_encod
|
||||
elif cls_name == transformers.Qwen2_5_VLForConditionalGeneration and allow_shared and shared.opts.te_shared_t5:
|
||||
repo_id = 'hunyuanvideo-community/HunyuanImage-2.1-Diffusers'
|
||||
subfolder = 'text_encoder'
|
||||
shared.log.debug(f'Load model: text_encoder="{repo_id}" cls={cls_name.__name__} quant="{quant_type}" shared={shared.opts.te_shared_t5}')
|
||||
shared.log.debug(f'Load model: text_encoder="{repo_id}" cls={cls_name.__name__} quant="{quant_type}" loader={_loader("transformers")} shared={shared.opts.te_shared_t5}')
|
||||
text_encoder = cls_name.from_pretrained(
|
||||
repo_id,
|
||||
cache_dir=shared.opts.hfcache_dir,
|
||||
@@ -192,7 +202,7 @@ def load_text_encoder(repo_id, cls_name, load_config=None, subfolder="text_encod
|
||||
|
||||
# load from repo
|
||||
if text_encoder is None:
|
||||
shared.log.debug(f'Load model: text_encoder="{repo_id}" cls={cls_name.__name__} quant="{quant_type}" shared={shared.opts.te_shared_t5}')
|
||||
shared.log.debug(f'Load model: text_encoder="{repo_id}" cls={cls_name.__name__} quant="{quant_type}" loader={_loader("transformers")} shared={shared.opts.te_shared_t5}')
|
||||
if subfolder is not None:
|
||||
load_args['subfolder'] = subfolder
|
||||
if variant is not None:
|
||||
|
||||
@@ -33,7 +33,7 @@ class ScriptPostprocessingUpscale(scripts_postprocessing.ScriptPostprocessing):
|
||||
extras_upscaler_2 = gr.Dropdown(label='Refine upscaler', elem_id="extras_upscaler_2", choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name)
|
||||
extras_upscaler_2_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="Blend strength", value=0.0, elem_id="extras_upscaler_2_visibility")
|
||||
|
||||
upscaling_res_switch_btn.click(lambda w, h: (h, w), inputs=[upscaling_resize_w, upscaling_resize_h], outputs=[upscaling_resize_w, upscaling_resize_h], show_progress=False)
|
||||
upscaling_res_switch_btn.click(lambda w, h: (h, w), inputs=[upscaling_resize_w, upscaling_resize_h], outputs=[upscaling_resize_w, upscaling_resize_h], show_progress='hidden')
|
||||
tab_scale_by.select(fn=lambda: 0, inputs=[], outputs=[selected_tab])
|
||||
tab_scale_to.select(fn=lambda: 1, inputs=[], outputs=[selected_tab])
|
||||
|
||||
|
||||
@@ -105,8 +105,8 @@ class Script(scripts_manager.Script):
|
||||
checkbox_iterate_batch = gr.Checkbox(label="Use same seed", value=False, elem_id=self.elem_id("checkbox_iterate_batch"))
|
||||
prompt_txt = gr.Textbox(label="Prompts", lines=2, elem_id=self.elem_id("prompt_txt"), value='')
|
||||
file = gr.File(label="Upload prompts", type='binary', elem_id=self.elem_id("file"))
|
||||
file.change(fn=load_prompt_file, inputs=[file], outputs=[file, prompt_txt, prompt_txt], show_progress=False)
|
||||
prompt_txt.change(lambda tb: gr.update(lines=7) if ("\n" in tb) else gr.update(lines=2), inputs=[prompt_txt], outputs=[prompt_txt], show_progress=False)
|
||||
file.change(fn=load_prompt_file, inputs=[file], outputs=[file, prompt_txt, prompt_txt], show_progress='hidden')
|
||||
prompt_txt.change(lambda tb: gr.update(lines=7) if ("\n" in tb) else gr.update(lines=2), inputs=[prompt_txt], outputs=[prompt_txt], show_progress='hidden')
|
||||
return [checkbox_iterate, checkbox_iterate_batch, prompt_txt]
|
||||
|
||||
def run(self, p, checkbox_iterate, checkbox_iterate_batch, prompt_txt: str): # pylint: disable=arguments-differ
|
||||
|
||||
Reference in New Issue
Block a user