diff --git a/devTools/types/FC/util.d.ts b/devTools/types/FC/util.d.ts index 3ed3029b8ef312d7d6465dbab41f8009f3ada656..8123985964b9bac8c4856555f616c230430f0e20 100644 --- a/devTools/types/FC/util.d.ts +++ b/devTools/types/FC/util.d.ts @@ -18,4 +18,8 @@ declare namespace FC { min: number; max: number; } + + type PromiseWithProgress<T> = Promise<T> & { + onProgress: (fn: (progress: number) => void) => PromiseWithProgress<T> + } } diff --git a/src/art/artJS.js b/src/art/artJS.js index c633ffb87a2c0c3157ff2320b543e055f9f7fbb6..b792dc42340a49fda3a8e20f77eb817f27fae0d0 100644 --- a/src/art/artJS.js +++ b/src/art/artJS.js @@ -621,13 +621,15 @@ App.Art.aiArtElement = function(slave, imageSize, isEventImage = null) { updateAndRefresh: (index = null) => { container.classList.add("refreshing"); - App.Art.GenAI.staticCache.updateSlave(slave, index, isEventImage).then(() => { - staticSpecific.refresh(); - }).catch(error => { - console.log(error.message || error); - }).finally(() => { - container.classList.remove("refreshing"); - }); + App.Art.GenAI.staticCache.updateSlave(slave, index, isEventImage) + .onProgress((progressNum) => progress.value = progressNum) + .then(() => { + staticSpecific.refresh(); + }).catch(error => { + console.log(error.message || error); + }).finally(() => { + container.classList.remove("refreshing"); + }); }, refresh: () => { renderAIArt(slave, imageSize, slave.custom.aiDisplayImageIdx) diff --git a/src/art/genAI/reactiveImageDB.js b/src/art/genAI/reactiveImageDB.js index a5127ab07319e32084eafd163e98e6ec0cf515ac..cccd4a894d290268318d71795747399a3ca5e82a 100644 --- a/src/art/genAI/reactiveImageDB.js +++ b/src/art/genAI/reactiveImageDB.js @@ -320,18 +320,13 @@ App.Art.GenAI.reactiveImageDB = (function() { return fuzzyResults; } - /** - * @typedef {Promise<T> & { onProgress: (fn: (progress: number) => void) => PromiseWithProgress<T> }} PromiseWithProgress<T> - * @template {any} T - */ - /** * Get an image from the IndexedDB * @param {App.Entity.SlaveState[]} slaves The ID of the image to retrieve * @param {Partial<App.Art.GenAI.GetImageOptions>} [options] Misc options. * Defaults: action='overview', size=App.Art.ArtSizes.SMALL, forceRegenerate: false, isEventImage: false * - * @returns {PromiseWithProgress<App.Art.GenAI.EventStore.Entry>} Promise object that resolves with the retrieved image data + * @returns {FC.PromiseWithProgress<App.Art.GenAI.EventStore.Entry>} Promise object that resolves with the retrieved image data */ function getImage(slaves, options = {}) { const progressFns = []; @@ -408,7 +403,7 @@ App.Art.GenAI.reactiveImageDB = (function() { /** * Do something when there's progress on generating an image * @param {(progress: number) => void} fn A function to call when there's progress - * @returns {PromiseWithProgress<App.Art.GenAI.EventStore.Entry>} + * @returns {FC.PromiseWithProgress<App.Art.GenAI.EventStore.Entry>} */ onProgress(fn) { progressFns.push(fn); diff --git a/src/art/genAI/stableDiffusion.js b/src/art/genAI/stableDiffusion.js index be45c82c7789ee5134a7292029607136c610d972..777a6e194c9602965b710ce2ed6c9a5646ad9062 100644 --- a/src/art/genAI/stableDiffusion.js +++ b/src/art/genAI/stableDiffusion.js @@ -641,34 +641,70 @@ App.Art.GenAI.StaticCaching = class { * @param {FC.SlaveState} slave - The slave to update * @param {number | null} replacementImageIndex - If provided, replace the image at this index * @param {boolean | null} isEventImage - Whether request is canceled on passage change and which step setting to use. true => V.aiSamplingStepsEvent, false => V.aiSamplingSteps, null => chosen based on passage tags + * @returns {FC.PromiseWithProgress<void>} */ - async updateSlave(slave, replacementImageIndex = null, isEventImage = null) { - const base64Image = await this.fetchImageForSlave(slave, isEventImage); - const imageData = getImageData(base64Image); - const imagePreexisting = await compareExistingImages(slave, imageData); - if (!isEventImage) { - let vSlave = globalThis.getSlave(slave.ID); - // if `slave` is owned but the variable has become detached from V.slaves, save the image changes to V.slaves instead - // but don't do it for temporary images because they might be intentionally using a copy of a slave for temporary changes - if (vSlave && slave !== vSlave) { - slave = vSlave; - } - } - // If new image, add or replace it in - if (imagePreexisting === -1) { - const imageId = await App.Art.GenAI.staticImageDB.putImage({data: imageData}); - if (replacementImageIndex !== null) { - await App.Art.GenAI.staticImageDB.removeImage(slave.custom.aiImageIds[replacementImageIndex]); - slave.custom.aiImageIds[replacementImageIndex] = imageId; - } else { - slave.custom.aiImageIds.push(imageId); - slave.custom.aiDisplayImageIdx = slave.custom.aiImageIds.indexOf(imageId); + updateSlave(slave, replacementImageIndex = null, isEventImage = null) { + const progressFns = []; + const result = Object.assign( + new Promise((resolve, reject) => { + (async () => { + const base64Image = await this.fetchImageForSlave(slave, isEventImage); + const imageData = getImageData(base64Image); + const imagePreexisting = await compareExistingImages(slave, imageData); + if (!isEventImage) { + let vSlave = globalThis.getSlave(slave.ID); + // if `slave` is owned but the variable has become detached from V.slaves, save the image changes to V.slaves instead + // but don't do it for temporary images because they might be intentionally using a copy of a slave for temporary changes + if (vSlave && slave !== vSlave) { + slave = vSlave; + } + } + // If new image, add or replace it in + if (imagePreexisting === -1) { + const imageId = await App.Art.GenAI.staticImageDB.putImage({data: imageData}); + if (replacementImageIndex !== null) { + await App.Art.GenAI.staticImageDB.removeImage(slave.custom.aiImageIds[replacementImageIndex]); + slave.custom.aiImageIds[replacementImageIndex] = imageId; + } else { + slave.custom.aiImageIds.push(imageId); + slave.custom.aiDisplayImageIdx = slave.custom.aiImageIds.indexOf(imageId); + } + // If image already exists, just update the display idx to it + } else { + console.log('Generated redundant image, no image stored'); + slave.custom.aiDisplayImageIdx = imagePreexisting; + } + })().then(resolve).catch(reject); + }), { + /** + * Do something when there's progress on generating an image + * @param {(progress: number) => void} fn A function to call when there's progress + * @returns {FC.PromiseWithProgress<void>} + */ + onProgress(fn) { + progressFns.push(fn); + return result; + } } - // If image already exists, just update the display idx to it - } else { - console.log('Generated redundant image, no image stored'); - slave.custom.aiDisplayImageIdx = imagePreexisting; - } + ); + + // TODO: Only do this if it's the current image being generated + const interval = setInterval(async () => { + const response = await fetch('https://home.local:9728/sdapi/v1/progress?skip_current_image=true', { + method: 'GET', + headers: [ + ['accept', 'application/json'], + ], + }); + const progress = (await response.json()).progress; + progressFns.forEach((fn) => fn(progress)); + }, 1000); + result.finally(() => { + clearInterval(interval); + progressFns.forEach((fn) => fn(1)); + }); + + return result; } };