mirror of
https://github.com/Comfy-Org/ComfyUI-Manager.git
synced 2025-12-22 04:40:49 +08:00
refactor: remove preview_method and component legacy features
Preview Method Removal: - Remove preview method UI from Manager settings panel - Remove /v2/manager/preview_method API endpoint (legacy) - Remove set_preview_method() and get_current_preview_method() functions - Remove preview_method from config read/write operations - Clean up latent_preview imports Use ComfyUI Settings > Execution > Live preview method instead. Component Feature Removal: - Delete components-manager.js entirely - Remove ComponentBuilderDialog, load_components, set_component_policy - Remove component policy UI from Manager settings panel - Remove /v2/manager/policy/component API endpoint - Remove /v2/manager/component/save and /loads API endpoints - Remove component_policy from config read/write operations - Remove manager_components_path from context
This commit is contained in:
parent
a7eb93fff0
commit
b9def4cb6e
@ -31,7 +31,6 @@ manager_startup_script_path:str = None
|
|||||||
manager_snapshot_path = None
|
manager_snapshot_path = None
|
||||||
manager_pip_overrides_path = None
|
manager_pip_overrides_path = None
|
||||||
manager_pip_blacklist_path = None
|
manager_pip_blacklist_path = None
|
||||||
manager_components_path = None
|
|
||||||
manager_batch_history_path = None
|
manager_batch_history_path = None
|
||||||
|
|
||||||
def update_user_directory(manager_dir):
|
def update_user_directory(manager_dir):
|
||||||
@ -42,7 +41,6 @@ def update_user_directory(manager_dir):
|
|||||||
global manager_snapshot_path
|
global manager_snapshot_path
|
||||||
global manager_pip_overrides_path
|
global manager_pip_overrides_path
|
||||||
global manager_pip_blacklist_path
|
global manager_pip_blacklist_path
|
||||||
global manager_components_path
|
|
||||||
global manager_batch_history_path
|
global manager_batch_history_path
|
||||||
|
|
||||||
manager_files_path = manager_dir
|
manager_files_path = manager_dir
|
||||||
@ -61,7 +59,6 @@ def update_user_directory(manager_dir):
|
|||||||
manager_channel_list_path = os.path.join(manager_files_path, 'channels.list')
|
manager_channel_list_path = os.path.join(manager_files_path, 'channels.list')
|
||||||
manager_pip_overrides_path = os.path.join(manager_files_path, "pip_overrides.json")
|
manager_pip_overrides_path = os.path.join(manager_files_path, "pip_overrides.json")
|
||||||
manager_pip_blacklist_path = os.path.join(manager_files_path, "pip_blacklist.list")
|
manager_pip_blacklist_path = os.path.join(manager_files_path, "pip_blacklist.list")
|
||||||
manager_components_path = os.path.join(manager_files_path, "components")
|
|
||||||
manager_util.cache_dir = os.path.join(manager_files_path, "cache")
|
manager_util.cache_dir = os.path.join(manager_files_path, "cache")
|
||||||
manager_batch_history_path = os.path.join(manager_files_path, "batch_history")
|
manager_batch_history_path = os.path.join(manager_files_path, "batch_history")
|
||||||
|
|
||||||
|
|||||||
@ -1574,9 +1574,6 @@ class ManagerFuncs:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def get_current_preview_method(self):
|
|
||||||
return "none"
|
|
||||||
|
|
||||||
def run_script(self, cmd, cwd='.'):
|
def run_script(self, cmd, cwd='.'):
|
||||||
if len(cmd) > 0 and cmd[0].startswith("#"):
|
if len(cmd) > 0 and cmd[0].startswith("#"):
|
||||||
print(f"[ComfyUI-Manager] Unexpected behavior: `{cmd}`")
|
print(f"[ComfyUI-Manager] Unexpected behavior: `{cmd}`")
|
||||||
@ -1594,14 +1591,12 @@ def write_config():
|
|||||||
config = configparser.ConfigParser(strict=False)
|
config = configparser.ConfigParser(strict=False)
|
||||||
|
|
||||||
config['default'] = {
|
config['default'] = {
|
||||||
'preview_method': manager_funcs.get_current_preview_method(),
|
|
||||||
'git_exe': get_config()['git_exe'],
|
'git_exe': get_config()['git_exe'],
|
||||||
'use_uv': get_config()['use_uv'],
|
'use_uv': get_config()['use_uv'],
|
||||||
'channel_url': get_config()['channel_url'],
|
'channel_url': get_config()['channel_url'],
|
||||||
'share_option': get_config()['share_option'],
|
'share_option': get_config()['share_option'],
|
||||||
'bypass_ssl': get_config()['bypass_ssl'],
|
'bypass_ssl': get_config()['bypass_ssl'],
|
||||||
"file_logging": get_config()['file_logging'],
|
"file_logging": get_config()['file_logging'],
|
||||||
'component_policy': get_config()['component_policy'],
|
|
||||||
'update_policy': get_config()['update_policy'],
|
'update_policy': get_config()['update_policy'],
|
||||||
'windows_selector_event_loop_policy': get_config()['windows_selector_event_loop_policy'],
|
'windows_selector_event_loop_policy': get_config()['windows_selector_event_loop_policy'],
|
||||||
'model_download_by_agent': get_config()['model_download_by_agent'],
|
'model_download_by_agent': get_config()['model_download_by_agent'],
|
||||||
@ -1634,7 +1629,6 @@ def read_config():
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
'http_channel_enabled': get_bool('http_channel_enabled', False),
|
'http_channel_enabled': get_bool('http_channel_enabled', False),
|
||||||
'preview_method': default_conf.get('preview_method', manager_funcs.get_current_preview_method()).lower(),
|
|
||||||
'git_exe': default_conf.get('git_exe', ''),
|
'git_exe': default_conf.get('git_exe', ''),
|
||||||
'use_uv': get_bool('use_uv', True),
|
'use_uv': get_bool('use_uv', True),
|
||||||
'channel_url': default_conf.get('channel_url', DEFAULT_CHANNEL),
|
'channel_url': default_conf.get('channel_url', DEFAULT_CHANNEL),
|
||||||
@ -1642,7 +1636,6 @@ def read_config():
|
|||||||
'share_option': default_conf.get('share_option', 'all').lower(),
|
'share_option': default_conf.get('share_option', 'all').lower(),
|
||||||
'bypass_ssl': get_bool('bypass_ssl', False),
|
'bypass_ssl': get_bool('bypass_ssl', False),
|
||||||
'file_logging': get_bool('file_logging', True),
|
'file_logging': get_bool('file_logging', True),
|
||||||
'component_policy': default_conf.get('component_policy', 'workflow').lower(),
|
|
||||||
'update_policy': default_conf.get('update_policy', 'stable-comfyui').lower(),
|
'update_policy': default_conf.get('update_policy', 'stable-comfyui').lower(),
|
||||||
'windows_selector_event_loop_policy': get_bool('windows_selector_event_loop_policy', False),
|
'windows_selector_event_loop_policy': get_bool('windows_selector_event_loop_policy', False),
|
||||||
'model_download_by_agent': get_bool('model_download_by_agent', False),
|
'model_download_by_agent': get_bool('model_download_by_agent', False),
|
||||||
@ -1661,7 +1654,6 @@ def read_config():
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
'http_channel_enabled': False,
|
'http_channel_enabled': False,
|
||||||
'preview_method': manager_funcs.get_current_preview_method(),
|
|
||||||
'git_exe': '',
|
'git_exe': '',
|
||||||
'use_uv': manager_util.use_uv,
|
'use_uv': manager_util.use_uv,
|
||||||
'channel_url': DEFAULT_CHANNEL,
|
'channel_url': DEFAULT_CHANNEL,
|
||||||
@ -1669,7 +1661,6 @@ def read_config():
|
|||||||
'share_option': 'all',
|
'share_option': 'all',
|
||||||
'bypass_ssl': manager_util.bypass_ssl,
|
'bypass_ssl': manager_util.bypass_ssl,
|
||||||
'file_logging': True,
|
'file_logging': True,
|
||||||
'component_policy': 'workflow',
|
|
||||||
'update_policy': 'stable-comfyui',
|
'update_policy': 'stable-comfyui',
|
||||||
'windows_selector_event_loop_policy': False,
|
'windows_selector_event_loop_policy': False,
|
||||||
'model_download_by_agent': False,
|
'model_download_by_agent': False,
|
||||||
|
|||||||
@ -27,7 +27,6 @@ from typing import Any, Optional
|
|||||||
from comfyui_manager.common.timestamp_utils import get_timestamp_for_filename, get_now
|
from comfyui_manager.common.timestamp_utils import get_timestamp_for_filename, get_now
|
||||||
|
|
||||||
import folder_paths
|
import folder_paths
|
||||||
import latent_preview
|
|
||||||
import nodes
|
import nodes
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
from comfy.cli_args import args
|
from comfy.cli_args import args
|
||||||
@ -131,16 +130,6 @@ def error_response(
|
|||||||
|
|
||||||
|
|
||||||
class ManagerFuncsInComfyUI(core.ManagerFuncs):
|
class ManagerFuncsInComfyUI(core.ManagerFuncs):
|
||||||
def get_current_preview_method(self):
|
|
||||||
if args.preview_method == latent_preview.LatentPreviewMethod.Auto:
|
|
||||||
return "auto"
|
|
||||||
elif args.preview_method == latent_preview.LatentPreviewMethod.Latent2RGB:
|
|
||||||
return "latent2rgb"
|
|
||||||
elif args.preview_method == latent_preview.LatentPreviewMethod.TAESD:
|
|
||||||
return "taesd"
|
|
||||||
else:
|
|
||||||
return "none"
|
|
||||||
|
|
||||||
def run_script(self, cmd, cwd="."):
|
def run_script(self, cmd, cwd="."):
|
||||||
if len(cmd) > 0 and cmd[0].startswith("#"):
|
if len(cmd) > 0 and cmd[0].startswith("#"):
|
||||||
logging.error(f"[ComfyUI-Manager] Unexpected behavior: `{cmd}`")
|
logging.error(f"[ComfyUI-Manager] Unexpected behavior: `{cmd}`")
|
||||||
@ -704,8 +693,6 @@ class TaskQueue:
|
|||||||
cli_args["listen"] = args.listen
|
cli_args["listen"] = args.listen
|
||||||
if hasattr(args, "port"):
|
if hasattr(args, "port"):
|
||||||
cli_args["port"] = args.port
|
cli_args["port"] = args.port
|
||||||
if hasattr(args, "preview_method"):
|
|
||||||
cli_args["preview_method"] = str(args.preview_method)
|
|
||||||
if hasattr(args, "enable_manager_legacy_ui"):
|
if hasattr(args, "enable_manager_legacy_ui"):
|
||||||
cli_args["enable_manager_legacy_ui"] = args.enable_manager_legacy_ui
|
cli_args["enable_manager_legacy_ui"] = args.enable_manager_legacy_ui
|
||||||
if hasattr(args, "front_end_version"):
|
if hasattr(args, "front_end_version"):
|
||||||
@ -819,14 +806,6 @@ class TaskQueue:
|
|||||||
|
|
||||||
task_queue = TaskQueue()
|
task_queue = TaskQueue()
|
||||||
|
|
||||||
# Preview method initialization
|
|
||||||
if args.preview_method == latent_preview.LatentPreviewMethod.NoPreviews:
|
|
||||||
environment_utils.set_preview_method(core.get_config()["preview_method"])
|
|
||||||
else:
|
|
||||||
logging.warning(
|
|
||||||
"[ComfyUI-Manager] Since --preview-method is set, ComfyUI-Manager's preview method feature will be ignored."
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def task_worker():
|
async def task_worker():
|
||||||
logging.debug("[ComfyUI-Manager] Task worker started")
|
logging.debug("[ComfyUI-Manager] Task worker started")
|
||||||
|
|||||||
@ -5,8 +5,6 @@ import traceback
|
|||||||
|
|
||||||
from comfyui_manager.common import context
|
from comfyui_manager.common import context
|
||||||
import folder_paths
|
import folder_paths
|
||||||
from comfy.cli_args import args
|
|
||||||
import latent_preview
|
|
||||||
|
|
||||||
from comfyui_manager.glob import manager_core as core
|
from comfyui_manager.glob import manager_core as core
|
||||||
from comfyui_manager.common import cm_global
|
from comfyui_manager.common import cm_global
|
||||||
@ -93,19 +91,6 @@ def print_comfyui_version():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def set_preview_method(method):
|
|
||||||
if method == "auto":
|
|
||||||
args.preview_method = latent_preview.LatentPreviewMethod.Auto
|
|
||||||
elif method == "latent2rgb":
|
|
||||||
args.preview_method = latent_preview.LatentPreviewMethod.Latent2RGB
|
|
||||||
elif method == "taesd":
|
|
||||||
args.preview_method = latent_preview.LatentPreviewMethod.TAESD
|
|
||||||
else:
|
|
||||||
args.preview_method = latent_preview.LatentPreviewMethod.NoPreviews
|
|
||||||
|
|
||||||
core.get_config()["preview_method"] = method
|
|
||||||
|
|
||||||
|
|
||||||
def set_update_policy(mode):
|
def set_update_policy(mode):
|
||||||
core.get_config()["update_policy"] = mode
|
core.get_config()["update_policy"] = mode
|
||||||
|
|
||||||
@ -135,7 +120,6 @@ def initialize_environment():
|
|||||||
# manager_util.comfyui_manager_path, "extension-node-map.json"
|
# manager_util.comfyui_manager_path, "extension-node-map.json"
|
||||||
# )
|
# )
|
||||||
|
|
||||||
set_preview_method(core.get_config()["preview_method"])
|
|
||||||
print_comfyui_version()
|
print_comfyui_version()
|
||||||
setup_environment()
|
setup_environment()
|
||||||
|
|
||||||
|
|||||||
@ -16,7 +16,6 @@ import {
|
|||||||
rebootAPI, setManagerInstance, show_message, customAlert, customPrompt,
|
rebootAPI, setManagerInstance, show_message, customAlert, customPrompt,
|
||||||
infoToast, showTerminal, setNeedRestart, generateUUID
|
infoToast, showTerminal, setNeedRestart, generateUUID
|
||||||
} from "./common.js";
|
} from "./common.js";
|
||||||
import { ComponentBuilderDialog, load_components, set_component_policy } from "./components-manager.js";
|
|
||||||
import { CustomNodesManager } from "./custom-nodes-manager.js";
|
import { CustomNodesManager } from "./custom-nodes-manager.js";
|
||||||
import { ModelManager } from "./model-manager.js";
|
import { ModelManager } from "./model-manager.js";
|
||||||
import { SnapshotManager } from "./snapshot.js";
|
import { SnapshotManager } from "./snapshot.js";
|
||||||
@ -957,25 +956,6 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
|
|
||||||
const dbRetrievalSetttingItem = createSettingsCombo("DB", this.datasrc_combo);
|
const dbRetrievalSetttingItem = createSettingsCombo("DB", this.datasrc_combo);
|
||||||
|
|
||||||
// preview method
|
|
||||||
let preview_combo = document.createElement("select");
|
|
||||||
preview_combo.setAttribute("title", "Configure how latent variables will be decoded during preview in the sampling process.");
|
|
||||||
preview_combo.className = "cm-menu-combo p-select p-component p-inputwrapper p-inputwrapper-filled";
|
|
||||||
preview_combo.appendChild($el('option', { value: 'auto', text: 'Auto' }, []));
|
|
||||||
preview_combo.appendChild($el('option', { value: 'taesd', text: 'TAESD (slow)' }, []));
|
|
||||||
preview_combo.appendChild($el('option', { value: 'latent2rgb', text: 'Latent2RGB (fast)' }, []));
|
|
||||||
preview_combo.appendChild($el('option', { value: 'none', text: 'None (very fast)' }, []));
|
|
||||||
|
|
||||||
api.fetchApi('/v2/manager/preview_method')
|
|
||||||
.then(response => response.text())
|
|
||||||
.then(data => { preview_combo.value = data; });
|
|
||||||
|
|
||||||
preview_combo.addEventListener('change', function (event) {
|
|
||||||
api.fetchApi(`/v2/manager/preview_method?value=${event.target.value}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
const previewSetttingItem = createSettingsCombo("Preview method", preview_combo);
|
|
||||||
|
|
||||||
// channel
|
// channel
|
||||||
let channel_combo = document.createElement("select");
|
let channel_combo = document.createElement("select");
|
||||||
channel_combo.setAttribute("title", "Configure the channel for retrieving data from the Custom Node list (including missing nodes) or the Model list.");
|
channel_combo.setAttribute("title", "Configure the channel for retrieving data from the Custom Node list (including missing nodes) or the Model list.");
|
||||||
@ -1044,26 +1024,6 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
|
|
||||||
const shareSetttingItem = createSettingsCombo("Share", share_combo);
|
const shareSetttingItem = createSettingsCombo("Share", share_combo);
|
||||||
|
|
||||||
let component_policy_combo = document.createElement("select");
|
|
||||||
component_policy_combo.setAttribute("title", "When loading the workflow, configure which version of the component to use.");
|
|
||||||
component_policy_combo.className = "cm-menu-combo p-select p-component p-inputwrapper p-inputwrapper-filled";
|
|
||||||
component_policy_combo.appendChild($el('option', { value: 'workflow', text: 'Use workflow version' }, []));
|
|
||||||
component_policy_combo.appendChild($el('option', { value: 'higher', text: 'Use higher version' }, []));
|
|
||||||
component_policy_combo.appendChild($el('option', { value: 'mine', text: 'Use my version' }, []));
|
|
||||||
api.fetchApi('/v2/manager/policy/component')
|
|
||||||
.then(response => response.text())
|
|
||||||
.then(data => {
|
|
||||||
component_policy_combo.value = data;
|
|
||||||
set_component_policy(data);
|
|
||||||
});
|
|
||||||
|
|
||||||
component_policy_combo.addEventListener('change', function (event) {
|
|
||||||
api.fetchApi(`/v2/manager/policy/component?value=${event.target.value}`);
|
|
||||||
set_component_policy(event.target.value);
|
|
||||||
});
|
|
||||||
|
|
||||||
const componentSetttingItem = createSettingsCombo("Component", component_policy_combo);
|
|
||||||
|
|
||||||
update_policy_combo = document.createElement("select");
|
update_policy_combo = document.createElement("select");
|
||||||
|
|
||||||
update_policy_combo.setAttribute("title", "Sets the policy to be applied when performing an update.");
|
update_policy_combo.setAttribute("title", "Sets the policy to be applied when performing an update.");
|
||||||
@ -1088,9 +1048,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
return [
|
return [
|
||||||
dbRetrievalSetttingItem,
|
dbRetrievalSetttingItem,
|
||||||
channelSetttingItem,
|
channelSetttingItem,
|
||||||
previewSetttingItem,
|
|
||||||
shareSetttingItem,
|
shareSetttingItem,
|
||||||
componentSetttingItem,
|
|
||||||
updateSetttingItem,
|
updateSetttingItem,
|
||||||
//[TODO] replace mt-2 with wrapper div with flex column gap
|
//[TODO] replace mt-2 with wrapper div with flex column gap
|
||||||
$el("filedset.cm-experimental.mt-auto", {}, [
|
$el("filedset.cm-experimental.mt-auto", {}, [
|
||||||
@ -1430,14 +1388,6 @@ app.registerExtension({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
async setup() {
|
async setup() {
|
||||||
let orig_clear = app.graph.clear;
|
|
||||||
app.graph.clear = function () {
|
|
||||||
orig_clear.call(app.graph);
|
|
||||||
load_components();
|
|
||||||
};
|
|
||||||
|
|
||||||
load_components();
|
|
||||||
|
|
||||||
const menu = document.querySelector(".comfy-menu");
|
const menu = document.querySelector(".comfy-menu");
|
||||||
const separator = document.createElement("hr");
|
const separator = document.createElement("hr");
|
||||||
|
|
||||||
@ -1566,19 +1516,6 @@ app.registerExtension({
|
|||||||
node.prototype.getExtraMenuOptions = function (_, options) {
|
node.prototype.getExtraMenuOptions = function (_, options) {
|
||||||
origGetExtraMenuOptions?.apply?.(this, arguments);
|
origGetExtraMenuOptions?.apply?.(this, arguments);
|
||||||
|
|
||||||
if (node.category.startsWith('group nodes>')) {
|
|
||||||
options.push({
|
|
||||||
content: "Save As Component",
|
|
||||||
callback: (obj) => {
|
|
||||||
if (!ComponentBuilderDialog.instance) {
|
|
||||||
ComponentBuilderDialog.instance = new ComponentBuilderDialog();
|
|
||||||
}
|
|
||||||
ComponentBuilderDialog.instance.target_node = node;
|
|
||||||
ComponentBuilderDialog.instance.show();
|
|
||||||
}
|
|
||||||
}, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isOutputNode(node)) {
|
if (isOutputNode(node)) {
|
||||||
const { potential_outputs } = getPotentialOutputsAndOutputNodes([this]);
|
const { potential_outputs } = getPotentialOutputsAndOutputNodes([this]);
|
||||||
const hasOutput = potential_outputs.length > 0;
|
const hasOutput = potential_outputs.length > 0;
|
||||||
|
|||||||
@ -1,812 +0,0 @@
|
|||||||
import { app } from "../../scripts/app.js";
|
|
||||||
import { api } from "../../scripts/api.js"
|
|
||||||
import { sleep, show_message, customConfirm, customAlert } from "./common.js";
|
|
||||||
import { GroupNodeConfig, GroupNodeHandler } from "../../extensions/core/groupNode.js";
|
|
||||||
import { ComfyDialog, $el } from "../../scripts/ui.js";
|
|
||||||
|
|
||||||
const SEPARATOR = ">"
|
|
||||||
|
|
||||||
let pack_map = {};
|
|
||||||
let rpack_map = {};
|
|
||||||
|
|
||||||
export function getPureName(node) {
|
|
||||||
// group nodes/
|
|
||||||
let category = null;
|
|
||||||
if(node.category) {
|
|
||||||
category = node.category.substring(12);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
category = node.constructor.category?.substring(12);
|
|
||||||
}
|
|
||||||
if(category) {
|
|
||||||
let purename = node.comfyClass.substring(category.length+1);
|
|
||||||
return purename;
|
|
||||||
}
|
|
||||||
else if(node.comfyClass.startsWith('workflow/') || node.comfyClass.startsWith(`workflow${SEPARATOR}`)) {
|
|
||||||
return node.comfyClass.substring(9);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return node.comfyClass;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isValidVersionString(version) {
|
|
||||||
const versionPattern = /^(\d+)\.(\d+)(\.(\d+))?$/;
|
|
||||||
|
|
||||||
const match = version.match(versionPattern);
|
|
||||||
|
|
||||||
return match !== null &&
|
|
||||||
parseInt(match[1], 10) >= 0 &&
|
|
||||||
parseInt(match[2], 10) >= 0 &&
|
|
||||||
(!match[3] || parseInt(match[4], 10) >= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function register_pack_map(name, data) {
|
|
||||||
if(data.packname) {
|
|
||||||
pack_map[data.packname] = name;
|
|
||||||
rpack_map[name] = data;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
rpack_map[name] = data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function storeGroupNode(name, data, register=true) {
|
|
||||||
let extra = app.graph.extra;
|
|
||||||
if (!extra) app.graph.extra = extra = {};
|
|
||||||
let groupNodes = extra.groupNodes;
|
|
||||||
if (!groupNodes) extra.groupNodes = groupNodes = {};
|
|
||||||
groupNodes[name] = data;
|
|
||||||
|
|
||||||
if(register) {
|
|
||||||
register_pack_map(name, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function load_components() {
|
|
||||||
let data = await api.fetchApi('/v2/manager/component/loads', {method: "POST"});
|
|
||||||
let components = await data.json();
|
|
||||||
|
|
||||||
let start_time = Date.now();
|
|
||||||
let failed = [];
|
|
||||||
let failed2 = [];
|
|
||||||
|
|
||||||
for(let name in components) {
|
|
||||||
if(app.graph.extra?.groupNodes?.[name]) {
|
|
||||||
if(data) {
|
|
||||||
let data = components[name];
|
|
||||||
|
|
||||||
let category = data.packname;
|
|
||||||
if(data.category) {
|
|
||||||
category += SEPARATOR + data.category;
|
|
||||||
}
|
|
||||||
if(category == '') {
|
|
||||||
category = 'components';
|
|
||||||
}
|
|
||||||
|
|
||||||
const config = new GroupNodeConfig(name, data);
|
|
||||||
await config.registerType(category);
|
|
||||||
|
|
||||||
register_pack_map(name, data);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let nodeData = components[name];
|
|
||||||
|
|
||||||
storeGroupNode(name, nodeData);
|
|
||||||
|
|
||||||
const config = new GroupNodeConfig(name, nodeData);
|
|
||||||
|
|
||||||
while(true) {
|
|
||||||
try {
|
|
||||||
let category = nodeData.packname;
|
|
||||||
if(nodeData.category) {
|
|
||||||
category += SEPARATOR + nodeData.category;
|
|
||||||
}
|
|
||||||
if(category == '') {
|
|
||||||
category = 'components';
|
|
||||||
}
|
|
||||||
|
|
||||||
await config.registerType(category);
|
|
||||||
register_pack_map(name, nodeData);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
let elapsed_time = Date.now() - start_time;
|
|
||||||
if (elapsed_time > 5000) {
|
|
||||||
failed.push(name);
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
await sleep(100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fallback1
|
|
||||||
for(let i in failed) {
|
|
||||||
let name = failed[i];
|
|
||||||
|
|
||||||
if(app.graph.extra?.groupNodes?.[name]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let nodeData = components[name];
|
|
||||||
|
|
||||||
storeGroupNode(name, nodeData);
|
|
||||||
|
|
||||||
const config = new GroupNodeConfig(name, nodeData);
|
|
||||||
while(true) {
|
|
||||||
try {
|
|
||||||
let category = nodeData.packname;
|
|
||||||
if(nodeData.workflow.category) {
|
|
||||||
category += SEPARATOR + nodeData.category;
|
|
||||||
}
|
|
||||||
if(category == '') {
|
|
||||||
category = 'components';
|
|
||||||
}
|
|
||||||
|
|
||||||
await config.registerType(category);
|
|
||||||
register_pack_map(name, nodeData);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
let elapsed_time = Date.now() - start_time;
|
|
||||||
if (elapsed_time > 10000) {
|
|
||||||
failed2.push(name);
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
await sleep(100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fallback2
|
|
||||||
for(let name in failed2) {
|
|
||||||
let name = failed2[i];
|
|
||||||
|
|
||||||
let nodeData = components[name];
|
|
||||||
|
|
||||||
storeGroupNode(name, nodeData);
|
|
||||||
|
|
||||||
const config = new GroupNodeConfig(name, nodeData);
|
|
||||||
while(true) {
|
|
||||||
try {
|
|
||||||
let category = nodeData.workflow.packname;
|
|
||||||
if(nodeData.workflow.category) {
|
|
||||||
category += SEPARATOR + nodeData.category;
|
|
||||||
}
|
|
||||||
if(category == '') {
|
|
||||||
category = 'components';
|
|
||||||
}
|
|
||||||
|
|
||||||
await config.registerType(category);
|
|
||||||
register_pack_map(name, nodeData);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
let elapsed_time = Date.now() - start_time;
|
|
||||||
if (elapsed_time > 30000) {
|
|
||||||
failed.push(name);
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
await sleep(100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function save_as_component(node, version, author, prefix, nodename, packname, category) {
|
|
||||||
let component_name = `${prefix}::${nodename}`;
|
|
||||||
|
|
||||||
let subgraph = app.graph.extra?.groupNodes?.[component_name];
|
|
||||||
if(!subgraph) {
|
|
||||||
subgraph = app.graph.extra?.groupNodes?.[getPureName(node)];
|
|
||||||
}
|
|
||||||
|
|
||||||
subgraph.version = version;
|
|
||||||
subgraph.author = author;
|
|
||||||
subgraph.datetime = Date.now();
|
|
||||||
subgraph.packname = packname;
|
|
||||||
subgraph.category = category;
|
|
||||||
|
|
||||||
let body =
|
|
||||||
{
|
|
||||||
name: component_name,
|
|
||||||
workflow: subgraph
|
|
||||||
};
|
|
||||||
|
|
||||||
pack_map[packname] = component_name;
|
|
||||||
rpack_map[component_name] = subgraph;
|
|
||||||
|
|
||||||
const res = await api.fetchApi('/v2/manager/component/save', {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify(body),
|
|
||||||
});
|
|
||||||
|
|
||||||
if(res.status == 200) {
|
|
||||||
storeGroupNode(component_name, subgraph);
|
|
||||||
const config = new GroupNodeConfig(component_name, subgraph);
|
|
||||||
|
|
||||||
let category = body.workflow.packname;
|
|
||||||
if(body.workflow.category) {
|
|
||||||
category += SEPARATOR + body.workflow.category;
|
|
||||||
}
|
|
||||||
if(category == '') {
|
|
||||||
category = 'components';
|
|
||||||
}
|
|
||||||
|
|
||||||
await config.registerType(category);
|
|
||||||
|
|
||||||
let path = await res.text();
|
|
||||||
show_message(`Component '${component_name}' is saved into:\n${path}`);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
show_message(`Failed to save component.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function import_component(component_name, component, mode) {
|
|
||||||
if(mode) {
|
|
||||||
let body =
|
|
||||||
{
|
|
||||||
name: component_name,
|
|
||||||
workflow: component
|
|
||||||
};
|
|
||||||
|
|
||||||
const res = await api.fetchApi('/v2/manager/component/save', {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json", },
|
|
||||||
body: JSON.stringify(body)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let category = component.packname;
|
|
||||||
if(component.category) {
|
|
||||||
category += SEPARATOR + component.category;
|
|
||||||
}
|
|
||||||
if(category == '') {
|
|
||||||
category = 'components';
|
|
||||||
}
|
|
||||||
|
|
||||||
storeGroupNode(component_name, component);
|
|
||||||
const config = new GroupNodeConfig(component_name, component);
|
|
||||||
await config.registerType(category);
|
|
||||||
}
|
|
||||||
|
|
||||||
function restore_to_loaded_component(component_name) {
|
|
||||||
if(rpack_map[component_name]) {
|
|
||||||
let component = rpack_map[component_name];
|
|
||||||
storeGroupNode(component_name, component, false);
|
|
||||||
const config = new GroupNodeConfig(component_name, component);
|
|
||||||
config.registerType(component.category);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Using a timestamp prevents duplicate pastes and ensures the prevention of re-deletion of litegrapheditor_clipboard.
|
|
||||||
let last_paste_timestamp = null;
|
|
||||||
|
|
||||||
function versionCompare(v1, v2) {
|
|
||||||
let ver1;
|
|
||||||
let ver2;
|
|
||||||
if(v1 && v1 != '') {
|
|
||||||
ver1 = v1.split('.');
|
|
||||||
ver1[0] = parseInt(ver1[0]);
|
|
||||||
ver1[1] = parseInt(ver1[1]);
|
|
||||||
if(ver1.length == 2)
|
|
||||||
ver1.push(0);
|
|
||||||
else
|
|
||||||
ver1[2] = parseInt(ver2[2]);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ver1 = [0,0,0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if(v2 && v2 != '') {
|
|
||||||
ver2 = v2.split('.');
|
|
||||||
ver2[0] = parseInt(ver2[0]);
|
|
||||||
ver2[1] = parseInt(ver2[1]);
|
|
||||||
if(ver2.length == 2)
|
|
||||||
ver2.push(0);
|
|
||||||
else
|
|
||||||
ver2[2] = parseInt(ver2[2]);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ver2 = [0,0,0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ver1[0] > ver2[0])
|
|
||||||
return -1;
|
|
||||||
else if(ver1[0] < ver2[0])
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
if(ver1[1] > ver2[1])
|
|
||||||
return -1;
|
|
||||||
else if(ver1[1] < ver2[1])
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
if(ver1[2] > ver2[2])
|
|
||||||
return -1;
|
|
||||||
else if(ver1[2] < ver2[2])
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkVersion(name, component) {
|
|
||||||
let msg = '';
|
|
||||||
if(rpack_map[name]) {
|
|
||||||
let old_version = rpack_map[name].version;
|
|
||||||
if(!old_version || old_version == '') {
|
|
||||||
msg = ` '${name}' Upgrade (V0.0 -> V${component.version})`;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
let c = versionCompare(old_version, component.version);
|
|
||||||
if(c < 0) {
|
|
||||||
msg = ` '${name}' Downgrade (V${old_version} -> V${component.version})`;
|
|
||||||
}
|
|
||||||
else if(c > 0) {
|
|
||||||
msg = ` '${name}' Upgrade (V${old_version} -> V${component.version})`;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
msg = ` '${name}' Same version (V${component.version})`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
msg = `'${name}' NEW (V${component.version})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handle_import_components(components) {
|
|
||||||
let msg = 'Components:\n';
|
|
||||||
let cnt = 0;
|
|
||||||
for(let name in components) {
|
|
||||||
let component = components[name];
|
|
||||||
let v = checkVersion(name, component);
|
|
||||||
|
|
||||||
if(cnt < 10) {
|
|
||||||
msg += v + '\n';
|
|
||||||
}
|
|
||||||
else if (cnt == 10) {
|
|
||||||
msg += '...\n';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
cnt++;
|
|
||||||
}
|
|
||||||
|
|
||||||
let last_name = null;
|
|
||||||
msg += '\nWill you load components?\n';
|
|
||||||
const confirmed = await customConfirm(msg);
|
|
||||||
if(confirmed) {
|
|
||||||
const mode = await customConfirm('\nWill you save components?\n(cancel=load without save)');
|
|
||||||
|
|
||||||
for(let name in components) {
|
|
||||||
let component = components[name];
|
|
||||||
import_component(name, component, mode);
|
|
||||||
last_name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(mode) {
|
|
||||||
show_message('Components are saved.');
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
show_message('Components are loaded.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(cnt == 1 && last_name) {
|
|
||||||
const node = LiteGraph.createNode(`workflow${SEPARATOR}${last_name}`);
|
|
||||||
node.pos = [app.canvas.graph_mouse[0], app.canvas.graph_mouse[1]];
|
|
||||||
app.canvas.graph.add(node, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handlePaste(e) {
|
|
||||||
let data = (e.clipboardData || window.clipboardData);
|
|
||||||
const items = data.items;
|
|
||||||
for(const item of items) {
|
|
||||||
if(item.kind == 'string' && item.type == 'text/plain') {
|
|
||||||
data = data.getData("text/plain");
|
|
||||||
try {
|
|
||||||
let json_data = JSON.parse(data);
|
|
||||||
if(json_data.kind == 'ComfyUI Components' && last_paste_timestamp != json_data.timestamp) {
|
|
||||||
last_paste_timestamp = json_data.timestamp;
|
|
||||||
await handle_import_components(json_data.components);
|
|
||||||
|
|
||||||
// disable paste node
|
|
||||||
localStorage.removeItem("litegrapheditor_clipboard", null);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.log('This components are already pasted: ignored');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
// nothing to do
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener("paste", handlePaste);
|
|
||||||
|
|
||||||
|
|
||||||
export class ComponentBuilderDialog extends ComfyDialog {
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
clear() {
|
|
||||||
while (this.element.children.length) {
|
|
||||||
this.element.removeChild(this.element.children[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
show() {
|
|
||||||
this.invalidateControl();
|
|
||||||
|
|
||||||
this.element.style.display = "block";
|
|
||||||
this.element.style.zIndex = 1099;
|
|
||||||
this.element.style.width = "500px";
|
|
||||||
this.element.style.height = "480px";
|
|
||||||
}
|
|
||||||
|
|
||||||
invalidateControl() {
|
|
||||||
this.clear();
|
|
||||||
|
|
||||||
let self = this;
|
|
||||||
|
|
||||||
const close_button = $el("button", { id: "cm-close-button", type: "button", textContent: "Close", onclick: () => self.close() });
|
|
||||||
this.save_button = $el("button",
|
|
||||||
{ id: "cm-save-button", type: "button", textContent: "Save", onclick: () =>
|
|
||||||
{
|
|
||||||
save_as_component(self.target_node, self.version_string.value.trim(), self.author.value.trim(), self.node_prefix.value.trim(),
|
|
||||||
self.getNodeName(), self.getPackName(), self.category.value.trim());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let default_nodename = getPureName(this.target_node).trim();
|
|
||||||
|
|
||||||
let groupNode = app.graph.extra.groupNodes[default_nodename];
|
|
||||||
let default_packname = groupNode.packname;
|
|
||||||
if(!default_packname) {
|
|
||||||
default_packname = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
let default_category = groupNode.category;
|
|
||||||
if(!default_category) {
|
|
||||||
default_category = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
this.default_ver = groupNode.version;
|
|
||||||
if(!this.default_ver) {
|
|
||||||
this.default_ver = '0.0';
|
|
||||||
}
|
|
||||||
|
|
||||||
let default_author = groupNode.author;
|
|
||||||
if(!default_author) {
|
|
||||||
default_author = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
let delimiterIndex = default_nodename.indexOf('::');
|
|
||||||
let default_prefix = "";
|
|
||||||
if(delimiterIndex != -1) {
|
|
||||||
default_prefix = default_nodename.substring(0, delimiterIndex);
|
|
||||||
default_nodename = default_nodename.substring(delimiterIndex + 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!default_prefix) {
|
|
||||||
this.save_button.disabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.pack_list = this.createPackListCombo();
|
|
||||||
|
|
||||||
let version_string = this.createLabeledInput('input version (e.g. 1.0)', '*Version : ', this.default_ver);
|
|
||||||
this.version_string = version_string[1];
|
|
||||||
this.version_string.disabled = true;
|
|
||||||
|
|
||||||
let author = this.createLabeledInput('input author (e.g. Dr.Lt.Data)', 'Author : ', default_author);
|
|
||||||
this.author = author[1];
|
|
||||||
|
|
||||||
let node_prefix = this.createLabeledInput('input node prefix (e.g. mypack)', '*Prefix : ', default_prefix);
|
|
||||||
this.node_prefix = node_prefix[1];
|
|
||||||
|
|
||||||
let manual_nodename = this.createLabeledInput('input node name (e.g. MAKE_BASIC_PIPE)', 'Nodename : ', default_nodename);
|
|
||||||
this.manual_nodename = manual_nodename[1];
|
|
||||||
|
|
||||||
let manual_packname = this.createLabeledInput('input pack name (e.g. mypack)', 'Packname : ', default_packname);
|
|
||||||
this.manual_packname = manual_packname[1];
|
|
||||||
|
|
||||||
let category = this.createLabeledInput('input category (e.g. util/pipe)', 'Category : ', default_category);
|
|
||||||
this.category = category[1];
|
|
||||||
|
|
||||||
this.node_label = this.createNodeLabel();
|
|
||||||
|
|
||||||
let author_mode = this.createAuthorModeCheck();
|
|
||||||
this.author_mode = author_mode[0];
|
|
||||||
|
|
||||||
const content =
|
|
||||||
$el("div.comfy-modal-content",
|
|
||||||
[
|
|
||||||
$el("tr.cm-title", {}, [
|
|
||||||
$el("font", {size:6, color:"white"}, [`ComfyUI-Manager: Component Builder`])]
|
|
||||||
),
|
|
||||||
$el("br", {}, []),
|
|
||||||
$el("div.cm-menu-container",
|
|
||||||
[
|
|
||||||
author_mode[0],
|
|
||||||
author_mode[1],
|
|
||||||
category[0],
|
|
||||||
author[0],
|
|
||||||
node_prefix[0],
|
|
||||||
manual_nodename[0],
|
|
||||||
manual_packname[0],
|
|
||||||
version_string[0],
|
|
||||||
this.pack_list,
|
|
||||||
$el("br", {}, []),
|
|
||||||
this.node_label
|
|
||||||
]),
|
|
||||||
|
|
||||||
$el("br", {}, []),
|
|
||||||
this.save_button,
|
|
||||||
close_button,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
content.style.width = '100%';
|
|
||||||
content.style.height = '100%';
|
|
||||||
|
|
||||||
this.element = $el("div.comfy-modal", { id:'cm-manager-dialog', parent: document.body }, [ content ]);
|
|
||||||
}
|
|
||||||
|
|
||||||
validateInput() {
|
|
||||||
let msg = "";
|
|
||||||
|
|
||||||
if(!isValidVersionString(this.version_string.value)) {
|
|
||||||
msg += 'Invalid version string: '+event.value+"\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.node_prefix.value.trim() == '') {
|
|
||||||
msg += 'Node prefix cannot be empty\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.manual_nodename.value.trim() == '') {
|
|
||||||
msg += 'Node name cannot be empty\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
if(msg != '') {
|
|
||||||
// alert(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.save_button.disabled = msg != "";
|
|
||||||
}
|
|
||||||
|
|
||||||
getPackName() {
|
|
||||||
if(this.pack_list.selectedIndex == 0) {
|
|
||||||
return this.manual_packname.value.trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.pack_list.value.trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
getNodeName() {
|
|
||||||
if(this.manual_nodename.value.trim() != '') {
|
|
||||||
return this.manual_nodename.value.trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
return getPureName(this.target_node);
|
|
||||||
}
|
|
||||||
|
|
||||||
createAuthorModeCheck() {
|
|
||||||
let check = $el("input",{type:'checkbox', id:"author-mode"},[])
|
|
||||||
const check_label = $el("label",{for:"author-mode"},["Enable author mode"]);
|
|
||||||
check_label.style.color = "var(--fg-color)";
|
|
||||||
check_label.style.cursor = "pointer";
|
|
||||||
check.checked = false;
|
|
||||||
|
|
||||||
let self = this;
|
|
||||||
check.onchange = () => {
|
|
||||||
self.version_string.disabled = !check.checked;
|
|
||||||
|
|
||||||
if(!check.checked) {
|
|
||||||
self.version_string.value = self.default_ver;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
customAlert('If you are not the author, it is not recommended to change the version, as it may cause component update issues.');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return [check, check_label];
|
|
||||||
}
|
|
||||||
|
|
||||||
createNodeLabel() {
|
|
||||||
let label = $el('p');
|
|
||||||
label.className = 'cb-node-label';
|
|
||||||
if(this.target_node.comfyClass.includes('::'))
|
|
||||||
label.textContent = getPureName(this.target_node);
|
|
||||||
else
|
|
||||||
label.textContent = " _::" + getPureName(this.target_node);
|
|
||||||
return label;
|
|
||||||
}
|
|
||||||
|
|
||||||
createLabeledInput(placeholder, label, value) {
|
|
||||||
let textbox = $el('input.cb-widget-input', {type:'text', placeholder:placeholder, value:value}, []);
|
|
||||||
|
|
||||||
let self = this;
|
|
||||||
textbox.onchange = () => {
|
|
||||||
this.validateInput.call(self);
|
|
||||||
this.node_label.textContent = this.node_prefix.value + "::" + this.manual_nodename.value;
|
|
||||||
}
|
|
||||||
let row = $el('span.cb-widget', {}, [ $el('span.cb-widget-input-label', label), textbox]);
|
|
||||||
|
|
||||||
return [row, textbox];
|
|
||||||
}
|
|
||||||
|
|
||||||
createPackListCombo() {
|
|
||||||
let combo = document.createElement("select");
|
|
||||||
combo.className = "cb-widget";
|
|
||||||
let default_packname_option = { value: '##manual', text: 'Packname: Manual' };
|
|
||||||
|
|
||||||
combo.appendChild($el('option', default_packname_option, []));
|
|
||||||
for(let name in pack_map) {
|
|
||||||
combo.appendChild($el('option', { value: name, text: 'Packname: '+ name }, []));
|
|
||||||
}
|
|
||||||
|
|
||||||
let self = this;
|
|
||||||
combo.onchange = function () {
|
|
||||||
if(combo.selectedIndex == 0) {
|
|
||||||
self.manual_packname.disabled = false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
self.manual_packname.disabled = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return combo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let orig_handleFile = app.handleFile;
|
|
||||||
|
|
||||||
async function handleFile(file) {
|
|
||||||
if (file.name?.endsWith(".json") || file.name?.endsWith(".pack")) {
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.onload = async () => {
|
|
||||||
let is_component = false;
|
|
||||||
const jsonContent = JSON.parse(reader.result);
|
|
||||||
for(let name in jsonContent) {
|
|
||||||
let cand = jsonContent[name];
|
|
||||||
is_component = cand.datetime && cand.version;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(is_component) {
|
|
||||||
await handle_import_components(jsonContent);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
orig_handleFile.call(app, file);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
reader.readAsText(file);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
orig_handleFile.call(app, file);
|
|
||||||
}
|
|
||||||
|
|
||||||
app.handleFile = handleFile;
|
|
||||||
|
|
||||||
let current_component_policy = 'workflow';
|
|
||||||
try {
|
|
||||||
api.fetchApi('/v2/manager/policy/component')
|
|
||||||
.then(response => response.text())
|
|
||||||
.then(data => { current_component_policy = data; });
|
|
||||||
}
|
|
||||||
catch {}
|
|
||||||
|
|
||||||
function getChangedVersion(groupNodes) {
|
|
||||||
if(!Object.keys(pack_map).length || !groupNodes)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
let res = {};
|
|
||||||
for(let component_name in groupNodes) {
|
|
||||||
let data = groupNodes[component_name];
|
|
||||||
|
|
||||||
if(rpack_map[component_name]) {
|
|
||||||
let v = versionCompare(data.version, rpack_map[component_name].version);
|
|
||||||
res[component_name] = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadGraphData = app.loadGraphData;
|
|
||||||
app.loadGraphData = async function () {
|
|
||||||
if(arguments.length == 0)
|
|
||||||
return await loadGraphData.apply(this, arguments);
|
|
||||||
|
|
||||||
let graphData = arguments[0];
|
|
||||||
let groupNodes = graphData.extra?.groupNodes;
|
|
||||||
let res = getChangedVersion(groupNodes);
|
|
||||||
|
|
||||||
if(res) {
|
|
||||||
let target_components = null;
|
|
||||||
switch(current_component_policy) {
|
|
||||||
case 'higher':
|
|
||||||
target_components = Object.keys(res).filter(key => res[key] == 1);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'mine':
|
|
||||||
target_components = Object.keys(res);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
if(target_components) {
|
|
||||||
for(let i in target_components) {
|
|
||||||
let component_name = target_components[i];
|
|
||||||
let component = rpack_map[component_name];
|
|
||||||
if(component && graphData.extra?.groupNodes) {
|
|
||||||
graphData.extra.groupNodes[component_name] = component;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.log('Empty components: policy ignored');
|
|
||||||
}
|
|
||||||
|
|
||||||
arguments[0] = graphData;
|
|
||||||
return await loadGraphData.apply(this, arguments);
|
|
||||||
};
|
|
||||||
|
|
||||||
export function set_component_policy(v) {
|
|
||||||
current_component_policy = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
let graphToPrompt = app.graphToPrompt;
|
|
||||||
app.graphToPrompt = async function () {
|
|
||||||
let p = await graphToPrompt.call(app);
|
|
||||||
try {
|
|
||||||
let groupNodes = p.workflow.extra?.groupNodes;
|
|
||||||
if(groupNodes) {
|
|
||||||
p.workflow.extra = { ... p.workflow.extra};
|
|
||||||
|
|
||||||
// get used group nodes
|
|
||||||
let used_group_nodes = new Set();
|
|
||||||
for(let node of p.workflow.nodes) {
|
|
||||||
if(node.type.startsWith(`workflow/`) || node.type.startsWith(`workflow${SEPARATOR}`)) {
|
|
||||||
used_group_nodes.add(node.type.substring(9));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove unused group nodes
|
|
||||||
let new_groupNodes = {};
|
|
||||||
for (let key in p.workflow.extra.groupNodes) {
|
|
||||||
if (used_group_nodes.has(key)) {
|
|
||||||
new_groupNodes[key] = p.workflow.extra.groupNodes[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.workflow.extra.groupNodes = new_groupNodes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(e) {
|
|
||||||
console.log(`Failed to filtering group nodes: ${e}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
@ -1577,9 +1577,6 @@ class ManagerFuncs:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def get_current_preview_method(self):
|
|
||||||
return "none"
|
|
||||||
|
|
||||||
def run_script(self, cmd, cwd='.'):
|
def run_script(self, cmd, cwd='.'):
|
||||||
if len(cmd) > 0 and cmd[0].startswith("#"):
|
if len(cmd) > 0 and cmd[0].startswith("#"):
|
||||||
print(f"[ComfyUI-Manager] Unexpected behavior: `{cmd}`")
|
print(f"[ComfyUI-Manager] Unexpected behavior: `{cmd}`")
|
||||||
@ -1597,14 +1594,12 @@ def write_config():
|
|||||||
config = configparser.ConfigParser(strict=False)
|
config = configparser.ConfigParser(strict=False)
|
||||||
|
|
||||||
config['default'] = {
|
config['default'] = {
|
||||||
'preview_method': manager_funcs.get_current_preview_method(),
|
|
||||||
'git_exe': get_config()['git_exe'],
|
'git_exe': get_config()['git_exe'],
|
||||||
'use_uv': get_config()['use_uv'],
|
'use_uv': get_config()['use_uv'],
|
||||||
'channel_url': get_config()['channel_url'],
|
'channel_url': get_config()['channel_url'],
|
||||||
'share_option': get_config()['share_option'],
|
'share_option': get_config()['share_option'],
|
||||||
'bypass_ssl': get_config()['bypass_ssl'],
|
'bypass_ssl': get_config()['bypass_ssl'],
|
||||||
"file_logging": get_config()['file_logging'],
|
"file_logging": get_config()['file_logging'],
|
||||||
'component_policy': get_config()['component_policy'],
|
|
||||||
'update_policy': get_config()['update_policy'],
|
'update_policy': get_config()['update_policy'],
|
||||||
'windows_selector_event_loop_policy': get_config()['windows_selector_event_loop_policy'],
|
'windows_selector_event_loop_policy': get_config()['windows_selector_event_loop_policy'],
|
||||||
'model_download_by_agent': get_config()['model_download_by_agent'],
|
'model_download_by_agent': get_config()['model_download_by_agent'],
|
||||||
@ -1637,7 +1632,6 @@ def read_config():
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
'http_channel_enabled': get_bool('http_channel_enabled', False),
|
'http_channel_enabled': get_bool('http_channel_enabled', False),
|
||||||
'preview_method': default_conf.get('preview_method', manager_funcs.get_current_preview_method()).lower(),
|
|
||||||
'git_exe': default_conf.get('git_exe', ''),
|
'git_exe': default_conf.get('git_exe', ''),
|
||||||
'use_uv': get_bool('use_uv', True),
|
'use_uv': get_bool('use_uv', True),
|
||||||
'channel_url': default_conf.get('channel_url', DEFAULT_CHANNEL),
|
'channel_url': default_conf.get('channel_url', DEFAULT_CHANNEL),
|
||||||
@ -1645,7 +1639,6 @@ def read_config():
|
|||||||
'share_option': default_conf.get('share_option', 'all').lower(),
|
'share_option': default_conf.get('share_option', 'all').lower(),
|
||||||
'bypass_ssl': get_bool('bypass_ssl', False),
|
'bypass_ssl': get_bool('bypass_ssl', False),
|
||||||
'file_logging': get_bool('file_logging', True),
|
'file_logging': get_bool('file_logging', True),
|
||||||
'component_policy': default_conf.get('component_policy', 'workflow').lower(),
|
|
||||||
'update_policy': default_conf.get('update_policy', 'stable-comfyui').lower(),
|
'update_policy': default_conf.get('update_policy', 'stable-comfyui').lower(),
|
||||||
'windows_selector_event_loop_policy': get_bool('windows_selector_event_loop_policy', False),
|
'windows_selector_event_loop_policy': get_bool('windows_selector_event_loop_policy', False),
|
||||||
'model_download_by_agent': get_bool('model_download_by_agent', False),
|
'model_download_by_agent': get_bool('model_download_by_agent', False),
|
||||||
@ -1662,7 +1655,6 @@ def read_config():
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
'http_channel_enabled': False,
|
'http_channel_enabled': False,
|
||||||
'preview_method': manager_funcs.get_current_preview_method(),
|
|
||||||
'git_exe': '',
|
'git_exe': '',
|
||||||
'use_uv': True,
|
'use_uv': True,
|
||||||
'channel_url': DEFAULT_CHANNEL,
|
'channel_url': DEFAULT_CHANNEL,
|
||||||
@ -1670,7 +1662,6 @@ def read_config():
|
|||||||
'share_option': 'all',
|
'share_option': 'all',
|
||||||
'bypass_ssl': manager_util.bypass_ssl,
|
'bypass_ssl': manager_util.bypass_ssl,
|
||||||
'file_logging': True,
|
'file_logging': True,
|
||||||
'component_policy': 'workflow',
|
|
||||||
'update_policy': 'stable-comfyui',
|
'update_policy': 'stable-comfyui',
|
||||||
'windows_selector_event_loop_policy': False,
|
'windows_selector_event_loop_policy': False,
|
||||||
'model_download_by_agent': False,
|
'model_download_by_agent': False,
|
||||||
|
|||||||
@ -61,7 +61,6 @@ def handle_stream(stream, prefix):
|
|||||||
|
|
||||||
|
|
||||||
from comfy.cli_args import args
|
from comfy.cli_args import args
|
||||||
import latent_preview
|
|
||||||
|
|
||||||
def is_loopback(address):
|
def is_loopback(address):
|
||||||
import ipaddress
|
import ipaddress
|
||||||
@ -146,16 +145,6 @@ async def get_risky_level(files, pip_packages):
|
|||||||
|
|
||||||
|
|
||||||
class ManagerFuncsInComfyUI(core.ManagerFuncs):
|
class ManagerFuncsInComfyUI(core.ManagerFuncs):
|
||||||
def get_current_preview_method(self):
|
|
||||||
if args.preview_method == latent_preview.LatentPreviewMethod.Auto:
|
|
||||||
return "auto"
|
|
||||||
elif args.preview_method == latent_preview.LatentPreviewMethod.Latent2RGB:
|
|
||||||
return "latent2rgb"
|
|
||||||
elif args.preview_method == latent_preview.LatentPreviewMethod.TAESD:
|
|
||||||
return "taesd"
|
|
||||||
else:
|
|
||||||
return "none"
|
|
||||||
|
|
||||||
def run_script(self, cmd, cwd='.'):
|
def run_script(self, cmd, cwd='.'):
|
||||||
if len(cmd) > 0 and cmd[0].startswith("#"):
|
if len(cmd) > 0 and cmd[0].startswith("#"):
|
||||||
logging.error(f"[ComfyUI-Manager] Unexpected behavior: `{cmd}`")
|
logging.error(f"[ComfyUI-Manager] Unexpected behavior: `{cmd}`")
|
||||||
@ -188,25 +177,6 @@ local_db_custom_node_list = os.path.join(manager_util.comfyui_manager_path, "cus
|
|||||||
local_db_extension_node_mappings = os.path.join(manager_util.comfyui_manager_path, "extension-node-map.json")
|
local_db_extension_node_mappings = os.path.join(manager_util.comfyui_manager_path, "extension-node-map.json")
|
||||||
|
|
||||||
|
|
||||||
def set_preview_method(method):
|
|
||||||
if method == 'auto':
|
|
||||||
args.preview_method = latent_preview.LatentPreviewMethod.Auto
|
|
||||||
elif method == 'latent2rgb':
|
|
||||||
args.preview_method = latent_preview.LatentPreviewMethod.Latent2RGB
|
|
||||||
elif method == 'taesd':
|
|
||||||
args.preview_method = latent_preview.LatentPreviewMethod.TAESD
|
|
||||||
else:
|
|
||||||
args.preview_method = latent_preview.LatentPreviewMethod.NoPreviews
|
|
||||||
|
|
||||||
core.get_config()['preview_method'] = method
|
|
||||||
|
|
||||||
|
|
||||||
set_preview_method(core.get_config()['preview_method'])
|
|
||||||
|
|
||||||
|
|
||||||
def set_component_policy(mode):
|
|
||||||
core.get_config()['component_policy'] = mode
|
|
||||||
|
|
||||||
def set_update_policy(mode):
|
def set_update_policy(mode):
|
||||||
core.get_config()['update_policy'] = mode
|
core.get_config()['update_policy'] = mode
|
||||||
|
|
||||||
@ -1725,17 +1695,6 @@ async def _install_model(json_data):
|
|||||||
return web.Response(status=200)
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
@routes.get("/v2/manager/preview_method")
|
|
||||||
async def preview_method(request):
|
|
||||||
if "value" in request.rel_url.query:
|
|
||||||
set_preview_method(request.rel_url.query['value'])
|
|
||||||
core.write_config()
|
|
||||||
else:
|
|
||||||
return web.Response(text=core.manager_funcs.get_current_preview_method(), status=200)
|
|
||||||
|
|
||||||
return web.Response(status=200)
|
|
||||||
|
|
||||||
|
|
||||||
@routes.get("/v2/manager/db_mode")
|
@routes.get("/v2/manager/db_mode")
|
||||||
async def db_mode(request):
|
async def db_mode(request):
|
||||||
if "value" in request.rel_url.query:
|
if "value" in request.rel_url.query:
|
||||||
@ -1747,18 +1706,6 @@ async def db_mode(request):
|
|||||||
return web.Response(status=200)
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@routes.get("/v2/manager/policy/component")
|
|
||||||
async def component_policy(request):
|
|
||||||
if "value" in request.rel_url.query:
|
|
||||||
set_component_policy(request.rel_url.query['value'])
|
|
||||||
core.write_config()
|
|
||||||
else:
|
|
||||||
return web.Response(text=core.get_config()['component_policy'], status=200)
|
|
||||||
|
|
||||||
return web.Response(status=200)
|
|
||||||
|
|
||||||
|
|
||||||
@routes.get("/v2/manager/policy/update")
|
@routes.get("/v2/manager/policy/update")
|
||||||
async def update_policy(request):
|
async def update_policy(request):
|
||||||
if "value" in request.rel_url.query:
|
if "value" in request.rel_url.query:
|
||||||
@ -1897,61 +1844,6 @@ def restart(self):
|
|||||||
return os.execv(sys.executable, cmds)
|
return os.execv(sys.executable, cmds)
|
||||||
|
|
||||||
|
|
||||||
@routes.post("/v2/manager/component/save")
|
|
||||||
async def save_component(request):
|
|
||||||
try:
|
|
||||||
data = await request.json()
|
|
||||||
name = data['name']
|
|
||||||
workflow = data['workflow']
|
|
||||||
|
|
||||||
if not os.path.exists(context.manager_components_path):
|
|
||||||
os.mkdir(context.manager_components_path)
|
|
||||||
|
|
||||||
if 'packname' in workflow and workflow['packname'] != '':
|
|
||||||
sanitized_name = manager_util.sanitize_filename(workflow['packname']) + '.pack'
|
|
||||||
else:
|
|
||||||
sanitized_name = manager_util.sanitize_filename(name) + '.json'
|
|
||||||
|
|
||||||
filepath = os.path.join(context.manager_components_path, sanitized_name)
|
|
||||||
components = {}
|
|
||||||
if os.path.exists(filepath):
|
|
||||||
with open(filepath) as f:
|
|
||||||
components = json.load(f)
|
|
||||||
|
|
||||||
components[name] = workflow
|
|
||||||
|
|
||||||
with open(filepath, 'w') as f:
|
|
||||||
json.dump(components, f, indent=4, sort_keys=True)
|
|
||||||
return web.Response(text=filepath, status=200)
|
|
||||||
except Exception:
|
|
||||||
return web.Response(status=400)
|
|
||||||
|
|
||||||
|
|
||||||
@routes.post("/v2/manager/component/loads")
|
|
||||||
async def load_components(request):
|
|
||||||
if os.path.exists(context.manager_components_path):
|
|
||||||
try:
|
|
||||||
json_files = [f for f in os.listdir(context.manager_components_path) if f.endswith('.json')]
|
|
||||||
pack_files = [f for f in os.listdir(context.manager_components_path) if f.endswith('.pack')]
|
|
||||||
|
|
||||||
components = {}
|
|
||||||
for json_file in json_files + pack_files:
|
|
||||||
file_path = os.path.join(context.manager_components_path, json_file)
|
|
||||||
with open(file_path, 'r') as file:
|
|
||||||
try:
|
|
||||||
# When there is a conflict between the .pack and the .json, the pack takes precedence and overrides.
|
|
||||||
components.update(json.load(file))
|
|
||||||
except json.JSONDecodeError as e:
|
|
||||||
logging.error(f"[ComfyUI-Manager] Error decoding component file in file {json_file}: {e}")
|
|
||||||
|
|
||||||
return web.json_response(components)
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"[ComfyUI-Manager] failed to load components\n{e}")
|
|
||||||
return web.Response(status=400)
|
|
||||||
else:
|
|
||||||
return web.json_response({})
|
|
||||||
|
|
||||||
|
|
||||||
@routes.get("/v2/manager/version")
|
@routes.get("/v2/manager/version")
|
||||||
async def get_version(request):
|
async def get_version(request):
|
||||||
return web.Response(text=core.version_str, status=200)
|
return web.Response(text=core.version_str, status=200)
|
||||||
|
|||||||
@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
[project]
|
[project]
|
||||||
name = "comfyui-manager"
|
name = "comfyui-manager"
|
||||||
license = { text = "GPL-3.0-only" }
|
license = { text = "GPL-3.0-only" }
|
||||||
version = "4.0.3b6"
|
version = "4.0.3b7"
|
||||||
requires-python = ">= 3.9"
|
requires-python = ">= 3.9"
|
||||||
description = "ComfyUI-Manager provides features to install and manage custom nodes for ComfyUI, as well as various functionalities to assist with ComfyUI."
|
description = "ComfyUI-Manager provides features to install and manage custom nodes for ComfyUI, as well as various functionalities to assist with ComfyUI."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user