Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6158b5aa44 | ||
|
|
5aae916c66 | ||
|
|
f12b4bd54e | ||
|
|
0284075392 | ||
|
|
bcbed07560 | ||
|
|
685c5c0d00 | ||
|
|
697f12fbdb | ||
|
|
e8817bc348 | ||
|
|
9c6065af6d | ||
|
|
8db9411e94 | ||
|
|
18f02d98b1 | ||
|
|
cab499e30a | ||
|
|
71a68650fe | ||
|
|
3bcc1d43a0 | ||
|
|
395869085c | ||
|
|
384284fe0b | ||
|
|
c6d53ecd4d | ||
|
|
44cfac4f0a | ||
|
|
bfaaa6c570 | ||
|
|
d29c41809f |
@@ -2,6 +2,7 @@
|
||||
This repository offers various extension nodes for ComfyUI. Nodes here have different characteristics compared to those in the ComfyUI Impact Pack. The Impact Pack has become too large now...
|
||||
|
||||
## Notice:
|
||||
* v1.9.1 To avoid confusion with the `NOISE` type in core, the type name has been changed to `NOISE_IMAGE`.
|
||||
* V0.73 The Variation Seed feature is added to Regional Prompt nodes, and it is only compatible with versions Impact Pack V5.10 and above.
|
||||
* V0.69 incompatible with the outdated **ComfyUI IPAdapter Plus**. (A version dated March 24th or later is required.)
|
||||
* V0.64 add sigma_factor to RegionalPrompt... nodes required Impact Pack V4.76 or later.
|
||||
@@ -24,6 +25,7 @@ This repository offers various extension nodes for ComfyUI. Nodes here have diff
|
||||
* Save LoRA Block Weight: Save LBW_MODEL as a .lbw.safetensors file
|
||||
* Load LoRA Block Weight: Load LBW_MODEL from .lbw.safetensors file
|
||||
|
||||
|
||||
### SEGS Supports nodes - This is a node that supports ApplyControlNet (SEGS) from the Impact Pack.
|
||||
* `OpenPose Preprocessor Provider (SEGS)`: OpenPose preprocessor is applied for the purpose of using OpenPose ControlNet in SEGS.
|
||||
* You need to install [ControlNet Auxiliary Preprocessors](https://github.com/Fannovel16/comfyui_controlnet_aux) to use this.
|
||||
@@ -34,6 +36,7 @@ This repository offers various extension nodes for ComfyUI. Nodes here have diff
|
||||
`Color Preprocessor Provider (SEGS)`, `Inpaint Preprocessor Provider (SEGS)`, `Tile Preprocessor Provider (SEGS)`, `MeshGraphormer Depth Map Preprocessor Provider (SEGS)`
|
||||
* `MediaPipeFaceMeshDetectorProvider`: This node provides `BBOX_DETECTOR` and `SEGM_DETECTOR` that can be used in Impact Pack's Detector using the `MediaPipe-FaceMesh Preprocessor` of ControlNet Auxiliary Preprocessors.
|
||||
|
||||
|
||||
### A1111 Compatibility support - These nodes assists in replicating the creation of A1111 in ComfyUI exactly.
|
||||
* `KSampler (Inspire)`: ComfyUI uses the CPU for generating random noise, while A1111 uses the GPU. One of the three factors that significantly impact reproducing A1111's results in ComfyUI can be addressed using `KSampler (Inspire)`.
|
||||
* Other point #1 : Please make sure you haven't forgotten to include 'embedding:' in the embedding used in the prompt, like 'embedding:easynegative.'
|
||||
@@ -48,6 +51,7 @@ This repository offers various extension nodes for ComfyUI. Nodes here have diff
|
||||
* `variation_seed` and `variation_strength` - Initial noise generated by the seed is transformed to the shape of `variation_seed` by `variation_strength`. If `variation_strength` is 0, it only relies on the influence of the seed, and if `variation_strength` is 1.0, it is solely influenced by `variation_seed`.
|
||||
* These parameters are used when you want to maintain the composition of an image generated by the seed but wish to introduce slight changes.
|
||||
|
||||
|
||||
### Sampler nodes
|
||||
* `KSampler Progress (Inspire)` - In KSampler, the sampling process generates latent batches. By using `Video Combine` node from [ComfyUI-VideoHelperSuite](https://github.com/Kosinkadink/ComfyUI-VideoHelperSuite), you can create a video from the progress.
|
||||
* `Scheduled CFGGuider (Inspire)` - This is a CFGGuider that adjusts the schedule from from_cfg to to_cfg using linear, log, and exp methods.
|
||||
@@ -59,9 +63,17 @@ This repository offers various extension nodes for ComfyUI. Nodes here have diff
|
||||
* Specify the directories located under `ComfyUI-Inspire-Pack/prompts/`
|
||||
* One prompts file can have multiple prompts separated by `---`.
|
||||
* e.g. `prompts/example`
|
||||
* `Load Prompts From File (Inspire)`: It sequentially reads prompts from the specified file. The output it returns is ZIPPED_PROMPT.
|
||||
* **NOTE**: This node provides advanced option via `Show advanced`
|
||||
* load_cap, start_index
|
||||
|
||||
* `Load Prompts From File (Inspire)`: It sequentially reads prompts from the specified file. The output it returns is ZIPPED_PROMPT.
|
||||
* Specify the file located under `ComfyUI-Inspire-Pack/prompts/`
|
||||
* e.g. `prompts/example/prompt2.txt`
|
||||
* e.g. `prompts/example/prompt2.txt`
|
||||
* **NOTE**: This node provides advanced option via `Show advanced`
|
||||
* load_cap, start_index
|
||||
|
||||
* `Load Single Prompt From File (Inspire)`: Loads a single prompt from a file containing multiple prompts by using an index.
|
||||
* The prompts file directory can be specified as `inspire_prompts` in `extra_model_paths.yaml`
|
||||
* `Unzip Prompt (Inspire)`: Separate ZIPPED_PROMPT into `positive`, `negative`, and name components.
|
||||
* `positive` and `negative` represent text prompts, while `name` represents the name of the prompt. When loaded from a file using `Load Prompts From File (Inspire)`, the name corresponds to the file name.
|
||||
* `Zip Prompt (Inspire)`: Create ZIPPED_PROMPT from positive, negative, and name_opt.
|
||||
@@ -141,7 +153,11 @@ This repository offers various extension nodes for ComfyUI. Nodes here have diff
|
||||
* `Shared Checkpoint Loader (Inspire)`: When loading a checkpoint through this loader, it is automatically cached in the backend cache. Additionally, if it is already cached, it retrieves it from the cache instead of loading it anew.
|
||||
* When `key_opt` is empty, the `ckpt_name` is set as the cache key. The cache key output can be used for deletion purposes with Remove Back End.
|
||||
* This node resolves the issue of reloading checkpoints during workflow switching.
|
||||
* `Shared Diffusion Model Loader (Inspire)`: Similar to the `Shared Checkpoint Loader (Inspire)` but used for loading Diffusion models instead of Checkpoints.
|
||||
* `Shared Text Encoder Loader (Inspire)`: Similar to the `Shared Checkpoint Loader (Inspire)` but used for loading Text Encoder models instead of Checkpoints.
|
||||
* This node also functions as a unified node for `CLIPLoader`, `DualCLIPLoader`, and `TripleCLIPLoader`.
|
||||
* `Stable Cascade Checkpoint Loader (Inspire)`: This node provides a feature that allows you to load the `stage_b` and `stage_c` checkpoints of Stable Cascade at once, and it also provides a backend caching feature, optionally.
|
||||
* `Is Cached (Inspire)`: Returns whether the cache exists.
|
||||
|
||||
### Conditioning - Nodes for conditionings
|
||||
* `Concat Conditionings with Multiplier (Inspire)`: Concatenating an arbitrary number of Conditionings while applying a multiplier for each Conditioning. The multiplier depends on `comfy_PoP`, so [comfy_PoP](https://github.com/picturesonpictures/comfy_PoP) must be installed.
|
||||
@@ -152,8 +168,13 @@ This repository offers various extension nodes for ComfyUI. Nodes here have diff
|
||||
* `IPAdapter Model Helper (Inspire)`: This provides presets that allow for easy loading of the IPAdapter related models. However, it is essential for the model's name to be accurate.
|
||||
* You can download the appropriate model through ComfyUI-Manager.
|
||||
|
||||
### Util - Utilities
|
||||
### List - Nodes for List processing
|
||||
* `Float Range (Inspire)`: Create a float list that increases the value by `step` from `start` to `stop`. A list as large as the maximum limit is created, and when `ensure_end` is enabled, the last value of the list becomes the stop value.
|
||||
* `Worklist To Item List (Inspire)`: The list in ComfyUI allows for repeated execution of a sub-workflow. This groups these repetitions (a.k.a. list) into a single ITEM_LIST output. ITEM_LIST can then be used in ForeachList.
|
||||
* `▶Foreach List (Inspire)`: A starting node for performing iterative tasks by retrieving items one by one from the ITEM_LIST.\nGenerate a new intermediate_output using item and intermediate_output as inputs, then connect it to ForeachListEnd.\nNOTE:If initial_input is omitted, the first item in item_list is used as the initial value, and the processing starts from the second item in item_list.
|
||||
* `Foreach List◀ (Inspire)`: A end node for performing iterative tasks by retrieving items one by one from the ITEM_LIST.\nNOTE:Directly connect the outputs of ForeachListBegin to 'flow_control' and 'remained_list'.
|
||||
|
||||
### Util - Utilities
|
||||
* `ToIPAdapterPipe (Inspire)`, `FromIPAdapterPipe (Inspire)`: These nodes assists in conveniently using the bundled ipadapter_model, clip_vision, and model required for applying IPAdapter.
|
||||
* `List Counter (Inspire)`: When each item in the list traverses through this node, it increments a counter by one, generating an integer value.
|
||||
* `RGB Hex To HSV (Inspire)`: Convert an RGB hex string like `#FFD500` to HSV:
|
||||
@@ -177,3 +198,5 @@ cubiq/[ComfyUI_IPAdapter_plus](https://github.com/cubiq/ComfyUI_IPAdapter_plus)
|
||||
Davemane42/[ComfyUI_Dave_CustomNode](https://github.com/Davemane42/ComfyUI_Dave_CustomNode) - Original author of ConditioningStretch, ConditioningUpscale
|
||||
|
||||
BlenderNeko/[ComfyUI_Noise](https://github.com/BlenderNeko/ComfyUI_Noise) - slerp code for noise variation
|
||||
|
||||
BadCafeCode/[execution-inversion-demo-comfyui](https://github.com/BadCafeCode/execution-inversion-demo-comfyui) - reference loop implementation for ComfyUI
|
||||
|
||||
+2
-2
@@ -2,12 +2,12 @@
|
||||
@author: Dr.Lt.Data
|
||||
@title: Inspire Pack
|
||||
@nickname: Inspire Pack
|
||||
@description: This extension provides various nodes to support Lora Block Weight and the Impact Pack.
|
||||
@description: This extension provides various nodes to support Lora Block Weight, Regional Nodes, Backend Cache, Prompt Utils, List Utils and the Impact Pack.
|
||||
"""
|
||||
|
||||
import importlib
|
||||
|
||||
version_code = [1, 5]
|
||||
version_code = [1, 12, 1]
|
||||
version_str = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
|
||||
print(f"### Loading: ComfyUI-Inspire-Pack ({version_str})")
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@ class KSamplerAdvanced_inspire:
|
||||
"optional":
|
||||
{
|
||||
"variation_method": (["linear", "slerp"],),
|
||||
"noise_opt": ("NOISE",),
|
||||
"noise_opt": ("NOISE_IMAGE",),
|
||||
"scheduler_func_opt": ("SCHEDULER_FUNC",),
|
||||
}
|
||||
}
|
||||
@@ -253,7 +253,7 @@ class KSamplerAdvanced_inspire_pipe:
|
||||
},
|
||||
"optional":
|
||||
{
|
||||
"noise_opt": ("NOISE",),
|
||||
"noise_opt": ("NOISE_IMAGE",),
|
||||
"scheduler_func_opt": ("SCHEDULER_FUNC",),
|
||||
}
|
||||
}
|
||||
|
||||
+249
-2
@@ -1,5 +1,6 @@
|
||||
import json
|
||||
import os
|
||||
from .libs import common
|
||||
|
||||
import folder_paths
|
||||
import nodes
|
||||
@@ -7,6 +8,8 @@ from server import PromptServer
|
||||
|
||||
from .libs.utils import TaggedCache, any_typ
|
||||
|
||||
import logging
|
||||
|
||||
root_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
settings_file = os.path.join(root_dir, 'cache_settings.json')
|
||||
try:
|
||||
@@ -400,6 +403,174 @@ class CheckpointLoaderSimpleShared(nodes.CheckpointLoaderSimple):
|
||||
return (None, cache_weak_hash(key))
|
||||
|
||||
|
||||
class LoadDiffusionModelShared(nodes.UNETLoader):
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": { "model_name": (folder_paths.get_filename_list("diffusion_models"), {"tooltip": "Diffusion Model Name"}),
|
||||
"weight_dtype": (["default", "fp8_e4m3fn", "fp8_e4m3fn_fast", "fp8_e5m2"],),
|
||||
"key_opt": ("STRING", {"multiline": False, "placeholder": "If empty, use 'model_name' as the key."}),
|
||||
"mode": (['Auto', 'Override Cache', 'Read Only'],),
|
||||
}
|
||||
}
|
||||
RETURN_TYPES = ("MODEL", "STRING")
|
||||
RETURN_NAMES = ("model", "cache key")
|
||||
|
||||
FUNCTION = "doit"
|
||||
|
||||
CATEGORY = "InspirePack/Backend"
|
||||
|
||||
def doit(self, model_name, weight_dtype, key_opt, mode='Auto'):
|
||||
if mode == 'Read Only':
|
||||
if key_opt.strip() == '':
|
||||
raise Exception("[LoadDiffusionModelShared] key_opt cannot be omit if mode is 'Read Only'")
|
||||
key = key_opt.strip()
|
||||
elif key_opt.strip() == '':
|
||||
key = f"{model_name}_{weight_dtype}"
|
||||
else:
|
||||
key = key_opt.strip()
|
||||
|
||||
if key not in cache or mode == 'Override Cache':
|
||||
model = self.load_unet(model_name, weight_dtype)[0]
|
||||
update_cache(key, "diffusion", (False, model))
|
||||
print(f"[Inspire Pack] LoadDiffusionModelShared: diffusion model '{model_name}' is cached to '{key}'.")
|
||||
else:
|
||||
_, (_, model) = cache[key]
|
||||
print(f"[Inspire Pack] LoadDiffusionModelShared: Cached diffusion model '{key}' is loaded. (Loading skip)")
|
||||
|
||||
return model, key
|
||||
|
||||
@staticmethod
|
||||
def IS_CHANGED(model_name, weight_dtype, key_opt, mode='Auto'):
|
||||
if mode == 'Read Only':
|
||||
if key_opt.strip() == '':
|
||||
raise Exception("[LoadDiffusionModelShared] key_opt cannot be omit if mode is 'Read Only'")
|
||||
key = key_opt.strip()
|
||||
elif key_opt.strip() == '':
|
||||
key = f"{model_name}_{weight_dtype}"
|
||||
else:
|
||||
key = key_opt.strip()
|
||||
|
||||
if mode == 'Read Only':
|
||||
return None, cache_weak_hash(key)
|
||||
elif mode == 'Override Cache':
|
||||
return model_name, key
|
||||
|
||||
return None, cache_weak_hash(key)
|
||||
|
||||
|
||||
class LoadTextEncoderShared:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": { "model_name1": (folder_paths.get_filename_list("text_encoders"), ),
|
||||
"model_name2": (["None"] + folder_paths.get_filename_list("text_encoders"), ),
|
||||
"model_name3": (["None"] + folder_paths.get_filename_list("text_encoders"), ),
|
||||
"type": (["stable_diffusion", "stable_cascade", "sd3", "stable_audio", "mochi", "ltxv", "pixart", "cosmos", "sdxl", "flux", "hunyuan_video"], ),
|
||||
"key_opt": ("STRING", {"multiline": False, "placeholder": "If empty, use 'model_name' as the key."}),
|
||||
"mode": (['Auto', 'Override Cache', 'Read Only'],),
|
||||
},
|
||||
"optional": { "device": (["default", "cpu"], {"advanced": True}), }
|
||||
}
|
||||
RETURN_TYPES = ("CLIP", "STRING")
|
||||
RETURN_NAMES = ("clip", "cache key")
|
||||
|
||||
FUNCTION = "doit"
|
||||
|
||||
CATEGORY = "InspirePack/Backend"
|
||||
|
||||
DESCRIPTION = \
|
||||
("[Recipes single]\n"
|
||||
"stable_diffusion: clip-l\n"
|
||||
"stable_cascade: clip-g\n"
|
||||
"sd3: t5 / clip-g / clip-l\n"
|
||||
"stable_audio: t5\n"
|
||||
"mochi: t5\n"
|
||||
"cosmos: old t5 xxl\n\n"
|
||||
"[Recipes dual]\n"
|
||||
"sdxl: clip-l, clip-g\n"
|
||||
"sd3: clip-l, clip-g / clip-l, t5 / clip-g, t5\n"
|
||||
"flux: clip-l, t5\n\n"
|
||||
"[Recipes triple]\n"
|
||||
"sd3: clip-l, clip-g, t5")
|
||||
|
||||
def doit(self, model_name1, model_name2, model_name3, type, key_opt, mode='Auto', device="default"):
|
||||
if mode == 'Read Only':
|
||||
if key_opt.strip() == '':
|
||||
raise Exception("[LoadTextEncoderShared] key_opt cannot be omit if mode is 'Read Only'")
|
||||
key = key_opt.strip()
|
||||
elif key_opt.strip() == '':
|
||||
key = model_name1
|
||||
if model_name2 is not None:
|
||||
key += f"_{model_name2}"
|
||||
if model_name3 is not None:
|
||||
key += f"_{model_name3}"
|
||||
key += f"_{type}_{device}"
|
||||
else:
|
||||
key = key_opt.strip()
|
||||
|
||||
if key not in cache or mode == 'Override Cache':
|
||||
if model_name2 != "None" and model_name3 != "None": # triple text encoder
|
||||
if len({model_name1, model_name2, model_name3}) < 3:
|
||||
logging.error("[LoadTextEncoderShared] The same model has been selected multiple times.")
|
||||
raise ValueError("The same model has been selected multiple times.")
|
||||
|
||||
if type not in ["sd3"]:
|
||||
logging.error("[LoadTextEncoderShared] Currently, the triple text encoder is only supported in `sd3`.")
|
||||
raise ValueError("Currently, the triple text encoder is only supported in `sd3`.")
|
||||
|
||||
res = nodes.NODE_CLASS_MAPPINGS["TripleCLIPLoader"]().load_clip(model_name1, model_name2, model_name3)[0]
|
||||
|
||||
elif model_name2 != "None" or model_name3 != "None": # dual text encoder
|
||||
second_model = model_name2 if model_name2 != "None" else model_name3
|
||||
|
||||
if model_name1 == second_model:
|
||||
logging.error("[LoadTextEncoderShared] You have selected the same model for both.")
|
||||
raise ValueError("[LoadTextEncoderShared] You have selected the same model for both.")
|
||||
|
||||
if type not in ["sdxl", "sd3", "flux", "hunyuan_video"]:
|
||||
logging.error("[LoadTextEncoderShared] Currently, the triple text encoder is only supported in `sdxl, sd3, flux, hunyuan_video`.")
|
||||
raise ValueError("Currently, the triple text encoder is only supported in `sdxl, sd3, flux, hunyuan_video`.")
|
||||
|
||||
res = nodes.NODE_CLASS_MAPPINGS["DualCLIPLoader"]().load_clip(model_name1, second_model, type=type, device=device)[0]
|
||||
|
||||
else: # single text encoder
|
||||
if type not in ["stable_diffusion", "stable_cascade", "sd3", "stable_audio", "mochi", "ltxv", "pixart", "cosmos"]:
|
||||
logging.error("[LoadTextEncoderShared] Currently, the single text encoder is only supported in `stable_diffusion, stable_cascade, sd3, stable_audio, mochi, ltxv, pixart, cosmos`.")
|
||||
raise ValueError("Currently, the single text encoder is only supported in `stable_diffusion, stable_cascade, sd3, stable_audio, mochi, ltxv, pixart, cosmos`.")
|
||||
|
||||
res = nodes.NODE_CLASS_MAPPINGS["CLIPLoader"]().load_clip(model_name1, type=type, device=device)[0]
|
||||
|
||||
update_cache(key, "diffusion", (False, res))
|
||||
print(f"[Inspire Pack] LoadTextEncoderShared: text encoder model set is cached to '{key}'.")
|
||||
else:
|
||||
_, (_, res) = cache[key]
|
||||
print(f"[Inspire Pack] LoadTextEncoderShared: Cached text encoder model set '{key}' is loaded. (Loading skip)")
|
||||
|
||||
return res, key
|
||||
|
||||
@staticmethod
|
||||
def IS_CHANGED(model_name1, model_name2, model_name3, type, key_opt, mode='Auto', device="default"):
|
||||
if mode == 'Read Only':
|
||||
if key_opt.strip() == '':
|
||||
raise Exception("[LoadTextEncoderShared] key_opt cannot be omit if mode is 'Read Only'")
|
||||
key = key_opt.strip()
|
||||
elif key_opt.strip() == '':
|
||||
key = model_name1
|
||||
if model_name2 is not None:
|
||||
key += f"_{model_name2}"
|
||||
if model_name3 is not None:
|
||||
key += f"_{model_name3}"
|
||||
key += f"_{type}_{device}"
|
||||
else:
|
||||
key = key_opt.strip()
|
||||
|
||||
if mode == 'Read Only':
|
||||
return None, cache_weak_hash(key)
|
||||
elif mode == 'Override Cache':
|
||||
return f"{model_name1}_{model_name2}_{model_name3}_{type}_{device}", key
|
||||
|
||||
return None, cache_weak_hash(key)
|
||||
|
||||
|
||||
class StableCascade_CheckpointLoader:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
@@ -478,6 +649,74 @@ class StableCascade_CheckpointLoader:
|
||||
return b_model, b_vae, c_model, c_vae, clip_vision, clip, key_b, key_c
|
||||
|
||||
|
||||
class IsCached:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"key": ("STRING", {"multiline": False}),
|
||||
},
|
||||
"hidden": {
|
||||
"unique_id": "UNIQUE_ID"
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BOOLEAN", )
|
||||
FUNCTION = "doit"
|
||||
|
||||
CATEGORY = "InspirePack/Backend"
|
||||
|
||||
@staticmethod
|
||||
def IS_CHANGED(key, unique_id):
|
||||
return common.is_changed(unique_id, key in cache)
|
||||
|
||||
def doit(self, key, unique_id):
|
||||
return (key in cache,)
|
||||
|
||||
|
||||
# WIP: not properly working, yet
|
||||
class CacheBridge:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"value": (any_typ,),
|
||||
"mode": ("BOOLEAN", {"default": True, "label_off": "cached", "label_on": "passthrough"}),
|
||||
},
|
||||
"hidden": {
|
||||
"unique_id": "UNIQUE_ID"
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = (any_typ, )
|
||||
RETURN_NAMES = ("value",)
|
||||
|
||||
FUNCTION = "doit"
|
||||
|
||||
CATEGORY = "InspirePack/Backend"
|
||||
|
||||
@staticmethod
|
||||
def IS_CHANGED(value, mode, unique_id):
|
||||
if not mode and unique_id in common.changed_cache:
|
||||
return common.not_changed_value(unique_id)
|
||||
else:
|
||||
return common.changed_value(unique_id)
|
||||
|
||||
def doit(self, value, mode, unique_id):
|
||||
if not mode:
|
||||
# cache mode
|
||||
if unique_id not in common.changed_cache:
|
||||
common.changed_cache[unique_id] = value
|
||||
common.changed_count_cache[unique_id] = 0
|
||||
|
||||
return (common.changed_cache[unique_id],)
|
||||
else:
|
||||
common.changed_cache[unique_id] = value
|
||||
common.changed_count_cache[unique_id] = 0
|
||||
|
||||
return (common.changed_cache[unique_id],)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"CacheBackendData //Inspire": CacheBackendData,
|
||||
"CacheBackendDataNumberKey //Inspire": CacheBackendDataNumberKey,
|
||||
@@ -489,7 +728,11 @@ NODE_CLASS_MAPPINGS = {
|
||||
"RemoveBackendDataNumberKey //Inspire": RemoveBackendDataNumberKey,
|
||||
"ShowCachedInfo //Inspire": ShowCachedInfo,
|
||||
"CheckpointLoaderSimpleShared //Inspire": CheckpointLoaderSimpleShared,
|
||||
"StableCascade_CheckpointLoader //Inspire": StableCascade_CheckpointLoader
|
||||
"LoadDiffusionModelShared //Inspire": LoadDiffusionModelShared,
|
||||
"LoadTextEncoderShared //Inspire": LoadTextEncoderShared,
|
||||
"StableCascade_CheckpointLoader //Inspire": StableCascade_CheckpointLoader,
|
||||
"IsCached //Inspire": IsCached,
|
||||
# "CacheBridge //Inspire": CacheBridge,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
@@ -503,5 +746,9 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"RemoveBackendDataNumberKey //Inspire": "Remove Backend Data [NumberKey] (Inspire)",
|
||||
"ShowCachedInfo //Inspire": "Show Cached Info (Inspire)",
|
||||
"CheckpointLoaderSimpleShared //Inspire": "Shared Checkpoint Loader (Inspire)",
|
||||
"StableCascade_CheckpointLoader //Inspire": "Stable Cascade Checkpoint Loader (Inspire)"
|
||||
"LoadDiffusionModelShared //Inspire": "Shared Diffusion Model Loader (Inspire)",
|
||||
"LoadTextEncoderShared //Inspire": "Shared Text Encoder Loader (Inspire)",
|
||||
"StableCascade_CheckpointLoader //Inspire": "Stable Cascade Checkpoint Loader (Inspire)",
|
||||
"IsCached //Inspire": "Is Cached (Inspire)",
|
||||
# "CacheBridge //Inspire": "Cache Bridge (Inspire)"
|
||||
}
|
||||
|
||||
@@ -8,19 +8,24 @@ from nodes import MAX_RESOLUTION
|
||||
class ConcatConditioningsWithMultiplier:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
flex_inputs = {}
|
||||
|
||||
stack = inspect.stack()
|
||||
if stack[1].function == 'get_input_data':
|
||||
if stack[1].function == 'get_input_info':
|
||||
# bypass validation
|
||||
for x in range(0, 100):
|
||||
flex_inputs[f"multiplier{x}"] = ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01})
|
||||
else:
|
||||
flex_inputs["multiplier1"] = ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01})
|
||||
class AllContainer:
|
||||
def __contains__(self, item):
|
||||
return True
|
||||
|
||||
def __getitem__(self, key):
|
||||
return "FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}
|
||||
|
||||
return {
|
||||
"required": {"conditioning1": ("CONDITIONING",), },
|
||||
"optional": AllContainer()
|
||||
}
|
||||
|
||||
return {
|
||||
"required": {"conditioning1": ("CONDITIONING",), },
|
||||
"optional": flex_inputs
|
||||
"optional": {"multiplier1": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), },
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("CONDITIONING",)
|
||||
|
||||
@@ -18,7 +18,7 @@ class LoadImagesFromDirBatch:
|
||||
},
|
||||
"optional": {
|
||||
"image_load_cap": ("INT", {"default": 0, "min": 0, "step": 1}),
|
||||
"start_index": ("INT", {"default": 0, "min": -1, "step": 1}),
|
||||
"start_index": ("INT", {"default": 0, "min": -1, "max": 0xffffffffffffffff, "step": 1}),
|
||||
"load_always": ("BOOLEAN", {"default": False, "label_on": "enabled", "label_off": "disabled"}),
|
||||
}
|
||||
}
|
||||
@@ -121,7 +121,7 @@ class LoadImagesFromDirList:
|
||||
},
|
||||
"optional": {
|
||||
"image_load_cap": ("INT", {"default": 0, "min": 0, "step": 1}),
|
||||
"start_index": ("INT", {"default": 0, "min": 0, "step": 1}),
|
||||
"start_index": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "step": 1}),
|
||||
"load_always": ("BOOLEAN", {"default": False, "label_on": "enabled", "label_off": "disabled"}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ from enum import Enum
|
||||
from . import prompt_support
|
||||
from aiohttp import web
|
||||
from . import backend_support
|
||||
from .libs import common
|
||||
|
||||
|
||||
max_seed = 2**32 - 1
|
||||
@@ -271,7 +272,17 @@ def populate_wildcards(json_data):
|
||||
for k, v in prompt.items():
|
||||
if 'class_type' in v and v['class_type'] == 'WildcardEncode //Inspire':
|
||||
inputs = v['inputs']
|
||||
if inputs['mode'] and isinstance(inputs['populated_text'], str):
|
||||
|
||||
# legacy adapter
|
||||
if isinstance(inputs['mode'], bool):
|
||||
if inputs['mode']:
|
||||
new_mode = 'populate'
|
||||
else:
|
||||
new_mode = 'fixed'
|
||||
|
||||
inputs['mode'] = new_mode
|
||||
|
||||
if inputs['mode'] == 'populate' and isinstance(inputs['populated_text'], str):
|
||||
if isinstance(inputs['seed'], list):
|
||||
try:
|
||||
input_node = prompt[inputs['seed'][0]]
|
||||
@@ -292,14 +303,17 @@ def populate_wildcards(json_data):
|
||||
input_seed = int(inputs['seed'])
|
||||
|
||||
inputs['populated_text'] = wildcard_process(text=inputs['wildcard_text'], seed=input_seed)
|
||||
inputs['mode'] = False
|
||||
inputs['mode'] = 'reproduce'
|
||||
|
||||
server.PromptServer.instance.send_sync("inspire-node-feedback", {"node_id": k, "widget_name": "populated_text", "type": "text", "data": inputs['populated_text']})
|
||||
updated_widget_values[k] = inputs['populated_text']
|
||||
|
||||
if inputs['mode'] == 'reproduce':
|
||||
server.PromptServer.instance.send_sync("inspire-node-feedback", {"node_id": k, "widget_name": "mode", "type": "text", "value": 'populate'})
|
||||
|
||||
elif 'class_type' in v and v['class_type'] == 'MakeBasicPipe //Inspire':
|
||||
inputs = v['inputs']
|
||||
if inputs['wildcard_mode'] and (isinstance(inputs['positive_populated_text'], str) or isinstance(inputs['negative_populated_text'], str)):
|
||||
if inputs['wildcard_mode'] == 'populate' and (isinstance(inputs['positive_populated_text'], str) or isinstance(inputs['negative_populated_text'], str)):
|
||||
if isinstance(inputs['seed'], list):
|
||||
try:
|
||||
input_node = prompt[inputs['seed'][0]]
|
||||
@@ -327,21 +341,24 @@ def populate_wildcards(json_data):
|
||||
inputs['negative_populated_text'] = wildcard_process(text=inputs['negative_wildcard_text'], seed=input_seed)
|
||||
server.PromptServer.instance.send_sync("inspire-node-feedback", {"node_id": k, "widget_name": "negative_populated_text", "type": "text", "data": inputs['negative_populated_text']})
|
||||
|
||||
inputs['wildcard_mode'] = False
|
||||
inputs['wildcard_mode'] = 'reproduce'
|
||||
mbp_updated_widget_values[k] = inputs['positive_populated_text'], inputs['negative_populated_text']
|
||||
|
||||
if inputs['wildcard_mode'] == 'reproduce':
|
||||
server.PromptServer.instance.send_sync("inspire-node-feedback", {"node_id": k, "widget_name": "wildcard_mode", "type": "text", "value": 'populate'})
|
||||
|
||||
if 'extra_data' in json_data and 'extra_pnginfo' in json_data['extra_data']:
|
||||
extra_pnginfo = json_data['extra_data']['extra_pnginfo']
|
||||
if 'workflow' in extra_pnginfo and 'nodes' in extra_pnginfo['workflow']:
|
||||
if 'workflow' in extra_pnginfo and extra_pnginfo['workflow'] is not None and 'nodes' in extra_pnginfo['workflow']:
|
||||
for node in extra_pnginfo['workflow']['nodes']:
|
||||
key = str(node['id'])
|
||||
if key in updated_widget_values:
|
||||
node['widgets_values'][3] = updated_widget_values[key]
|
||||
node['widgets_values'][4] = False
|
||||
node['widgets_values'][4] = 'reproduce'
|
||||
if key in mbp_updated_widget_values:
|
||||
node['widgets_values'][7] = mbp_updated_widget_values[key][0]
|
||||
node['widgets_values'][8] = mbp_updated_widget_values[key][1]
|
||||
node['widgets_values'][5] = False
|
||||
node['widgets_values'][5] = 'reproduce'
|
||||
|
||||
|
||||
def force_reset_useless_params(json_data):
|
||||
@@ -354,6 +371,21 @@ def force_reset_useless_params(json_data):
|
||||
return json_data
|
||||
|
||||
|
||||
def clear_unused_node_changed_cache(json_data):
|
||||
prompt = json_data['prompt']
|
||||
|
||||
unused = []
|
||||
for x in common.changed_cache.keys():
|
||||
if x not in prompt:
|
||||
unused.append(x)
|
||||
|
||||
for x in unused:
|
||||
del common.changed_cache[x]
|
||||
del common.changed_count_cache[x]
|
||||
|
||||
return json_data
|
||||
|
||||
|
||||
def onprompt(json_data):
|
||||
prompt_support.list_counter_map = {}
|
||||
|
||||
@@ -367,6 +399,7 @@ def onprompt(json_data):
|
||||
populate_wildcards(json_data)
|
||||
|
||||
force_reset_useless_params(json_data)
|
||||
clear_unused_node_changed_cache(json_data)
|
||||
|
||||
return json_data
|
||||
|
||||
|
||||
@@ -12,3 +12,30 @@ def impact_sampling(*args, **kwargs):
|
||||
raise Exception(f"[ERROR] You need to install 'ComfyUI-Impact-Pack'")
|
||||
|
||||
return nodes.NODE_CLASS_MAPPINGS['RegionalSampler'].separated_sample(*args, **kwargs)
|
||||
|
||||
|
||||
changed_count_cache = {}
|
||||
changed_cache = {}
|
||||
|
||||
|
||||
def changed_value(uid):
|
||||
v = changed_count_cache.get(uid, 0)
|
||||
changed_count_cache[uid] = v + 1
|
||||
return v + 1
|
||||
|
||||
|
||||
def not_changed_value(uid):
|
||||
return changed_count_cache.get(uid, 0)
|
||||
|
||||
|
||||
def is_changed(uid, value):
|
||||
if uid not in changed_cache or changed_cache[uid] != value:
|
||||
res = changed_value(uid)
|
||||
else:
|
||||
res = not_changed_value(uid)
|
||||
|
||||
changed_cache[uid] = value
|
||||
|
||||
print(f"keys: {changed_cache.keys()}")
|
||||
|
||||
return res
|
||||
|
||||
+165
-2
@@ -1,3 +1,6 @@
|
||||
from comfy_execution.graph_utils import GraphBuilder, is_link
|
||||
from .libs.utils import any_typ
|
||||
|
||||
class FloatRange:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
@@ -15,7 +18,7 @@ class FloatRange:
|
||||
|
||||
FUNCTION = "doit"
|
||||
|
||||
CATEGORY = "InspirePack/Util"
|
||||
CATEGORY = "InspirePack/List"
|
||||
|
||||
def doit(self, start, stop, step, limit, ensure_end):
|
||||
if start == stop or step == 0:
|
||||
@@ -47,10 +50,170 @@ class FloatRange:
|
||||
return (res, )
|
||||
|
||||
|
||||
class WorklistToItemList:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"item": (any_typ, ),
|
||||
}
|
||||
}
|
||||
|
||||
INPUT_IS_LIST = True
|
||||
|
||||
RETURN_TYPES = ("ITEM_LIST",)
|
||||
RETURN_NAMES = ("item_list",)
|
||||
|
||||
FUNCTION = "doit"
|
||||
|
||||
DESCRIPTION = "The list in ComfyUI allows for repeated execution of a sub-workflow.\nThis groups these repetitions (a.k.a. list) into a single ITEM_LIST output.\nITEM_LIST can then be used in ForeachList."
|
||||
|
||||
CATEGORY = "InspirePack/List"
|
||||
|
||||
def doit(self, item):
|
||||
return (item, )
|
||||
|
||||
|
||||
# Loop nodes are implemented based on BadCafeCode's reference loop implementation
|
||||
# https://github.com/BadCafeCode/execution-inversion-demo-comfyui/blob/main/flow_control.py
|
||||
|
||||
class ForeachListBegin:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"item_list": ("ITEM_LIST", {"tooltip": "ITEM_LIST containing items to be processed iteratively."}),
|
||||
},
|
||||
"optional": {
|
||||
"initial_input": (any_typ, {"tooltip": "If initial_input is omitted, the first item in item_list is used as the initial value, and the processing starts from the second item in item_list."}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("FOREACH_LIST_CONTROL", "ITEM_LIST", any_typ, any_typ)
|
||||
RETURN_NAMES = ("flow_control", "remained_list", "item", "intermediate_output")
|
||||
OUTPUT_TOOLTIPS = (
|
||||
"Pass ForeachListEnd as is to indicate the end of the iteration.",
|
||||
"Output the ITEM_LIST containing the remaining items during the iteration, passing ForeachListEnd as is to indicate the end of the iteration.",
|
||||
"Output the current item during the iteration.",
|
||||
"Output the intermediate results during the iteration.")
|
||||
|
||||
FUNCTION = "doit"
|
||||
|
||||
DESCRIPTION = "A starting node for performing iterative tasks by retrieving items one by one from the ITEM_LIST.\nGenerate a new intermediate_output using item and intermediate_output as inputs, then connect it to ForeachListEnd.\nNOTE:If initial_input is omitted, the first item in item_list is used as the initial value, and the processing starts from the second item in item_list."
|
||||
|
||||
CATEGORY = "InspirePack/List"
|
||||
|
||||
def doit(self, item_list, initial_input=None):
|
||||
if initial_input is None:
|
||||
initial_input = item_list[0]
|
||||
item_list = item_list[1:]
|
||||
|
||||
if len(item_list) > 0:
|
||||
return ("stub", item_list[1:], item_list[0], initial_input)
|
||||
|
||||
return ("stub", [], None, initial_input)
|
||||
|
||||
|
||||
class ForeachListEnd:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"flow_control": ("FOREACH_LIST_CONTROL", {"rawLink": True, "tooltip": "Directly connect the output of ForeachListBegin, the starting node of the iteration."}),
|
||||
"remained_list": ("ITEM_LIST", {"tooltip":"Directly connect the output of ForeachListBegin, the starting node of the iteration."}),
|
||||
"intermediate_output": (any_typ, {"tooltip":"Connect the intermediate outputs processed within the iteration here."}),
|
||||
},
|
||||
"hidden": {
|
||||
"dynprompt": "DYNPROMPT",
|
||||
"unique_id": "UNIQUE_ID",
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = (any_typ,)
|
||||
RETURN_NAMES = ("result",)
|
||||
OUTPUT_TOOLTIPS = ("This is the final output value.",)
|
||||
|
||||
FUNCTION = "doit"
|
||||
|
||||
DESCRIPTION = "A end node for performing iterative tasks by retrieving items one by one from the ITEM_LIST.\nNOTE:Directly connect the outputs of ForeachListBegin to 'flow_control' and 'remained_list'."
|
||||
|
||||
CATEGORY = "InspirePack/List"
|
||||
|
||||
def explore_dependencies(self, node_id, dynprompt, upstream):
|
||||
node_info = dynprompt.get_node(node_id)
|
||||
if "inputs" not in node_info:
|
||||
return
|
||||
for k, v in node_info["inputs"].items():
|
||||
if is_link(v):
|
||||
parent_id = v[0]
|
||||
if parent_id not in upstream:
|
||||
upstream[parent_id] = []
|
||||
self.explore_dependencies(parent_id, dynprompt, upstream)
|
||||
upstream[parent_id].append(node_id)
|
||||
|
||||
def collect_contained(self, node_id, upstream, contained):
|
||||
if node_id not in upstream:
|
||||
return
|
||||
for child_id in upstream[node_id]:
|
||||
if child_id not in contained:
|
||||
contained[child_id] = True
|
||||
self.collect_contained(child_id, upstream, contained)
|
||||
|
||||
def doit(self, flow_control, remained_list, intermediate_output, dynprompt, unique_id):
|
||||
if len(remained_list) == 0:
|
||||
return (intermediate_output,)
|
||||
|
||||
# We want to loop
|
||||
this_node = dynprompt.get_node(unique_id)
|
||||
upstream = {}
|
||||
|
||||
# Get the list of all nodes between the open and close nodes
|
||||
self.explore_dependencies(unique_id, dynprompt, upstream)
|
||||
|
||||
contained = {}
|
||||
open_node = flow_control[0]
|
||||
self.collect_contained(open_node, upstream, contained)
|
||||
contained[unique_id] = True
|
||||
contained[open_node] = True
|
||||
|
||||
# We'll use the default prefix, but to avoid having node names grow exponentially in size,
|
||||
# we'll use "Recurse" for the name of the recursively-generated copy of this node.
|
||||
graph = GraphBuilder()
|
||||
for node_id in contained:
|
||||
original_node = dynprompt.get_node(node_id)
|
||||
node = graph.node(original_node["class_type"], "Recurse" if node_id == unique_id else node_id)
|
||||
node.set_override_display_id(node_id)
|
||||
|
||||
for node_id in contained:
|
||||
original_node = dynprompt.get_node(node_id)
|
||||
node = graph.lookup_node("Recurse" if node_id == unique_id else node_id)
|
||||
for k, v in original_node["inputs"].items():
|
||||
if is_link(v) and v[0] in contained:
|
||||
parent = graph.lookup_node(v[0])
|
||||
node.set_input(k, parent.out(v[1]))
|
||||
else:
|
||||
node.set_input(k, v)
|
||||
|
||||
new_open = graph.lookup_node(open_node)
|
||||
new_open.set_input("item_list", remained_list)
|
||||
new_open.set_input("initial_input", intermediate_output)
|
||||
|
||||
my_clone = graph.lookup_node("Recurse" )
|
||||
result = (my_clone.out(0),)
|
||||
|
||||
return {
|
||||
"result": result,
|
||||
"expand": graph.finalize(),
|
||||
}
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"FloatRange //Inspire": FloatRange,
|
||||
"WorklistToItemList //Inspire": WorklistToItemList,
|
||||
"ForeachListBegin //Inspire": ForeachListBegin,
|
||||
"ForeachListEnd //Inspire": ForeachListEnd,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"FloatRange //Inspire": "Float Range (Inspire)"
|
||||
"FloatRange //Inspire": "Float Range (Inspire)",
|
||||
"WorklistToItemList //Inspire": "Worklist To Item List (Inspire)",
|
||||
"ForeachListBegin //Inspire": "▶Foreach List (Inspire)",
|
||||
"ForeachListEnd //Inspire": "Foreach List◀ (Inspire)",
|
||||
}
|
||||
|
||||
@@ -484,7 +484,7 @@ class LoraLoaderBlockWeight:
|
||||
|
||||
if k in muted_weights:
|
||||
pass
|
||||
elif 'text' in k:
|
||||
elif 'text' in k or 'encoder' in k:
|
||||
new_clip.add_patches({k: weights}, strength_clip * ratio)
|
||||
else:
|
||||
new_modelpatcher.add_patches({k: weights}, strength_model * ratio)
|
||||
@@ -546,7 +546,7 @@ class ApplyLBW:
|
||||
|
||||
if k in muted_weights:
|
||||
pass
|
||||
elif 'text' in k:
|
||||
elif 'text' in k or 'encoder' in k:
|
||||
new_clip.add_patches({k: weights}, strength_clip * ratio)
|
||||
else:
|
||||
new_modelpatcher.add_patches({k: weights}, strength_model * ratio)
|
||||
@@ -822,9 +822,13 @@ class LoraBlockInfo:
|
||||
output_blocks = []
|
||||
output_blocks_map = {}
|
||||
|
||||
text_block_count = set()
|
||||
text_blocks = []
|
||||
text_blocks_map = {}
|
||||
text_block_count1 = set()
|
||||
text_blocks1 = []
|
||||
text_blocks_map1 = {}
|
||||
|
||||
text_block_count2 = set()
|
||||
text_blocks2 = []
|
||||
text_blocks_map2 = {}
|
||||
|
||||
double_block_count = set()
|
||||
double_blocks = []
|
||||
@@ -902,12 +906,23 @@ class LoraBlockInfo:
|
||||
k_unet_num = k_unet[len("er.text_model.encoder.layers."):len("er.text_model.encoder.layers.")+2]
|
||||
k_unet_int = parse_unet_num(k_unet_num)
|
||||
|
||||
text_block_count.add(k_unet_int)
|
||||
text_blocks.append(k_unet)
|
||||
if k_unet_int in text_blocks_map:
|
||||
text_blocks_map[k_unet_int].append(k_unet)
|
||||
text_block_count1.add(k_unet_int)
|
||||
text_blocks1.append(k_unet)
|
||||
if k_unet_int in text_blocks_map1:
|
||||
text_blocks_map1[k_unet_int].append(k_unet)
|
||||
else:
|
||||
text_blocks_map[k_unet_int] = [k_unet]
|
||||
text_blocks_map1[k_unet_int] = [k_unet]
|
||||
|
||||
elif k_unet.startswith("r.encoder.block."):
|
||||
k_unet_num = k_unet[len("r.encoder.block."):len("r.encoder.block.")+2]
|
||||
k_unet_int = parse_unet_num(k_unet_num)
|
||||
|
||||
text_block_count2.add(k_unet_int)
|
||||
text_blocks2.append(k_unet)
|
||||
if k_unet_int in text_blocks_map2:
|
||||
text_blocks_map2[k_unet_int].append(k_unet)
|
||||
else:
|
||||
text_blocks_map2[k_unet_int] = [k_unet]
|
||||
|
||||
else:
|
||||
others.append(k_unet)
|
||||
@@ -951,10 +966,14 @@ class LoraBlockInfo:
|
||||
for x in single_keys:
|
||||
text += f" SINGLE{x}: {len(single_blocks_map[x])}\n"
|
||||
|
||||
text += f"\n-------[Base blocks] ({len(text_block_count) + len(others)}, Subs={len(text_blocks) + len(others)})-------\n"
|
||||
text_keys = sorted(text_blocks_map.keys())
|
||||
for x in text_keys:
|
||||
text += f" TXT_ENC{x}: {len(text_blocks_map[x])}\n"
|
||||
text += f"\n-------[Base blocks] ({len(text_block_count1) + len(text_block_count2) + len(others)}, Subs={len(text_blocks1) + len(text_blocks2) + len(others)})-------\n"
|
||||
text_keys1 = sorted(text_blocks_map1.keys())
|
||||
for x in text_keys1:
|
||||
text += f" TXT_ENC{x}: {len(text_blocks_map1[x])}\n"
|
||||
|
||||
text_keys2 = sorted(text_blocks_map2.keys())
|
||||
for x in text_keys2:
|
||||
text += f" TXT_ENC{x} [B]: {len(text_blocks_map2[x])}\n"
|
||||
|
||||
for x in others:
|
||||
text += f" {x}\n"
|
||||
|
||||
+165
-75
@@ -18,15 +18,17 @@ from server import PromptServer
|
||||
from .libs import utils, common
|
||||
from .backend_support import CheckpointLoaderSimpleShared
|
||||
|
||||
|
||||
model_path = folder_paths.models_dir
|
||||
utils.add_folder_path_and_extensions("inspire_prompts", [os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "prompts"))], {'.txt'})
|
||||
|
||||
|
||||
prompt_builder_preset = {}
|
||||
|
||||
|
||||
resource_path = os.path.join(os.path.dirname(__file__), "..", "resources")
|
||||
resource_path = os.path.abspath(resource_path)
|
||||
|
||||
prompts_path = os.path.join(os.path.dirname(__file__), "..", "prompts")
|
||||
prompts_path = os.path.abspath(prompts_path)
|
||||
|
||||
|
||||
try:
|
||||
pb_yaml_path = os.path.join(resource_path, 'prompt-builder.yaml')
|
||||
@@ -44,9 +46,12 @@ except Exception as e:
|
||||
class LoadPromptsFromDir:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
global prompts_path
|
||||
try:
|
||||
prompt_dirs = [d for d in os.listdir(prompts_path) if os.path.isdir(os.path.join(prompts_path, d))]
|
||||
prompt_dirs = []
|
||||
for x in folder_paths.get_folder_paths('inspire_prompts'):
|
||||
for d in os.listdir(x):
|
||||
if os.path.isdir(os.path.join(x, d)):
|
||||
prompt_dirs.append(d)
|
||||
except Exception:
|
||||
prompt_dirs = []
|
||||
|
||||
@@ -55,83 +60,112 @@ class LoadPromptsFromDir:
|
||||
},
|
||||
"optional": {
|
||||
"reload": ("BOOLEAN", { "default": False, "label_on": "if file changed", "label_off": "if value changed"}),
|
||||
"load_cap": ("INT", {"default": 0, "min": 0, "step": 1, "advanced": True, "tooltip": "The amount of prompts to load at once:\n0: Load all\n1 or higher: Load a specified number"}),
|
||||
"start_index": ("INT", {"default": 0, "min": -1, "step": 1, "max": 0xffffffffffffffff, "advanced": True, "tooltip": "Starting index for loading prompts:\n-1: The last prompt\n0 or higher: Load from the specified index"}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("ZIPPED_PROMPT",)
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
RETURN_TYPES = ("ZIPPED_PROMPT", "INT", "INT")
|
||||
RETURN_NAMES = ("zipped_prompt", "count", "remaining_count")
|
||||
OUTPUT_IS_LIST = (True, False, False)
|
||||
|
||||
FUNCTION = "doit"
|
||||
|
||||
CATEGORY = "InspirePack/Prompt"
|
||||
|
||||
@staticmethod
|
||||
def IS_CHANGED(prompt_dir, reload=False):
|
||||
def IS_CHANGED(prompt_dir, reload=False, load_cap=0, start_index=-1):
|
||||
if not reload:
|
||||
return prompt_dir
|
||||
return prompt_dir, load_cap, start_index
|
||||
else:
|
||||
global prompts_path
|
||||
prompt_dir = os.path.join(prompts_path, prompt_dir)
|
||||
files = [f for f in os.listdir(prompt_dir) if f.endswith(".txt")]
|
||||
candidates = []
|
||||
for d in folder_paths.get_folder_paths('inspire_prompts'):
|
||||
candidates.append(os.path.join(d, prompt_dir))
|
||||
|
||||
prompt_files = []
|
||||
for x in candidates:
|
||||
for root, dirs, files in os.walk(x):
|
||||
for file in files:
|
||||
if file.endswith(".txt"):
|
||||
prompt_files.append(os.path.join(root, file))
|
||||
|
||||
prompt_files.sort()
|
||||
|
||||
md5 = hashlib.md5()
|
||||
files.sort()
|
||||
|
||||
for file in files:
|
||||
md5.update(file.encode('utf-8'))
|
||||
with open(os.path.join(prompt_dir, file), 'rb') as f:
|
||||
for file_name in prompt_files:
|
||||
md5.update(file_name.encode('utf-8'))
|
||||
with open(folder_paths.get_full_path('inspire_prompts', file_name), 'rb') as f:
|
||||
while True:
|
||||
chunk = f.read(4096)
|
||||
if not chunk:
|
||||
break
|
||||
md5.update(chunk)
|
||||
|
||||
return md5.hexdigest()
|
||||
return md5.hexdigest(), load_cap, start_index
|
||||
|
||||
@staticmethod
|
||||
def doit(prompt_dir, reload=False):
|
||||
global prompts_path
|
||||
prompt_dir = os.path.join(prompts_path, prompt_dir)
|
||||
files = [f for f in os.listdir(prompt_dir) if f.endswith(".txt")]
|
||||
files.sort()
|
||||
def doit(prompt_dir, reload=False, load_cap=0, start_index=-1):
|
||||
candidates = []
|
||||
for d in folder_paths.get_folder_paths('inspire_prompts'):
|
||||
candidates.append(os.path.join(d, prompt_dir))
|
||||
|
||||
prompt_files = []
|
||||
for x in candidates:
|
||||
for root, dirs, files in os.walk(x):
|
||||
for file in files:
|
||||
if file.endswith(".txt"):
|
||||
prompt_files.append(os.path.join(root, file))
|
||||
|
||||
prompt_files.sort()
|
||||
|
||||
prompts = []
|
||||
for file_name in files:
|
||||
for file_name in prompt_files:
|
||||
print(f"file_name: {file_name}")
|
||||
try:
|
||||
with open(os.path.join(prompt_dir, file_name), "r", encoding="utf-8") as file:
|
||||
with open(file_name, "r", encoding="utf-8") as file:
|
||||
prompt_data = file.read()
|
||||
prompt_list = re.split(r'\n\s*-+\s*\n', prompt_data)
|
||||
|
||||
for prompt in prompt_list:
|
||||
pattern = r"positive:(.*?)(?:\n*|$)negative:(.*)"
|
||||
pattern = r"^(?:(?:positive:(?P<positive>.*?)|negative:(?P<negative>.*?)|name:(?P<name>.*?))\n*)+$"
|
||||
matches = re.search(pattern, prompt, re.DOTALL)
|
||||
|
||||
if matches:
|
||||
positive_text = matches.group(1).strip()
|
||||
negative_text = matches.group(2).strip()
|
||||
result_tuple = (positive_text, negative_text, file_name)
|
||||
positive_text = matches.group('positive').strip()
|
||||
negative_text = matches.group('negative').strip()
|
||||
name_text = matches.group('name').strip() if matches.group('name') else file_name
|
||||
result_tuple = (positive_text, negative_text, name_text)
|
||||
prompts.append(result_tuple)
|
||||
else:
|
||||
print(f"[WARN] LoadPromptsFromDir: invalid prompt format in '{file_name}'")
|
||||
except Exception as e:
|
||||
print(f"[ERROR] LoadPromptsFromDir: an error occurred while processing '{file_name}': {str(e)}\nNOTE: Only files with UTF-8 encoding are supported.")
|
||||
|
||||
return (prompts, )
|
||||
# slicing [start_index ~ start_index + load_cap]
|
||||
total_prompts = len(prompts)
|
||||
prompts = prompts[start_index:]
|
||||
remaining_count = False
|
||||
if load_cap > 0:
|
||||
remaining_count = max(0, len(prompts) - load_cap)
|
||||
prompts = prompts[:load_cap]
|
||||
|
||||
return prompts, total_prompts, remaining_count
|
||||
|
||||
|
||||
class LoadPromptsFromFile:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
global prompts_path
|
||||
prompt_files = []
|
||||
try:
|
||||
prompt_files = []
|
||||
for root, dirs, files in os.walk(prompts_path):
|
||||
for file in files:
|
||||
if file.endswith(".txt"):
|
||||
file_path = os.path.join(root, file)
|
||||
rel_path = os.path.relpath(file_path, prompts_path)
|
||||
prompt_files.append(rel_path)
|
||||
prompts_paths = folder_paths.get_folder_paths('inspire_prompts')
|
||||
for prompts_path in prompts_paths:
|
||||
for root, dirs, files in os.walk(prompts_path):
|
||||
for file in files:
|
||||
if file.endswith(".txt"):
|
||||
file_path = os.path.join(root, file)
|
||||
rel_path = os.path.relpath(file_path, prompts_path)
|
||||
prompt_files.append(rel_path)
|
||||
except Exception:
|
||||
prompt_files = []
|
||||
|
||||
@@ -141,81 +175,114 @@ class LoadPromptsFromFile:
|
||||
"optional": {
|
||||
"text_data_opt": ("STRING", {"defaultInput": True}),
|
||||
"reload": ("BOOLEAN", {"default": False, "label_on": "if file changed", "label_off": "if value changed"}),
|
||||
"load_cap": ("INT", {"default": 0, "min": 0, "step": 1, "advanced": True, "tooltip": "The amount of prompts to load at once:\n0: Load all\n1 or higher: Load a specified number"}),
|
||||
"start_index": ("INT", {"default": 0, "min": -1, "max": 0xffffffffffffffff, "step": 1, "advanced": True, "tooltip": "Starting index for loading prompts:\n-1: The last prompt\n0 or higher: Load from the specified index"}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("ZIPPED_PROMPT",)
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
RETURN_TYPES = ("ZIPPED_PROMPT", "INT", "INT")
|
||||
RETURN_NAMES = ("zipped_prompt", "count", "remaining_count")
|
||||
OUTPUT_IS_LIST = (True, False, False)
|
||||
|
||||
FUNCTION = "doit"
|
||||
|
||||
CATEGORY = "InspirePack/Prompt"
|
||||
|
||||
@staticmethod
|
||||
def IS_CHANGED(prompt_file, text_data_opt=None, reload=False):
|
||||
def IS_CHANGED(prompt_file, text_data_opt=None, reload=False, load_cap=0, start_index=-1):
|
||||
md5 = hashlib.md5()
|
||||
|
||||
if text_data_opt is not None:
|
||||
md5.update(text_data_opt)
|
||||
return md5.hexdigest()
|
||||
return md5.hexdigest(), load_cap, start_index
|
||||
elif not reload:
|
||||
return prompt_file
|
||||
return prompt_file, load_cap, start_index
|
||||
else:
|
||||
prompt_path = os.path.join(prompts_path, prompt_file)
|
||||
matched_path = None
|
||||
for x in folder_paths.get_folder_paths('inspire_prompts'):
|
||||
matched_path = os.path.join(x, prompt_file)
|
||||
if not os.path.exists(matched_path):
|
||||
matched_path = None
|
||||
else:
|
||||
break
|
||||
|
||||
with open(prompt_path, 'rb') as f:
|
||||
if matched_path is None:
|
||||
return float('NaN')
|
||||
|
||||
with open(matched_path, 'rb') as f:
|
||||
while True:
|
||||
chunk = f.read(4096)
|
||||
if not chunk:
|
||||
break
|
||||
md5.update(chunk)
|
||||
|
||||
return md5.hexdigest()
|
||||
return md5.hexdigest(), load_cap, start_index
|
||||
|
||||
@staticmethod
|
||||
def doit(prompt_file, text_data_opt=None, reload=False):
|
||||
prompt_path = os.path.join(prompts_path, prompt_file)
|
||||
def doit(prompt_file, text_data_opt=None, reload=False, load_cap=0, start_index=-1):
|
||||
matched_path = None
|
||||
for d in folder_paths.get_folder_paths('inspire_prompts'):
|
||||
matched_path = os.path.join(d, prompt_file)
|
||||
if os.path.exists(matched_path):
|
||||
break
|
||||
else:
|
||||
matched_path = None
|
||||
|
||||
if matched_path:
|
||||
print(f"[INFO] LoadPromptsFromFile: file found '{prompt_file}'")
|
||||
else:
|
||||
print(f"[WARN] LoadPromptsFromFile: file not found '{prompt_file}'")
|
||||
|
||||
prompts = []
|
||||
try:
|
||||
if not text_data_opt:
|
||||
with open(prompt_path, "r", encoding="utf-8") as file:
|
||||
with open(matched_path, "r", encoding="utf-8") as file:
|
||||
prompt_data = file.read()
|
||||
else:
|
||||
prompt_data = text_data_opt
|
||||
|
||||
prompt_list = re.split(r'\n\s*-+\s*\n', prompt_data)
|
||||
|
||||
pattern = r"positive:(.*?)(?:\n*|$)negative:(.*)"
|
||||
pattern = r"^(?:(?:positive:(?P<positive>.*?)|negative:(?P<negative>.*?)|name:(?P<name>.*?))\n*)+$"
|
||||
|
||||
for prompt in prompt_list:
|
||||
matches = re.search(pattern, prompt, re.DOTALL)
|
||||
for p in prompt_list:
|
||||
matches = re.search(pattern, p, re.DOTALL)
|
||||
|
||||
if matches:
|
||||
positive_text = matches.group(1).strip()
|
||||
negative_text = matches.group(2).strip()
|
||||
result_tuple = (positive_text, negative_text, prompt_file)
|
||||
positive_text = matches.group('positive').strip()
|
||||
negative_text = matches.group('negative').strip()
|
||||
name_text = matches.group('name').strip() if matches.group('name') else prompt_file
|
||||
result_tuple = (positive_text, negative_text, name_text)
|
||||
prompts.append(result_tuple)
|
||||
else:
|
||||
print(f"[WARN] LoadPromptsFromFile: invalid prompt format in '{prompt_file}'")
|
||||
except Exception as e:
|
||||
print(f"[ERROR] LoadPromptsFromFile: an error occurred while processing '{prompt_file}': {str(e)}\nNOTE: Only files with UTF-8 encoding are supported.")
|
||||
|
||||
return (prompts, )
|
||||
# slicing [start_index ~ start_index + load_cap]
|
||||
total_prompts = len(prompts)
|
||||
prompts = prompts[start_index:]
|
||||
remaining_count = 0
|
||||
if load_cap > 0:
|
||||
remaining_count = max(0, len(prompts) - load_cap)
|
||||
prompts = prompts[:load_cap]
|
||||
|
||||
return prompts, total_prompts, remaining_count
|
||||
|
||||
|
||||
class LoadSinglePromptFromFile:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
global prompts_path
|
||||
prompt_files = []
|
||||
try:
|
||||
prompt_files = []
|
||||
for root, dirs, files in os.walk(prompts_path):
|
||||
for file in files:
|
||||
if file.endswith(".txt"):
|
||||
file_path = os.path.join(root, file)
|
||||
rel_path = os.path.relpath(file_path, prompts_path)
|
||||
prompt_files.append(rel_path)
|
||||
prompts_paths = folder_paths.get_folder_paths('inspire_prompts')
|
||||
for prompts_path in prompts_paths:
|
||||
for root, dirs, files in os.walk(prompts_path):
|
||||
for file in files:
|
||||
if file.endswith(".txt"):
|
||||
file_path = os.path.join(root, file)
|
||||
rel_path = os.path.relpath(file_path, prompts_path)
|
||||
prompt_files.append(rel_path)
|
||||
except Exception:
|
||||
prompt_files = []
|
||||
|
||||
@@ -235,7 +302,19 @@ class LoadSinglePromptFromFile:
|
||||
|
||||
@staticmethod
|
||||
def doit(prompt_file, index, text_data_opt=None):
|
||||
prompt_path = os.path.join(prompts_path, prompt_file)
|
||||
prompt_path = None
|
||||
prompts_paths = folder_paths.get_folder_paths('inspire_prompts')
|
||||
for d in prompts_paths:
|
||||
prompt_path = os.path.join(d, prompt_file)
|
||||
if os.path.exists(prompt_path):
|
||||
break
|
||||
else:
|
||||
prompt_path = None
|
||||
|
||||
if prompt_path:
|
||||
print(f"[INFO] LoadSinglePromptFromFile: file found '{prompt_file}'")
|
||||
else:
|
||||
print(f"[WARN] LoadSinglePromptFromFile: file not found '{prompt_file}'")
|
||||
|
||||
prompts = []
|
||||
try:
|
||||
@@ -251,13 +330,14 @@ class LoadSinglePromptFromFile:
|
||||
except Exception:
|
||||
prompt = prompt_list[-1]
|
||||
|
||||
pattern = r"positive:(.*?)(?:\n*|$)negative:(.*)"
|
||||
pattern = r"^(?:(?:positive:(?P<positive>.*?)|negative:(?P<negative>.*?)|name:(?P<name>.*?))\n*)+$"
|
||||
matches = re.search(pattern, prompt, re.DOTALL)
|
||||
|
||||
if matches:
|
||||
positive_text = matches.group(1).strip()
|
||||
negative_text = matches.group(2).strip()
|
||||
result_tuple = (positive_text, negative_text, prompt_file)
|
||||
positive_text = matches.group('positive').strip()
|
||||
negative_text = matches.group('negative').strip()
|
||||
name_text = matches.group('name').strip() if matches.group('name') else prompt_file
|
||||
result_tuple = (positive_text, negative_text, name_text)
|
||||
prompts.append(result_tuple)
|
||||
else:
|
||||
print(f"[WARN] LoadSinglePromptFromFile: invalid prompt format in '{prompt_file}'")
|
||||
@@ -497,7 +577,13 @@ class WildcardEncodeInspire:
|
||||
"weight_interpretation": (["comfy", "A1111", "compel", "comfy++", "down_weight"], {'default': 'comfy++'}),
|
||||
"wildcard_text": ("STRING", {"multiline": True, "dynamicPrompts": False, 'placeholder': 'Wildcard Prompt (User Input)'}),
|
||||
"populated_text": ("STRING", {"multiline": True, "dynamicPrompts": False, 'placeholder': 'Populated Prompt (Will be generated automatically)'}),
|
||||
"mode": ("BOOLEAN", {"default": True, "label_on": "Populate", "label_off": "Fixed"}),
|
||||
|
||||
"mode": (["populate", "fixed", "reproduce"], {"default": "populate", "tooltip":
|
||||
"populate: Before running the workflow, it overwrites the existing value of 'populated_text' with the prompt processed from 'wildcard_text'. In this mode, 'populated_text' cannot be edited.\n"
|
||||
"fixed: Ignores wildcard_text and keeps 'populated_text' as is. You can edit 'populated_text' in this mode.\n"
|
||||
"reproduce: This mode operates as 'fixed' mode only once for reproduction, and then it switches to 'populate' mode."
|
||||
}),
|
||||
|
||||
"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"],),
|
||||
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
||||
@@ -538,7 +624,11 @@ class MakeBasicPipe:
|
||||
"Add selection to": ("BOOLEAN", {"default": True, "label_on": "Positive", "label_off": "Negative"}),
|
||||
"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"],),
|
||||
"wildcard_mode": ("BOOLEAN", {"default": True, "label_on": "Populate", "label_off": "Fixed"}),
|
||||
"wildcard_mode": (["populate", "fixed", "reproduce"], {"default": "populate", "tooltip":
|
||||
"populate: Before running the workflow, it overwrites the existing value of 'populated_text' with the prompt processed from 'wildcard_text'. In this mode, 'populated_text' cannot be edited.\n"
|
||||
"fixed: Ignores wildcard_text and keeps 'populated_text' as is. You can edit 'populated_text' in this mode.\n"
|
||||
"reproduce: This mode operates as 'fixed' mode only once for reproduction, and then it switches to 'populate' mode."
|
||||
}),
|
||||
|
||||
"positive_populated_text": ("STRING", {"multiline": True, "dynamicPrompts": False, 'placeholder': 'Populated Positive Prompt (Will be generated automatically)'}),
|
||||
"negative_populated_text": ("STRING", {"multiline": True, "dynamicPrompts": False, 'placeholder': 'Populated Negative Prompt (Will be generated automatically)'}),
|
||||
@@ -623,11 +713,11 @@ class SeedExplorer:
|
||||
"optional":
|
||||
{
|
||||
"variation_method": (["linear", "slerp"],),
|
||||
"model": ("model",),
|
||||
"model": ("MODEL",),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("NOISE",)
|
||||
RETURN_TYPES = ("NOISE_IMAGE",)
|
||||
FUNCTION = "doit"
|
||||
|
||||
CATEGORY = "InspirePack/Prompt"
|
||||
@@ -702,15 +792,15 @@ class CompositeNoise:
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"destination": ("NOISE",),
|
||||
"source": ("NOISE",),
|
||||
"destination": ("NOISE_IMAGE",),
|
||||
"source": ("NOISE_IMAGE",),
|
||||
"mode": (["center", "left-top", "right-top", "left-bottom", "right-bottom", "xy"], ),
|
||||
"x": ("INT", {"default": 0, "min": 0, "max": nodes.MAX_RESOLUTION, "step": 8}),
|
||||
"y": ("INT", {"default": 0, "min": 0, "max": nodes.MAX_RESOLUTION, "step": 8}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("NOISE",)
|
||||
RETURN_TYPES = ("NOISE_IMAGE",)
|
||||
FUNCTION = "doit"
|
||||
|
||||
CATEGORY = "InspirePack/Prompt"
|
||||
|
||||
@@ -460,7 +460,7 @@ class RegionalSeedExplorerMask:
|
||||
"required": {
|
||||
"mask": ("MASK",),
|
||||
|
||||
"noise": ("NOISE",),
|
||||
"noise": ("NOISE_IMAGE",),
|
||||
"seed_prompt": ("STRING", {"multiline": True, "dynamicPrompts": False, "pysssss.autocomplete": False}),
|
||||
"enable_additional": ("BOOLEAN", {"default": True, "label_on": "true", "label_off": "false"}),
|
||||
"additional_seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
||||
@@ -471,7 +471,7 @@ class RegionalSeedExplorerMask:
|
||||
{"variation_method": (["linear", "slerp"],), }
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("NOISE",)
|
||||
RETURN_TYPES = ("NOISE_IMAGE",)
|
||||
FUNCTION = "doit"
|
||||
|
||||
CATEGORY = "InspirePack/Regional"
|
||||
@@ -517,7 +517,7 @@ class RegionalSeedExplorerColorMask:
|
||||
"color_mask": ("IMAGE",),
|
||||
"mask_color": ("STRING", {"multiline": False, "default": "#FFFFFF"}),
|
||||
|
||||
"noise": ("NOISE",),
|
||||
"noise": ("NOISE_IMAGE",),
|
||||
"seed_prompt": ("STRING", {"multiline": True, "dynamicPrompts": False, "pysssss.autocomplete": False}),
|
||||
"enable_additional": ("BOOLEAN", {"default": True, "label_on": "true", "label_off": "false"}),
|
||||
"additional_seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
||||
@@ -528,7 +528,7 @@ class RegionalSeedExplorerColorMask:
|
||||
{"variation_method": (["linear", "slerp"],), }
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("NOISE", "MASK")
|
||||
RETURN_TYPES = ("NOISE_IMAGE", "MASK")
|
||||
FUNCTION = "doit"
|
||||
|
||||
CATEGORY = "InspirePack/Regional"
|
||||
|
||||
+84
-86
@@ -34,19 +34,17 @@ app.registerExtension({
|
||||
|
||||
node._value = "Preset";
|
||||
|
||||
node.widgets[preset_i].callback = (v, canvas, node, pos, e) => {
|
||||
node.widgets[vector_i].value = node._value.split(':')[1];
|
||||
if(node.widgets_values) {
|
||||
node.widgets_values[vector_i] = node.widgets[preset_i].value;
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(node.widgets[preset_i], "value", {
|
||||
set: (value) => {
|
||||
const stackTrace = new Error().stack;
|
||||
if(stackTrace.includes('inner_value_change')) {
|
||||
if(value != "Preset") {
|
||||
node.widgets[vector_i].value = value.split(':')[1];
|
||||
if(node.widgets_values) {
|
||||
node.widgets_values[vector_i] = node.widgets[preset_i].value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node._value = value;
|
||||
if(value != "Preset")
|
||||
node._value = value;
|
||||
},
|
||||
get: () => {
|
||||
return node._value;
|
||||
@@ -77,86 +75,86 @@ app.registerExtension({
|
||||
let preset_i = 9;
|
||||
let vector_i = 10;
|
||||
node._value = "Preset";
|
||||
Object.defineProperty(node.widgets[preset_i], "value", {
|
||||
set: (value) => {
|
||||
const stackTrace = new Error().stack;
|
||||
if(stackTrace.includes('inner_value_change')) {
|
||||
if(value != "Preset") {
|
||||
if(!value.startsWith('@') && node.widgets[vector_i].value != "")
|
||||
node.widgets[vector_i].value += "\n";
|
||||
if(value.startsWith('@')) {
|
||||
let spec = value.split(':')[1];
|
||||
var n;
|
||||
var sub_n = null;
|
||||
var block = null;
|
||||
|
||||
if(isNaN(spec)) {
|
||||
let sub_spec = spec.split(',');
|
||||
node.widgets[preset_i].callback = (v, canvas, node, pos, e) => {
|
||||
let value = node._value;
|
||||
if(!value.startsWith('@') && node.widgets[vector_i].value != "")
|
||||
node.widgets[vector_i].value += "\n";
|
||||
if(value.startsWith('@')) {
|
||||
let spec = value.split(':')[1];
|
||||
var n;
|
||||
var sub_n = null;
|
||||
var block = null;
|
||||
|
||||
if(sub_spec.length != 3) {
|
||||
node.widgets_values[vector_i] = '!! SPEC ERROR !!';
|
||||
node._value = '';
|
||||
return;
|
||||
}
|
||||
if(isNaN(spec)) {
|
||||
let sub_spec = spec.split(',');
|
||||
|
||||
n = parseInt(sub_spec[0].trim());
|
||||
sub_n = parseInt(sub_spec[1].trim());
|
||||
block = parseInt(sub_spec[2].trim());
|
||||
}
|
||||
else {
|
||||
n = parseInt(spec.trim());
|
||||
}
|
||||
|
||||
node.widgets[vector_i].value = "";
|
||||
if(sub_n == null) {
|
||||
for(let i=1; i<=n; i++) {
|
||||
var temp = "";
|
||||
for(let j=1; j<=n; j++) {
|
||||
if(temp!='')
|
||||
temp += ',';
|
||||
if(j==i)
|
||||
temp += 'A';
|
||||
else
|
||||
temp += '0';
|
||||
}
|
||||
|
||||
node.widgets[vector_i].value += `B${i}:${temp}\n`;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for(let i=1; i<=sub_n; i++) {
|
||||
var temp = "";
|
||||
for(let j=1; j<=n; j++) {
|
||||
if(temp!='')
|
||||
temp += ',';
|
||||
|
||||
if(block!=j)
|
||||
temp += '0';
|
||||
else {
|
||||
temp += ' ';
|
||||
for(let k=1; k<=sub_n; k++) {
|
||||
if(k==i)
|
||||
temp += 'A ';
|
||||
else
|
||||
temp += '0 ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node.widgets[vector_i].value += `B${block}.SUB${i}:${temp}\n`;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
node.widgets[vector_i].value += `${value}/${value.split(':')[0]}`;
|
||||
}
|
||||
if(node.widgets_values) {
|
||||
node.widgets_values[vector_i] = node.widgets[preset_i].value;
|
||||
}
|
||||
}
|
||||
if(sub_spec.length != 3) {
|
||||
node.widgets_values[vector_i] = '!! SPEC ERROR !!';
|
||||
node._value = '';
|
||||
return;
|
||||
}
|
||||
|
||||
node._value = value;
|
||||
n = parseInt(sub_spec[0].trim());
|
||||
sub_n = parseInt(sub_spec[1].trim());
|
||||
block = parseInt(sub_spec[2].trim());
|
||||
}
|
||||
else {
|
||||
n = parseInt(spec.trim());
|
||||
}
|
||||
|
||||
node.widgets[vector_i].value = "";
|
||||
if(sub_n == null) {
|
||||
for(let i=1; i<=n; i++) {
|
||||
var temp = "";
|
||||
for(let j=1; j<=n; j++) {
|
||||
if(temp!='')
|
||||
temp += ',';
|
||||
if(j==i)
|
||||
temp += 'A';
|
||||
else
|
||||
temp += '0';
|
||||
}
|
||||
|
||||
node.widgets[vector_i].value += `B${i}:${temp}\n`;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for(let i=1; i<=sub_n; i++) {
|
||||
var temp = "";
|
||||
for(let j=1; j<=n; j++) {
|
||||
if(temp!='')
|
||||
temp += ',';
|
||||
|
||||
if(block!=j)
|
||||
temp += '0';
|
||||
else {
|
||||
temp += ' ';
|
||||
for(let k=1; k<=sub_n; k++) {
|
||||
if(k==i)
|
||||
temp += 'A ';
|
||||
else
|
||||
temp += '0 ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node.widgets[vector_i].value += `B${block}.SUB${i}:${temp}\n`;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
node.widgets[vector_i].value += `${value}/${value.split(':')[0]}`;
|
||||
}
|
||||
if(node.widgets_values) {
|
||||
node.widgets_values[vector_i] = node.widgets[preset_i].value;
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(node.widgets[preset_i], "value", {
|
||||
set: (value) => {
|
||||
if(value != 'Preset')
|
||||
node._value = value;
|
||||
},
|
||||
get: () => {
|
||||
return node._value;
|
||||
|
||||
+94
-82
@@ -42,35 +42,38 @@ app.registerExtension({
|
||||
// lora selector, wildcard selector
|
||||
let combo_id = 5;
|
||||
|
||||
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);
|
||||
}
|
||||
// lora
|
||||
node.widgets[combo_id].callback = (value, canvas, node, pos, e) => {
|
||||
let lora_name = node._value;
|
||||
if(lora_name.endsWith('.safetensors')) {
|
||||
lora_name = lora_name.slice(0, -12);
|
||||
}
|
||||
|
||||
wildcard_text_widget.value += `<lora:${lora_name}>`;
|
||||
}
|
||||
}
|
||||
},
|
||||
get: () => { return "Select the LoRA to add to the text"; }
|
||||
});
|
||||
wildcard_text_widget.value += `<lora:${lora_name}>`;
|
||||
}
|
||||
|
||||
Object.defineProperty(node.widgets[combo_id], "value", {
|
||||
set: (value) => {
|
||||
if (value !== "Select the LoRA to add to the text")
|
||||
node._value = value;
|
||||
},
|
||||
|
||||
get: () => { return "Select the LoRA to add to the text"; }
|
||||
});
|
||||
|
||||
// wildcard
|
||||
node.widgets[combo_id+1].callback = (value, canvas, node, pos, e) => {
|
||||
if(wildcard_text_widget.value != '')
|
||||
wildcard_text_widget.value += ', '
|
||||
|
||||
wildcard_text_widget.value += node._wildcard_value;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
},
|
||||
if (value !== "Select the Wildcard to add to the text")
|
||||
node._wildcard_value = value;
|
||||
},
|
||||
get: () => { return "Select the Wildcard to add to the text"; }
|
||||
});
|
||||
|
||||
@@ -92,14 +95,20 @@ app.registerExtension({
|
||||
// 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";
|
||||
if(value == true)
|
||||
node._mode_value = "populate";
|
||||
else if(value == false)
|
||||
node._mode_value = "fixed";
|
||||
else
|
||||
node._mode_value = value; // combo value
|
||||
|
||||
populated_text_widget.inputEl.disabled = node._mode_value != 'populate';
|
||||
},
|
||||
get: () => {
|
||||
if(node._mode_value != undefined)
|
||||
return node._mode_value;
|
||||
else
|
||||
return true;
|
||||
return 'populate';
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -115,47 +124,46 @@ app.registerExtension({
|
||||
// lora selector, wildcard selector
|
||||
let combo_id = 5;
|
||||
|
||||
node.widgets[combo_id].callback = (value, canvas, node, pos, e) => {
|
||||
let lora_name = node._lora_value;
|
||||
if (lora_name.endsWith('.safetensors')) {
|
||||
lora_name = lora_name.slice(0, -12);
|
||||
}
|
||||
|
||||
if(direction_widget.value) {
|
||||
pos_wildcard_text_widget.value += `<lora:${lora_name}>`;
|
||||
}
|
||||
else {
|
||||
neg_wildcard_text_widget.value += `<lora:${lora_name}>`;
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
if(direction_widget.value) {
|
||||
pos_wildcard_text_widget.value += `<lora:${lora_name}>`;
|
||||
}
|
||||
else {
|
||||
neg_wildcard_text_widget.value += `<lora:${lora_name}>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (value !== "Select the LoRA to add to the text")
|
||||
node._lora_value = value;
|
||||
},
|
||||
get: () => { return "Select the LoRA to add to the text"; }
|
||||
});
|
||||
|
||||
node.widgets[combo_id+1].callback = (value, canvas, node, pos, e) => {
|
||||
let w = null;
|
||||
if(direction_widget.value) {
|
||||
w = pos_wildcard_text_widget;
|
||||
}
|
||||
else {
|
||||
w = neg_wildcard_text_widget;
|
||||
}
|
||||
|
||||
if(w.value != '')
|
||||
w.value += ', '
|
||||
|
||||
w.value += node._wildcard_value;
|
||||
}
|
||||
|
||||
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") {
|
||||
let w = null;
|
||||
if(direction_widget.value) {
|
||||
w = pos_wildcard_text_widget;
|
||||
}
|
||||
else {
|
||||
w = neg_wildcard_text_widget;
|
||||
}
|
||||
|
||||
if(w.value != '')
|
||||
w.value += ', '
|
||||
|
||||
w.value += value;
|
||||
}
|
||||
}
|
||||
if (value !== "Select the Wildcard to add to the text")
|
||||
node._wildcard_value = value;
|
||||
},
|
||||
get: () => { return "Select the Wildcard to add to the text"; }
|
||||
});
|
||||
@@ -178,15 +186,21 @@ app.registerExtension({
|
||||
// mode combo
|
||||
Object.defineProperty(mode_widget, "value", {
|
||||
set: (value) => {
|
||||
pos_populated_text_widget.inputEl.disabled = node._mode_value;
|
||||
neg_populated_text_widget.inputEl.disabled = node._mode_value;
|
||||
node._mode_value = value;
|
||||
if(value == true)
|
||||
node._mode_value = "populate";
|
||||
else if(value == false)
|
||||
node._mode_value = "fixed";
|
||||
else
|
||||
node._mode_value = value; // combo value
|
||||
|
||||
pos_populated_text_widget.inputEl.disabled = node._mode_value != 'populate';
|
||||
neg_populated_text_widget.inputEl.disabled = node._mode_value != 'populate';
|
||||
},
|
||||
get: () => {
|
||||
if(node._mode_value != undefined)
|
||||
return node._mode_value;
|
||||
else
|
||||
return true;
|
||||
return 'populate';
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -205,24 +219,22 @@ app.registerExtension({
|
||||
}
|
||||
});
|
||||
|
||||
preset_widget.callback = (value, canvas, node, pos, e) => {
|
||||
if(node.widgets[2].value) {
|
||||
node.widgets[2].value += ', ';
|
||||
}
|
||||
|
||||
const y = node._preset_value.split(':');
|
||||
if(y.length == 2)
|
||||
node.widgets[2].value += y[1].trim();
|
||||
else
|
||||
node.widgets[2].value += node._preset_value.trim();
|
||||
}
|
||||
|
||||
Object.defineProperty(preset_widget, "value", {
|
||||
set: (x) => {
|
||||
const stackTrace = new Error().stack;
|
||||
if(stackTrace.includes('inner_value_change')) {
|
||||
if(node.widgets[2].value) {
|
||||
node.widgets[2].value += ', ';
|
||||
}
|
||||
|
||||
const y = x.split(':');
|
||||
if(y.length == 2)
|
||||
node.widgets[2].value += y[1].trim();
|
||||
else
|
||||
node.widgets[2].value += x.trim();
|
||||
|
||||
if(node.widgets_values) {
|
||||
node.widgets_values[2] = node.widgets[2].values;
|
||||
}
|
||||
};
|
||||
set: (value) => {
|
||||
if (value !== "#PRESET")
|
||||
node._preset_value = value;
|
||||
},
|
||||
get: () => { return '#PRESET'; }
|
||||
});
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "comfyui-inspire-pack"
|
||||
description = "This extension provides various nodes to support Lora Block Weight and the Impact Pack. Provides many easily applicable regional features and applications for Variation Seed."
|
||||
version = "1.5"
|
||||
description = "This extension provides various nodes to support Lora Block Weight, Regional Nodes, Backend Cache, Prompt Utils, List Utils, Noise(Seed) Utils, ... and the Impact Pack."
|
||||
version = "1.12.1"
|
||||
license = { file = "LICENSE" }
|
||||
dependencies = ["matplotlib", "cachetools"]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user