PROJECT INIT
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
# ComfyUI Path Helper
|
||||
|
||||
This module provides a set of custom nodes to help with creating file paths for ComfyUI. Paths are joined using python os library. This should help issues where people are manually constructing paths with strings which then dont work for someone if they are using a different OS then them
|
||||
|
||||
## Features
|
||||
|
||||
- **Create project root folder**
|
||||
- **Add Folders**
|
||||
- **Add file prefix**
|
||||
- **Show path/strings in a node**
|
||||
- **Prefix/Postfix datetime to paths**
|
||||
- **Format variables into paths**
|
||||
|
||||
## Nodes
|
||||
#### CreateProjectRoot
|
||||
#### AddFolder
|
||||
#### AddFolderAdvanced
|
||||
#### AddFileNamePrefix
|
||||
#### AddFileNamePrefixAdvanced
|
||||
#### JoinVariables
|
||||
#### ShowPath
|
||||
#### ShowString
|
||||
|
||||
## Basic usage
|
||||

|
||||
## Advanced usage
|
||||
|
||||
### Date Time
|
||||
Advanced Nodes allow you to prefix or postfix datetime to a path
|
||||
Datetime is formatted using strftime so can be customized to any format you like
|
||||
more info on strftime here https://strftime.org/
|
||||

|
||||
|
||||
### Variable formatting
|
||||
Advanced Nodes also allow for the formatting of variables with the node being constructed. Use the {} notion where you wish for variable to be formatted within the given string
|
||||

|
||||
|
||||
### Multiple Variable formatting
|
||||
If you wish to format multiple variables then this is also possible by passing a comma seperated string into 'input_variables'. You can use the Join Variables node to do this for you.
|
||||
Note order of variables is import
|
||||

