diff --git a/ui/easydiffusion/bucket_manager.py b/ui/easydiffusion/bucket_manager.py index f587bc7f..60a4ed6c 100644 --- a/ui/easydiffusion/bucket_manager.py +++ b/ui/easydiffusion/bucket_manager.py @@ -69,9 +69,8 @@ def init(): bucket = crud.get_bucket_by_path(db, path) if bucket == None: - bucket_id = crud.create_bucket(db=db, bucket=schemas.BucketCreate(path=path)) - else: - bucket_id = bucket.id + bucket = crud.create_bucket(db=db, bucket=schemas.BucketCreate(path=path)) + bucket_id = bucket.id bucketfile = schemas.BucketFileCreate(filename=filename, data=file) result = crud.create_bucketfile(db=db, bucketfile=bucketfile, bucket_id=bucket_id) diff --git a/ui/index.html b/ui/index.html index faee0ff2..f69efd0b 100644 --- a/ui/index.html +++ b/ui/index.html @@ -704,13 +704,13 @@
-

Use the thumbnail for …

-
- - + +
diff --git a/ui/media/css/main.css b/ui/media/css/main.css index be3e92f0..4d68b9db 100644 --- a/ui/media/css/main.css +++ b/ui/media/css/main.css @@ -1650,6 +1650,35 @@ body.wait-pause { } } +.spinner-container { + width: 80px; + height: 100px; + margin: 100px auto; + margin-top: 30vH; +} + +.spinner-block { + position: relative; + box-sizing: border-box; + float: left; + margin: 0 10px 10px 0; + width: 12px; + height: 12px; + border-radius: 3px; + background: var(--accent-color); +} + +.spinner-block:nth-child(4n+1) { animation: spinner-wave 2s ease .0s infinite; } +.spinner-block:nth-child(4n+2) { animation: spinner-wave 2s ease .2s infinite; } +.spinner-block:nth-child(4n+3) { animation: spinner-wave 2s ease .4s infinite; } +.spinner-block:nth-child(4n+4) { animation: spinner-wave 2s ease .6s infinite; margin-right: 0; } + +@keyframes spinner-wave { + 0% { top: 0; opacity: 1; } + 50% { top: 30px; opacity: .2; } + 100% { top: 0; opacity: 1; } +} + #embeddings-dialog { overflow: clip; } @@ -1748,7 +1777,7 @@ body.wait-pause { grid-auto-flow: row; grid-template-areas: "uat-preview uat-select" - "uat-buttons uat-buttons"; + "uat-preview uat-buttons"; } .use-as-thumb-preview { @@ -1857,4 +1886,4 @@ div#enlarge-buttons { /* hack for fixing Image Modifier Improvements plugin */ #imageTagPopupContainer { position: absolute; -} \ No newline at end of file +} diff --git a/ui/media/js/main.js b/ui/media/js/main.js index f340a79c..30ed6012 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -171,8 +171,11 @@ let saveAllJSONToggle = document.querySelector("#json_toggle") let saveAllFoldersOption = document.querySelector("#download-add-folders") let splashScreenPopup = document.querySelector("#splash-screen") let useAsThumbDialog = document.querySelector("#use-as-thumb-dialog") +let useAsThumbDialogCloseBtn = document.querySelector("#use-as-thumb-dialog-close-button") let useAsThumbImageContainer = document.querySelector("#use-as-thumb-img-container") let useAsThumbSelect = document.querySelector("#use-as-thumb-select") +let useAsThumbSaveBtn = document.querySelector("#use-as-thumb-save") +let useAsThumbCancelBtn = document.querySelector("#use-as-thumb-cancel") let maskSetting = document.querySelector("#enable_mask") @@ -703,8 +706,8 @@ function onUseAsThumbnailClick(req, img) { let scale = 1 let targetWidth = img.naturalWidth let targetHeight = img.naturalHeight - let resize = false + onUseAsThumbnailClick.img = img if ( typeof(onUseAsThumbnailClick.croppr) == 'undefined' ) { onUseAsThumbnailClick.croppr = new Croppr("#use-as-thumb-image", { aspectRatio: 1, minSize: [384,384,'px'], startSize: [512, 512, 'px'], returnMode:"real" }) @@ -725,6 +728,10 @@ function onUseAsThumbnailClick(req, img) { resize = true } } + + onUseAsThumbnailClick.croppr.options.minSize = {width: 384*scale>>>0, height: 384*scale>>>0} + onUseAsThumbnailClick.croppr.options.startSize = {width: 512*scale>>>0, height: 512*scale>>>0} + if (resize) { const canvas = document.createElement('canvas') canvas.width = targetWidth @@ -732,9 +739,6 @@ function onUseAsThumbnailClick(req, img) { const ctx = canvas.getContext('2d') ctx.drawImage(img, 0, 0, targetWidth, targetHeight) - onUseAsThumbnailClick.croppr.options.minSize = {width: 384*scale>>>0, height: 384*scale>>>0} - onUseAsThumbnailClick.croppr.options.startSize = {width: 512*scale>>>0, height: 512*scale>>>0} - onUseAsThumbnailClick.croppr.setImage(canvas.toDataURL('image/png')) } else { onUseAsThumbnailClick.croppr.setImage(img.src) @@ -759,21 +763,51 @@ function onUseAsThumbnailClick(req, img) { useAsThumbSelect.replaceChildren(optgroup) useAsThumbDialog.showModal() + onUseAsThumbnailClick.scale = scale - -// fetch(img.src) -// .then(response => response.blob()) -// .then(async function(blob) { -// const formData = new FormData() -// formData.append("file", blob) -// const response = await fetch(`bucket/embeddings/${embedding}.jpg`, { method: 'POST', body: formData }); -// console.log(response) -// }) } modalDialogCloseOnBackdropClick(useAsThumbDialog) makeDialogDraggable(useAsThumbDialog) +useAsThumbDialogCloseBtn.addEventListener("click", () => { + useAsThumbDialog.close() +}) + +useAsThumbCancelBtn.addEventListener("click", () => { + useAsThumbDialog.close() +}) + +useAsThumbSaveBtn.addEventListener("click", (e) => { + let scale = 1/onUseAsThumbnailClick.scale + let crop = onUseAsThumbnailClick.croppr.getValue() + + let len = Math.max(crop.width*scale, 384) + let profileName = profileNameField.value + + cropImageDataUrl(onUseAsThumbnailClick.img.src, crop.x*scale, crop.y*scale, len, len) + .then(thumb => fetch(thumb)) + .then(response => response.blob()) + .then(async function(blob) { + const formData = new FormData() + formData.append("file", blob) + let options = useAsThumbSelect.selectedOptions + let promises = [] + console.log(options) + for (let embedding of options) { + console.log(`bucket/${profileName}/${embedding.dataset["type"]}/${embedding.value}.png`) + promises.push(fetch(`bucket/${profileName}/${embedding.dataset["type"]}/${embedding.value}.png`, { method: 'POST', body: formData })) + } + return Promise.all(promises) + }).then(() => { + useAsThumbDialog.close() + }) + .catch(error => { + console.error(error) + showToast("Couldn't save thumbnail.
"+error) + }) +}) + function enqueueImageVariationTask(req, img, reqDiff) { const imageSeed = img.getAttribute("data-seed") @@ -2672,6 +2706,16 @@ function updateEmbeddingsList(filter = "") { } } + // Usually the rendering of the Embeddings HTML takes less than a second. In case it takes longer, show a spinner + embeddingsList.innerHTML = ` +
+
+
+
+
+
+ ` + // Remove after fixing https://github.com/huggingface/diffusers/issues/3922 let warning = "" if (vramUsageLevelField.value == "low") { @@ -2682,9 +2726,11 @@ function updateEmbeddingsList(filter = "") { } // END of remove block - fetch("/bucket/embeddings/") + let profileName = profileNameField.value + fetch(`/bucket/${profileName}/embeddings/`) .then(response => response.status==200 ? response.json(): []) - .then(iconlist => { + .then(async function(iconlist) { + embeddingsList.innerHTML = warning + html(modelsOptions.embeddings, iconlist, "", filter) embeddingsList.querySelectorAll("button").forEach((b) => { b.addEventListener("click", onButtonClick) diff --git a/ui/media/js/utils.js b/ui/media/js/utils.js index 9db0d1cc..673ba3dd 100644 --- a/ui/media/js/utils.js +++ b/ui/media/js/utils.js @@ -1097,6 +1097,30 @@ async function deleteKeys(keyToDelete) { } } +function cropImageDataUrl(dataUrl, x, y, width, height) { + return new Promise((resolve, reject) => { + const image = new Image() + image.src = dataUrl + + image.onload = () => { + const canvas = document.createElement('canvas') + canvas.width = width + canvas.height = height + + const ctx = canvas.getContext('2d') + ctx.drawImage(image, x, y, width, height, 0, 0, width, height) + + const croppedDataUrl = canvas.toDataURL('image/png') + resolve(croppedDataUrl) + } + + image.onerror = (error) => { + reject(error) + } + }) +} + + function modalDialogCloseOnBackdropClick(dialog) { dialog.addEventListener('mousedown', function (event) { // Firefox creates an event with clientX|Y = 0|0 when choosing an