Files

400 lines
11 KiB
Python

#
"""
@author:"˶𝞢⤬⫒ⵖsᐼ˶"
@title: "Selector"
@nickname: "Selector"
@version: "3.1.0"
@project: "https://github.com/exdysa/comfyui-selector",
@description: "EXDYSA. Selector and Recourse. Presets & failsafes. Work flow."
"""
import comfy.samplers
import comfy.sd
import comfy.utils
import comfy.model_base
import comfy.model_management
import comfy.model_sampling
MODEL_TO_TYPE = {
"StableCascade_B": 8,
"StableCascade_C": 7,
"SD3": 6,
"HunyuanDiT": 5,
"AuraFlow": 4,
"Flux": 3,
"SDXL": 2,
"SDXL_instructpix2pix": 2,
"SDXLRefiner": 2,
"BaseModel": 1,
}
TYPE_DESC = "SD1, SDXL, FLUX, AURAFLOW, HUNYUANDIT, SD3, STABLE_CASCADE_C, STABLE_CASCADE_B"
RECOURSE_CHECK_DESC = f"Ensure connection of first active input by type, then signal type as integer based on MODEL. {MODEL_TO_TYPE}"
SELECTOR_CATEGORY_PATH = "Selector_Recourse"
MODEL_1 = "This port requires MODEL to send the MODEL_TYPE to other nodes."
MODEL_2 = "This port is activated in case MODEL_1 has no signal."
SD_RES = 8
XL_RES = 9
MAX_RES = 24
SD_SAMPLERS = {}
XL_SAMPLERS = {}
FLUX_SAMPLERS = {}
AURAFLOW_SAMPLERS = {}
HUNYUANDIT_SAMPLERS = {}
SD3_SAMPLERS = {}
class Selectah:
"""Choose settings for multiple nodes in a single place"""
def __init__(self):
pass
# dimensions sourced from: https://arxiv.org/abs/2307.01952
# https://github.com/Stability-AI/generative-models
# https://huggingface.co/nvidia/Cosmos-1.0-Diffusion-7B-Text2World
# https://huggingface.co/THUDM/CogVideoX-5b
#
RATIO = [
("1:1___1024x1024", 1024, 1024),
("16:15_1024x960", 1024, 960),
("17:15_1088x960", 1088, 960),
("17:14_1088x896", 1088, 896),
("4:3___1152x896", 1152, 896),
("18:13_1152x832", 1152, 832),
("3:2___1216x832", 1216, 832),
("72:32_1232x832", 1232, 832),
("5:3___1280x768", 1280, 768),
("7:4___1344x768", 1344, 768),
("21:11_1344x704", 1344, 704),
("2:1___1408x704", 1408, 704),
("23:11_1472x704", 1472, 704),
("21:9__1536x640", 1536, 640),
("2:1___1536x768", 1536, 768),
("5:2___1600x640", 1600, 640),
("26:9__1664x576", 1664, 576),
("3:1___1728x576", 1728, 576),
("28:9__1792x576", 1792, 576),
("29:8__1856x512", 1856, 512),
("15:4__1920x512", 1920, 512),
("31:8__1984x512", 1984, 512),
("4:1___2048x512", 2048, 512),
("1:1___V_256x256", 256, 256),
("4:3___V_320x240", 320, 240),
("32:27_V_576x486", 576, 486),
("4:3___V_832x624", 832, 624),
("22:15_V_704x480", 704, 480),
("9:5___V_720x400", 720, 400),
("3:2___V_720x480", 720, 480),
("5:4___V_720x576", 720, 576),
("3:2___V_768x512", 768, 512),
("4:3___V 960x704", 960, 704),
("1:1___V_960x960", 960, 960),
("20:11_V_1280x704", 1280, 704),
("16:9__V_1024X576", 1024, 576),
("1:1__SV3D_576x576", 576, 576),
("1:1____SD_512x512", 512, 512),
("4:3____SD_682x512", 682, 512),
("3:2____SD_768x512", 768, 512),
("16:9___SD_910x512", 910, 512),
("1:85:1_SD_952x512", 952, 512),
("1:1___SD2_768x768", 768, 768),
("2:1____SD_1024x512", 1024, 512),
]
@classmethod
def INPUT_TYPES(cls):
"""User inputs"""
aspect_ratio_titles = [title for title, res1, res2 in cls.RATIO]
rotation = ("landscape", "portrait")
return {
"required": {
"aspect_ratio": (
aspect_ratio_titles,
{
"default": ("1:1___1024x1024"),
},
),
"rotation": (rotation,),
},
"optional": {
"batch": (
"INT",
{
"default": 1,
"min": -10000,
"max": 10000,
},
),
"steps": (
"INT",
{
"default": 20,
"min": -10000,
"max": 10000,
},
),
"refiner_steps": (
"INT",
{
"default": 0,
"min": -10000,
"max": 10000,
},
),
"cfg": (
"FLOAT",
{
"default": 1.000,
"min": 0.000,
"max": 1000.000,
"step": 0.001,
"round": 0.001,
},
),
"refiner_cfg": (
"FLOAT",
{
"default": 1.000,
"min": 0.000,
"max": 1000.000,
"step": 0.001,
"round": 0.001,
},
),
"str_denoise": (
"FLOAT",
{
"default": 1.000,
"min": 0.000,
"max": 1000.000,
"step": 0.001,
"round": 0.001,
},
),
"scale": (
"FLOAT",
{
"default": 2.000,
"min": 0.000,
"max": 1000.000,
"step": 0.001,
"round": 0.001,
},
),
"variation_str": (
"FLOAT",
{
"default": 0.000,
"min": 0.000,
"max": 1000.000,
"step": 0.001,
"round": 0.001,
},
),
"sampler": (comfy.samplers.KSampler.SAMPLERS,),
"scheduler": (comfy.samplers.KSampler.SCHEDULERS,),
},
}
RETURN_TYPES = (
"INT",
"INT",
"INT",
"INT",
"INT",
"FLOAT",
"FLOAT",
"FLOAT",
"FLOAT",
"FLOAT",
comfy.samplers.KSampler.SAMPLERS,
comfy.samplers.KSampler.SCHEDULERS,
)
RETURN_NAMES = (
"WIDTH",
"HEIGHT",
"BATCH_SIZE",
"STEPS",
"REFINER_STEPS",
"CFG",
"REFINER_CFG",
"STRENGTH",
"VARIATION_STR",
"SCALE",
"SAMPLER",
"SCHEDULER",
)
FUNCTION = "selectah"
CATEGORY = SELECTOR_CATEGORY_PATH
def selectah(self, aspect_ratio, rotation, batch, steps, refiner_steps, cfg, refiner_cfg, str_denoise, scale, variation_str, sampler, scheduler):
"""
Choose universal settings for multiple repeat settings workflows\n
:param aR: Desired aspect ratio of image
:type aR: tuple
:param rotation: Orientation of the desired image
:type rotation: str
:param args: The remaining arguments of the node
:type args: tuple
:return: A tuple of the settings, or None for width/height if aspect ratio is not found
"""
for title, width, height in self.RATIO:
if title == aspect_ratio:
if rotation == "portrait":
width, height = height, width # Swap for portrait orientation
return (width, height, batch, steps, refiner_steps, cfg, refiner_cfg, str_denoise, scale, variation_str, sampler, scheduler)
# If aspect ratio is not found, return None for width and height
return (None, None, batch, steps, refiner_steps, cfg, refiner_cfg, str_denoise, scale, variation_str, sampler, scheduler)
class RecourseCheckpoint:
"""Redirect flow by first active and model type"""
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"optional": {
"model_opta": ("MODEL",),
"model_optb": ("MODEL",),
"clip_opta": ("CLIP",),
"clip_optb": ("CLIP",),
"clip_optc": ("CLIP",),
"vae_opta": ("VAE",),
"vae_optb": ("VAE",),
},
"required": {},
}
RETURN_TYPES = (
"MODEL",
"CLIP",
"VAE",
"INT",
)
RETURN_NAMES = (
"MODEL",
"CLIP",
"VAE",
"MODEL_TYPE",
)
FUNCTION = "checkckpt"
CATEGORY = SELECTOR_CATEGORY_PATH
DESCRIPTION = RECOURSE_CHECK_DESC
def checkckpt(self, model_opta=None, model_optb=None, clip_opta=None, clip_optb=None, clip_optc=None, vae_opta=None, vae_optb=None, model_type=1):
model_out = next((model for model in [model_opta, model_optb] if model), None)
clip_out = next((clip for clip in [clip_opta, clip_optb, clip_optc] if clip), None)
vae_out = next((vae for vae in [vae_opta, vae_optb] if vae), None)
model_id = type(model_out.model).__name__
model_type = MODEL_TO_TYPE.get(model_id)
print(f"Model configuration {model_id}, Selected output {model_type}")
return (
model_out,
clip_out,
vae_out,
model_type,
)
class RecourseStrings:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"model_1": (
"STRING",
{
"multiline": True,
"default": "",
"dynamicPrompts": True,
},
),
},
"optional": {
"model_2": (
"STRING",
{
"multiline": True,
"default": "",
"dynamicPrompts": True,
},
),
"model_3": (
"STRING",
{
"multiline": True,
"default": "",
"dynamicPrompts": True,
},
),
"seed": ("INT", {"default": 0, "min": 0, "max": 0xFFFFFFFE}),
"noise_seed": ("INT", {"default": 0, "min": 0, "max": 0xFFFFFFFFFFFFFFFF}),
},
}
RETURN_TYPES = (
"STRING",
"STRING",
"STRING",
"STRING",
"INT",
"INT",
)
RETURN_NAMES = (
"LINKED_STRINGS",
"MODEL_1",
"MODEL_2",
"MODEL_3",
"SEED",
"NOISE_SEED",
)
FUNCTION = "recourse_string"
CATEGORY = SELECTOR_CATEGORY_PATH
def recourse_string(
self,
seed,
noise_seed,
model_1,
model_2="",
model_3="",
):
data = [model_1, model_2, model_3]
full_out = "".join(data)
return (
full_out,
model_1,
model_2,
model_3,
seed,
noise_seed,
)
NODE_CLASS_MAPPINGS = {
"Selector": Selectah,
"RecourseCkpt": RecourseCheckpoint,
"RecourseStrings": RecourseStrings,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"Selector": "Selector...",
"RecourseCkpt": "RecourseCheck...",
"RecourseStrings": "RecourseStrings...",
}