From 98ad05f11b33acc3cbda6ee453c9cc5c85510abc Mon Sep 17 00:00:00 2001 From: Sirshendu Ganguly Date: Mon, 22 Dec 2025 12:58:34 -0500 Subject: [PATCH] Add Bria Provider Key Points node and registration files --- __init__.py | 2 + clientlibs/main.js | 4 +- clientlibs/types.js | 5 + clientlibs/utils.js | 100 +++++++++++ .../providerSettings/briaProviderKeyPoints.py | 169 ++++++++++++++++++ 5 files changed, 279 insertions(+), 1 deletion(-) create mode 100644 modules/providerSettings/briaProviderKeyPoints.py diff --git a/__init__.py b/__init__.py index 8657554..4cb9bab 100644 --- a/__init__.py +++ b/__init__.py @@ -32,6 +32,7 @@ from .modules.providerSettings.pixverseProviderSettings import RunwarePixversePr from .modules.providerSettings.openaiProviderSettings import RunwareOpenAIProviderSettings from .modules.providerSettings.bytedanceImageProviderSettings import RunwareBytedanceProviderSettings from .modules.providerSettings.briaImageProviderSettings import RunwareBriaProviderSettings +from .modules.providerSettings.briaProviderKeyPoints import RunwareBriaProviderKeyPoints from .modules.audioInference import RunwareAudioInference from .modules.audioModelSearch import RunwareAudioModelSearch from .modules.audioSections import RunwareAudioSections @@ -113,6 +114,7 @@ NODE_CLASS_MAPPINGS = { "Runware OpenAI Provider Settings": RunwareOpenAIProviderSettings, "Runware Bytedance Provider Settings": RunwareBytedanceProviderSettings, "Runware Bria Provider Settings": RunwareBriaProviderSettings, + "Runware Bria Provider Key Points": RunwareBriaProviderKeyPoints, "Runware Audio Inference": RunwareAudioInference, "Runware Audio Model Search": RunwareAudioModelSearch, "Runware Audio Sections": RunwareAudioSections, diff --git a/clientlibs/main.js b/clientlibs/main.js index b7ffd31..19f4a0e 100644 --- a/clientlibs/main.js +++ b/clientlibs/main.js @@ -1,6 +1,6 @@ import { app } from "../../scripts/app.js"; import { api } from "../../scripts/api.js"; -import { promptEnhanceHandler, syncDimensionsNodeHandler, searchNodeHandler, APIKeyHandler, captionNodeHandler, mediaUUIDHandler, videoTranscriptionHandler, handleCustomErrors, videoInferenceDimensionsHandler, videoModelSearchFilterHandler, audioModelSearchFilterHandler, useParameterToggleHandler, imageInferenceToggleHandler, upscalerToggleHandler, videoUpscalerToggleHandler, audioInferenceToggleHandler, acceleratorOptionsToggleHandler, bytedanceProviderSettingsToggleHandler, openaiProviderSettingsToggleHandler, lightricksProviderSettingsToggleHandler, klingProviderSettingsToggleHandler, lumaProviderSettingsToggleHandler, briaProviderSettingsToggleHandler, pixverseProviderSettingsToggleHandler, alibabaProviderSettingsToggleHandler, googleProviderSettingsToggleHandler, syncProviderSettingsToggleHandler, syncSegmentToggleHandler, settingsToggleHandler, audioInputToggleHandler, speechInputToggleHandler } from "./utils.js"; +import { promptEnhanceHandler, syncDimensionsNodeHandler, searchNodeHandler, APIKeyHandler, captionNodeHandler, mediaUUIDHandler, videoTranscriptionHandler, handleCustomErrors, videoInferenceDimensionsHandler, videoModelSearchFilterHandler, audioModelSearchFilterHandler, useParameterToggleHandler, imageInferenceToggleHandler, upscalerToggleHandler, videoUpscalerToggleHandler, audioInferenceToggleHandler, acceleratorOptionsToggleHandler, bytedanceProviderSettingsToggleHandler, openaiProviderSettingsToggleHandler, lightricksProviderSettingsToggleHandler, klingProviderSettingsToggleHandler, lumaProviderSettingsToggleHandler, briaProviderSettingsToggleHandler, pixverseProviderSettingsToggleHandler, alibabaProviderSettingsToggleHandler, googleProviderSettingsToggleHandler, syncProviderSettingsToggleHandler, syncSegmentToggleHandler, settingsToggleHandler, audioInputToggleHandler, speechInputToggleHandler, briaProviderKeyPointsToggleHandler } from "./utils.js"; import { RUNWARE_NODE_TYPES, RUNWARE_NODE_PROPS, SEARCH_TERMS } from "./types.js"; const nodeInitList = []; @@ -85,6 +85,8 @@ app.registerExtension({ audioInputToggleHandler(node); } else if(nodeClass === RUNWARE_NODE_TYPES.SPEECHINPUT) { speechInputToggleHandler(node); + } else if(nodeClass === RUNWARE_NODE_TYPES.BRIAPROVIDERKEYPOINTS) { + briaProviderKeyPointsToggleHandler(node); } if(crNodeProps.colorModeOnly === true) return; diff --git a/clientlibs/types.js b/clientlibs/types.js index 6bd447b..7711a35 100644 --- a/clientlibs/types.js +++ b/clientlibs/types.js @@ -86,6 +86,7 @@ const RUNWARE_NODE_TYPES = { OPENAIPROVIDERSETTINGS: "Runware OpenAI Provider Settings", BYTEDANCEPROVIDERSETTINGS: "Runware Bytedance Provider Settings", BRIAPROVIDERSETTINGS: "Runware Bria Provider Settings", + BRIAPROVIDERKEYPOINTS: "Runware Bria Provider Key Points", ELEVENLABSPROVIDERSETTINGS: "Runware ElevenLabs Provider Settings", ELEVENLABSPROVIDERSETTINGSSECTIONS: "Runware ElevenLabs Provider Settings Sections", VIDUPROVIDERSETTINGS: "Runware Vidu Provider Settings", @@ -275,6 +276,10 @@ const RUNWARE_NODE_PROPS = { bgColor: DEFAULT_BGCOLOR, colorModeOnly: true, }, + [RUNWARE_NODE_TYPES.BRIAPROVIDERKEYPOINTS]: { + bgColor: DEFAULT_BGCOLOR, + colorModeOnly: true, + }, [RUNWARE_NODE_TYPES.ELEVENLABSPROVIDERSETTINGS]: { bgColor: DEFAULT_BGCOLOR, colorModeOnly: true, diff --git a/clientlibs/utils.js b/clientlibs/utils.js index 873eb78..fbb913e 100644 --- a/clientlibs/utils.js +++ b/clientlibs/utils.js @@ -1737,6 +1737,9 @@ function videoModelSearchFilterHandler(videoModelSearchNode) { "sync:lipsync-2-pro@1 (Sync LipSync 2 Pro)", "sync:react-1@1 (Sync React-1)", ], + "Bria": [ + "bria:60@1 (Bria Video Eraser)", + ], }; const MODEL_DIMENSIONS = { @@ -1798,6 +1801,7 @@ function videoModelSearchFilterHandler(videoModelSearchNode) { "sync:lipsync-2@1": {"width": 0, "height": 0}, "sync:lipsync-2-pro@1": {"width": 0, "height": 0}, "sync:react-1@1": {"width": 0, "height": 0}, + "bria:60@1": {"width": 1280, "height": 720}, }; const MODEL_RESOLUTIONS = { @@ -1859,6 +1863,7 @@ function videoModelSearchFilterHandler(videoModelSearchNode) { "sync:lipsync-2@1": "720p", "sync:lipsync-2-pro@1": "720p", "sync:react-1@1": "720p", + "bria:60@1": "720p", }; const DEFAULT_DIMENSIONS = {"width": 1024, "height": 576}; @@ -2418,6 +2423,14 @@ function briaProviderSettingsToggleHandler(briaNode) { const originalQualityWidget = briaNode.widgets.find(w => w.name === "originalQuality"); const useForceBackgroundDetectionWidget = briaNode.widgets.find(w => w.name === "useForceBackgroundDetection"); const forceBackgroundDetectionWidget = briaNode.widgets.find(w => w.name === "forceBackgroundDetection"); + const useAutoTrimWidget = briaNode.widgets.find(w => w.name === "useAutoTrim"); + const autoTrimWidget = briaNode.widgets.find(w => w.name === "autoTrim"); + const useMaskForegroundWidget = briaNode.widgets.find(w => w.name === "useMaskForeground"); + const maskForegroundWidget = briaNode.widgets.find(w => w.name === "maskForeground"); + const useMaskPromptWidget = briaNode.widgets.find(w => w.name === "useMaskPrompt"); + const maskPromptWidget = briaNode.widgets.find(w => w.name === "maskPrompt"); + const useMaskFrameIndexWidget = briaNode.widgets.find(w => w.name === "useMaskFrameIndex"); + const maskFrameIndexWidget = briaNode.widgets.find(w => w.name === "maskFrameIndex"); // Helper function to toggle widget enabled state (exact same pattern) function toggleWidgetState(useWidget, paramWidget, paramName) { @@ -2521,6 +2534,22 @@ function briaProviderSettingsToggleHandler(briaNode) { if (useForceBackgroundDetectionWidget && forceBackgroundDetectionWidget) { toggleWidgetState(useForceBackgroundDetectionWidget, forceBackgroundDetectionWidget, "forceBackgroundDetection"); } + + if (useAutoTrimWidget && autoTrimWidget) { + toggleWidgetState(useAutoTrimWidget, autoTrimWidget, "autoTrim"); + } + + if (useMaskForegroundWidget && maskForegroundWidget) { + toggleWidgetState(useMaskForegroundWidget, maskForegroundWidget, "maskForeground"); + } + + if (useMaskPromptWidget && maskPromptWidget) { + toggleWidgetState(useMaskPromptWidget, maskPromptWidget, "maskPrompt"); + } + + if (useMaskFrameIndexWidget && maskFrameIndexWidget) { + toggleWidgetState(useMaskFrameIndexWidget, maskFrameIndexWidget, "maskFrameIndex"); + } } function audioInputToggleHandler(audioInputNode) { @@ -2637,6 +2666,76 @@ function speechInputToggleHandler(speechInputNode) { } } +function briaProviderKeyPointsToggleHandler(keyPointsNode) { + // Helper function to toggle widget enabled state for multiple widgets + function toggleWidgetState(useWidget, paramWidgets, paramNames) { + if (!useWidget || !paramWidgets || paramWidgets.length === 0) return; + + function toggleEnabled() { + const enabled = useWidget.value === true; + + paramWidgets.forEach((paramWidget, idx) => { + if (!paramWidget) return; + + if (paramWidget.inputEl) { + paramWidget.inputEl.disabled = !enabled; + paramWidget.inputEl.style.opacity = enabled ? "1" : "0.5"; + paramWidget.inputEl.style.cursor = enabled ? "text" : "not-allowed"; + paramWidget.inputEl.readOnly = !enabled; + } + + // Handle dropdown widgets (for Type fields) + if (paramWidget.options && paramWidget.options.element) { + paramWidget.options.element.disabled = !enabled; + paramWidget.options.element.style.opacity = enabled ? "1" : "0.5"; + paramWidget.options.element.style.pointerEvents = enabled ? "auto" : "none"; + } + + paramWidget.disabled = !enabled; + + // Fallback: try to find inputs via DOM if inputEl is not available + if (!paramWidget.inputEl && paramNames[idx]) { + const nodeElement = keyPointsNode.htmlElements?.widgetsContainer || keyPointsNode.htmlElements; + if (nodeElement) { + const input = nodeElement.querySelector(`input[name="${paramNames[idx]}"], textarea[name="${paramNames[idx]}"], select[name="${paramNames[idx]}"]`); + if (input) { + input.disabled = !enabled; + input.style.opacity = enabled ? "1" : "0.5"; + input.style.cursor = enabled ? "text" : "not-allowed"; + input.readOnly = !enabled; + if (input.tagName === "SELECT") { + input.style.pointerEvents = enabled ? "auto" : "none"; + } + } + } + } + }); + + keyPointsNode.setDirtyCanvas(true); + } + + // Set up callback + appendWidgetCB(useWidget, () => { + setTimeout(toggleEnabled, 50); + }); + + // Initial call to set initial state + setTimeout(toggleEnabled, 100); + } + + // Set up toggle handlers for all 6 key points + for (let i = 1; i <= 6; i++) { + const useWidget = keyPointsNode.widgets.find(w => w.name === `use_${i}`); + const xWidget = keyPointsNode.widgets.find(w => w.name === `X${i}`); + const yWidget = keyPointsNode.widgets.find(w => w.name === `Y${i}`); + const typeWidget = keyPointsNode.widgets.find(w => w.name === `Type${i}`); + + if (useWidget && xWidget && yWidget && typeWidget) { + toggleWidgetState(useWidget, [xWidget, yWidget, typeWidget], [`X${i}`, `Y${i}`, `Type${i}`]); + } + } +} + export { notifyUser, promptEnhanceHandler, @@ -2670,6 +2769,7 @@ export { settingsToggleHandler, audioInputToggleHandler, speechInputToggleHandler, + briaProviderKeyPointsToggleHandler, }; function googleProviderSettingsToggleHandler(googleNode) { diff --git a/modules/providerSettings/briaProviderKeyPoints.py b/modules/providerSettings/briaProviderKeyPoints.py new file mode 100644 index 0000000..15f2c5d --- /dev/null +++ b/modules/providerSettings/briaProviderKeyPoints.py @@ -0,0 +1,169 @@ +""" +Runware Bria Provider Key Points Node +Creates key points configuration for Bria mask settings +""" + +from typing import Optional, Dict, Any, List + + +class RunwareBriaProviderKeyPoints: + """Runware Bria Provider Key Points Node""" + + @classmethod + def INPUT_TYPES(cls): + return { + "required": {}, + "optional": { + "use_1": ("BOOLEAN", { + "tooltip": "Enable to include the first key point in the mask configuration.", + "default": False, + }), + "X1": ("INT", { + "tooltip": "X-coordinate of the first key point on the frame", + "default": 0, + "min": 0, + }), + "Y1": ("INT", { + "tooltip": "Y-coordinate of the first key point on the frame", + "default": 0, + "min": 0, + }), + "Type1": (["positive", "negative"], { + "tooltip": "Type of mask hint for first key point. 'positive' includes area, 'negative' excludes area.", + "default": "positive", + }), + "use_2": ("BOOLEAN", { + "tooltip": "Enable to include the second key point in the mask configuration.", + "default": False, + }), + "X2": ("INT", { + "tooltip": "X-coordinate of the second key point on the frame", + "default": 0, + "min": 0, + }), + "Y2": ("INT", { + "tooltip": "Y-coordinate of the second key point on the frame", + "default": 0, + "min": 0, + }), + "Type2": (["positive", "negative"], { + "tooltip": "Type of mask hint for second key point. 'positive' includes area, 'negative' excludes area.", + "default": "positive", + }), + "use_3": ("BOOLEAN", { + "tooltip": "Enable to include the third key point in the mask configuration.", + "default": False, + }), + "X3": ("INT", { + "tooltip": "X-coordinate of the third key point on the frame", + "default": 0, + "min": 0, + }), + "Y3": ("INT", { + "tooltip": "Y-coordinate of the third key point on the frame", + "default": 0, + "min": 0, + }), + "Type3": (["positive", "negative"], { + "tooltip": "Type of mask hint for third key point. 'positive' includes area, 'negative' excludes area.", + "default": "positive", + }), + "use_4": ("BOOLEAN", { + "tooltip": "Enable to include the fourth key point in the mask configuration.", + "default": False, + }), + "X4": ("INT", { + "tooltip": "X-coordinate of the fourth key point on the frame", + "default": 0, + "min": 0, + }), + "Y4": ("INT", { + "tooltip": "Y-coordinate of the fourth key point on the frame", + "default": 0, + "min": 0, + }), + "Type4": (["positive", "negative"], { + "tooltip": "Type of mask hint for fourth key point. 'positive' includes area, 'negative' excludes area.", + "default": "positive", + }), + "use_5": ("BOOLEAN", { + "tooltip": "Enable to include the fifth key point in the mask configuration.", + "default": False, + }), + "X5": ("INT", { + "tooltip": "X-coordinate of the fifth key point on the frame", + "default": 0, + "min": 0, + }), + "Y5": ("INT", { + "tooltip": "Y-coordinate of the fifth key point on the frame", + "default": 0, + "min": 0, + }), + "Type5": (["positive", "negative"], { + "tooltip": "Type of mask hint for fifth key point. 'positive' includes area, 'negative' excludes area.", + "default": "positive", + }), + "use_6": ("BOOLEAN", { + "tooltip": "Enable to include the sixth key point in the mask configuration.", + "default": False, + }), + "X6": ("INT", { + "tooltip": "X-coordinate of the sixth key point on the frame", + "default": 0, + "min": 0, + }), + "Y6": ("INT", { + "tooltip": "Y-coordinate of the sixth key point on the frame", + "default": 0, + "min": 0, + }), + "Type6": (["positive", "negative"], { + "tooltip": "Type of mask hint for sixth key point. 'positive' includes area, 'negative' excludes area.", + "default": "positive", + }), + } + } + + RETURN_TYPES = ("RUNWAREBRIAPROVIDERKEYPOINTS",) + RETURN_NAMES = ("Key Points",) + FUNCTION = "createKeyPoints" + CATEGORY = "Runware/Provider Settings" + DESCRIPTION = "Create key points configuration for Bria mask settings. Each key point defines a coordinate (x, y) and type (positive/negative) to guide mask generation." + + def createKeyPoints(self, **kwargs) -> tuple[List[Dict[str, Any]]]: + """Create key points array from provided parameters""" + + keyPoints: List[Dict[str, Any]] = [] + + # Process up to 6 key points + for i in range(1, 7): + use_key = kwargs.get(f"use_{i}", False) + + # Only process key point if use toggle is enabled + if use_key: + x = kwargs.get(f"X{i}", 0) + y = kwargs.get(f"Y{i}", 0) + point_type = kwargs.get(f"Type{i}", "positive") + + # Only add key point if both x and y are provided and at least one is non-zero + # This allows users to leave unused key points at 0,0 + if x > 0 or y > 0: + keyPoints.append({ + "x": x, + "y": y, + "type": point_type + }) + + return (keyPoints,) + + +# Node class mappings +NODE_CLASS_MAPPINGS = { + "RunwareBriaProviderKeyPoints": RunwareBriaProviderKeyPoints, +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "RunwareBriaProviderKeyPoints": "Runware Bria Provider Key Points", +} +