mirror of
https://git.datalinker.icu/ltdrdata/ComfyUI-Manager
synced 2025-12-08 21:54:26 +08:00
feat: A functionality to save a group node as a component.
This commit is contained in:
parent
019897eae2
commit
7f10374a9f
56
__init__.py
56
__init__.py
@ -28,7 +28,7 @@ except:
|
||||
print(f"[WARN] ComfyUI-Manager: Your ComfyUI version is outdated. Please update to the latest version.")
|
||||
|
||||
|
||||
version = [1, 26, 1]
|
||||
version = [2, 0]
|
||||
version_str = f"V{version[0]}.{version[1]}" + (f'.{version[2]}' if len(version) > 2 else '')
|
||||
print(f"### Loading: ComfyUI-Manager ({version_str})")
|
||||
|
||||
@ -118,6 +118,7 @@ local_db_alter = os.path.join(comfyui_manager_path, "alter-list.json")
|
||||
local_db_custom_node_list = os.path.join(comfyui_manager_path, "custom-node-list.json")
|
||||
local_db_extension_node_mappings = os.path.join(comfyui_manager_path, "extension-node-map.json")
|
||||
git_script_path = os.path.join(os.path.dirname(__file__), "git_helper.py")
|
||||
components_path = os.path.join(comfyui_manager_path, 'components')
|
||||
|
||||
startup_script_path = os.path.join(comfyui_manager_path, "startup-scripts")
|
||||
config_path = os.path.join(os.path.dirname(__file__), "config.ini")
|
||||
@ -1838,6 +1839,59 @@ def restart(self):
|
||||
return os.execv(sys.executable, [sys.executable] + sys.argv)
|
||||
|
||||
|
||||
def sanitize_filename(input_string):
|
||||
# 알파벳, 숫자, 및 밑줄 이외의 문자를 밑줄로 대체
|
||||
result_string = re.sub(r'[^a-zA-Z0-9_]', '_', input_string)
|
||||
return result_string
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.post("/manager/component/save")
|
||||
async def save_component(request):
|
||||
try:
|
||||
data = await request.json()
|
||||
name = data['name']
|
||||
workflow = data['workflow']
|
||||
|
||||
if not os.path.exists(components_path):
|
||||
os.mkdir(components_path)
|
||||
|
||||
sanitized_name = sanitize_filename(name)
|
||||
|
||||
filepath = os.path.join(components_path, sanitized_name+'.json')
|
||||
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:
|
||||
return web.Response(status=400)
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.post("/manager/component/loads")
|
||||
async def load_components(request):
|
||||
try:
|
||||
json_files = [f for f in os.listdir(components_path) if f.endswith('.json')]
|
||||
|
||||
components = {}
|
||||
for json_file in json_files:
|
||||
file_path = os.path.join(components_path, json_file)
|
||||
with open(file_path, 'r') as file:
|
||||
try:
|
||||
components.update(json.load(file))
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"[ComfyUI-Manager] Error decoding component file in file {json_file}: {e}")
|
||||
|
||||
return web.json_response(components)
|
||||
except Exception as e:
|
||||
print(f"[ComfyUI-Manager] failed to load components\n{e}")
|
||||
return web.Response(status=400)
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.get("/manager/share_option")
|
||||
async def share_option(request):
|
||||
if "value" in request.rel_url.query:
|
||||
|
||||
1
components/.gitignore
vendored
Normal file
1
components/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.json
|
||||
@ -16,7 +16,7 @@ import { AlternativesInstaller } from "./a1111-alter-downloader.js";
|
||||
import { SnapshotManager } from "./snapshot.js";
|
||||
import { ModelInstaller } from "./model-downloader.js";
|
||||
import { manager_instance, setManagerInstance, install_via_git_url, install_pip, rebootAPI, free_models } from "./common.js";
|
||||
import { save_as_component } from "./components-manager.js";
|
||||
import { load_components, save_as_component } from "./components-manager.js";
|
||||
|
||||
var docStyle = document.createElement('style');
|
||||
docStyle.innerHTML = `
|
||||
@ -1111,6 +1111,14 @@ app.registerExtension({
|
||||
});
|
||||
},
|
||||
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 separator = document.createElement("hr");
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { api } from "../../scripts/api.js"
|
||||
import { api } from "../../scripts/api.js";
|
||||
|
||||
export async function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
||||
191
js/components-manager.js
Normal file
191
js/components-manager.js
Normal file
@ -0,0 +1,191 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { api } from "../../scripts/api.js"
|
||||
import { sleep } from "./common.js";
|
||||
import { GroupNodeConfig, GroupNodeHandler } from "../../extensions/core/groupNode.js";
|
||||
|
||||
function storeGroupNode(name, data) {
|
||||
let extra = app.graph.extra;
|
||||
if (!extra) app.graph.extra = extra = {};
|
||||
let groupNodes = extra.groupNodes;
|
||||
if (!groupNodes) extra.groupNodes = groupNodes = {};
|
||||
groupNodes[name] = data;
|
||||
}
|
||||
|
||||
export async function load_components() {
|
||||
let data = await api.fetchApi('/manager/component/loads', {method: "POST"});
|
||||
let components = await data.json();
|
||||
|
||||
// while(!app.graph) {
|
||||
// await sleep(100);
|
||||
// }
|
||||
|
||||
let start_time = Date.now();
|
||||
let failed = [];
|
||||
let failed2 = [];
|
||||
|
||||
for(let name in components) {
|
||||
if(app.graph.extra?.groupNodes?.[name]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let nodeData = components[name];
|
||||
|
||||
storeGroupNode(name, nodeData);
|
||||
|
||||
const config = new GroupNodeConfig(name, nodeData);
|
||||
while(!success) {
|
||||
var success = false;
|
||||
try {
|
||||
await config.registerType();
|
||||
}
|
||||
catch {
|
||||
let elapsed_time = Date.now() - start_time;
|
||||
if (elapsed_time > 5000) {
|
||||
failed.push(name);
|
||||
success = true;
|
||||
} else {
|
||||
await sleep(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const groupNode = LiteGraph.createNode(`workflow/${name}`);
|
||||
}
|
||||
|
||||
// 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(!success) {
|
||||
var success = false;
|
||||
try {
|
||||
await config.registerType();
|
||||
}
|
||||
catch {
|
||||
let elapsed_time = Date.now() - start_time;
|
||||
if (elapsed_time > 10000) {
|
||||
failed2.push(name);
|
||||
success = true;
|
||||
} else {
|
||||
await sleep(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const groupNode = LiteGraph.createNode(`workflow/${name}`);
|
||||
}
|
||||
|
||||
// fallback2
|
||||
for(let name in failed2) {
|
||||
let name = failed2[i];
|
||||
|
||||
let nodeData = components[name];
|
||||
|
||||
storeGroupNode(name, nodeData);
|
||||
|
||||
const config = new GroupNodeConfig(name, nodeData);
|
||||
while(!success) {
|
||||
var success = false;
|
||||
try {
|
||||
await config.registerType();
|
||||
}
|
||||
catch {
|
||||
let elapsed_time = Date.now() - start_time;
|
||||
if (elapsed_time > 30000) {
|
||||
failed.push(name);
|
||||
success = true;
|
||||
} else {
|
||||
await sleep(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const groupNode = LiteGraph.createNode(`workflow/${name}`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function save_as_component(node, app) {
|
||||
let pure_name = node.comfyClass.substring(9);
|
||||
let subgraph = app.graph.extra?.groupNodes?.[pure_name];
|
||||
|
||||
if(!subgraph) {
|
||||
app.ui.dialog.show(`Failed to retrieve the group node '${pure_name}'.`);
|
||||
return;
|
||||
}
|
||||
|
||||
if(node.comfyClass.includes('::')) {
|
||||
let component_name = node.comfyClass.substring(9);
|
||||
|
||||
if(confirm(`Will you save/overwrite component '${component_name}'?`)) {
|
||||
let subgraph = app.graph.extra?.groupNodes?.[component_name];
|
||||
let body =
|
||||
{
|
||||
name: component_name,
|
||||
workflow: subgraph
|
||||
};
|
||||
|
||||
const res = await api.fetchApi('/manager/component/save', {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", },
|
||||
body: JSON.stringify(body)
|
||||
});
|
||||
|
||||
if(res.status == 200) {
|
||||
storeGroupNode(name, subgraph);
|
||||
const config = new GroupNodeConfig(name, subgraph);
|
||||
await config.registerType();
|
||||
|
||||
let path = await res.text();
|
||||
app.ui.dialog.show(`Component '${component_name}' is saved into:\n${path}`);
|
||||
}
|
||||
else
|
||||
app.ui.dialog.show(`Failed to save component.`);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var prefix = prompt("To save as a component, a unique prefix is required. (e.g., the 'Impact' in Impact::MAKE_BASIC_PIPE)", "PREFIX");
|
||||
|
||||
if(!prefix) {
|
||||
return;
|
||||
}
|
||||
|
||||
prefix = prefix.trim();
|
||||
|
||||
if(prefix == 'PREFIX') {
|
||||
app.ui.dialog.show(`The placeholder 'PREFIX' isn't allowed for component prefix.`);
|
||||
return;
|
||||
}
|
||||
|
||||
let component_name = prefix+'::'+pure_name;
|
||||
let body =
|
||||
{
|
||||
name: component_name,
|
||||
workflow: subgraph
|
||||
};
|
||||
|
||||
const res = await api.fetchApi('/manager/component/save', {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
if(res.status == 200) {
|
||||
let path = await res.text();
|
||||
app.ui.dialog.show(`Component '${component_name}' is saved into:\n${path}`);
|
||||
}
|
||||
else
|
||||
app.ui.dialog.show(`Failed to save component.`);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user