diff --git a/external.py b/external.py new file mode 100644 index 0000000..21a9d08 --- /dev/null +++ b/external.py @@ -0,0 +1,73 @@ +from .autonode import node_wrapper, get_node_names_mappings, validate, anytype +from .imgio.converter import PILHandlingHodes +from .webuiapi.out_api import get_image_from_prompt, get_image_from_prompt_fallback + +from PIL import Image + + +external_classes = [] +external_nodes = node_wrapper(external_classes) + + +@external_nodes + +class SDWebuiAPINode: + FUNCTION = "get_image_from_prompt" + RETURN_TYPES = ("IMAGE",) + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "prompt": ("STRING", {"default": ""}), + "api_endpoint": ("STRING", {"default": ""}), + }, + "optional": { + "auth": ("STRING", {"default": ""}), + "seed": ("INT", {"default": -1}), + "negative_prompt": ("STRING", {"default": ""}), + "steps": ("INT", {"default": 28}), + "width": ("INT", {"default": 1024}), + "height": ("INT", {"default": 1024}), + "hr_scale": ("FLOAT", {"default": 1.5}), + "hr_upscale": ("STRING", {"default": "Latent"}), + "enable_hr": ("BOOL", {"default": False}), + "cfg_scale": ("INT", {"default": 7}), + } + } + CATEGORY = "WebUI API" + custom_name = "Get Image From Prompt" + @PILHandlingHodes.output_wrapper + def get_image_from_prompt(self, prompt, api_endpoint, auth="", seed=-1, negative_prompt="", steps=28, width=1024, height=1024, hr_scale=1.5, hr_upscale="Latent", enable_hr=False, cfg_scale=7): + return (get_image_from_prompt(prompt, api_endpoint, auth, seed, negative_prompt, steps, width, height, hr_scale, hr_upscale, enable_hr, cfg_scale)[0],) +@external_nodes +class SDWebuiAPIFallbackNode: + FUNCTION = "get_image_from_prompt_fallback" + RETURN_TYPES = ("IMAGE",) + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "prompt": ("STRING", {"default": ""}), + "api_endpoint": ("STRING", {"default": ""}), + }, + "optional": { + "auth": ("STRING", {"default": ""}), + "seed": ("INT", {"default": -1}), + "negative_prompt": ("STRING", {"default": ""}), + "steps": ("INT", {"default": 28}), + "width": ("INT", {"default": 1024}), + "height": ("INT", {"default": 1024}), + "hr_scale": ("FLOAT", {"default": 1.5}), + "hr_upscale": ("STRING", {"default": "Latent"}), + "enable_hr": ("BOOL", {"default": False}), + "cfg_scale": ("INT", {"default": 7}), + } + } + CATEGORY = "WebUI API" + custom_name = "Get Image From Prompt (Fallback)" + @PILHandlingHodes.output_wrapper + def get_image_from_prompt_fallback(self, prompt, api_endpoint, auth="", seed=-1, negative_prompt="", steps=28, width=1024, height=1024, hr_scale=1.5, hr_upscale="Latent", enable_hr=False, cfg_scale=7): + return (get_image_from_prompt_fallback(prompt, api_endpoint, auth, seed, negative_prompt, steps, width, height, hr_scale, hr_upscale, enable_hr, cfg_scale)[0],) + +CLASS_MAPPINGS, CLASS_NAMES = get_node_names_mappings(external_classes) +validate(external_classes) \ No newline at end of file diff --git a/nodes.py b/nodes.py index b1fd889..851e237 100644 --- a/nodes.py +++ b/nodes.py @@ -1,24 +1,28 @@ - -from .logic_gates import CLASS_MAPPINGS as LogicMapping, CLASS_NAMES as LogicNames -from .randomness import CLASS_MAPPINGS as RandomMapping, CLASS_NAMES as RandomNames -from .conversion import CLASS_MAPPINGS as ConversionMapping, CLASS_NAMES as ConversionNames -from .math_nodes import CLASS_MAPPINGS as MathMapping, CLASS_NAMES as MathNames -from .io_node import CLASS_MAPPINGS as IOMapping, CLASS_NAMES as IONames - - -NODE_CLASS_MAPPINGS = { -} -NODE_CLASS_MAPPINGS.update(IOMapping) -NODE_CLASS_MAPPINGS.update(LogicMapping) -NODE_CLASS_MAPPINGS.update(RandomMapping) -NODE_CLASS_MAPPINGS.update(ConversionMapping) -NODE_CLASS_MAPPINGS.update(MathMapping) - -NODE_DISPLAY_NAME_MAPPINGS = { - -} -NODE_DISPLAY_NAME_MAPPINGS.update(IONames) -NODE_DISPLAY_NAME_MAPPINGS.update(LogicNames) -NODE_DISPLAY_NAME_MAPPINGS.update(RandomNames) -NODE_DISPLAY_NAME_MAPPINGS.update(ConversionNames) -NODE_DISPLAY_NAME_MAPPINGS.update(MathNames) + +from .logic_gates import CLASS_MAPPINGS as LogicMapping, CLASS_NAMES as LogicNames +from .randomness import CLASS_MAPPINGS as RandomMapping, CLASS_NAMES as RandomNames +from .conversion import CLASS_MAPPINGS as ConversionMapping, CLASS_NAMES as ConversionNames +from .math_nodes import CLASS_MAPPINGS as MathMapping, CLASS_NAMES as MathNames +from .io_node import CLASS_MAPPINGS as IOMapping, CLASS_NAMES as IONames + +from .external import CLASS_MAPPINGS as ExternalMapping, CLASS_NAMES as ExternalNames + + +NODE_CLASS_MAPPINGS = { +} +NODE_CLASS_MAPPINGS.update(IOMapping) +NODE_CLASS_MAPPINGS.update(LogicMapping) +NODE_CLASS_MAPPINGS.update(RandomMapping) +NODE_CLASS_MAPPINGS.update(ConversionMapping) +NODE_CLASS_MAPPINGS.update(MathMapping) +NODE_CLASS_MAPPINGS.update(ExternalMapping) + +NODE_DISPLAY_NAME_MAPPINGS = { + +} +NODE_DISPLAY_NAME_MAPPINGS.update(IONames) +NODE_DISPLAY_NAME_MAPPINGS.update(LogicNames) +NODE_DISPLAY_NAME_MAPPINGS.update(RandomNames) +NODE_DISPLAY_NAME_MAPPINGS.update(ConversionNames) +NODE_DISPLAY_NAME_MAPPINGS.update(MathNames) +NODE_DISPLAY_NAME_MAPPINGS.update(ExternalNames) \ No newline at end of file diff --git a/webuiapi/__init__.py b/webuiapi/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/webuiapi/out_api.py b/webuiapi/out_api.py new file mode 100644 index 0000000..0672f66 --- /dev/null +++ b/webuiapi/out_api.py @@ -0,0 +1,112 @@ +import requests +import json +from PIL import Image +import io +import base64 +from typing import Optional + + +def send_request(api_endpoint:str, auth:Optional[str], arguments:dict) -> list[Image.Image]: + session = requests.Session() + if auth: + session.auth = (auth.split(":")[0], auth.split(":")[1]) + api_endpoint = api_endpoint.rstrip("/") + "/sdapi/v1/txt2img" + response = session.post(api_endpoint, json=arguments) + response.raise_for_status() + response_json = response.json() + if "images" in response_json.keys(): + images = [Image.open(io.BytesIO(base64.b64decode(i))) for i in response_json["images"]] + elif "image" in response_json.keys(): + images = [Image.open(io.BytesIO(base64.b64decode(response_json["image"])))] + else: + raise ValueError("No image data in response") + return images + +def construct_args( + prompt:str, + seed:int=-1, + negative_prompt:Optional[str] = None, + steps:int = 28, + width:int = 1024, + height:int = 1024, + hr_scale:float = 1.5, + hr_upscale:str = "Latent", + enable_hr:bool = False, + cfg_scale:int = 7, +): + arguments = { + "prompt": prompt, + "seed": seed, + "steps": steps, + "width": width, + "height": height, + "hr_scale": hr_scale, + "hr_upscale": hr_upscale, + "enable_hr": enable_hr, + "cfg_scale": cfg_scale, + } + if negative_prompt: + arguments["negative_prompt"] = negative_prompt + else: + arguments["negative_prompt"] = "" + return arguments + +def get_image_from_prompt( + prompt:str, + api_endpoint:str, + auth:Optional[str]=None, + seed:int=-1, + negative_prompt:Optional[str] = None, + steps:int = 28, + width:int = 1024, + height:int = 1024, + hr_scale:float = 1.5, + hr_upscale:str = "Latent", + enable_hr:bool = False, + cfg_scale:int = 7, +): + arguments = construct_args( + prompt=prompt, + seed=seed, + negative_prompt=negative_prompt, + steps=steps, + width=width, + height=height, + hr_scale=hr_scale, + hr_upscale=hr_upscale, + enable_hr=enable_hr, + cfg_scale=cfg_scale, + ) + return send_request(api_endpoint, auth, arguments) + +def get_image_from_prompt_fallback( + prompt:str, + api_endpoint:str, + auth:Optional[str]=None, + seed:int=-1, + negative_prompt:Optional[str] = None, + steps:int = 28, + width:int = 1024, + height:int = 1024, + hr_scale:float = 1.5, + hr_upscale:str = "Latent", + enable_hr:bool = False, + cfg_scale:int = 7, +): + arguments = construct_args( + prompt=prompt, + seed=seed, + negative_prompt=negative_prompt, + steps=steps, + width=width, + height=height, + hr_scale=hr_scale, + hr_upscale=hr_upscale, + enable_hr=enable_hr, + cfg_scale=cfg_scale, + ) + try: + return send_request(api_endpoint, auth, arguments) + except Exception as e: + # create blank image + return [Image.new("RGB", (width, height), (255, 255, 255))]