# Conflicts:
#	ui/media/js/main.js
#	ui/sd_internal/runtime.py
#	ui/server.py
This commit is contained in:
Marc-Andre Ferland 2022-10-20 20:08:23 -04:00
commit 849d1d7ebd
7 changed files with 398 additions and 124 deletions

View File

@ -6,6 +6,7 @@
<link rel="icon" type="image/png" href="/media/images/favicon-32x32.png" sizes="32x32"> <link rel="icon" type="image/png" href="/media/images/favicon-32x32.png" sizes="32x32">
<link rel="stylesheet" href="/media/css/fonts.css?v=1"> <link rel="stylesheet" href="/media/css/fonts.css?v=1">
<link rel="stylesheet" href="/media/css/themes.css?v=1"> <link rel="stylesheet" href="/media/css/themes.css?v=1">
<link rel="stylesheet" href="/media/css/auto-save.css?v=1">
<link rel="stylesheet" href="/media/css/main.css?v=1"> <link rel="stylesheet" href="/media/css/main.css?v=1">
<link rel="stylesheet" href="/media/css/modifier-thumbnails.css?v=1"> <link rel="stylesheet" href="/media/css/modifier-thumbnails.css?v=1">
<link rel="stylesheet" href="/media/css/fontawesome-all.min.css?v=1"> <link rel="stylesheet" href="/media/css/fontawesome-all.min.css?v=1">
@ -17,7 +18,7 @@
<div id="container"> <div id="container">
<div id="top-nav"> <div id="top-nav">
<div id="logo"> <div id="logo">
<h1>Stable Diffusion UI <small>v2.29 <span id="updateBranchLabel"></span></small></h1> <h1>Stable Diffusion UI <small>v2.3.2 <span id="updateBranchLabel"></span></small></h1>
</div> </div>
<ul id="top-nav-items"> <ul id="top-nav-items">
<li class="dropdown"> <li class="dropdown">
@ -107,7 +108,7 @@
<ul id="editor-settings-entries" class="collapsible-content"> <ul id="editor-settings-entries" class="collapsible-content">
<li><table> <li><table>
<tr><b class="settings-subheader">Image Settings</b></tr> <tr><b class="settings-subheader">Image Settings</b></tr>
<tr class="pl-5"><td><label for="seed">Seed:</label></td><td><input id="seed" name="seed" size="10" value="30000"> <input id="random_seed" name="random_seed" type="checkbox" checked> <label for="random_seed">Random</label></td></tr> <tr class="pl-5"><td><label for="seed">Seed:</label></td><td><input id="seed" name="seed" size="10" value="30000"> <input id="random_seed" name="random_seed" type="checkbox" checked><label for="random_seed">Random</label></td></tr>
<tr class="pl-5"><td><label for="num_outputs_total">Number of Images:</label></td><td><input id="num_outputs_total" name="num_outputs_total" value="1" size="1"> <label><small>(total)</small></label> <input id="num_outputs_parallel" name="num_outputs_parallel" value="1" size="1"> <label for="num_outputs_parallel"><small>(in parallel)</small></label></td></tr> <tr class="pl-5"><td><label for="num_outputs_total">Number of Images:</label></td><td><input id="num_outputs_total" name="num_outputs_total" value="1" size="1"> <label><small>(total)</small></label> <input id="num_outputs_parallel" name="num_outputs_parallel" value="1" size="1"> <label for="num_outputs_parallel"><small>(in parallel)</small></label></td></tr>
<tr class="pl-5"><td><label for="stable_diffusion_model">Model:</label></td><td> <tr class="pl-5"><td><label for="stable_diffusion_model">Model:</label></td><td>
<select id="stable_diffusion_model" name="stable_diffusion_model"> <select id="stable_diffusion_model" name="stable_diffusion_model">
@ -251,11 +252,12 @@
</div> </div>
</body> </body>
<script src="media/js/utils.js?v=1"></script> <script src="media/js/plugins.js?v=1"></script>
<script src="media/js/utils.js?v=3"></script>
<script src="media/js/inpainting-editor.js?v=1"></script> <script src="media/js/inpainting-editor.js?v=1"></script>
<script src="media/js/image-modifiers.js"></script> <script src="media/js/image-modifiers.js"></script>
<script src="media/js/auto-save.js?v=1"></script> <script src="media/js/auto-save.js?v=1"></script>
<script src="media/js/main.js?v=1"></script> <script src="media/js/main.js?v=3"></script>
<script src="media/js/themes.js?v=1"></script> <script src="media/js/themes.js?v=1"></script>
<script> <script>
async function init() { async function init() {
@ -264,6 +266,7 @@ async function init() {
await getAppConfig() await getAppConfig()
await getModels() await getModels()
await initSettings() await initSettings()
await loadUIPlugins()
setInterval(healthCheck, HEALTH_PING_INTERVAL * 1000) setInterval(healthCheck, HEALTH_PING_INTERVAL * 1000)
healthCheck() healthCheck()

View File

@ -257,11 +257,6 @@ function logError(msg, res, outputMsg) {
console.log('request error', res) console.log('request error', res)
setStatus('request', 'error', 'error') setStatus('request', 'error', 'error')
} }
function asyncDelay(timeout) {
return new Promise(function(resolve, reject) {
setTimeout(resolve, timeout, true)
})
}
function playSound() { function playSound() {
const audio = new Audio('/media/ding.mp3') const audio = new Audio('/media/ding.mp3')
@ -366,34 +361,42 @@ function showImages(reqBody, res, outputContainer, livePreview) {
const imageSeedLabel = imageItemElem.querySelector('.imgSeedLabel') const imageSeedLabel = imageItemElem.querySelector('.imgSeedLabel')
imageSeedLabel.innerText = 'Seed: ' + req.seed imageSeedLabel.innerText = 'Seed: ' + req.seed
const buttons = { let buttons = [
'imgUseBtn': { html: 'Use as Input', click: getUseAsInputHandler(imageItemElem) }, { text: 'Use as Input', on_click: onUseAsInputClick },
'imgSaveBtn': { html: 'Download', click: getSaveImageHandler(imageItemElem, req['output_format']) }, { text: 'Download', on_click: onDownloadImageClick },
'imgX2Btn': { html: 'Double Size', click: getStartNewTaskHandler(req, imageItemElem, 'img2img_X2') }, { text: 'Make Similar Images', on_click: onMakeSimilarClick },
'imgRedoBtn': { html: 'Redo', click: getStartNewTaskHandler(req, imageItemElem, 'img2img') }, { text: 'Draw another 25 steps', on_click: onContinueDrawingClick },
} { text: 'Upscale', on_click: onUpscaleClick, filter: (req, img) => !req.use_upscale },
if (!req.use_upscale) { { text: 'Fix Faces', on_click: onFixFacesClick, filter: (req, img) => !req.use_face_correction }
buttons.upscaleBtn = { html: 'Upscale', click: getStartNewTaskHandler(req, imageItemElem, 'upscale') } ]
}
// include the plugins
buttons = buttons.concat(PLUGINS['IMAGE_INFO_BUTTONS'])
const imgItemInfo = imageItemElem.querySelector('.imgItemInfo') const imgItemInfo = imageItemElem.querySelector('.imgItemInfo')
const createButton = function(name, btnInfo) { const img = imageItemElem.querySelector('img')
const createButton = function(btnInfo) {
const newButton = document.createElement('button') const newButton = document.createElement('button')
newButton.classList.add(name)
newButton.classList.add('tasksBtns') newButton.classList.add('tasksBtns')
newButton.innerHTML = btnInfo.html newButton.innerText = btnInfo.text
newButton.addEventListener('click', btnInfo.click) newButton.addEventListener('click', function() {
btnInfo.on_click(req, img)
})
imgItemInfo.appendChild(newButton) 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) { function onUseAsInputClick(req, img) {
return function() { const imgData = img.src
const imageElem = imageItemElem.querySelector('img')
const imgData = imageElem.src
const imageSeed = imageElem.getAttribute('data-seed')
initImageSelector.value = null initImageSelector.value = null
initImagePreview.src = imgData initImagePreview.src = imgData
@ -403,79 +406,80 @@ function getUseAsInputHandler(imageItemElem) {
promptStrengthContainer.style.display = 'table-row' promptStrengthContainer.style.display = 'table-row'
maskSetting.checked = false maskSetting.checked = false
samplerSelectionContainer.style.display = 'none' samplerSelectionContainer.style.display = 'none'
// maskSetting.style.display = 'block'
// randomSeedField.checked = false
// seedField.value = imageSeed
// seedField.disabled = false
}
} }
function getSaveImageHandler(imageItemElem, outputFormat) { function onDownloadImageClick(req, img) {
return function() { const imgData = img.src
const imageElem = imageItemElem.querySelector('img') const imageSeed = img.getAttribute('data-seed')
const imgData = imageElem.src const imagePrompt = img.getAttribute('data-prompt')
const imageSeed = imageElem.getAttribute('data-seed') const imageInferenceSteps = img.getAttribute('data-steps')
const imagePrompt = imageElem.getAttribute('data-prompt') const imageGuidanceScale = img.getAttribute('data-guidance')
const imageInferenceSteps = imageElem.getAttribute('data-steps')
const imageGuidanceScale = imageElem.getAttribute('data-guidance')
const imgDownload = document.createElement('a') const imgDownload = document.createElement('a')
imgDownload.download = createFileName(imagePrompt, imageSeed, imageInferenceSteps, imageGuidanceScale, outputFormat) imgDownload.download = createFileName(imagePrompt, imageSeed, imageInferenceSteps, imageGuidanceScale, req['output_format'])
imgDownload.href = imgData imgDownload.href = imgData
imgDownload.click() imgDownload.click()
}
} }
function getStartNewTaskHandler(reqBody, imageItemElem, mode) {
return function() { function modifyCurrentRequest(req, reqDiff) {
if (!isServerAvailable()) {
alert('The server is not available.')
return
}
const imageElem = imageItemElem.querySelector('img')
const newTaskRequest = getCurrentUserRequest() const newTaskRequest = getCurrentUserRequest()
switch (mode) {
case 'img2img': newTaskRequest.reqBody = Object.assign({}, req, reqDiff, {
case 'img2img_X2': use_cpu: useCPUField.checked
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.seed = newTaskRequest.reqBody.seed
newTaskRequest.numOutputsTotal = 1
newTaskRequest.batchCount = 1 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) 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 // makes a single image. don't call this directly, use makeImage() instead
@ -484,6 +488,11 @@ async function doMakeImage(task) {
return 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 reqBody = task.reqBody
const batchCount = task.batchCount const batchCount = task.batchCount
const outputContainer = document.createElement('div') const outputContainer = document.createElement('div')
@ -509,11 +518,12 @@ async function doMakeImage(task) {
}) })
renderRequest = await res.json() renderRequest = await res.json()
// status_code 503, already a task running. // 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') { if (typeof renderRequest?.stream !== 'string') {
console.log('Endpoint response: ', renderRequest) console.log('Endpoint response: ', renderRequest)
throw new Error(renderRequest.detail || 'Endpoint response does not contains a response stream url.') throw new Error(renderRequest.detail || 'Endpoint response does not contains a response stream url.')
} }
task['taskStatusLabel'].innerText = "Waiting" task['taskStatusLabel'].innerText = "Waiting"
task['taskStatusLabel'].classList.add('waitingTaskLabel') task['taskStatusLabel'].classList.add('waitingTaskLabel')
task['taskStatusLabel'].classList.remove('activeTaskLabel') task['taskStatusLabel'].classList.remove('activeTaskLabel')
@ -523,7 +533,7 @@ async function doMakeImage(task) {
if (!isServerAvailable()) { if (!isServerAvailable()) {
throw new Error('Connexion with server lost.') 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) { switch(serverState.session) {
case 'pending': case 'pending':
case 'running': case 'running':
@ -535,9 +545,10 @@ async function doMakeImage(task) {
default: default:
throw new Error('Unexpected server task state: ' + serverState.session || 'Undefined') throw new Error('Unexpected server task state: ' + serverState.session || 'Undefined')
} }
while (serverState.task === renderRequest.task && serverState.session === 'pending') { while (serverState.task === renderRequest.task && serverState.session === 'pending') {
// Wait for task to start on server. // Wait for task to start on server.
await asyncDelay(1500) await asyncDelay(TASK_START_DELAY_ON_SERVER)
} }
// Task started! // Task started!
@ -631,7 +642,7 @@ async function doMakeImage(task) {
} }
if (readComplete && finalJSON.length <= 0) { if (readComplete && finalJSON.length <= 0) {
if (res.status === 200) { if (res.status === 200) {
await asyncDelay(1000) await asyncDelay(RETRY_DELAY_IF_BUFFER_IS_EMPTY)
res = await fetch(renderRequest.stream, { res = await fetch(renderRequest.stream, {
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
@ -747,7 +758,7 @@ async function checkTasks() {
}) })
} }
if (genSeeds) { 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 newTask.seed = newTask.reqBody.seed
} else if (newTask.seed !== newTask.reqBody.seed) { } else if (newTask.seed !== newTask.reqBody.seed) {
newTask.seed = newTask.reqBody.seed newTask.seed = newTask.reqBody.seed
@ -944,14 +955,34 @@ function getPrompts() {
} }
prompts = prompts.split('\n') prompts = prompts.split('\n')
prompts = prompts.map(prompt => prompt.trim())
prompts = prompts.filter(prompt => prompt !== '')
let promptsToMake = [] let promptsToMake = applySetOperator(prompts)
prompts.forEach(prompt => { promptsToMake = applyPermuteOperator(promptsToMake)
prompt = prompt.trim()
if (prompt === '') { if (activeTags.length <= 0) {
return 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 => {
let promptMatrix = prompt.split('|') let promptMatrix = prompt.split('|')
prompt = promptMatrix.shift().trim() prompt = promptMatrix.shift().trim()
promptsToMake.push(prompt) promptsToMake.push(prompt)
@ -964,11 +995,8 @@ function getPrompts() {
promptsToMake = promptsToMake.concat(promptPermutations) promptsToMake = promptsToMake.concat(promptPermutations)
} }
}) })
if (activeTags.length <= 0) {
return promptsToMake return promptsToMake
}
const promptTags = activeTags.map(x => x.name).join(", ")
return promptsToMake.map((prompt) => `${prompt}, ${promptTags}`)
} }
function permutePrompts(promptBase, promptMatrix) { function permutePrompts(promptBase, promptMatrix) {
@ -1150,6 +1178,7 @@ function updateGuidanceScaleSlider() {
} }
guidanceScaleSlider.value = guidanceScaleField.value * 10 guidanceScaleSlider.value = guidanceScaleField.value * 10
guidanceScaleSlider.dispatchEvent(new Event("change"))
} }
guidanceScaleSlider.addEventListener('input', updateGuidanceScale) guidanceScaleSlider.addEventListener('input', updateGuidanceScale)
@ -1168,6 +1197,7 @@ function updatePromptStrengthSlider() {
} }
promptStrengthSlider.value = promptStrengthField.value * 100 promptStrengthSlider.value = promptStrengthField.value * 100
promptStrengthSlider.dispatchEvent(new Event("change"))
} }
promptStrengthSlider.addEventListener('input', updatePromptStrength) promptStrengthSlider.addEventListener('input', updatePromptStrength)
@ -1177,7 +1207,7 @@ updatePromptStrength()
useBetaChannelField.addEventListener('click', async function(e) { useBetaChannelField.addEventListener('click', async function(e) {
if (!isServerAvailable()) { if (!isServerAvailable()) {
// logError('The server is still starting up..') // logError('The server is still starting up..')
alert('The server is not available.') alert('The server is still starting up..')
e.preventDefault() e.preventDefault()
return false return false
} }

47
ui/media/js/plugins.js Normal file
View File

@ -0,0 +1,47 @@
const PLUGIN_API_VERSION = "1.0"
const PLUGINS = {
/**
* Register new buttons to show on each output image.
*
* Example:
* PLUGINS['IMAGE_INFO_BUTTONS'].push({
* text: 'Make a Similar Image',
* on_click: function(origRequest, image) {
* let newTaskRequest = getCurrentUserRequest()
* newTaskRequest.reqBody = Object.assign({}, origRequest, {
* init_image: image.src,
* prompt_strength: 0.7,
* seed: Math.floor(Math.random() * 10000000)
* })
* newTaskRequest.seed = newTaskRequest.reqBody.seed
* createTask(newTaskRequest)
* },
* filter: function(origRequest, image) {
* // this is an optional function. return true/false to show/hide the button
* // if this function isn't set, the button will always be visible
* return true
* }
* })
*/
IMAGE_INFO_BUTTONS: []
}
async function loadUIPlugins() {
try {
let res = await fetch('/get/ui_plugins')
if (res.status === 200) {
res = await res.json()
res.forEach(pluginPath => {
let script = document.createElement('script')
script.src = pluginPath + '?t=' + Date.now()
console.log('loading plugin', pluginPath)
document.head.appendChild(script)
})
}
} catch (e) {
console.log('error fetching plugin paths', e)
}
}

View File

@ -101,3 +101,182 @@ function millisecondsToStr(milliseconds) {
return s return s
} }
// https://rosettacode.org/wiki/Brace_expansion#JavaScript
function BraceExpander() {
'use strict'
// Index of any closing brace matching the opening
// brace at iPosn,
// with the indices of any immediately-enclosed commas.
function bracePair(tkns, iPosn, iNest, lstCommas) {
if (iPosn >= tkns.length || iPosn < 0) return null;
var t = tkns[iPosn],
n = (t === '{') ? (
iNest + 1
) : (t === '}' ? (
iNest - 1
) : iNest),
lst = (t === ',' && iNest === 1) ? (
lstCommas.concat(iPosn)
) : lstCommas;
return n ? bracePair(tkns, iPosn + 1, n, lst) : {
close: iPosn,
commas: lst
};
}
// Parse of a SYNTAGM subtree
function andTree(dctSofar, tkns) {
if (!tkns.length) return [dctSofar, []];
var dctParse = dctSofar ? dctSofar : {
fn: and,
args: []
},
head = tkns[0],
tail = head ? tkns.slice(1) : [],
dctBrace = head === '{' ? bracePair(
tkns, 0, 0, []
) : null,
lstOR = dctBrace && (
dctBrace.close
) && dctBrace.commas.length ? (
splitAt(dctBrace.close + 1, tkns)
) : null;
return andTree({
fn: and,
args: dctParse.args.concat(
lstOR ? (
orTree(dctParse, lstOR[0], dctBrace.commas)
) : head
)
}, lstOR ? (
lstOR[1]
) : tail);
}
// Parse of a PARADIGM subtree
function orTree(dctSofar, tkns, lstCommas) {
if (!tkns.length) return [dctSofar, []];
var iLast = lstCommas.length;
return {
fn: or,
args: splitsAt(
lstCommas, tkns
).map(function (x, i) {
var ts = x.slice(
1, i === iLast ? (
-1
) : void 0
);
return ts.length ? ts : [''];
}).map(function (ts) {
return ts.length > 1 ? (
andTree(null, ts)[0]
) : ts[0];
})
};
}
// List of unescaped braces and commas, and remaining strings
function tokens(str) {
// Filter function excludes empty splitting artefacts
var toS = function (x) {
return x.toString();
};
return str.split(/(\\\\)/).filter(toS).reduce(function (a, s) {
return a.concat(s.charAt(0) === '\\' ? s : s.split(
/(\\*[{,}])/
).filter(toS));
}, []);
}
// PARSE TREE OPERATOR (1 of 2)
// Each possible head * each possible tail
function and(args) {
var lng = args.length,
head = lng ? args[0] : null,
lstHead = "string" === typeof head ? (
[head]
) : head;
return lng ? (
1 < lng ? lstHead.reduce(function (a, h) {
return a.concat(
and(args.slice(1)).map(function (t) {
return h + t;
})
);
}, []) : lstHead
) : [];
}
// PARSE TREE OPERATOR (2 of 2)
// Each option flattened
function or(args) {
return args.reduce(function (a, b) {
return a.concat(b);
}, []);
}
// One list split into two (first sublist length n)
function splitAt(n, lst) {
return n < lst.length + 1 ? [
lst.slice(0, n), lst.slice(n)
] : [lst, []];
}
// One list split into several (sublist lengths [n])
function splitsAt(lstN, lst) {
return lstN.reduceRight(function (a, x) {
return splitAt(x, a[0]).concat(a.slice(1));
}, [lst]);
}
// Value of the parse tree
function evaluated(e) {
return typeof e === 'string' ? e :
e.fn(e.args.map(evaluated));
}
// JSON prettyprint (for parse tree, token list etc)
function pp(e) {
return JSON.stringify(e, function (k, v) {
return typeof v === 'function' ? (
'[function ' + v.name + ']'
) : v;
}, 2)
}
// ----------------------- MAIN ------------------------
// s -> [s]
this.expand = function(s) {
// BRACE EXPRESSION PARSED
var dctParse = andTree(null, tokens(s))[0];
// ABSTRACT SYNTAX TREE LOGGED
// console.log(pp(dctParse));
// AST EVALUATED TO LIST OF STRINGS
return evaluated(dctParse);
}
}
function asyncDelay(timeout) {
return new Promise(function(resolve, reject) {
setTimeout(resolve, timeout, true)
})
}

View File

@ -576,7 +576,7 @@ def do_mk_img(req: Request):
if (len(filters_applied) > 0): if (len(filters_applied) > 0):
filtered_image = Image.fromarray(x_sample) filtered_image = Image.fromarray(x_sample)
filtered_img_data = img_to_base64_str(filtered_image, req.output_format) filtered_img_data = img_to_base64_str(filtered_image, req.output_format)
response_image = ResponseImage(data=filtered_img_data, seed=req.seed) response_image = ResponseImage(data=filtered_img_data, seed=opt_seed)
res.images.append(response_image) res.images.append(response_image)
if req.save_to_disk_path is not None: if req.save_to_disk_path is not None:
filtered_img_out_path = get_base_path(req.save_to_disk_path, req.session_id, prompts[0], img_id, req.output_format, "_".join(filters_applied)) filtered_img_out_path = get_base_path(req.save_to_disk_path, req.session_id, prompts[0], img_id, req.output_format, "_".join(filters_applied))

View File

@ -7,7 +7,7 @@ Notes:
import json import json
import traceback import traceback
TASK_TTL = 15 * 60 # Discard last session's task timeout TASK_TTL = 15 * 60 # seconds, Discard last session's task timeout
import queue, threading, time, weakref import queue, threading, time, weakref
from typing import Any, Generator, Hashable, Optional, Union from typing import Any, Generator, Hashable, Optional, Union

View File

@ -16,6 +16,7 @@ sys.path.append(os.path.dirname(SD_UI_DIR))
CONFIG_DIR = os.path.abspath(os.path.join(SD_UI_DIR, '..', 'scripts')) CONFIG_DIR = os.path.abspath(os.path.join(SD_UI_DIR, '..', 'scripts'))
MODELS_DIR = os.path.abspath(os.path.join(SD_DIR, '..', 'models')) MODELS_DIR = os.path.abspath(os.path.join(SD_DIR, '..', 'models'))
UI_PLUGINS_DIR = os.path.abspath(os.path.join(SD_DIR, '..', 'plugins', 'ui'))
OUTPUT_DIRNAME = "Stable Diffusion UI" # in the user's home folder OUTPUT_DIRNAME = "Stable Diffusion UI" # in the user's home folder
TASK_TTL = 15 * 60 # Discard last session's task timeout TASK_TTL = 15 * 60 # Discard last session's task timeout
@ -47,11 +48,15 @@ app = FastAPI()
modifiers_cache = None modifiers_cache = None
outpath = os.path.join(os.path.expanduser("~"), OUTPUT_DIRNAME) outpath = os.path.join(os.path.expanduser("~"), OUTPUT_DIRNAME)
os.makedirs(UI_PLUGINS_DIR, exist_ok=True)
# don't show access log entries for URLs that start with the given prefix # don't show access log entries for URLs that start with the given prefix
ACCESS_LOG_SUPPRESS_PATH_PREFIXES = ['/ping', '/image', '/modifier-thumbnails'] ACCESS_LOG_SUPPRESS_PATH_PREFIXES = ['/ping', '/image', '/modifier-thumbnails']
NOCACHE_HEADERS={"Cache-Control": "no-cache, no-store, must-revalidate", "Pragma": "no-cache", "Expires": "0"} NOCACHE_HEADERS={"Cache-Control": "no-cache, no-store, must-revalidate", "Pragma": "no-cache", "Expires": "0"}
app.mount('/media', StaticFiles(directory=os.path.join(SD_UI_DIR, 'media/')), name="media")
app.mount('/media', StaticFiles(directory=os.path.join(SD_UI_DIR, 'media')), name="media")
app.mount('/plugins', StaticFiles(directory=UI_PLUGINS_DIR), name="plugins")
config_cached = None config_cached = None
config_last_mod_time = 0 config_last_mod_time = 0
@ -211,6 +216,15 @@ def getModels():
return models return models
def getUIPlugins():
plugins = []
for file in os.listdir(UI_PLUGINS_DIR):
if file.endswith('.plugin.js'):
plugins.append(f'/plugins/{file}')
return plugins
@app.get('/get/{key:path}') @app.get('/get/{key:path}')
def read_web_data(key:str=None): def read_web_data(key:str=None):
if not key: # /get without parameters, stable-diffusion easter egg. if not key: # /get without parameters, stable-diffusion easter egg.
@ -224,6 +238,7 @@ def read_web_data(key:str=None):
return JSONResponse(getModels(), headers=NOCACHE_HEADERS) return JSONResponse(getModels(), headers=NOCACHE_HEADERS)
elif key == 'modifiers': return FileResponse(os.path.join(SD_UI_DIR, 'modifiers.json'), headers=NOCACHE_HEADERS) elif key == 'modifiers': return FileResponse(os.path.join(SD_UI_DIR, 'modifiers.json'), headers=NOCACHE_HEADERS)
elif key == 'output_dir': return JSONResponse({ 'output_dir': outpath }, headers=NOCACHE_HEADERS) elif key == 'output_dir': return JSONResponse({ 'output_dir': outpath }, headers=NOCACHE_HEADERS)
elif key == 'ui_plugins': return JSONResponse(getUIPlugins(), headers=NOCACHE_HEADERS)
else: else:
raise HTTPException(status_code=404, detail=f'Request for unknown {key}') # HTTP404 Not Found raise HTTPException(status_code=404, detail=f'Request for unknown {key}') # HTTP404 Not Found