import logging from .base_node import ( SupersideFalNode, ImageProcessingMixin, APIClientMixin, API_KEY_INPUT_SPEC, ) logger = logging.getLogger(__name__) class SupersideBriaReplaceBackgroundNode( SupersideFalNode, ImageProcessingMixin, APIClientMixin ): """ Bria Replace Background V2 Node: Replace an image's background using Bria's Replace Background V2 model on fal.ai (endpoint id "bria/replace-background" - note there is no "fal-ai/" prefix on this one; the prefixed form 404s). Generates a new background with realistic lighting and perspective from a text prompt, trained on licensed data for commercial use. """ @classmethod def INPUT_TYPES(cls): return { "required": { "image": ("IMAGE",), "prompt": ( "STRING", { "multiline": True, "placeholder": "Describe the new background scene", }, ), "api_key": API_KEY_INPUT_SPEC, }, "optional": { "negative_prompt": ( "STRING", { "multiline": True, "default": "", "placeholder": "Describe what to avoid in the background", }, ), "steps_num": ("INT", {"default": 30, "min": 1, "max": 100}), "seed": ("INT", {"default": -1, "min": -1, "max": 2147483647, "tooltip": "-1 = random"}), "sync_mode": ("BOOLEAN", {"default": False}), }, "hidden": { "unique_id": "UNIQUE_ID", "extra_pnginfo": "EXTRA_PNGINFO", }, } RETURN_TYPES = ("IMAGE", "STRING") RETURN_NAMES = ("image", "info") FUNCTION = "generate" OUTPUT_NODE = True DESCRIPTION = ( "Replace an image's background using Bria Replace Background V2 on " "fal.ai (endpoint bria/replace-background). Generates realistic " "lighting and perspective from a text prompt." ) def prepare_arguments(self, client, image, prompt, **kwargs): image_url = self.upload_image(client, image) arguments = { "image_url": image_url, "prompt": prompt, } if kwargs.get("negative_prompt"): arguments["negative_prompt"] = kwargs["negative_prompt"] if kwargs.get("steps_num") is not None: arguments["steps_num"] = kwargs["steps_num"] seed = kwargs.get("seed", -1) if seed is not None and seed != -1: arguments["seed"] = seed if kwargs.get("sync_mode") is not None: arguments["sync_mode"] = kwargs["sync_mode"] return arguments def generate(self, image, prompt, api_key, unique_id=None, extra_pnginfo=None, **kwargs): try: client = self.get_client(api_key) arguments = self.prepare_arguments(client, image, prompt, **kwargs) result = self.call_api(client, "bria/replace-background", arguments) # This endpoint returns a single "image" object, not an "images" list. image_data = result.get("image") if not isinstance(image_data, dict) or "url" not in image_data: raise RuntimeError("No image was generated by the API.") images = self.process_images({"images": [image_data]}) info = image_data.get("url", "") if unique_id is not None and extra_pnginfo is not None: if ( isinstance(extra_pnginfo, list) and isinstance(extra_pnginfo[0], dict) and "workflow" in extra_pnginfo[0] ): workflow = extra_pnginfo[0]["workflow"] node = next( (x for x in workflow["nodes"] if str(x["id"]) == str(unique_id)), None, ) if node: node["widgets_values"] = [info] return {"ui": {"text": [info]}, "result": (images[0], info)} except Exception as e: logger.error(f"Bria replace background failed: {str(e)}") raise RuntimeError(f"Bria replace background failed: {str(e)}") from e