toto10's picture
Upload folder using huggingface_hub (#1)
34097e9
raw
history blame
26.5 kB
const general = require('../general')
const psapi = require('../../psapi')
const html_manip = require('../html_manip')
const layer_util = require('../layer')
const dummy = require('../dummy')
const io = require('../io')
class HordeSettings {
static {}
static async saveSettings() {
try {
const settings = await getSettings()
let native_horde_settings = await mapPluginSettingsToHorde(settings)
const horde_api_key = html_manip.getHordeApiKey()
native_horde_settings['api_key'] = html_manip.getHordeApiKey()
await io.IOJson.saveHordeSettingsToFile(native_horde_settings)
} catch (e) {
console.warn(e)
}
}
static async loadSettings() {
try {
let native_horde_settings =
await io.IOJson.loadHordeSettingsFromFile()
html_manip.setHordeApiKey(native_horde_settings['api_key'])
} catch (e) {
console.warn(e)
}
}
}
class hordeGenerator {
//horde generation process:
//*) get the settings
//*) get send request
//*) wait for response
//*) load the image to the canvas
//*) move and scale image to the selection
//*) save the image to history/data folder
//*) load the image data into the plugin / viewer tab
//*)
//other options:
//*)interrupt the generation process
//*)cancel the generation process on error
constructor() {
this.horde_settings
this.plugin_settings
this.currentGenerationResult = null
this.requestStatus = null
this.isProcessHordeResultCalled = false
this.maxWaitTime = 0
this.waiting = 0
this.isCanceled = false
this.horde_id = null
this.last_horde_id = null
}
async getSettings() {
try {
const workers = await getWorkers()
const workers_ids = getWorkerID(workers)
const settings = await getSettings()
this.plugin_settings = settings
let payload = await mapPluginSettingsToHorde(settings)
// payload['workers'] = workers_ids
payload['workers'] = []
this.horde_settings = payload
return this.horde_settings
} catch (e) {
console.warn('getSettings: ', e)
}
}
/**
* @returns {json}{payload, dir_name, images_info, metadata}
*/
async generateRequest(settings) {
try {
this.horde_id = null //reset request_id
this.requestStatus = await requestHorde(settings)
if (this.requestStatus?.message) {
await app.showAlert(this.requestStatus?.message)
}
this.horde_id = this.requestStatus.id
console.log(
'generateRequest this.requestStatus: ',
this.requestStatus
)
const images_info = await this.startCheckingProgress()
const result = await this.toGenerationFormat(images_info)
console.warn('generateRequest() images_info: ', images_info)
console.warn('generateRequest() result: ', result)
html_manip.updateProgressBarsHtml(0) // reset progress bar
return result
} catch (e) {
this.horde_id = null
console.warn(e)
}
}
async generate() {
//*) get the settings
this.horde_settings = await this.getSettings()
//*) send generateRequest() and trigger the progress bar update
this.isCanceled = false
const result = await this.generateRequest(this.horde_settings)
return result
//*) store the generation result in the currentGenerationResult
//*) return the generation currentGenerationResult
}
isValidGeneration() {
if (this.currentGenerationResult) {
return true // if true if valid, false otherwise
} else {
return false
}
}
preGenerate() {}
// async layerToBase64WebpToFile
//convert layer to .webp file
//read the .webp file as buffer data base64 .webp
async layerToBase64Webp(layer, document_name, image_name) {
const width = html_manip.getWidth()
const height = html_manip.getHeight()
const image_buffer = await psapi.newExportPng(
layer,
image_name,
width,
height
)
const base64_image = _arrayBufferToBase64(image_buffer) //convert the buffer to base64
//send the base64 to the server to save the file in the desired directory
// await sdapi.requestSavePng(base64_image, image_name)
await saveFileInSubFolder(base64_image, document_name, image_name)
return base64_image
}
async layerToBase64ToFile(layer, document_name, image_name) {
const width = html_manip.getWidth()
const height = html_manip.getHeight()
const image_buffer = await psapi.newExportPng(
layer,
image_name,
width,
height
)
const base64_image = _arrayBufferToBase64(image_buffer) //convert the buffer to base64
//send the base64 to the server to save the file in the desired directory
// await sdapi.requestSavePng(base64_image, image_name)
await saveFileInSubFolder(base64_image, document_name, image_name)
return base64_image
}
async toGenerationFormat(images_info) {
//convert the output of native horde generation to the values that generate() can use
try {
//images_info[0] = {path:path,base64:base64png}
// let last_images_paths = await silentImagesToLayersExe(images_info)
let last_images_paths = {}
for (const image_info of images_info) {
const path = image_info['path']
// const base64_image = image_info['base64']
const layer = image_info['layer']
const [document_name, image_name] = path.split('/')
// await saveFileInSubFolder(base64_image, document_name, image_name)
image_info['base64'] = await this.layerToBase64ToFile(
layer,
document_name,
image_name
)
// delete the layer made by the webp image.
await layer_util.deleteLayers([layer])
// await layer.delete()
// const json_file_name = `${image_name.split('.')[0]}.json`
this.plugin_settings['auto_metadata'] =
image_info?.auto_metadata
// g_generation_session.base64OutputImages[path] =
// image_info['base64']
// await saveJsonFileInSubFolder(
// this.plugin_settings,
// document_name,
// json_file_name
// ) //save the settings
// last_images_paths[path] = image_info['layer']
// images_info.push({
// base64: i,
// path: image_path,
// auto_metadata: auto_metadata_json,
// })
// // console.log("metadata_json: ", metadata_json)
}
// if (g_generation_session.isFirstGeneration) {
// //store them in the generation session for viewer manager to use
// g_generation_session.image_paths_to_layers = last_images_paths
// } else {
// g_generation_session.image_paths_to_layers = {
// ...g_generation_session.image_paths_to_layers,
// ...last_images_paths,
// }
// // g_number_generation_per_session++
// }
const dir_name = 'temp_dir_name'
return {
// payload: payload,
dir_name: dir_name,
images_info: images_info,
metadata: this.plugin_settings,
}
} catch (e) {
console.warn(e)
}
}
async toSession(images_info) {
try {
//images_info[0] = {path:path,base64:base64png}
// let last_images_paths = await silentImagesToLayersExe(images_info)
let last_images_paths = {}
for (const image_info of images_info) {
const path = image_info['path']
// const base64_image = image_info['base64']
const layer = image_info['layer']
const [document_name, image_name] = path.split('/')
// await saveFileInSubFolder(base64_image, document_name, image_name)
image_info['base64'] = await this.layerToBase64ToFile(
layer,
document_name,
image_name
)
const json_file_name = `${image_name.split('.')[0]}.json`
this.plugin_settings['auto_metadata'] =
image_info?.auto_metadata
g_generation_session.base64OutputImages[path] =
image_info['base64']
await saveJsonFileInSubFolder(
this.plugin_settings,
document_name,
json_file_name
) //save the settings
last_images_paths[path] = image_info['layer']
}
if (g_generation_session.isFirstGeneration) {
//store them in the generation session for viewer manager to use
g_generation_session.image_paths_to_layers = last_images_paths
} else {
g_generation_session.image_paths_to_layers = {
...g_generation_session.image_paths_to_layers,
...last_images_paths,
}
// g_number_generation_per_session++
}
} catch (e) {
console.warn(e)
}
}
async interruptRequest(horde_id) {
try {
console.log('interruptRquest():')
const full_url = `https://stablehorde.net/api/v2/generate/status/${horde_id}`
console.log(full_url)
let response = await fetch(full_url, {
method: 'DELETE',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
// 'Client-Agent': '4c79ab19-8e6c-4054-83b3-773b7ce71ece',
'Client-Agent': 'unknown:0:unknown',
},
})
let result = await response.json()
console.log('interruptReqquest result:', result)
return result
} catch (e) {
console.warn(e)
return
}
}
async interrupt() {
try {
html_manip.updateProgressBarsHtml(0)
// g_generation_session.request_status = Enum.requestStatus['']
this.last_horde_id = this.horde_id
this.horde_id = null //horde_id could be used startCheckingprogress() so we need to nullify it as soon as possible. TODO: refactor this dependency.
this.isCanceled = true
// this.interval_id = clearTimeout(this.interval_id)
await this.interruptRequest(this.last_horde_id)
} catch (e) {
console.warn(e)
}
}
async postGeneration() {
toggleTwoButtonsByClass(false, 'btnGenerateClass', 'btnInterruptClass')
}
async processHordeResult() {
//*) get the result from the horde server
//*) save them locally to output directory
//*) import them into the canvas
//*) resize and move the layers to fit the selection
//*) return the results to be stored and processed by the g_generation_session
try {
if (this.isProcessHordeResultCalled) {
return
}
this.isProcessHordeResultCalled = true
console.log('horde request is done')
// g_b_request_result = true
const temp_id = this.horde_id //this.horde_id will reset
// cancelRequestClientSide()
g_horde_generation_result = await requestHordeStatus(temp_id)
const generations = g_horde_generation_result.generations
const writeable_entry = await getCurrentDocFolder()
const images_info = [] //{path:image_path,base64:}
for (const image_horde_container of generations) {
try {
const url = image_horde_container.img
const image_file_name = general.newOutputImageName('webp')
const image_layer = await downloadItExe(
url,
writeable_entry,
image_file_name
) //download the image from url, it works even with .webp format
const image_png_file_name =
general.convertImageNameToPng(image_file_name)
const uuid = await getUniqueDocumentId()
const image_path = `${uuid}/${image_png_file_name}` //this is the png path
images_info.push({
path: image_path,
base64: dummy.getDummyBase64(), //TODO:change this to the base64_png
layer: image_layer,
})
await psapi.layerToSelection(
g_generation_session.selectionInfo
) //TODO: create a safe layerToSelection function
} catch (e) {
console.warn(e)
}
}
this.isProcessHordeResultCalled = false //reset for next generation
return images_info
} catch (e) {
console.warn(e)
}
}
updateHordeProgressBar(check_horde_status) {
//update the progress bar proceduer
console.log('this.maxWaitTime: ', this.maxWaitTime)
console.log(
"check_horde_status['wait_time']: ",
check_horde_status['wait_time']
)
console.log(
"check_horde_status['waiting']: ",
check_horde_status['waiting']
)
this.maxWaitTime = Math.max(
check_horde_status['wait_time'],
this.maxWaitTime
) // return the max time value, so we could use to calculate the complection percentage
const delta_time = this.maxWaitTime - check_horde_status['wait_time']
if (isNaN(this.maxWaitTime) || parseInt(this.maxWaitTime) === 0) {
this.maxWaitTime = 0 // reset to zero
} else {
console.log('delta_time:', delta_time)
console.log('this.maxWaitTime:', this.maxWaitTime)
const completion_percentage = (delta_time / this.maxWaitTime) * 100
console.log('completion_percentage:', completion_percentage)
html_manip.updateProgressBarsHtml(completion_percentage)
}
}
async startCheckingProgress() {
console.log('startCheckingProgress is called')
return await new Promise((resolve, reject) => {
if (this.horde_id && !this.isCanceled) {
this.interval_id = setTimeout(async () => {
try {
console.warn(
'startCheckingProgress(): horde_id and isCanceled',
this.horde_id,
this.isCanceled
)
//check the request status
const check_json = await requestHordeCheck(
this.horde_id
)
this.updateHordeProgressBar(check_json)
if (check_json['done']) {
// this.interval_id = clearTimeout(this.interval_id)
const images_info = await this.processHordeResult()
if (this.horde_id) {
this.last_horde_id = this.horde_id
this.horde_id = null
}
return resolve(images_info)
} else {
//the request is not done and the user hasn't canceled it
console.warn(
'startCheckingProgress(): reqursive startCheckingProgress call',
this.horde_id,
this.isCanceled
)
const horde_result =
await this.startCheckingProgress() // start another check
return resolve(horde_result) // return the result of the new check
}
} catch (e) {
console.warn(e)
const result = await this.startCheckingProgress()
return resolve(result)
}
}, 3000)
} else {
console.warn(
'startCheckingProgress: else block',
this.horde_id,
this.isCanceled
)
return resolve()
}
})
}
}
const webui_to_horde_samplers = {
'Euler a': 'k_euler_a',
Euler: 'k_euler',
LMS: 'k_lms',
Heun: 'k_heun',
DPM2: 'k_dpm_2',
'DPM2 a': 'k_dpm_2_a',
'DPM++ 2S a': 'k_dpmpp_2s_a',
'DPM++ 2M': 'k_dpmpp_2m',
'DPM++ SDE': 'k_dpmpp_sde',
'DPM fast': 'k_dpm_fast',
'DPM adaptive': 'k_dpm_adaptive',
'LMS Karras': 'k_lms',
'DPM2 Karras': 'k_dpm_2',
'DPM2 a Karras': 'k_dpm_2_a',
'DPM++ 2S a Karras': 'k_dpmpp_2s_a',
'DPM++ 2M Karras': 'k_dpmpp_2m',
'DPM++ SDE Karras': 'k_dpmpp_sde',
DDIM: 'ddim',
PLMS: 'plms',
}
//get workers
//select a worker
//send a request => requestHorde(horde_settings)
//check for progress => requestHordeCheck(request_id)
//when progress is full, request the result => requestHordeStatus(request_id)
async function mapPluginSettingsToHorde(plugin_settings) {
const { getModelHorde } = require('../sd_scripts/horde')
const ps = plugin_settings // for shortness
const sampler = webui_to_horde_samplers[ps['sampler_index']]
const model = getModelHorde()
let horde_prompt
if (ps['negative_prompt'].length > 0) {
horde_prompt = `${ps['prompt']} ### ${ps['negative_prompt']}`
} else {
horde_prompt = ps['prompt'] //no negative prompt
}
const extra_payload = {}
if (ps['mode'] === 'img2img') {
// payload['source_image'] = ps['init_images']
// let current_doc_entry =await getCurrentDocFolder()
// let webp_file = await current_doc_entry.getEntry('temp.webp')
// let base64_webp = await io.IO.base64WebpFromFile(webp_file)
// payload['source_image'] = io.IO.base64WebpFromFile()
// console.log('base64_webp:', base64_webp)
// const dummy_str = getDummyWebpBase64()
// if (base64_webp === dummy_str) {
// console.warn('the same base64')
// } else {
// console.warn('different base64')
// }
// payload['source_image'] = dummy_str
// payload['source_image'] = base64.b64encode(buffer.getvalue()).decode() //does it need to be webp?
const init_image_base64_webp = await io.IO.base64PngToBase64Webp(
ps['init_images'][0]
)
extra_payload['source_image'] = init_image_base64_webp
extra_payload['source_processing'] = 'img2img'
} else if (ps['mode'] === 'inpaint' || ps['mode'] === 'outpaint') {
const init_image_base64_webp = await io.IO.base64PngToBase64Webp(
ps['init_images'][0]
)
const mask_base64_webp = await io.IO.base64PngToBase64Webp(ps['mask'])
extra_payload['source_processing'] = 'inpainting'
extra_payload['source_image'] = init_image_base64_webp
extra_payload['source_mask'] = mask_base64_webp
// payload["source_mask"] = base64.b64encode(buffer.getvalue()).decode()//does it need to be webp?
}
let seed = ps['seed']
if (parseInt(ps['seed']) === -1) {
const random_seed = Math.floor(Math.random() * 100000000000 + 1) // Date.now() doesn't have enough resolution to avoid duplicate
seed = random_seed.toString()
}
const width = general.nearestMultiple(ps['width'], 64)
const height = general.nearestMultiple(ps['height'], 64)
const nsfw = html_manip.getUseNsfw()
let horde_payload = {
prompt: horde_prompt,
params: {
sampler_name: sampler,
toggles: [1, 4],
cfg_scale: ps['cfg_scale'],
denoising_strength: ps['denoising_strength'],
seed: seed,
height: height,
width: width,
seed_variation: 1,
post_processing: ['GFPGAN'],
karras: false,
tiling: false,
steps: parseInt(ps['steps']),
n: 1,
},
nsfw: nsfw,
trusted_workers: true,
censor_nsfw: false,
// workers: ['4c79ab19-8e6c-4054-83b3-773b7ce71ece'],
// workers: workers_ids,
// models: ['stable_diffusion'],
models: [model],
// source_image: 'string',
// source_processing: 'img2img',
// source_mask: 'string',
...extra_payload,
r2: true,
shared: false,
}
return horde_payload
}
function getWorkerID(workers_json) {
let workers_ids = []
for (worker of workers_json) {
workers_ids.push(worker?.id)
}
console.log('workers_ids:', workers_ids)
return workers_ids
}
async function getWorkers() {
const full_url = 'https://stablehorde.net/api/v2/workers'
// const full_url = 'https://stablehorde.net/api/v2/generate/sync'
console.log(full_url)
let request = await fetch(full_url, {
method: 'GET',
headers: {
Accept: 'application/json',
},
})
let workers = await request.json()
// const workers_ids = getWorkerID(workers)
console.log('requestHorde workers:', workers)
return workers
}
async function requestHorde(payload) {
// const workers = await getWorkers()
// const workers_ids = getWorkerID(workers)
// const settings = await getSettings()
// payload = mapPluginSettingsToHorde(settings)
// payload['workers'] = workers_ids
// payload = {
// prompt: 'string',
// params: {
// sampler_name: 'k_lms',
// toggles: [1, 4],
// cfg_scale: 5,
// denoising_strength: 0.75,
// // seed: 'string',
// height: 512,
// width: 512,
// seed_variation: 1,
// post_processing: ['GFPGAN'],
// karras: false,
// tiling: false,
// steps: 5,
// n: 1,
// },
// nsfw: false,
// trusted_workers: true,
// censor_nsfw: false,
// // workers: ['4c79ab19-8e6c-4054-83b3-773b7ce71ece'],
// workers: workers_ids,
// models: ['stable_diffusion'],
// // source_image: 'string',
// // source_processing: 'img2img',
// // source_mask: 'string',
// r2: true,
// shared: false,
// }
try {
console.log('requestHorde():')
const full_url = 'https://stablehorde.net/api/v2/generate/async'
// const full_url = 'https://stablehorde.net/api/v2/generate/sync'
console.log(full_url)
const horde_api_key = html_manip.getHordeApiKey()
let request = await fetch(full_url, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
apikey: horde_api_key,
'Client-Agent': 'unknown:0:unknown',
},
body: JSON.stringify(payload),
})
let json = await request.json()
console.log('requestHorde json:', json)
return json
} catch (e) {
console.warn(e)
return {}
}
}
async function requestHordeCheck(id) {
try {
console.log('requestHordeCheck():')
const base_url = 'https://stablehorde.net/api/v2/generate/check'
const full_url = `${base_url}/${id}`
// const full_url = 'https://stablehorde.net/api/v2/generate/sync'
console.log(full_url)
const payload = {}
let request = await fetch(full_url, {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
// 'Client-Agent': '4c79ab19-8e6c-4054-83b3-773b7ce71ece',
'Client-Agent': 'unknown:0:unknown',
},
})
let json = await request.json()
console.log('requestHordeCheck json:', json)
return json
} catch (e) {
console.warn(e)
return {}
}
}
async function requestHordeStatus(id) {
try {
console.log('requestHordeStatus():')
const base_url = 'https://stablehorde.net/api/v2/generate/status'
const full_url = `${base_url}/${id}`
// const full_url = 'https://stablehorde.net/api/v2/generate/sync'
console.log(full_url)
const payload = {}
let request = await fetch(full_url, {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
// 'Client-Agent': '4c79ab19-8e6c-4054-83b3-773b7ce71ece',
'Client-Agent': 'unknown:0:unknown',
},
})
let json = await request.json()
console.log('requestHordeStatus json:', json)
return json
} catch (e) {
console.warn(e)
}
}
let g_horde_generation_result
let g_b_request_result = false
function cancelRequestClientSide() {
this.interval_id = clearTimeout(this.interval_id)
// g_id = null
g_b_request_result = false
}
module.exports = {
hordeGenerator,
HordeSettings,
}