feat: Wildcard Encode (Inspire)
This commit is contained in:
@@ -47,6 +47,10 @@ This repository offers various extension nodes for ComfyUI. Nodes here have diff
|
||||
* `Global Seed (Inspire)`: This is a node that controls the global seed without a separate connection line. It only controls when the widget's name is 'seed' or 'noise_seed'. Additionally, if 'control_before_generate' is checked, it controls the seed before executing the prompt.
|
||||
* Seeds that have been converted into inputs are excluded from the target. If you want to control the seed separately, convert it into an input and control it separately.
|
||||
* `Bind [ImageList, PromptList] (Inspire)`: Bind Image list and zipped prompt list to export `image`, `positive`, `negative`, and `prompt_label` in a list format. If there are more prompts than images, the excess prompts are ignored, and if there are not enough, the remainder is filled with default input based on the images.
|
||||
* `Wildcard Encode (Inspire)`: The combination node of [ImpactWildcardEncode](https://github.com/ltdrdata/ComfyUI-extension-tutorials/blob/Main/ComfyUI-Impact-Pack/tutorial/ImpactWildcard.md) and BlenderNeko's [CLIP Text Encode (Advanced)](https://github.com/BlenderNeko/ComfyUI_ADV_CLIP_emb).
|
||||
* To use this node, you need both the [Impact Pack](https://github.com/ltdrdata/ComfyUI-Impact-Pack) and the [Advanced CLIP Text Encode]((https://github.com/BlenderNeko/ComfyUI_ADV_CLIP_emb)) extensions.
|
||||
* This node is identical to `ImpactWildcardEncode`, but it encodes using `CLIP Text Encode (Advanced)` instead of the default CLIP Text Encode from ComfyUI for CLIP Text Encode.
|
||||
* Requirement: Impact Pack V4.18.6 or above
|
||||
|
||||
* Regional Sampler - These nodes assists in the easy utilization of the regional sampler in the `Impact Pack`.
|
||||
* `Regional Prompt Simple (Inspire)`: This node takes `mask` and `basic_pipe` as inputs and simplifies the creation of `REGIONAL_PROMPTS`.
|
||||
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
import { ComfyApp, app } from "../../scripts/app.js";
|
||||
import { api } from "../../scripts/api.js";
|
||||
|
||||
app.registerExtension({
|
||||
name: "Comfy.Inspire.Prompts",
|
||||
|
||||
nodeCreated(node, app) {
|
||||
if(node.comfyClass == "WildcardEncode //Inspire") {
|
||||
const wildcard_text_widget_index = node.widgets.findIndex((w) => w.name == 'wildcard_text');
|
||||
const populated_text_widget_index = node.widgets.findIndex((w) => w.name == 'populated_text');
|
||||
const mode_widget_index = node.widgets.findIndex((w) => w.name == 'mode');
|
||||
|
||||
const wildcard_text_widget = node.widgets[wildcard_text_widget_index];
|
||||
const populated_text_widget = node.widgets[populated_text_widget_index];
|
||||
|
||||
// lora selector, wildcard selector
|
||||
let combo_id = 5;
|
||||
|
||||
Object.defineProperty(node.widgets[combo_id+1], "value", {
|
||||
set: (value) => {
|
||||
const stackTrace = new Error().stack;
|
||||
if(stackTrace.includes('inner_value_change')) {
|
||||
if(value != "Select the Wildcard to add to the text") {
|
||||
if(wildcard_text_widget.value != '')
|
||||
wildcard_text_widget.value += ', '
|
||||
|
||||
wildcard_text_widget.value += value;
|
||||
}
|
||||
}
|
||||
|
||||
node._wvalue = value;
|
||||
},
|
||||
get: () => {
|
||||
return node._wvalue;
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(node.widgets[combo_id], "value", {
|
||||
set: (value) => {
|
||||
const stackTrace = new Error().stack;
|
||||
if(stackTrace.includes('inner_value_change')) {
|
||||
if(value != "Select the LoRA to add to the text") {
|
||||
let lora_name = value;
|
||||
if (lora_name.endsWith('.safetensors')) {
|
||||
lora_name = lora_name.slice(0, -12);
|
||||
}
|
||||
|
||||
wildcard_text_widget.value += `<lora:${lora_name}>`;
|
||||
if(node.widgets_values) {
|
||||
node.widgets_values[wildcard_text_widget_index] = wildcard_text_widget.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node._value = value;
|
||||
},
|
||||
get: () => {
|
||||
return node._value;
|
||||
}
|
||||
});
|
||||
|
||||
// Preventing validation errors from occurring in any situation.
|
||||
node.widgets[combo_id].serializeValue = () => { return "Select the LoRA to add to the text"; }
|
||||
node.widgets[combo_id+1].serializeValue = () => { return "Select the Wildcard to add to the text"; }
|
||||
|
||||
// wildcard populating
|
||||
populated_text_widget.inputEl.disabled = true;
|
||||
let populate_getter = populated_text_widget.__lookupGetter__('value');
|
||||
let populate_setter = populated_text_widget.__lookupSetter__('value');
|
||||
|
||||
const mode_widget = node.widgets[mode_widget_index];
|
||||
const seed_widget = node.widgets.find((w) => w.name == 'seed');
|
||||
|
||||
let force_serializeValue = async (n,i) =>
|
||||
{
|
||||
if(!mode_widget.value) {
|
||||
return populated_text_widget.value;
|
||||
}
|
||||
else {
|
||||
let wildcard_text = await wildcard_text_widget.serializeValue();
|
||||
|
||||
let response = await api.fetchApi(`/impact/wildcards`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({text: wildcard_text, seed: seed_widget.value})
|
||||
});
|
||||
|
||||
let populated = await response.json();
|
||||
|
||||
n.widgets_values[mode_widget_index] = false;
|
||||
n.widgets_values[populated_text_widget_index] = populated.text;
|
||||
populate_setter.call(populated_text_widget, populated.text);
|
||||
|
||||
return populated.text;
|
||||
}
|
||||
};
|
||||
|
||||
// mode combo
|
||||
Object.defineProperty(mode_widget, "value", {
|
||||
set: (value) => {
|
||||
node._mode_value = value == true || value == "Populate";
|
||||
populated_text_widget.inputEl.disabled = value == true || value == "Populate";
|
||||
},
|
||||
get: () => {
|
||||
if(node._mode_value != undefined)
|
||||
return node._mode_value;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// to avoid conflict with presetText.js of pythongosssss
|
||||
Object.defineProperty(populated_text_widget, "value", {
|
||||
set: (value) => {
|
||||
const stackTrace = new Error().stack;
|
||||
if(!stackTrace.includes('serializeValue'))
|
||||
populate_setter.call(populated_text_widget, value);
|
||||
},
|
||||
get: () => {
|
||||
return populate_getter.call(populated_text_widget);
|
||||
}
|
||||
});
|
||||
|
||||
wildcard_text_widget.serializeValue = (n,i) => {
|
||||
if(node.inputs) {
|
||||
let link_id = node.inputs.find(x => x.name=="wildcard_text")?.link;
|
||||
if(link_id != undefined) {
|
||||
let link = app.graph.links[link_id];
|
||||
let input_widget = app.graph._nodes_by_id[link.origin_id].widgets[link.origin_slot];
|
||||
if(input_widget.type == "customtext") {
|
||||
return input_widget.value;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return wildcard_text_widget.value;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return wildcard_text_widget.value;
|
||||
}
|
||||
};
|
||||
|
||||
populated_text_widget.serializeValue = force_serializeValue;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -300,6 +300,57 @@ class BindImageListPromptList:
|
||||
return (images, positives, negatives, prompt_labels)
|
||||
|
||||
|
||||
class BNK_EncoderWrapper:
|
||||
def __init__(self, token_normalization, weight_interpretation):
|
||||
self.token_normalization = token_normalization
|
||||
self.weight_interpretation = weight_interpretation
|
||||
|
||||
def encode(self, clip, text):
|
||||
if 'BNK_CLIPTextEncodeAdvanced' not in nodes.NODE_CLASS_MAPPINGS:
|
||||
raise Exception(f"[ERROR] To use MediaPipeFaceMeshDetector, you need to install 'Advanced CLIP Text Encode'")
|
||||
return nodes.NODE_CLASS_MAPPINGS['BNK_CLIPTextEncodeAdvanced']().encode(clip, text, self.token_normalization, self.weight_interpretation)
|
||||
|
||||
|
||||
class WildcardEncodeInspire:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
if 'ImpactWildcardEncode' in nodes.NODE_CLASS_MAPPINGS:
|
||||
wildcards = nodes.NODE_CLASS_MAPPINGS['ImpactWildcardEncode'].get_wildcard_list()
|
||||
else:
|
||||
wildcards = ["Impact Pack isn't installed"]
|
||||
|
||||
return {"required": {
|
||||
"model": ("MODEL",),
|
||||
"clip": ("CLIP",),
|
||||
"token_normalization": (["none", "mean", "length", "length+mean"], ),
|
||||
"weight_interpretation": (["comfy", "A1111", "compel", "comfy++", "down_weight"], {'default': 'comfy++'}),
|
||||
"wildcard_text": ("STRING", {"multiline": True, "dynamicPrompts": False, 'placeholder': 'Wildcard Prmopt (User Input)'}),
|
||||
"populated_text": ("STRING", {"multiline": True, "dynamicPrompts": False, 'placeholder': 'Populated Prmopt (Will be generated automatically)'}),
|
||||
"mode": ("BOOLEAN", {"default": True, "label_on": "Populate", "label_off": "Fixed"}),
|
||||
"Select to add LoRA": (["Select the LoRA to add to the text"] + folder_paths.get_filename_list("loras"), ),
|
||||
"Select to add Wildcard": (["Select the Wildcard to add to the text"] + wildcards, ),
|
||||
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
||||
},
|
||||
}
|
||||
|
||||
CATEGORY = "ImpactPack/Prompt"
|
||||
|
||||
RETURN_TYPES = ("MODEL", "CLIP", "CONDITIONING", "STRING")
|
||||
RETURN_NAMES = ("model", "clip", "conditioning", "populated_text")
|
||||
FUNCTION = "doit"
|
||||
|
||||
def doit(self, *args, **kwargs):
|
||||
populated = kwargs['populated_text']
|
||||
|
||||
clip_encoder = BNK_EncoderWrapper(kwargs['token_normalization'], kwargs['weight_interpretation'])
|
||||
|
||||
if 'ImpactWildcardEncode' not in nodes.NODE_CLASS_MAPPINGS:
|
||||
raise Exception(f"[ERROR] To use WildcardEncodeInspire, you need to install 'Impact Pack'")
|
||||
|
||||
model, clip, conditioning = nodes.NODE_CLASS_MAPPINGS['ImpactWildcardEncode'].process_with_loras(wildcard_opt=populated, model=kwargs['model'], clip=kwargs['clip'], clip_encoder=clip_encoder)
|
||||
return (model, clip, conditioning, populated)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LoadPromptsFromDir //Inspire": LoadPromptsFromDir,
|
||||
"LoadPromptsFromFile //Inspire": LoadPromptsFromFile,
|
||||
@@ -308,6 +359,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
"PromptExtractor //Inspire": PromptExtractor,
|
||||
"GlobalSeed //Inspire": GlobalSeed,
|
||||
"BindImageListPromptList //Inspire": BindImageListPromptList,
|
||||
"WildcardEncode //Inspire": WildcardEncodeInspire,
|
||||
}
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"LoadPromptsFromDir //Inspire": "Load Prompts From Dir (Inspire)",
|
||||
@@ -317,4 +369,5 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"PromptExtractor //Inspire": "Prompt Extractor (Inspire)",
|
||||
"GlobalSeed //Inspire": "Global Seed (Inspire)",
|
||||
"BindImageListPromptList //Inspire": "Bind [ImageList, PromptList] (Inspire)",
|
||||
"WildcardEncode //Inspire": "Wildcard Encode (Inspire)",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user