mirror of
https://github.com/easydiffusion/easydiffusion.git
synced 2025-08-09 23:57:42 +02:00
Merge branch 'beta' of https://github.com/cmdr2/stable-diffusion-ui.git into multi-gpu
# Conflicts: # ui/media/js/main.js # ui/sd_internal/runtime.py # ui/server.py
This commit is contained in:
@ -257,11 +257,6 @@ function logError(msg, res, outputMsg) {
|
||||
console.log('request error', res)
|
||||
setStatus('request', 'error', 'error')
|
||||
}
|
||||
function asyncDelay(timeout) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
setTimeout(resolve, timeout, true)
|
||||
})
|
||||
}
|
||||
|
||||
function playSound() {
|
||||
const audio = new Audio('/media/ding.mp3')
|
||||
@ -366,116 +361,125 @@ function showImages(reqBody, res, outputContainer, livePreview) {
|
||||
const imageSeedLabel = imageItemElem.querySelector('.imgSeedLabel')
|
||||
imageSeedLabel.innerText = 'Seed: ' + req.seed
|
||||
|
||||
const buttons = {
|
||||
'imgUseBtn': { html: 'Use as Input', click: getUseAsInputHandler(imageItemElem) },
|
||||
'imgSaveBtn': { html: 'Download', click: getSaveImageHandler(imageItemElem, req['output_format']) },
|
||||
'imgX2Btn': { html: 'Double Size', click: getStartNewTaskHandler(req, imageItemElem, 'img2img_X2') },
|
||||
'imgRedoBtn': { html: 'Redo', click: getStartNewTaskHandler(req, imageItemElem, 'img2img') },
|
||||
}
|
||||
if (!req.use_upscale) {
|
||||
buttons.upscaleBtn = { html: 'Upscale', click: getStartNewTaskHandler(req, imageItemElem, 'upscale') }
|
||||
}
|
||||
let buttons = [
|
||||
{ text: 'Use as Input', on_click: onUseAsInputClick },
|
||||
{ text: 'Download', on_click: onDownloadImageClick },
|
||||
{ text: 'Make Similar Images', on_click: onMakeSimilarClick },
|
||||
{ text: 'Draw another 25 steps', on_click: onContinueDrawingClick },
|
||||
{ text: 'Upscale', on_click: onUpscaleClick, filter: (req, img) => !req.use_upscale },
|
||||
{ text: 'Fix Faces', on_click: onFixFacesClick, filter: (req, img) => !req.use_face_correction }
|
||||
]
|
||||
|
||||
// include the plugins
|
||||
buttons = buttons.concat(PLUGINS['IMAGE_INFO_BUTTONS'])
|
||||
|
||||
const imgItemInfo = imageItemElem.querySelector('.imgItemInfo')
|
||||
const createButton = function(name, btnInfo) {
|
||||
const img = imageItemElem.querySelector('img')
|
||||
const createButton = function(btnInfo) {
|
||||
const newButton = document.createElement('button')
|
||||
newButton.classList.add(name)
|
||||
newButton.classList.add('tasksBtns')
|
||||
newButton.innerHTML = btnInfo.html
|
||||
newButton.addEventListener('click', btnInfo.click)
|
||||
newButton.innerText = btnInfo.text
|
||||
newButton.addEventListener('click', function() {
|
||||
btnInfo.on_click(req, img)
|
||||
})
|
||||
imgItemInfo.appendChild(newButton)
|
||||
}
|
||||
Object.keys(buttons).forEach((name) => createButton(name, buttons[name]))
|
||||
buttons.forEach(btn => {
|
||||
if (btn.filter && btn.filter(req, img) === false) {
|
||||
return
|
||||
}
|
||||
|
||||
createButton(btn)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getUseAsInputHandler(imageItemElem) {
|
||||
return function() {
|
||||
const imageElem = imageItemElem.querySelector('img')
|
||||
const imgData = imageElem.src
|
||||
const imageSeed = imageElem.getAttribute('data-seed')
|
||||
function onUseAsInputClick(req, img) {
|
||||
const imgData = img.src
|
||||
|
||||
initImageSelector.value = null
|
||||
initImagePreview.src = imgData
|
||||
initImageSelector.value = null
|
||||
initImagePreview.src = imgData
|
||||
|
||||
initImagePreviewContainer.style.display = 'block'
|
||||
inpaintingEditorContainer.style.display = 'none'
|
||||
promptStrengthContainer.style.display = 'table-row'
|
||||
maskSetting.checked = false
|
||||
samplerSelectionContainer.style.display = 'none'
|
||||
|
||||
// maskSetting.style.display = 'block'
|
||||
|
||||
// randomSeedField.checked = false
|
||||
// seedField.value = imageSeed
|
||||
// seedField.disabled = false
|
||||
}
|
||||
initImagePreviewContainer.style.display = 'block'
|
||||
inpaintingEditorContainer.style.display = 'none'
|
||||
promptStrengthContainer.style.display = 'table-row'
|
||||
maskSetting.checked = false
|
||||
samplerSelectionContainer.style.display = 'none'
|
||||
}
|
||||
|
||||
function getSaveImageHandler(imageItemElem, outputFormat) {
|
||||
return function() {
|
||||
const imageElem = imageItemElem.querySelector('img')
|
||||
const imgData = imageElem.src
|
||||
const imageSeed = imageElem.getAttribute('data-seed')
|
||||
const imagePrompt = imageElem.getAttribute('data-prompt')
|
||||
const imageInferenceSteps = imageElem.getAttribute('data-steps')
|
||||
const imageGuidanceScale = imageElem.getAttribute('data-guidance')
|
||||
function onDownloadImageClick(req, img) {
|
||||
const imgData = img.src
|
||||
const imageSeed = img.getAttribute('data-seed')
|
||||
const imagePrompt = img.getAttribute('data-prompt')
|
||||
const imageInferenceSteps = img.getAttribute('data-steps')
|
||||
const imageGuidanceScale = img.getAttribute('data-guidance')
|
||||
|
||||
const imgDownload = document.createElement('a')
|
||||
imgDownload.download = createFileName(imagePrompt, imageSeed, imageInferenceSteps, imageGuidanceScale, outputFormat)
|
||||
imgDownload.href = imgData
|
||||
imgDownload.click()
|
||||
}
|
||||
const imgDownload = document.createElement('a')
|
||||
imgDownload.download = createFileName(imagePrompt, imageSeed, imageInferenceSteps, imageGuidanceScale, req['output_format'])
|
||||
imgDownload.href = imgData
|
||||
imgDownload.click()
|
||||
}
|
||||
function getStartNewTaskHandler(reqBody, imageItemElem, mode) {
|
||||
return function() {
|
||||
if (!isServerAvailable()) {
|
||||
alert('The server is not available.')
|
||||
return
|
||||
}
|
||||
const imageElem = imageItemElem.querySelector('img')
|
||||
const newTaskRequest = getCurrentUserRequest()
|
||||
switch (mode) {
|
||||
case 'img2img':
|
||||
case 'img2img_X2':
|
||||
newTaskRequest.reqBody = Object.assign({}, reqBody, {
|
||||
num_outputs: 1,
|
||||
use_cpu: useCPUField.checked,
|
||||
})
|
||||
if (!newTaskRequest.reqBody.init_image || mode === 'img2img_X2') {
|
||||
newTaskRequest.reqBody.sampler = 'ddim'
|
||||
newTaskRequest.reqBody.prompt_strength = '0.5'
|
||||
newTaskRequest.reqBody.init_image = imageElem.src
|
||||
delete newTaskRequest.reqBody.mask
|
||||
} else {
|
||||
newTaskRequest.reqBody.seed = 1 + newTaskRequest.reqBody.seed
|
||||
}
|
||||
if (mode === 'img2img_X2') {
|
||||
newTaskRequest.reqBody.width = reqBody.width * 2
|
||||
newTaskRequest.reqBody.height = reqBody.height * 2
|
||||
newTaskRequest.reqBody.num_inference_steps = Math.min(100, reqBody.num_inference_steps * 2)
|
||||
if (useUpscalingField.checked) {
|
||||
newTaskRequest.reqBody.use_upscale = upscaleModelField.value
|
||||
} else {
|
||||
delete newTaskRequest.reqBody.use_upscale
|
||||
}
|
||||
}
|
||||
break
|
||||
case 'upscale':
|
||||
newTaskRequest.reqBody = Object.assign({}, reqBody, {
|
||||
num_outputs: 1,
|
||||
//use_face_correction: 'GFPGANv1.3',
|
||||
use_upscale: upscaleModelField.value,
|
||||
})
|
||||
break
|
||||
default:
|
||||
throw new Error("Unknown upscale mode: " + mode)
|
||||
}
|
||||
newTaskRequest.seed = newTaskRequest.reqBody.seed
|
||||
newTaskRequest.numOutputsTotal = 1
|
||||
newTaskRequest.batchCount = 1
|
||||
createTask(newTaskRequest)
|
||||
}
|
||||
|
||||
function modifyCurrentRequest(req, reqDiff) {
|
||||
const newTaskRequest = getCurrentUserRequest()
|
||||
|
||||
newTaskRequest.reqBody = Object.assign({}, req, reqDiff, {
|
||||
use_cpu: useCPUField.checked
|
||||
})
|
||||
newTaskRequest.seed = newTaskRequest.reqBody.seed
|
||||
|
||||
return newTaskRequest
|
||||
}
|
||||
|
||||
function onMakeSimilarClick(req, img) {
|
||||
const newTaskRequest = modifyCurrentRequest(req, {
|
||||
num_outputs: 1,
|
||||
num_inference_steps: 50,
|
||||
guidance_scale: 7.5,
|
||||
prompt_strength: 0.7,
|
||||
init_image: img.src,
|
||||
seed: Math.floor(Math.random() * 10000000)
|
||||
})
|
||||
|
||||
newTaskRequest.numOutputsTotal = 5
|
||||
newTaskRequest.batchCount = 5
|
||||
|
||||
delete newTaskRequest.reqBody.mask
|
||||
|
||||
createTask(newTaskRequest)
|
||||
}
|
||||
|
||||
function enqueueImageVariationTask(req, img, reqDiff) {
|
||||
const imageSeed = img.getAttribute('data-seed')
|
||||
|
||||
const newTaskRequest = modifyCurrentRequest(req, reqDiff, {
|
||||
num_outputs: 1, // this can be user-configurable in the future
|
||||
seed: imageSeed
|
||||
})
|
||||
|
||||
newTaskRequest.numOutputsTotal = 1 // this can be user-configurable in the future
|
||||
newTaskRequest.batchCount = 1
|
||||
|
||||
createTask(newTaskRequest)
|
||||
}
|
||||
|
||||
function onUpscaleClick(req, img) {
|
||||
enqueueImageVariationTask(req, img, {
|
||||
use_upscale: upscaleModelField.value
|
||||
})
|
||||
}
|
||||
|
||||
function onFixFacesClick(req, img) {
|
||||
enqueueImageVariationTask(req, img, {
|
||||
use_face_correction: 'GFPGANv1.3'
|
||||
})
|
||||
}
|
||||
|
||||
function onContinueDrawingClick(req, img) {
|
||||
enqueueImageVariationTask(req, img, {
|
||||
num_inference_steps: parseInt(req.num_inference_steps) + 25
|
||||
})
|
||||
}
|
||||
|
||||
// makes a single image. don't call this directly, use makeImage() instead
|
||||
@ -484,6 +488,11 @@ async function doMakeImage(task) {
|
||||
return
|
||||
}
|
||||
|
||||
const RETRY_DELAY_IF_BUFFER_IS_EMPTY = 1000 // ms
|
||||
const RETRY_DELAY_IF_SERVER_IS_BUSY = 30 * 1000 // ms, status_code 503, already a task running
|
||||
const TASK_START_DELAY_ON_SERVER = 1500 // ms
|
||||
const SERVER_STATE_VALIDITY_DURATION = 10 * 1000 // ms
|
||||
|
||||
const reqBody = task.reqBody
|
||||
const batchCount = task.batchCount
|
||||
const outputContainer = document.createElement('div')
|
||||
@ -509,11 +518,12 @@ async function doMakeImage(task) {
|
||||
})
|
||||
renderRequest = await res.json()
|
||||
// status_code 503, already a task running.
|
||||
} while (res.status === 503 && await asyncDelay(30 * 1000))
|
||||
} while (res.status === 503 && await asyncDelay(RETRY_DELAY_IF_SERVER_IS_BUSY))
|
||||
if (typeof renderRequest?.stream !== 'string') {
|
||||
console.log('Endpoint response: ', renderRequest)
|
||||
throw new Error(renderRequest.detail || 'Endpoint response does not contains a response stream url.')
|
||||
}
|
||||
|
||||
task['taskStatusLabel'].innerText = "Waiting"
|
||||
task['taskStatusLabel'].classList.add('waitingTaskLabel')
|
||||
task['taskStatusLabel'].classList.remove('activeTaskLabel')
|
||||
@ -523,7 +533,7 @@ async function doMakeImage(task) {
|
||||
if (!isServerAvailable()) {
|
||||
throw new Error('Connexion with server lost.')
|
||||
}
|
||||
} while (serverState.time > (Date.now() - (10 * 1000)) && serverState.task !== renderRequest.task)
|
||||
} while (Date.now() < (serverState.time + SERVER_STATE_VALIDITY_DURATION) && serverState.task !== renderRequest.task)
|
||||
switch(serverState.session) {
|
||||
case 'pending':
|
||||
case 'running':
|
||||
@ -535,9 +545,10 @@ async function doMakeImage(task) {
|
||||
default:
|
||||
throw new Error('Unexpected server task state: ' + serverState.session || 'Undefined')
|
||||
}
|
||||
|
||||
while (serverState.task === renderRequest.task && serverState.session === 'pending') {
|
||||
// Wait for task to start on server.
|
||||
await asyncDelay(1500)
|
||||
await asyncDelay(TASK_START_DELAY_ON_SERVER)
|
||||
}
|
||||
|
||||
// Task started!
|
||||
@ -631,7 +642,7 @@ async function doMakeImage(task) {
|
||||
}
|
||||
if (readComplete && finalJSON.length <= 0) {
|
||||
if (res.status === 200) {
|
||||
await asyncDelay(1000)
|
||||
await asyncDelay(RETRY_DELAY_IF_BUFFER_IS_EMPTY)
|
||||
res = await fetch(renderRequest.stream, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
@ -747,7 +758,7 @@ async function checkTasks() {
|
||||
})
|
||||
}
|
||||
if (genSeeds) {
|
||||
newTask.reqBody.seed = startSeed + (i * newTask.reqBody.num_outputs)
|
||||
newTask.reqBody.seed = parseInt(startSeed) + (i * newTask.reqBody.num_outputs)
|
||||
newTask.seed = newTask.reqBody.seed
|
||||
} else if (newTask.seed !== newTask.reqBody.seed) {
|
||||
newTask.seed = newTask.reqBody.seed
|
||||
@ -944,14 +955,34 @@ function getPrompts() {
|
||||
}
|
||||
|
||||
prompts = prompts.split('\n')
|
||||
prompts = prompts.map(prompt => prompt.trim())
|
||||
prompts = prompts.filter(prompt => prompt !== '')
|
||||
|
||||
let promptsToMake = applySetOperator(prompts)
|
||||
promptsToMake = applyPermuteOperator(promptsToMake)
|
||||
|
||||
if (activeTags.length <= 0) {
|
||||
return promptsToMake
|
||||
}
|
||||
|
||||
const promptTags = activeTags.map(x => x.name).join(", ")
|
||||
return promptsToMake.map((prompt) => `${prompt}, ${promptTags}`)
|
||||
}
|
||||
|
||||
function applySetOperator(prompts) {
|
||||
let promptsToMake = []
|
||||
let braceExpander = new BraceExpander()
|
||||
prompts.forEach(prompt => {
|
||||
let expandedPrompts = braceExpander.expand(prompt)
|
||||
promptsToMake = promptsToMake.concat(expandedPrompts)
|
||||
})
|
||||
|
||||
return promptsToMake
|
||||
}
|
||||
|
||||
function applyPermuteOperator(prompts) {
|
||||
let promptsToMake = []
|
||||
prompts.forEach(prompt => {
|
||||
prompt = prompt.trim()
|
||||
if (prompt === '') {
|
||||
return
|
||||
}
|
||||
|
||||
let promptMatrix = prompt.split('|')
|
||||
prompt = promptMatrix.shift().trim()
|
||||
promptsToMake.push(prompt)
|
||||
@ -964,11 +995,8 @@ function getPrompts() {
|
||||
promptsToMake = promptsToMake.concat(promptPermutations)
|
||||
}
|
||||
})
|
||||
if (activeTags.length <= 0) {
|
||||
return promptsToMake
|
||||
}
|
||||
const promptTags = activeTags.map(x => x.name).join(", ")
|
||||
return promptsToMake.map((prompt) => `${prompt}, ${promptTags}`)
|
||||
|
||||
return promptsToMake
|
||||
}
|
||||
|
||||
function permutePrompts(promptBase, promptMatrix) {
|
||||
@ -1150,6 +1178,7 @@ function updateGuidanceScaleSlider() {
|
||||
}
|
||||
|
||||
guidanceScaleSlider.value = guidanceScaleField.value * 10
|
||||
guidanceScaleSlider.dispatchEvent(new Event("change"))
|
||||
}
|
||||
|
||||
guidanceScaleSlider.addEventListener('input', updateGuidanceScale)
|
||||
@ -1168,6 +1197,7 @@ function updatePromptStrengthSlider() {
|
||||
}
|
||||
|
||||
promptStrengthSlider.value = promptStrengthField.value * 100
|
||||
promptStrengthSlider.dispatchEvent(new Event("change"))
|
||||
}
|
||||
|
||||
promptStrengthSlider.addEventListener('input', updatePromptStrength)
|
||||
@ -1177,7 +1207,7 @@ updatePromptStrength()
|
||||
useBetaChannelField.addEventListener('click', async function(e) {
|
||||
if (!isServerAvailable()) {
|
||||
// logError('The server is still starting up..')
|
||||
alert('The server is not available.')
|
||||
alert('The server is still starting up..')
|
||||
e.preventDefault()
|
||||
return false
|
||||
}
|
||||
|
Reference in New Issue
Block a user