Skip to content

Commit

Permalink
Further developing the download dialog for installing ComfyUI
Browse files Browse the repository at this point in the history
download dialog on attempt to switch to Workflow mode checks is ComfyUI is installed and will install it if necessary + customNodes for predefined workflows

Signed-off-by: marijnvg-tng <[email protected]>
  • Loading branch information
marijnvg-tng committed Dec 5, 2024
1 parent e55a9fe commit 285e909
Show file tree
Hide file tree
Showing 10 changed files with 201 additions and 78 deletions.
1 change: 0 additions & 1 deletion ComfyUI
Submodule ComfyUI deleted from 0d4e29
2 changes: 1 addition & 1 deletion WebUI/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
</enhance>
<answer v-show="activeTabIdx == 2" ref = "answer" @show-download-model-confirm="showDownloadModelConfirm" @show-model-request="showModelRequest"></answer>
<learn-more v-show="activeTabIdx == 3"></learn-more>
<app-settings v-if="showSetting" @close="hideAppSettings" @show-download-model-confirm="showDownloadModelConfirm"></app-settings>
<app-settings v-show="showSetting" @close="hideAppSettings" @show-download-model-confirm="showDownloadModelConfirm"></app-settings>
</div>
<download-dialog v-show="showDowloadDlg" ref="downloadDigCompt" @close="showDowloadDlg = false"></download-dialog>
<add-l-l-m-dialog v-show="showModelRequestDialog" ref="addLLMCompt" @close="showModelRequestDialog = false" @call-check-model="callCheckModel" @show-warning="showWarning"></add-l-l-m-dialog>
Expand Down
3 changes: 3 additions & 0 deletions WebUI/src/assets/i18n/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@
"COM_DO_NOT_SHOW_AGAIN": "Do not show again",
"SETTINGS_MODEL_IMAGE_RESOLUTION_HD_CONFIRM": "HD Mode can result in slower than normal performance on systems with less than 12 GB of VRAM or 24GB of system memory for Intel Core Ultra PCs",
"SETTINGS_MODEL_WORKFLOW_COMFYUI_CONFIRM": "The use of the workflow mode requires ComfyUI to be installed. This is an additional module that will be installed upon confirmation. Please make sure you have a stable internet connection.",
"SETTINGS_MODEL_WORKFLOW_COMFYUI_DOWNLOADING": "Installation in progress...",
"SETTINGS_MODEL_WORKFLOW_COMFYUI_COMPLETED": "Installation successful!",
"SETTINGS_MODEL_WORKFLOW_COMFYUI_ERROR": "Installation failed, due to ",
"SETTINGS_TAB_IMAGE": "Image",
"SETTINGS_TAB_BASIC": "Basic",
"SETTINGS_TAB_MODEL": "Models",
Expand Down
2 changes: 1 addition & 1 deletion WebUI/src/assets/js/store/imageGeneration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,7 @@ export const useImageGeneration = defineStore("imageGeneration", () => {
}
return {type: modelTypeToId(modelType), repo_id: repoAddress, backend: "comfyui"}
}
const checkList: CheckModelAlreadyLoadedParameters[] = activeWorkflow.value!.comfyUIRequirements!.requiredModels.map( extractDownloadModelParamsFromString)
const checkList: CheckModelAlreadyLoadedParameters[] = activeWorkflow.value!.comfyUIRequirements!.requiredModels.map( extractDownloadModelParamsFromString )
const checkedModels: CheckModelAlreadyLoadedResult[] = await globalSetup.checkModelAlreadyLoaded(checkList);
const modelsToBeLoaded = checkedModels.filter(checkModelExistsResult => !checkModelExistsResult.already_loaded)
for (const item of modelsToBeLoaded) {
Expand Down
84 changes: 17 additions & 67 deletions WebUI/src/components/SettingsImageWorkflowSelector.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,7 @@
</div>
</form>
</dialog>
<dialog ref="ComfyUIConfirmationDialog" class="bg-gray-600 max-w-md p-7 items-center justify-center rounded-lg shadow-lg text-white">
<form method="dialog" action="/submit" onsubmit="return false" class="items-center justify-center">
<p class="mb-4">
{{ languages.SETTINGS_MODEL_WORKFLOW_COMFYUI_CONFIRM }}
</p>
<div class="flex justify-between space-x-4 items-center">
<button @click="() => {cancel()}" type="submit" class="bg-slate-700 py-1 px-4 rounded">
{{ languages.COM_CANCEL }}
</button>
<div class="flex-end space-x-4">
<button @click="() => {comfyUIWarningOverride = true; currentWorkflowBackendType = 'comfyui'}" type="submit" class="bg-color-active py-1 px-4 rounded">
{{ languages.COM_CONFIRM }}
</button>
</div>
</div>
</form>
</dialog>
<comfy-u-i-download-dialog v-show="showComfyUIDownloadDialog" @close="onComfyUIDialogClose"></comfy-u-i-download-dialog>
<div class="items-center flex-wrap grid grid-cols-1 gap-2">
<div class="flex flex-col gap-2">
<p>{{ "Mode" }}</p>
Expand All @@ -45,7 +29,7 @@
@click="() => { imageGeneration.backend = 'default' }"></radio-block>
<radio-block :checked="imageGeneration.backend === 'comfyui'"
:text="'Workflow'"
@click="() => { currentWorkflowBackendType = 'comfyui' }"></radio-block>
@click="() => { onSwitchToComfyUI() }"></radio-block>
</div>
</div>
<div v-if="imageGeneration.backend === 'default'" class="flex flex-col gap-2">
Expand Down Expand Up @@ -109,19 +93,20 @@
</div>
</template>
<script setup lang="ts">
import {useImageGeneration} from "@/assets/js/store/imageGeneration";
import { useImageGeneration } from "@/assets/js/store/imageGeneration";
import DropSelector from "../components/DropSelector.vue";
import RadioBlock from "../components/RadioBlock.vue";
import {useGlobalSetup} from "@/assets/js/store/globalSetup.ts";
import ComfyUIDownloadDialog from "@/components/comfyUIDownloadDialog/ComfyUIDownloadDialog.vue";
const imageGeneration = useImageGeneration();
const globalSetup = useGlobalSetup();
const hdConfirmationDialog = ref<HTMLDialogElement>();
const hdWarningOverride = ref(false);
const ComfyUIConfirmationDialog = ref<HTMLDialogElement>();
const comfyUIWarningOverride = ref(false);
const showComfyUIDownloadDialog = ref(false);
const classicModel = computed({
get() {
Expand Down Expand Up @@ -152,55 +137,20 @@ const classicModel = computed({
}
})
const currentWorkflowBackendType = computed({
get() {
if (imageGeneration.backend === 'default') {
return 'default'
}
if (imageGeneration.backend === 'comfyui') {
return 'comfyui'
}
return 'default'
},
set(newValue) {
if (newValue === 'default') {
imageGeneration.backend = 'default'
return;
}
if (newValue === 'comfyui') {
if (!globalSetup.isComfyUiInstalled && !comfyUIWarningOverride.value) {
ComfyUIConfirmationDialog.value?.showModal();
return;
} else if (globalSetup.isComfyUiInstalled) {
// Comfy installed
comfyUIWarningOverride.value = false;
} else {
// Confirm
// comfyUIWarningOverride.value = false;
console.info("attempting to install comfyUI")
triggerInstallComfyUI().then(value => {
if (value.status === 200) {
console.info("installation completed")
globalSetup.isComfyUiInstalled = true
} else {
console.error(`installation of comfyUI failed due to ${value.statusText}`)
}
ComfyUIConfirmationDialog.value?.close();
})
imageGeneration.backend = 'comfyui'
}
}
}
})
function triggerInstallComfyUI(){
return fetch(`${globalSetup.apiHost}/api/comfy-ui/install`, {method: 'POST'});
function onSwitchToComfyUI() {
if (globalSetup.isComfyUiInstalled){
imageGeneration.backend = 'comfyui'
} else {
showComfyUIDownloadDialog.value = true
}
}
function cancel(){
// ToDo Cancel everything and delete everything!!
ComfyUIConfirmationDialog.value?.close();
function onComfyUIDialogClose(isInstallationSuccessful: boolean) {
if (isInstallationSuccessful) {
imageGeneration.backend = 'comfyui'
globalSetup.isComfyUiInstalled = true
}
showComfyUIDownloadDialog.value = false
}
const classicQuality = computed({
Expand Down
163 changes: 163 additions & 0 deletions WebUI/src/components/comfyUIDownloadDialog/ComfyUIDownloadDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
<template>
<div v-if="componentState === ComponentState.USER_CONFIRMATION">
<div ref="ComfyUIConfirmationdiv"
class="bg-gray-600 max-w-md p-7 items-center justify-center rounded-lg shadow-lg text-white">
<form method="dialog" action="/submit" onsubmit="return false" class="items-center justify-center">
<p class="mb-4">
{{ languages.SETTINGS_MODEL_WORKFLOW_COMFYUI_CONFIRM }}
</p>
<div class="flex justify-between space-x-4 items-center">
<button @click="() => {concludeDialog(false)}" type="submit" class="bg-slate-700 py-1 px-4 rounded">
{{ languages.COM_CANCEL }}
</button>
<div class="flex-end space-x-4">
<button @click="() => {onConfirm()}" type="submit" class="bg-color-active py-1 px-4 rounded">
{{ languages.COM_CONFIRM }}
</button>
</div>
</div>
</form>
</div>
</div>
<div v-else-if="componentState === ComponentState.DOWNLOADING">
<div ref="ComfyUIConfirmationdiv"
class="bg-gray-600 max-w-md p-7 items-center justify-center rounded-lg shadow-lg text-white">
<form method="dialog" action="/submit" onsubmit="return false" class="items-center justify-center">
<p class="mb-4">
{{ languages.SETTINGS_MODEL_WORKFLOW_COMFYUI_DOWNLOADING }}
</p>
<div class="flex justify-between space-x-4 items-center">
<!-- <button @click="() => {}" type="submit" class="bg-slate-700 py-1 px-4 rounded">-->
<!-- {{ languages.COM_CANCEL }}-->
<!-- </button>-->
<div class="flex-end space-x-4">
<span class="svg-icon i-loading h-6 w-6"></span>
</div>
</div>
</form>
</div>
</div>
<div v-else-if="componentState === ComponentState.INSTALLATION_SUCCESS">
<div ref="ComfyUIConfirmationdiv"
class="bg-gray-600 max-w-md p-7 items-center justify-center rounded-lg shadow-lg text-white">
<form method="dialog" action="/submit" onsubmit="return false" class="items-center justify-center">
<p class="mb-4">
{{ languages.SETTINGS_MODEL_WORKFLOW_COMFYUI_COMPLETED }}
</p>
<div class="flex justify-between space-x-4 items-center">
<button @click="() => {concludeDialog(true)}" type="submit" class="bg-slate-700 py-1 px-4 rounded">
{{ languages.COM_CLOSE }}
</button>
</div>
</form>
</div>
</div>
<div v-else-if="componentState === ComponentState.ERROR">
<div ref="ComfyUIConfirmationdiv"
class="bg-gray-600 max-w-md p-7 items-center justify-center rounded-lg shadow-lg text-white">
<form method="dialog" action="/submit" onsubmit="return false" class="items-center justify-center">
<p class="mb-4">
{{ languages.SETTINGS_MODEL_WORKFLOW_COMFYUI_ERROR + installationErrorMessage }}
</p>
<div class="flex justify-between space-x-4 items-center">
<button @click="() => {concludeDialog(false)}" type="submit" class="bg-slate-700 py-1 px-4 rounded">
{{ languages.COM_CLOSE }}
</button>
</div>
</form>
</div>
</div>
</template>

<script setup lang="ts">
import {useGlobalSetup} from "@/assets/js/store/globalSetup.ts";
import {useImageGeneration} from "@/assets/js/store/imageGeneration.ts";
const globalSetup = useGlobalSetup()
const imageGenerationSettings = useImageGeneration()
enum ComponentState {
UNINITIALIZED,
USER_CONFIRMATION,
DOWNLOADING,
ERROR,
INSTALLATION_SUCCESS
}
const componentState = ref(ComponentState.USER_CONFIRMATION)
const installationErrorMessage = ref("")
const emits = defineEmits<{
(e: "close", success: boolean): void,
}>();
function onConfirm() {
componentState.value = ComponentState.DOWNLOADING
console.info("attempting to install comfyUI")
triggerInstallComfyUI().then(value => {
console.log(value)
if (value.status === 200) {
console.info("comfyUI installation completed")
triggerInstallCustomNodes().then(value => {
if (value.status === 200) {
console.info("customNode installation completed")
componentState.value = ComponentState.INSTALLATION_SUCCESS
} else {
const data = value.json();
data.then(response => {
componentState.value = ComponentState.ERROR
console.error('installation of comfyUI failed')
if (value.status === 501) {
installationErrorMessage.value = response.error_message;
}
})
}
})
} else {
const data = value.json();
data.then(response => {
componentState.value = ComponentState.ERROR
console.error('installation of comfyUI failed')
if (value.status === 501) {
installationErrorMessage.value = response.error_message;
}
})
}
})
}
function triggerInstallComfyUI() {
return fetch(`${globalSetup.apiHost}/api/comfy-ui/install`, {method: 'POST'});
}
function triggerInstallCustomNodes() {
const uniqueCustomNodes = new Set(imageGenerationSettings.workflows.filter(w => w.backend === 'comfyui').flatMap((item) => item.comfyUIRequirements.customNodes))
const toBeInstalledCustomNodes = []
for (nodeName in uniqueCustomNodes) {
const [username, repoName] = nodeName.replace(" ", "").split("/")
const nodeID = ComfyUICustomNodesRequestParameters(username, repoName)
toBeInstalledCustomNodes.push(nodeID)
}
const response = fetch(`${globalSetup.apiHost}/api/comfy-ui/load_custom_nodes`, {
method: 'POST',
body: JSON.stringify(toRaw({'data': toBeInstalledCustomNodes})),
headers: {
"Content-Type": "application/json"
}
})
return reponse
}
function concludeDialog(isInstallationSuccessful: boolean) {
emits("close", isInstallationSuccessful)
componentState.value = ComponentState.USER_CONFIRMATION
installationErrorMessage.value = ""
}
</script>

<style scoped>
</style>
5 changes: 5 additions & 0 deletions WebUI/src/env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,11 @@ type DownloadModelRender = { size: string, gated?: boolean, accessGranted?: bool

type DownloadModelParam = CheckModelAlreadyLoadedParameters

type ComfyUICustomNodesRequestParameters = {
username: string,
repoName: string
}


type CheckModelAlreadyLoadedResult = {
already_loaded: boolean
Expand Down
10 changes: 5 additions & 5 deletions service/comfyui_downloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,23 +111,23 @@ def install_comfyUI() -> bool:


def is_custom_node_installed(node_repo_ref: ComfyUICustomNodesGithubRepoId) -> bool:
expected_custom_node_path = os.path.join(service_config.comfyUIRootPath, "custom_nodes", node_repo_ref.reponame)
expected_custom_node_path = os.path.join(service_config.comfyUIRootPath, "custom_nodes", node_repo_ref.repoName)
return os.path.exists(expected_custom_node_path)

def download_custom_node(node_repo_data: ComfyUICustomNodesGithubRepoId):
if is_custom_node_installed(node_repo_data):
return
else:
try:
expected_git_url = f"https://github.com/{node_repo_data.username}/{node_repo_data.reponame}"
expected_custom_node_path = os.path.join(service_config.comfyUIRootPath, "custom_nodes", node_repo_data.reponame)
expected_git_url = f"https://github.com/{node_repo_data.username}/{node_repo_data.repoName}"
expected_custom_node_path = os.path.join(service_config.comfyUIRootPath, "custom_nodes", node_repo_data.repoName)
potential_node_requirements = os.path.join(expected_custom_node_path, "requirements.txt")

_install_git_repo(expected_git_url, expected_custom_node_path)
_install_pip_requirements(potential_node_requirements)
logging.error(f"Failed to install custom comfy node {node_repo_data.username}/{node_repo_data.reponame} due to {e}")
logging.error(f"Failed to install custom comfy node {node_repo_data.username}/{node_repo_data.repoName} due to {e}")

Check failure on line 128 in service/comfyui_downloader.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (F821)

service/comfyui_downloader.py:128:125: F821 Undefined name `e`

return
except Exception as e:
logging.error(f"Failed to install custom comfy node {node_repo_data.username}/{node_repo_data.reponame} due to {e}")
logging.error(f"Failed to install custom comfy node {node_repo_data.username}/{node_repo_data.repoName} due to {e}")
raise e
7 changes: 5 additions & 2 deletions service/web_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import traceback

import logging
import time

Check failure on line 48 in service/web_api.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (F401)

service/web_api.py:48:8: F401 `time` imported but unused
logging.basicConfig(stream=sys.stdout, level=logging.INFO)


Expand Down Expand Up @@ -387,6 +388,8 @@ def is_comfyUI_loaded():

@app.post("/api/comfy-ui/install")
def install_comfyUI():
# time.sleep(5)
# return jsonify({"success": True, "error_message": "Test Error"})
try:
installation_success = comfyui_downloader.install_comfyUI()
return jsonify({"success": installation_success, "error_message": ""})
Expand All @@ -397,7 +400,7 @@ def install_comfyUI():
@app.post("/api/comfy-ui/are_custom_nodes_loaded")
@app.input(ComfyUICustomNodesDownloadRequest.Schema, location='json', arg_name='comfyNodeRequest')
def are_custom_nodes_installed(comfyNodeRequest: ComfyUICustomNodesDownloadRequest):
response = { f"{x.username}/{x.reponame}" : comfyui_downloader.is_custom_node_installed(x) for x in comfyNodeRequest.data }
response = { f"{x.username}/{x.repoName}" : comfyui_downloader.is_custom_node_installed(x) for x in comfyNodeRequest.data}
return jsonify(response)


Expand All @@ -407,7 +410,7 @@ def install_custom_nodes(comfyNodeRequest: ComfyUICustomNodesDownloadRequest):
try:
for x in comfyNodeRequest.data:
comfyui_downloader.download_custom_node(x)
return jsonify({ f"{x.username}/{x.reponame}" : {"success": True, "errorMessage": ""} for x in comfyNodeRequest.data })
return jsonify({ f"{x.username}/{x.repoName}" : {"success": True, "errorMessage": ""} for x in comfyNodeRequest.data})
except Exception as e:
return jsonify({'error_message': f'failed to at least one custom node due to {e}'}), 501

Expand Down
Loading

0 comments on commit 285e909

Please sign in to comment.