diff --git a/__init__.py b/__init__.py index 93d9f74..3207ca0 100644 --- a/__init__.py +++ b/__init__.py @@ -16,41 +16,9 @@ from custom_nodes.comfyui_gr85.nodes.random_seed.next_seed import NextSeed from custom_nodes.comfyui_gr85.nodes.random_numbers.random_float import RandomFloat from custom_nodes.comfyui_gr85.nodes.random_numbers.random_int import RandomInt -NODE_CLASS_MAPPINGS = { - "GR85_ImageDimensionResizer": ImageDimensionResizer, - "GR85_ImageSizer": ImageSizer, - "GR85_ImageSizerAll": ImageSizerAll, - "GR85_RandomRatio": RandomRatio, +NODE_CLASS_MAPPINGS = {} - "GR85_SeedBasedOutputSelector": SeedBasedOutputSelector, - "GR85_TagInjector": TagInjector, - "GR85_TagInjectorSingle": TagInjectorSingle, - "GR85_TagInjectorDuo": TagInjectorDuo, - "GR85_TagInjectorLarge": TagInjectorLarge, - - "GR85_RandomFloat": RandomFloat, - "GR85_RandomInt": RandomInt, - - "GR85_NextSeed": NextSeed, -} - -NODE_DISPLAY_NAME_MAPPINGS = { - "GR85_ImageDimensionResizer": "Image Dimension Resizer", - "GR85_ImageSizer": "Image Sizer", - "GR85_ImageSizerAll": "Image Sizer All", - "GR85_RandomRatio": "Random Ratio", - - "GR85_SeedBasedOutputSelector": "Seed Based Output Selector", - "GR85_TagInjector": "Tag Injector", - "GR85_TagInjectorSingle": "Tag Injector Single", - "GR85_TagInjectorDuo": "Tag Injector Duo", - "GR85_TagInjectorLarge": "Tag Injector Large", - - "GR85_RandomFloat": "Random Float", - "GR85_RandomInt": "Random Int", - - "GR85_NextSeed": "Next Seed", -} +NODE_DISPLAY_NAME_MAPPINGS = {} class GR85Extension(ComfyExtension): @@ -58,6 +26,18 @@ class GR85Extension(ComfyExtension): async def get_node_list(self) -> list[type[io.ComfyNode]]: return [ SimpleWildcardPicker, + SeedBasedOutputSelector, + TagInjectorSingle, + TagInjectorDuo, + TagInjector, + TagInjectorLarge, + RandomFloat, + RandomInt, + NextSeed, + ImageDimensionResizer, + ImageSizerAll, + ImageSizer, + RandomRatio, ] diff --git a/nodes/prompt_tags/tag_injector.py b/nodes/prompt_tags/tag_injector.py index 7be1a59..4a22400 100644 --- a/nodes/prompt_tags/tag_injector.py +++ b/nodes/prompt_tags/tag_injector.py @@ -1,7 +1,8 @@ import re +from comfy_api.latest import io -class TagInjectorSingle: +class TagInjectorSingle(io.ComfyNode): def __init__(self): pass @@ -23,6 +24,49 @@ class TagInjectorSingle: FUNCTION = "inject_tag" CATEGORY = "GR85/Prompt/Tags" + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id="GR85_TagInjectorSingle", + display_name="Tag Injector Single", + category="GR85/Prompt/Tags", + inputs=[ + io.String.Input( + "template", + multiline=True, + default="__elements__", + ), + io.String.Input( + "tag_1", + multiline=True, + default="", + ), + io.String.Input( + "tag_name_1", + default="elements", + ), + ], + outputs=[ + io.String.Output(), + io.String.Output(), + ], + ) + + @classmethod + def execute( + cls, + template: str, + tag_1: str = "", + tag_name_1: str = "elements", + ) -> io.NodeOutput: + instance = cls() + tagged_text, placeholders = instance.inject_tag( + template=template, + tag_1=tag_1, + tag_name_1=tag_name_1, + ) + return io.NodeOutput(tagged_text, placeholders) + def inject_tag(self, template, tag_1=None, tag_name_1="elements"): """ Inject a single tag into the template based on the given tag name and value. @@ -44,7 +88,7 @@ class TagInjectorSingle: return template, placeholders -class TagInjectorDuo: +class TagInjectorDuo(io.ComfyNode): def __init__(self): pass @@ -66,7 +110,63 @@ class TagInjectorDuo: RETURN_TYPES = ("STRING", "STRING") RETURN_NAMES = ("tagged_text", "placeholders") FUNCTION = "inject_tag" - CATEGORY = "GR85/Prompt" + CATEGORY = "GR85/Prompt/Tags" + + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id="GR85_TagInjectorDuo", + display_name="Tag Injector Duo", + category="GR85/Prompt/Tags", + inputs=[ + io.String.Input( + "template", + multiline=True, + default="__elements__", + ), + io.String.Input( + "tag_1", + multiline=True, + default="", + ), + io.String.Input( + "tag_2", + multiline=True, + default="", + ), + io.String.Input( + "tag_name_1", + default="elements", + ), + io.String.Input( + "tag_name_2", + default="stuff", + ), + ], + outputs=[ + io.String.Output(), + io.String.Output(), + ], + ) + + @classmethod + def execute( + cls, + template: str, + tag_1: str = "", + tag_2: str = "", + tag_name_1: str = "elements", + tag_name_2: str = "stuff", + ) -> io.NodeOutput: + instance = cls() + tagged_text, placeholders = instance.inject_tag( + template=template, + tag_1=tag_1, + tag_2=tag_2, + tag_name_1=tag_name_1, + tag_name_2=tag_name_2, + ) + return io.NodeOutput(tagged_text, placeholders) def inject_tag(self, template, tag_1=None, tag_2=None, tag_name_1="elements", tag_name_2="stuff"): @@ -90,7 +190,7 @@ class TagInjectorDuo: return template, placeholders -class TagInjector: +class TagInjector(io.ComfyNode): def __init__(self): pass @@ -114,7 +214,76 @@ class TagInjector: RETURN_TYPES = ("STRING", "STRING") RETURN_NAMES = ("tagged_text", "placeholders") FUNCTION = "inject_tag" - CATEGORY = "GR85/Prompt" + CATEGORY = "GR85/Prompt/Tags" + + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id="GR85_TagInjector", + display_name="Tag Injector", + category="GR85/Prompt/Tags", + inputs=[ + io.String.Input( + "template", + multiline=True, + default="__elements__", + ), + io.String.Input( + "tag_1", + multiline=True, + default="", + ), + io.String.Input( + "tag_2", + multiline=True, + default="", + ), + io.String.Input( + "tag_3", + multiline=True, + default="", + ), + io.String.Input( + "tag_name_1", + default="elements", + ), + io.String.Input( + "tag_name_2", + default="stuff", + ), + io.String.Input( + "tag_name_3", + default="things", + ), + ], + outputs=[ + io.String.Output(), + io.String.Output(), + ], + ) + + @classmethod + def execute( + cls, + template: str, + tag_1: str = "", + tag_2: str = "", + tag_3: str = "", + tag_name_1: str = "elements", + tag_name_2: str = "stuff", + tag_name_3: str = "things", + ) -> io.NodeOutput: + instance = cls() + tagged_text, placeholders = instance.inject_tag( + template=template, + tag_1=tag_1, + tag_2=tag_2, + tag_3=tag_3, + tag_name_1=tag_name_1, + tag_name_2=tag_name_2, + tag_name_3=tag_name_3, + ) + return io.NodeOutput(tagged_text, placeholders) def inject_tag(self, template, tag_1=None, tag_2=None, tag_3=None, tag_name_1="elements", tag_name_2="stuff", tag_name_3="things"): diff --git a/nodes/prompt_tags/tag_injector_large.py b/nodes/prompt_tags/tag_injector_large.py index a4808f8..f36676a 100644 --- a/nodes/prompt_tags/tag_injector_large.py +++ b/nodes/prompt_tags/tag_injector_large.py @@ -1,7 +1,8 @@ import re +from comfy_api.latest import io -class TagInjectorLarge: +class TagInjectorLarge(io.ComfyNode): def __init__(self): pass @@ -41,6 +42,100 @@ class TagInjectorLarge: FUNCTION = "inject_tag" CATEGORY = "GR85/Prompt/Tags" + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id="GR85_TagInjectorLarge", + display_name="Tag Injector Large", + category="GR85/Prompt/Tags", + inputs=[ + io.String.Input( + "template", + multiline=True, + default=( + "In a __location__ under a __weather__ sky, a __personality__ person shows " + "__emotion__ while wearing __style__. The __time__ is perfect for a __action__ " + "amidst __mood__." + ), + ), + io.String.Input("tag_1", multiline=True, default=""), + io.String.Input("tag_2", multiline=True, default=""), + io.String.Input("tag_3", multiline=True, default=""), + io.String.Input("tag_4", multiline=True, default=""), + io.String.Input("tag_5", multiline=True, default=""), + io.String.Input("tag_6", multiline=True, default=""), + io.String.Input("tag_7", multiline=True, default=""), + io.String.Input("tag_8", multiline=True, default=""), + io.String.Input("tag_9", multiline=True, default=""), + io.String.Input("tag_10", multiline=True, default=""), + io.String.Input("tag_name_1", default="location"), + io.String.Input("tag_name_2", default="color"), + io.String.Input("tag_name_3", default="object"), + io.String.Input("tag_name_4", default="emotion"), + io.String.Input("tag_name_5", default="weather"), + io.String.Input("tag_name_6", default="personality"), + io.String.Input("tag_name_7", default="time"), + io.String.Input("tag_name_8", default="action"), + io.String.Input("tag_name_9", default="style"), + io.String.Input("tag_name_10", default="mood"), + ], + outputs=[ + io.String.Output(), + io.String.Output(), + ], + ) + + @classmethod + def execute( + cls, + template: str, + tag_1: str = "", + tag_2: str = "", + tag_3: str = "", + tag_4: str = "", + tag_5: str = "", + tag_6: str = "", + tag_7: str = "", + tag_8: str = "", + tag_9: str = "", + tag_10: str = "", + tag_name_1: str = "location", + tag_name_2: str = "color", + tag_name_3: str = "object", + tag_name_4: str = "emotion", + tag_name_5: str = "weather", + tag_name_6: str = "personality", + tag_name_7: str = "time", + tag_name_8: str = "action", + tag_name_9: str = "style", + tag_name_10: str = "mood", + ) -> io.NodeOutput: + instance = cls() + tagged_text, placeholders = instance.inject_tag( + template=template, + tag_1=tag_1, + tag_2=tag_2, + tag_3=tag_3, + tag_4=tag_4, + tag_5=tag_5, + tag_6=tag_6, + tag_7=tag_7, + tag_8=tag_8, + tag_9=tag_9, + tag_10=tag_10, + tag_name_1=tag_name_1, + tag_name_2=tag_name_2, + tag_name_3=tag_name_3, + tag_name_4=tag_name_4, + tag_name_5=tag_name_5, + tag_name_6=tag_name_6, + tag_name_7=tag_name_7, + tag_name_8=tag_name_8, + tag_name_9=tag_name_9, + tag_name_10=tag_name_10, + ) + return io.NodeOutput(tagged_text, placeholders) + def inject_tag(self, template, tag_1=None, tag_2=None, tag_3=None, tag_4=None, tag_5=None, tag_6=None, tag_7=None, tag_8=None, tag_9=None, tag_10=None, tag_name_1="location", tag_name_2="color", tag_name_3="object", tag_name_4="emotion", diff --git a/nodes/random_numbers/random_float.py b/nodes/random_numbers/random_float.py index 5f7ac3c..757a65a 100644 --- a/nodes/random_numbers/random_float.py +++ b/nodes/random_numbers/random_float.py @@ -1,6 +1,7 @@ import random +from comfy_api.latest import io -class RandomFloat: +class RandomFloat(io.ComfyNode): """ A ComfyUI node class that generates a random float based on given inputs. @@ -29,6 +30,60 @@ class RandomFloat: FUNCTION = "generate_random_float" CATEGORY = "GR85/Random/Numbers" + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id="GR85_RandomFloat", + display_name="Random Float", + category="GR85/Random/Numbers", + inputs=[ + io.Int.Input( + "seed", + default=0, + min=0, + ), + io.Float.Input( + "min_value", + default=0.0, + min=-1e-10, + max=1e10, + step=0.0001, + ), + io.Float.Input( + "max_value", + default=1.0, + min=-1e-10, + max=1e10, + step=0.0001, + ), + io.Int.Input( + "decimal_places", + default=10, + min=0, + ), + ], + outputs=[ + io.Float.Output(), + ], + ) + + @classmethod + def execute( + cls, + seed: int, + min_value: float, + max_value: float, + decimal_places: int, + ) -> io.NodeOutput: + instance = cls() + (value,) = instance.generate_random_float( + seed=seed, + min_value=min_value, + max_value=max_value, + decimal_places=decimal_places, + ) + return io.NodeOutput(value) + def generate_random_float(self, seed: int, min_value: float, max_value: float, decimal_places: int) -> tuple: """ Generates a random float based on the given seed, min, max, and decimal places. diff --git a/nodes/random_numbers/random_int.py b/nodes/random_numbers/random_int.py index 7ee56b1..fc0d3cf 100644 --- a/nodes/random_numbers/random_int.py +++ b/nodes/random_numbers/random_int.py @@ -1,6 +1,7 @@ import random +from comfy_api.latest import io -class RandomInt: +class RandomInt(io.ComfyNode): """ A ComfyUI node class that generates a random integer based on given inputs. @@ -28,6 +29,51 @@ class RandomInt: FUNCTION = "generate_random_int" CATEGORY = "GR85/Random/Numbers" + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id="GR85_RandomInt", + display_name="Random Int", + category="GR85/Random/Numbers", + inputs=[ + io.Int.Input( + "seed", + default=0, + min=0, + ), + io.Int.Input( + "min_value", + default=0, + min=-10000000000, + max=10000000000, + ), + io.Int.Input( + "max_value", + default=100, + min=-10000000000, + max=10000000000, + ), + ], + outputs=[ + io.Int.Output(), + ], + ) + + @classmethod + def execute( + cls, + seed: int, + min_value: int, + max_value: int, + ) -> io.NodeOutput: + instance = cls() + (value,) = instance.generate_random_int( + seed=seed, + min_value=min_value, + max_value=max_value, + ) + return io.NodeOutput(value) + def generate_random_int(self, seed: int, min_value: int, max_value: int) -> tuple: """ Generates a random integer based on the given seed, min, and max values. diff --git a/nodes/random_seed/next_seed.py b/nodes/random_seed/next_seed.py index 8e44578..0587ca0 100644 --- a/nodes/random_seed/next_seed.py +++ b/nodes/random_seed/next_seed.py @@ -1,7 +1,8 @@ import random +from comfy_api.latest import io -class NextSeed: +class NextSeed(io.ComfyNode): def __init__(self): pass @@ -18,6 +19,34 @@ class NextSeed: FUNCTION = "next_seed" CATEGORY = "GR85/Random/Seed" + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id="GR85_NextSeed", + display_name="Next Seed", + category="GR85/Random/Seed", + inputs=[ + io.Int.Input( + "seed", + default=0, + min=0, + max=0xFFFFFFFFFFFFFFFF, + ), + ], + outputs=[ + io.Int.Output(), + ], + ) + + @classmethod + def execute( + cls, + seed: int, + ) -> io.NodeOutput: + instance = cls() + (value,) = instance.next_seed(seed=seed) + return io.NodeOutput(value) + def next_seed(self, seed): random.seed(seed) return (random.randint(0, 0xffffffffffffffff),) diff --git a/nodes/resolution/image_dimension_resizer.py b/nodes/resolution/image_dimension_resizer.py index 71dc841..c4c6abf 100644 --- a/nodes/resolution/image_dimension_resizer.py +++ b/nodes/resolution/image_dimension_resizer.py @@ -1,7 +1,8 @@ import math +from comfy_api.latest import io -class ImageDimensionResizer: +class ImageDimensionResizer(io.ComfyNode): def __init__(self): pass @@ -36,6 +37,51 @@ class ImageDimensionResizer: CATEGORY = "GR85/Resolution" + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id="GR85_ImageDimensionResizer", + display_name="Image Dimension Resizer", + category="GR85/Resolution", + inputs=[ + io.Int.Input( + "original_width", + default=512, + min=1, + max=4096, + ), + io.Int.Input( + "original_height", + default=512, + min=1, + max=4096, + ), + io.String.Input( + "target_dimensions", + default="512x512", + ), + ], + outputs=[ + io.Int.Output(), + io.Int.Output(), + ], + ) + + @classmethod + def execute( + cls, + original_width: int, + original_height: int, + target_dimensions: str, + ) -> io.NodeOutput: + instance = cls() + width, height = instance.resize_dimensions( + original_width=original_width, + original_height=original_height, + target_dimensions=target_dimensions, + ) + return io.NodeOutput(width, height) + def resize_dimensions(self, original_width, original_height, target_dimensions): """ Calculates new dimensions while maintaining pixel count diff --git a/nodes/resolution/image_sizer.py b/nodes/resolution/image_sizer.py index 7912d6a..b1d8fd7 100644 --- a/nodes/resolution/image_sizer.py +++ b/nodes/resolution/image_sizer.py @@ -1,6 +1,8 @@ import math +from comfy_api.latest import io -class ImageSizer: + +class ImageSizer(io.ComfyNode): def __init__(self): pass @@ -41,6 +43,65 @@ class ImageSizer: CATEGORY = "GR85/Resolution" + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id="GR85_ImageSizer", + display_name="Image Sizer", + category="GR85/Resolution", + inputs=[ + io.String.Input( + "original_dimensions", + default="512x512", + ), + io.Int.Input( + "width", + default=1, + min=1, + max=4096, + ), + io.Int.Input( + "height", + default=1, + min=1, + max=4096, + ), + io.String.Input( + "orientation", + default="original", + ), + io.Int.Input( + "tolerance", + default=16, + min=1, + max=128, + ), + ], + outputs=[ + io.Int.Output(), + io.Int.Output(), + ], + ) + + @classmethod + def execute( + cls, + original_dimensions: str, + width: int, + height: int, + orientation: str, + tolerance: int, + ) -> io.NodeOutput: + instance = cls() + new_width, new_height = instance.resize_dimensions( + original_dimensions=original_dimensions, + width=width, + height=height, + orientation=orientation, + tolerance=tolerance, + ) + return io.NodeOutput(new_width, new_height) + def resize_dimensions(self, original_dimensions, width, height, orientation, tolerance): source_width, source_height = map(int, original_dimensions.split("x")) diff --git a/nodes/resolution/image_sizer_all.py b/nodes/resolution/image_sizer_all.py index 866949e..766ec4d 100644 --- a/nodes/resolution/image_sizer_all.py +++ b/nodes/resolution/image_sizer_all.py @@ -1,6 +1,8 @@ import math +from comfy_api.latest import io -class ImageSizerAll: + +class ImageSizerAll(io.ComfyNode): def __init__(self): pass @@ -47,6 +49,67 @@ class ImageSizerAll: CATEGORY = "GR85/Resolution" + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id="GR85_ImageSizerAll", + display_name="Image Sizer All", + category="GR85/Resolution", + inputs=[ + io.Int.Input( + "pixel_amount", + default=1024 * 1024, + min=64, + max=0xFFFFFFFFFFFFFFFF, + ), + io.Int.Input( + "width", + default=1, + min=1, + max=4096, + ), + io.Int.Input( + "height", + default=1, + min=1, + max=4096, + ), + io.String.Input( + "orientation", + default="original", + ), + io.Int.Input( + "tolerance", + default=16, + min=1, + max=128, + ), + ], + outputs=[ + io.Int.Output(), + io.Int.Output(), + ], + ) + + @classmethod + def execute( + cls, + pixel_amount: int, + width: int, + height: int, + orientation: str, + tolerance: int, + ) -> io.NodeOutput: + instance = cls() + new_width, new_height = instance.resize_dimensions_all( + pixel_amount=pixel_amount, + width=width, + height=height, + orientation=orientation, + tolerance=tolerance, + ) + return io.NodeOutput(new_width, new_height) + def resize_dimensions_all(self, pixel_amount, width, height, orientation, tolerance): """ Calculates new dimensions for an image while maintaining the same pixel count diff --git a/nodes/resolution/random_ratio.py b/nodes/resolution/random_ratio.py index 0a2d7a6..401cced 100644 --- a/nodes/resolution/random_ratio.py +++ b/nodes/resolution/random_ratio.py @@ -1,7 +1,8 @@ import random +from comfy_api.latest import io -class RandomRatio: +class RandomRatio(io.ComfyNode): def __init__(self): pass @@ -49,6 +50,69 @@ class RandomRatio: CATEGORY = "GR85/Resolution" + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id="GR85_RandomRatio", + display_name="Random Ratio", + category="GR85/Resolution", + inputs=[ + io.Int.Input( + "seed", + default=0, + min=0, + max=0xFFFFFFFFFFFFFFFF, + ), + io.Int.Input( + "first_width", + default=1, + min=1, + max=4096, + ), + io.Int.Input( + "first_height", + default=1, + min=1, + max=4096, + ), + io.Int.Input( + "second_width", + default=1, + min=1, + max=4096, + ), + io.Int.Input( + "second_height", + default=1, + min=1, + max=4096, + ), + ], + outputs=[ + io.Int.Output(), + io.Int.Output(), + ], + ) + + @classmethod + def execute( + cls, + seed: int, + first_width: int, + first_height: int, + second_width: int, + second_height: int, + ) -> io.NodeOutput: + instance = cls() + width, height = instance.random_ratio( + seed=seed, + first_width=first_width, + first_height=first_height, + second_width=second_width, + second_height=second_height, + ) + return io.NodeOutput(width, height) + def random_ratio(self, seed, first_width, first_height, second_width, second_height): """ Calculates a random ratio between a min and max ratio. diff --git a/requirements.txt b/requirements.txt index c5fb12e..a428d8f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,11 +1,3 @@ -# Image processing library for handling masks and drawing operations -opencv-python>=4.5.5.64 - -# Graph library for constructing and computing the Minimum Spanning Tree (MST) -networkx>=3.1 - -# Image processing library for skeletonization -scikit-image>=0.19.3 - -# Scientific computing library for distance calculations -scipy>=1.10.1 +## comfyui_gr85 +# This extension relies only on ComfyUI's bundled Python packages. +# No additional pip requirements are needed.