|
||||
|
||||
** note input_variables will accept any type and attempt to format. I wanted to be able to pass strings, ints, floats etc but given its unrestricted this may break if given types such a LATENT
|
||||
|
||||
** primate node doesnt work however
|
||||
|
||||
## Known issues
|
||||
Show Path seems to always hold first value it receives and then will redner a second text window with any updated values. Not sure why this is happening. Appears to be something related to PATH being a custom type
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
from .nodes import *
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"Create Project Root": CreateProjectRoot,
|
||||
"Add Folder": AddFolder,
|
||||
"Add Folder Advanced": AddFolderAdvanced,
|
||||
"Add File Name Prefix": AddFileNamePrefix,
|
||||
"Add File Name Prefix Advanced": AddFileNamePrefixAdvanced,
|
||||
"Join Variables": JoinVariables,
|
||||
"Show Path": ShowPath,
|
||||
"Show String": ShowString,
|
||||
}
|
||||
|
||||
WEB_DIRECTORY = "./web"
|
||||
__all__ = ["NODE_CLASS_MAPPINGS", "WEB_DIRECTORY"]
|
||||
print("\033[34mComfyUI Path Helper Nodes: \033[92mLoaded\033[0m")
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 98 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 72 KiB |
@@ -0,0 +1,199 @@
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class AnyType(str):
|
||||
def __ne__(self, __value: object) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
ANY = AnyType("*")
|
||||
|
||||
|
||||
def format_date_time(string, position, datetime_format):
|
||||
today = datetime.now()
|
||||
if position == "prefix":
|
||||
return f"{today.strftime(datetime_format)}_{string}"
|
||||
if position == "postfix":
|
||||
return f"{string}_{today.strftime(datetime_format)}"
|
||||
|
||||
|
||||
def format_variables(string, input_variables):
|
||||
if input_variables:
|
||||
variables = str(input_variables).split(",")
|
||||
return string.format(*variables)
|
||||
else:
|
||||
return string
|
||||
|
||||
|
||||
#
|
||||
class CreateProjectRoot:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"project_root_name": ("STRING", {"multiline": False, "default": ""}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("PATH",)
|
||||
FUNCTION = "create_project_root"
|
||||
CATEGORY = "path_helper"
|
||||
|
||||
def create_project_root(self, project_root_name):
|
||||
return ("./" + project_root_name,)
|
||||
|
||||
|
||||
class AddFolder:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"path": ("PATH",),
|
||||
"folder_name": ("STRING", {"multiline": False, "default": ""}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("PATH",)
|
||||
FUNCTION = "add_folder"
|
||||
CATEGORY = "path_helper"
|
||||
|
||||
def add_folder(self, path, folder_name):
|
||||
new_path = os.path.join(path, folder_name)
|
||||
return (new_path,)
|
||||
|
||||
|
||||
class AddFolderAdvanced:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"path": ("PATH",),
|
||||
"folder_name": ("STRING", {"multiline": False, "default": ""}),
|
||||
"add_date_time": (["disable", "prefix", "postfix"],),
|
||||
"date_time_format": ("STRING", {"multiline": False, "default": "%Y_%m_%d_%H:%M:%S"}),
|
||||
},
|
||||
"optional": {
|
||||
"input_variables": (ANY,)
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("PATH",)
|
||||
FUNCTION = "add_folder"
|
||||
CATEGORY = "path_helper"
|
||||
|
||||
def add_folder(self, path, folder_name, add_date_time, date_time_format, input_variables=None):
|
||||
folder_name_parsed = format_variables(folder_name, input_variables)
|
||||
if add_date_time == "disable":
|
||||
new_path = os.path.join(path, folder_name_parsed)
|
||||
else:
|
||||
new_path = os.path.join(path, format_date_time(folder_name_parsed, add_date_time, date_time_format))
|
||||
return (new_path,)
|
||||
|
||||
|
||||
class AddFileNamePrefix:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"path": ("PATH",),
|
||||
"file_name_prefix": ("STRING", {"multiline": False, "default": ""}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
FUNCTION = "add_filename_prefix"
|
||||
CATEGORY = "path_helper"
|
||||
|
||||
def add_filename_prefix(self, path, file_name_prefix):
|
||||
new_path = os.path.join(path, file_name_prefix)
|
||||
return (new_path,)
|
||||
|
||||
|
||||
class AddFileNamePrefixAdvanced:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"path": ("PATH",),
|
||||
"file_name_prefix": ("STRING", {"multiline": False, "default": ""}),
|
||||
"add_date_time": (["disable", "prefix", "postfix"],),
|
||||
"date_time_format": ("STRING", {"multiline": False, "default": "%Y_%m_%d_%H:%M:%S"}),
|
||||
},
|
||||
"optional": {
|
||||
"input_variables": (ANY,)
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
FUNCTION = "add_filename_prefix"
|
||||
CATEGORY = "path_helper"
|
||||
|
||||
def add_filename_prefix(self, path, file_name_prefix, add_date_time, date_time_format, input_variables=None):
|
||||
filename_name_parsed = format_variables(file_name_prefix, input_variables)
|
||||
if add_date_time == "disable":
|
||||
new_path = os.path.join(path, filename_name_parsed)
|
||||
else:
|
||||
new_path = os.path.join(path, format_date_time(filename_name_parsed, add_date_time, date_time_format))
|
||||
return (new_path,)
|
||||
|
||||
|
||||
class JoinVariables:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"var_1": (ANY,),
|
||||
},
|
||||
"optional": {
|
||||
"var_2": (ANY,),
|
||||
"var_3": (ANY,),
|
||||
"var_4": (ANY,),
|
||||
}
|
||||
}
|
||||
|
||||
FUNCTION = "join_variables"
|
||||
CATEGORY = "path_helper"
|
||||
RETURN_TYPES = ("STRING",)
|
||||
|
||||
def join_variables(self, var_1, var_2=None, var_3=None, var_4=None):
|
||||
variables = [var_1, var_2, var_3, var_4]
|
||||
return (','.join([str(var) for var in variables if var is not None]),)
|
||||
|
||||
|
||||
class ShowPath:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"path": ("PATH",),
|
||||
},
|
||||
}
|
||||
|
||||
INPUT_IS_LIST = True
|
||||
FUNCTION = "show_path"
|
||||
CATEGORY = "path_helper"
|
||||
OUTPUT_NODE = True
|
||||
RETURN_TYPES = ("STRING",)
|
||||
|
||||
def show_path(self, path):
|
||||
return {"ui": {"text": path}, "result": (path,)}
|
||||
|
||||
|
||||
class ShowString:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"string": ("STRING", {"forceInput": True}),
|
||||
},
|
||||
}
|
||||
|
||||
INPUT_IS_LIST = True
|
||||
FUNCTION = "show_string"
|
||||
CATEGORY = "path_helper"
|
||||
OUTPUT_NODE = True
|
||||
RETURN_TYPES = ("STRING",)
|
||||
|
||||
def show_string(self, string):
|
||||
return {"ui": {"text": string}, "result": (string,)}
|
||||
@@ -0,0 +1,62 @@
|
||||
import {app} from "../../../scripts/app.js";
|
||||
import {ComfyWidgets} from "../../../scripts/widgets.js";
|
||||
|
||||
// Displays input path text on a node
|
||||
app.registerExtension({
|
||||
name: "path_helper.ShowPath",
|
||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
if (nodeType.comfyClass === "Show Path") {
|
||||
function populate(text) {
|
||||
console.log("sp")
|
||||
console.log(this.widgets)
|
||||
if (this.widgets) {
|
||||
for (let i = 1; i < this.widgets.length; i++) {
|
||||
this.widgets[i].onRemove?.();
|
||||
}
|
||||
this.widgets.length = 1;
|
||||
}
|
||||
|
||||
const v = [...text];
|
||||
if (!v[0]) {
|
||||
v.shift();
|
||||
}
|
||||
for (const list of v) {
|
||||
const w = ComfyWidgets["STRING"](this, "text", ["STRING", {multiline: true}], app).widget;
|
||||
w.inputEl.readOnly = true;
|
||||
w.inputEl.style.opacity = 0.6;
|
||||
w.value = list;
|
||||
}
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
const sz = this.computeSize();
|
||||
if (sz[0] < this.size[0]) {
|
||||
sz[0] = this.size[0];
|
||||
}
|
||||
if (sz[1] < this.size[1]) {
|
||||
sz[1] = this.size[1];
|
||||
}
|
||||
this.onResize?.(sz);
|
||||
app.graph.setDirtyCanvas(true, false);
|
||||
});
|
||||
}
|
||||
|
||||
// When the node is executed we will be sent the input text, display this in the widget
|
||||
const onExecuted = nodeType.prototype.onExecuted;
|
||||
nodeType.prototype.onExecuted = function (message) {
|
||||
console.log("one")
|
||||
onExecuted?.apply(this, arguments);
|
||||
populate.call(this, message.text);
|
||||
};
|
||||
|
||||
const onConfigure = nodeType.prototype.onConfigure;
|
||||
nodeType.prototype.onConfigure = function () {
|
||||
onConfigure?.apply(this, arguments);
|
||||
if (this.widgets_values?.length) {
|
||||
console.log("eeee")
|
||||
populate.call(this, this.widgets_values);
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import {app} from "../../../scripts/app.js";
|
||||
import {ComfyWidgets} from "../../../scripts/widgets.js";
|
||||
|
||||
app.registerExtension({
|
||||
name: "path_helper.ShowString",
|
||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
if (nodeType.comfyClass === "Show String") {
|
||||
function populate(text) {
|
||||
console.log("ss")
|
||||
console.log(this.widgets)
|
||||
if (this.widgets) {
|
||||
for (let i = 1; i < this.widgets.length; i++) {
|
||||
this.widgets[i].onRemove?.();
|
||||
}
|
||||
this.widgets.length = 1;
|
||||
}
|
||||
|
||||
const v = [...text];
|
||||
if (!v[0]) {
|
||||
v.shift();
|
||||
}
|
||||
for (const list of v) {
|
||||
const w = ComfyWidgets["STRING"](this, "text", ["STRING", {multiline: true}], app).widget;
|
||||
w.inputEl.readOnly = true;
|
||||
w.inputEl.style.opacity = 0.6;
|
||||
w.value = list;
|
||||
}
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
const sz = this.computeSize();
|
||||
if (sz[0] < this.size[0]) {
|
||||
sz[0] = this.size[0];
|
||||
}
|
||||
if (sz[1] < this.size[1]) {
|
||||
sz[1] = this.size[1];
|
||||
}
|
||||
this.onResize?.(sz);
|
||||
app.graph.setDirtyCanvas(true, false);
|
||||
});
|
||||
}
|
||||
|
||||
// When the node is executed we will be sent the input text, display this in the widget
|
||||
const onExecuted = nodeType.prototype.onExecuted;
|
||||
nodeType.prototype.onExecuted = function (message) {
|
||||
onExecuted?.apply(this, arguments);
|
||||
populate.call(this, message.text);
|
||||
};
|
||||
|
||||
const onConfigure = nodeType.prototype.onConfigure;
|
||||
nodeType.prototype.onConfigure = function () {
|
||||
onConfigure?.apply(this, arguments);
|
||||
if (this.widgets_values?.length) {
|
||||
populate.call(this, this.widgets_values);
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user