diff --git a/README.md b/README.md new file mode 100644 index 0000000..01536b0 --- /dev/null +++ b/README.md @@ -0,0 +1,59 @@ +# ComfyUI Random Switch + +A ComfyUI custom node that randomly selects one input from multiple inputs of the same type. + +## Features + +- **Random Selection**: Randomly pick one value from 2-32 inputs +- **Flexible Input Types**: Accepts any type (images, latents, conditioning, etc.) as long as all inputs are the same type +- **Seed Control**: Use an external seed for reproducible results, or auto-generate a seed on each run +- **Dynamic Input Growth**: UI automatically expands to show the next input when all current inputs are connected + +## Installation + +1. Clone or download this repository into your ComfyUI `custom_nodes` directory: + + ```bash + cd ComfyUI/custom_nodes + git clone https://github.com/tttamaki/Comfyui-RandomSwitch.git + ``` + +2. Restart ComfyUI + +## Usage + +### Basic Setup + +1. Add the **Random Switch** node to your workflow (search for "Random Switch") +2. Connect 2 or more inputs (images, latents, etc.) - all must be the same type +3. The node will randomly select and output one of the connected inputs + +### With Seed Control + +- Leave **seed** at `0` to get a random selection each run +- Connect a seed value for reproducible results (same seed = same selection) +- The node outputs both the selected value and the seed used + +### Example Workflows + +- **Random Image Selection**: Connect multiple images and randomly pick one +- **Random Latent Selection**: Use with latent inputs for probabilistic sampling +- **Random Conditioning**: Switch between different conditioning inputs + +## Inputs + +- **inputs** (Autogrow): 2-32 inputs of any type (must all be the same type) +- **seed** (Int): Random seed for reproducibility (0 = auto-generate) + +## Outputs + +- **output** (Any): The randomly selected input value +- **seed** (Int): The seed value that was used + +## License + +MIT License + +## Author + +tttamaki and GitHub Copilot diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..b402dec --- /dev/null +++ b/__init__.py @@ -0,0 +1,6 @@ +from .nodes.random_switch import RandomSwitch, comfy_entrypoint + +__all__ = [ + "RandomSwitch", + "comfy_entrypoint" +] diff --git a/nodes/random_switch.py b/nodes/random_switch.py new file mode 100644 index 0000000..98925b8 --- /dev/null +++ b/nodes/random_switch.py @@ -0,0 +1,69 @@ +import random +from typing_extensions import override +from comfy_api.latest import ComfyExtension, io + + +class RandomSwitch(io.ComfyNode): + """Randomly selects one input from multiple inputs of the same type.""" + + @classmethod + def define_schema(cls) -> io.Schema: + # Autogrow template to allow variable number of inputs + # Support up to 32 inputs - automatically expands when connected + template = io.Autogrow.TemplateNames( + input=io.AnyType.Input("input"), + names=[str(i) for i in range(1, 33)], # Support 1-32 inputs + min=2 # At least 2 inputs required + ) + return io.Schema( + node_id="RandomSwitch", + display_name="Random Switch", + category="tttamaki/random-switches", + description="Randomly select one input from multiple inputs" + " (all inputs must be the same type)", + inputs=[ + io.Autogrow.Input("inputs", template=template), + io.Int.Input( + "seed", + default=0, + min=0, + max=0xFFFFFFFFFFFFFFFF, + control_after_generate=True, + tooltip="Seed for random selection. " + "If 0, a random seed is generated." + ), + ], + outputs=[ + io.AnyType.Output(), + io.Int.Output(display_name="seed"), + ], + ) + + @classmethod + def execute(cls, inputs: io.Autogrow.Type, seed: int) -> io.NodeOutput: + values = list(inputs.values()) + + # If seed is 0, generate a random seed + if seed == 0: + seed = random.randint(0, 0xFFFFFFFFFFFFFFFF) + + # Use the seed to ensure reproducibility + rng = random.Random(seed) + selected = rng.choice(values) + + return io.NodeOutput(selected, seed) + + @classmethod + def IS_CHANGED(cls, **kwargs) -> float: + # Always mark as changed to force re-execution and new random selection + return float("nan") + + +class RandomSwitchExtension(ComfyExtension): + @override + async def get_node_list(self) -> list[type[io.ComfyNode]]: + return [RandomSwitch] + + +async def comfy_entrypoint() -> RandomSwitchExtension: + return RandomSwitchExtension()