Merge pull request #72 from Runware/feature-KlingReferenceVoices/wanAnimateAdvancedFeatureSettings
Add Wan Animate advanced feature settings, reference voices
This commit is contained in:
@@ -48,6 +48,7 @@ from .modules.audioInputCombine import RunwareAudioInputCombine
|
||||
from .modules.speechInput import RunwareSpeechInput
|
||||
from .modules.speechInputCombine import RunwareSpeechInputCombine
|
||||
from .modules.referenceVideos import referenceVideos
|
||||
from .modules.referenceVoices import referenceVoices
|
||||
from .modules.videoInferenceInputs import videoInferenceInputs
|
||||
from .modules.providerSettings.lightricksProviderSettings import RunwareLightricksProviderSettings
|
||||
from .modules.providerSettings.minimaxProviderSettings import RunwareMiniMaxProviderSettings
|
||||
@@ -68,6 +69,7 @@ from .modules.videoInputsFrame import RunwareVideoInputsFrameImages
|
||||
from .modules.safetyInputs import safetyInputs
|
||||
from .modules.settings import RunwareSettings
|
||||
from .modules.videoAdvancedFeatureInputs import videoAdvancedFeatureInputs
|
||||
from .modules.advancedFeatureSettings.wanAnimateAdvancedFeatureSettings import RunwareWanAnimateAdvancedFeatureSettings
|
||||
|
||||
RUNWARE_COMFYUI_VERSION = "1.4.0 Beta"
|
||||
|
||||
@@ -130,6 +132,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
"Runware Video Speech Input": RunwareSpeechInput,
|
||||
"Runware Speech Inputs Combine": RunwareSpeechInputCombine,
|
||||
"Runware Reference Videos": referenceVideos,
|
||||
"Runware Reference Voices": referenceVoices,
|
||||
"Runware Video Inference Inputs": videoInferenceInputs,
|
||||
"Runware Lightricks Provider Settings": RunwareLightricksProviderSettings,
|
||||
"Runware MiniMax Provider Settings": RunwareMiniMaxProviderSettings,
|
||||
@@ -150,6 +153,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
"Runware Safety Inputs": safetyInputs,
|
||||
"Runware Settings": RunwareSettings,
|
||||
"Runware Video Advanced Feature Inputs": videoAdvancedFeatureInputs,
|
||||
"Runware Wan Animate Advanced Feature Settings": RunwareWanAnimateAdvancedFeatureSettings,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
|
||||
+5
-1
@@ -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, briaProviderMaskToggleHandler } 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, briaProviderMaskToggleHandler, wanAnimateAdvancedFeatureSettingsToggleHandler, videoAdvancedFeatureInputsToggleHandler } from "./utils.js";
|
||||
import { RUNWARE_NODE_TYPES, RUNWARE_NODE_PROPS, SEARCH_TERMS } from "./types.js";
|
||||
|
||||
const nodeInitList = [];
|
||||
@@ -87,6 +87,10 @@ app.registerExtension({
|
||||
speechInputToggleHandler(node);
|
||||
} else if(nodeClass === RUNWARE_NODE_TYPES.BRIAPROVIDERMASK) {
|
||||
briaProviderMaskToggleHandler(node);
|
||||
} else if(nodeClass === RUNWARE_NODE_TYPES.WANANIMATEADVANCEDFEATURESETTINGS) {
|
||||
wanAnimateAdvancedFeatureSettingsToggleHandler(node);
|
||||
} else if(nodeClass === RUNWARE_NODE_TYPES.VIDEOADVANCEDFEATUREINPUTS) {
|
||||
videoAdvancedFeatureInputsToggleHandler(node);
|
||||
}
|
||||
|
||||
if(crNodeProps.colorModeOnly === true) return;
|
||||
|
||||
@@ -96,6 +96,7 @@ const RUNWARE_NODE_TYPES = {
|
||||
AUDIOINPUT: "Runware Video Audio Input",
|
||||
SPEECHINPUT: "Runware Video Speech Input",
|
||||
REFERENCEVIDEOS: "Runware Reference Videos",
|
||||
REFERENCEVOICES: "Runware Reference Voices",
|
||||
VIDEOINFERENCEINPUTS: "Runware Video Inference Inputs",
|
||||
LIGHTRICKSPROVIDERSETTINGS: "Runware Lightricks Provider Settings",
|
||||
KLINGPROVIDERSETTINGS: "Runware KlingAI Provider Settings",
|
||||
@@ -118,6 +119,7 @@ const RUNWARE_NODE_TYPES = {
|
||||
SAFETYINPUTS: "Runware Safety Inputs",
|
||||
SETTINGS: "Runware Settings",
|
||||
VIDEOADVANCEDFEATUREINPUTS: "Runware Video Advanced Feature Inputs",
|
||||
WANANIMATEADVANCEDFEATURESETTINGS: "Runware Wan Animate Advanced Feature Settings",
|
||||
};
|
||||
|
||||
const RUNWARE_NODE_PROPS = {
|
||||
@@ -316,6 +318,10 @@ const RUNWARE_NODE_PROPS = {
|
||||
bgColor: DEFAULT_BGCOLOR,
|
||||
colorModeOnly: true,
|
||||
},
|
||||
[RUNWARE_NODE_TYPES.REFERENCEVOICES]: {
|
||||
bgColor: DEFAULT_BGCOLOR,
|
||||
colorModeOnly: true,
|
||||
},
|
||||
[RUNWARE_NODE_TYPES.VIDEOINFERENCEINPUTS]: {
|
||||
bgColor: DEFAULT_BGCOLOR,
|
||||
colorModeOnly: true,
|
||||
@@ -405,6 +411,10 @@ const RUNWARE_NODE_PROPS = {
|
||||
bgColor: DEFAULT_BGCOLOR,
|
||||
colorModeOnly: true,
|
||||
},
|
||||
[RUNWARE_NODE_TYPES.WANANIMATEADVANCEDFEATURESETTINGS]: {
|
||||
bgColor: DEFAULT_BGCOLOR,
|
||||
colorModeOnly: true,
|
||||
},
|
||||
};
|
||||
|
||||
export {
|
||||
|
||||
@@ -1718,6 +1718,7 @@ function videoModelSearchFilterHandler(videoModelSearchNode) {
|
||||
"Wan": [
|
||||
"runware:200@1 (Wan 2.1 1.3B)", "runware:200@2 (Wan 2.1 14B)",
|
||||
"runware:200@6 (Wan 2.2)",
|
||||
"runware:200@8 (Wan 2.2 A14B Animate)",
|
||||
"alibaba:wan@2.6 (Wan 2.6)",
|
||||
],
|
||||
"OpenAI": [
|
||||
@@ -1794,6 +1795,7 @@ function videoModelSearchFilterHandler(videoModelSearchNode) {
|
||||
"runware:200@1": {"width": 853, "height": 480},
|
||||
"runware:200@2": {"width": 853, "height": 480},
|
||||
"runware:200@6": {"width": 1280, "height": 720},
|
||||
"runware:200@8": {"width": 1104, "height": 832},
|
||||
"alibaba:wan@2.6": {"width": 1280, "height": 720},
|
||||
"openai:3@1": {"width": 1280, "height": 720},
|
||||
"openai:3@0": {"width": 1280, "height": 720},
|
||||
@@ -1857,6 +1859,7 @@ function videoModelSearchFilterHandler(videoModelSearchNode) {
|
||||
"runware:200@1": "480p",
|
||||
"runware:200@2": "480p",
|
||||
"runware:200@6": "720p",
|
||||
"runware:200@8": "720p",
|
||||
"alibaba:wan@2.6": "720p",
|
||||
"openai:3@1": "720p",
|
||||
"openai:3@0": "720p",
|
||||
@@ -2127,6 +2130,10 @@ function klingProviderSettingsToggleHandler(klingNode) {
|
||||
const asmrModeWidget = klingNode.widgets.find(w => w.name === "asmrMode");
|
||||
const useKeepOriginalSoundWidget = klingNode.widgets.find(w => w.name === "useKeepOriginalSound");
|
||||
const keepOriginalSoundWidget = klingNode.widgets.find(w => w.name === "keepOriginalSound");
|
||||
const useSoundWidget = klingNode.widgets.find(w => w.name === "useSound");
|
||||
const soundWidget = klingNode.widgets.find(w => w.name === "sound");
|
||||
const useCharacterOrientationWidget = klingNode.widgets.find(w => w.name === "useCharacterOrientation");
|
||||
const characterOrientationWidget = klingNode.widgets.find(w => w.name === "characterOrientation");
|
||||
|
||||
// Helper function to toggle widget enabled state (EXACT same pattern as imageInferenceToggleHandler)
|
||||
function toggleWidgetState(useWidget, paramWidget, paramName) {
|
||||
@@ -2202,6 +2209,14 @@ function klingProviderSettingsToggleHandler(klingNode) {
|
||||
if (useKeepOriginalSoundWidget && keepOriginalSoundWidget) {
|
||||
toggleWidgetState(useKeepOriginalSoundWidget, keepOriginalSoundWidget, "keepOriginalSound");
|
||||
}
|
||||
|
||||
if (useSoundWidget && soundWidget) {
|
||||
toggleWidgetState(useSoundWidget, soundWidget, "sound");
|
||||
}
|
||||
|
||||
if (useCharacterOrientationWidget && characterOrientationWidget) {
|
||||
toggleWidgetState(useCharacterOrientationWidget, characterOrientationWidget, "characterOrientation");
|
||||
}
|
||||
}
|
||||
|
||||
function alibabaProviderSettingsToggleHandler(alibabaNode) {
|
||||
@@ -2752,6 +2767,203 @@ function briaProviderMaskToggleHandler(maskNode) {
|
||||
}
|
||||
}
|
||||
|
||||
function wanAnimateAdvancedFeatureSettingsToggleHandler(wanAnimateNode) {
|
||||
// Find widgets
|
||||
const useModeWidget = wanAnimateNode.widgets.find(w => w.name === "useMode");
|
||||
const modeWidget = wanAnimateNode.widgets.find(w => w.name === "mode");
|
||||
const useRetargetPoseWidget = wanAnimateNode.widgets.find(w => w.name === "useRetargetPose");
|
||||
const retargetPoseWidget = wanAnimateNode.widgets.find(w => w.name === "retargetPose");
|
||||
const usePrevSegCondFramesWidget = wanAnimateNode.widgets.find(w => w.name === "usePrevSegCondFrames");
|
||||
const prevSegCondFramesWidget = wanAnimateNode.widgets.find(w => w.name === "prevSegCondFrames");
|
||||
|
||||
// Helper function to toggle widget enabled state
|
||||
function toggleWidgetState(useWidget, paramWidget, paramName) {
|
||||
if (!useWidget || !paramWidget) return;
|
||||
|
||||
function toggleEnabled() {
|
||||
const enabled = useWidget.value === true;
|
||||
|
||||
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 mode field)
|
||||
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 && paramName) {
|
||||
const nodeElement = wanAnimateNode.htmlElements?.widgetsContainer || wanAnimateNode.htmlElements;
|
||||
if (nodeElement) {
|
||||
const input = nodeElement.querySelector(`input[name="${paramName}"], textarea[name="${paramName}"], select[name="${paramName}"]`);
|
||||
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";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wanAnimateNode.setDirtyCanvas(true);
|
||||
}
|
||||
|
||||
// Set up callback
|
||||
appendWidgetCB(useWidget, () => {
|
||||
setTimeout(toggleEnabled, 50);
|
||||
});
|
||||
|
||||
// Initial call to set initial state
|
||||
setTimeout(toggleEnabled, 100);
|
||||
}
|
||||
|
||||
// Set up toggle handler for mode
|
||||
if (useModeWidget && modeWidget) {
|
||||
toggleWidgetState(useModeWidget, modeWidget, "mode");
|
||||
}
|
||||
|
||||
// Set up toggle handler for retargetPose (with mode dependency)
|
||||
if (useRetargetPoseWidget && retargetPoseWidget) {
|
||||
function toggleRetargetPoseEnabled() {
|
||||
const useRetargetPoseEnabled = useRetargetPoseWidget.value === true;
|
||||
const modeValue = modeWidget ? modeWidget.value : "animate";
|
||||
// retargetPose is only supported for animate mode
|
||||
const enabled = useRetargetPoseEnabled && modeValue === "animate";
|
||||
|
||||
if (retargetPoseWidget.inputEl) {
|
||||
retargetPoseWidget.inputEl.disabled = !enabled;
|
||||
retargetPoseWidget.inputEl.style.opacity = enabled ? "1" : "0.5";
|
||||
retargetPoseWidget.inputEl.style.cursor = enabled ? "text" : "not-allowed";
|
||||
retargetPoseWidget.inputEl.readOnly = !enabled;
|
||||
}
|
||||
|
||||
retargetPoseWidget.disabled = !enabled;
|
||||
|
||||
// Fallback: try to find inputs via DOM
|
||||
if (!retargetPoseWidget.inputEl) {
|
||||
const nodeElement = wanAnimateNode.htmlElements?.widgetsContainer || wanAnimateNode.htmlElements;
|
||||
if (nodeElement) {
|
||||
const input = nodeElement.querySelector(`input[name="retargetPose"], textarea[name="retargetPose"], select[name="retargetPose"]`);
|
||||
if (input) {
|
||||
input.disabled = !enabled;
|
||||
input.style.opacity = enabled ? "1" : "0.5";
|
||||
input.style.cursor = enabled ? "text" : "not-allowed";
|
||||
input.readOnly = !enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wanAnimateNode.setDirtyCanvas(true);
|
||||
}
|
||||
|
||||
// Set up callback for useRetargetPose toggle
|
||||
appendWidgetCB(useRetargetPoseWidget, () => {
|
||||
setTimeout(toggleRetargetPoseEnabled, 50);
|
||||
});
|
||||
|
||||
// Set up callback for mode change (to disable retargetPose when mode is "replace")
|
||||
if (modeWidget) {
|
||||
appendWidgetCB(modeWidget, () => {
|
||||
setTimeout(toggleRetargetPoseEnabled, 50);
|
||||
});
|
||||
}
|
||||
|
||||
// Initial call to set initial state
|
||||
setTimeout(toggleRetargetPoseEnabled, 100);
|
||||
}
|
||||
|
||||
// Set up toggle handler for prevSegCondFrames
|
||||
if (usePrevSegCondFramesWidget && prevSegCondFramesWidget) {
|
||||
toggleWidgetState(usePrevSegCondFramesWidget, prevSegCondFramesWidget, "prevSegCondFrames");
|
||||
}
|
||||
}
|
||||
|
||||
function videoAdvancedFeatureInputsToggleHandler(advancedFeatureNode) {
|
||||
// Helper function to toggle widget enabled state
|
||||
function toggleWidgetState(useWidget, paramWidget, paramName) {
|
||||
if (!useWidget || !paramWidget) return;
|
||||
|
||||
function toggleEnabled() {
|
||||
const enabled = useWidget.value === true;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
paramWidget.disabled = !enabled;
|
||||
|
||||
// Fallback: try to find inputs via DOM if inputEl is not available
|
||||
if (!paramWidget.inputEl && paramName) {
|
||||
const nodeElement = advancedFeatureNode.htmlElements?.widgetsContainer || advancedFeatureNode.htmlElements;
|
||||
if (nodeElement) {
|
||||
const input = nodeElement.querySelector(`input[name="${paramName}"], textarea[name="${paramName}"], select[name="${paramName}"]`);
|
||||
if (input) {
|
||||
input.disabled = !enabled;
|
||||
input.style.opacity = enabled ? "1" : "0.5";
|
||||
input.style.cursor = enabled ? "text" : "not-allowed";
|
||||
input.readOnly = !enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
advancedFeatureNode.setDirtyCanvas(true);
|
||||
}
|
||||
|
||||
// Set up callback
|
||||
appendWidgetCB(useWidget, () => {
|
||||
setTimeout(toggleEnabled, 50);
|
||||
});
|
||||
|
||||
// Initial call to set initial state
|
||||
setTimeout(toggleEnabled, 100);
|
||||
}
|
||||
|
||||
// Set up toggle handlers for each parameter
|
||||
const useVideoCFGScaleWidget = advancedFeatureNode.widgets.find(w => w.name === "useVideoCFGScale");
|
||||
const videoCFGScaleWidget = advancedFeatureNode.widgets.find(w => w.name === "videoCFGScale");
|
||||
if (useVideoCFGScaleWidget && videoCFGScaleWidget) {
|
||||
toggleWidgetState(useVideoCFGScaleWidget, videoCFGScaleWidget, "videoCFGScale");
|
||||
}
|
||||
|
||||
const useAudioCFGScaleWidget = advancedFeatureNode.widgets.find(w => w.name === "useAudioCFGScale");
|
||||
const audioCFGScaleWidget = advancedFeatureNode.widgets.find(w => w.name === "audioCFGScale");
|
||||
if (useAudioCFGScaleWidget && audioCFGScaleWidget) {
|
||||
toggleWidgetState(useAudioCFGScaleWidget, audioCFGScaleWidget, "audioCFGScale");
|
||||
}
|
||||
|
||||
const useVideoNegativePromptWidget = advancedFeatureNode.widgets.find(w => w.name === "useVideoNegativePrompt");
|
||||
const videoNegativePromptWidget = advancedFeatureNode.widgets.find(w => w.name === "videoNegativePrompt");
|
||||
if (useVideoNegativePromptWidget && videoNegativePromptWidget) {
|
||||
toggleWidgetState(useVideoNegativePromptWidget, videoNegativePromptWidget, "videoNegativePrompt");
|
||||
}
|
||||
|
||||
const useAudioNegativePromptWidget = advancedFeatureNode.widgets.find(w => w.name === "useAudioNegativePrompt");
|
||||
const audioNegativePromptWidget = advancedFeatureNode.widgets.find(w => w.name === "audioNegativePrompt");
|
||||
if (useAudioNegativePromptWidget && audioNegativePromptWidget) {
|
||||
toggleWidgetState(useAudioNegativePromptWidget, audioNegativePromptWidget, "audioNegativePrompt");
|
||||
}
|
||||
|
||||
const useSlgLayerWidget = advancedFeatureNode.widgets.find(w => w.name === "useSlgLayer");
|
||||
const slgLayerWidget = advancedFeatureNode.widgets.find(w => w.name === "slgLayer");
|
||||
if (useSlgLayerWidget && slgLayerWidget) {
|
||||
toggleWidgetState(useSlgLayerWidget, slgLayerWidget, "slgLayer");
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
notifyUser,
|
||||
promptEnhanceHandler,
|
||||
@@ -2786,6 +2998,8 @@ export {
|
||||
audioInputToggleHandler,
|
||||
speechInputToggleHandler,
|
||||
briaProviderMaskToggleHandler,
|
||||
wanAnimateAdvancedFeatureSettingsToggleHandler,
|
||||
videoAdvancedFeatureInputsToggleHandler,
|
||||
};
|
||||
|
||||
function googleProviderSettingsToggleHandler(googleNode) {
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# Advanced Feature Settings module
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
"""
|
||||
Runware Wan Animate Advanced Feature Settings Node
|
||||
Provides Wan Animate-specific settings for video generation
|
||||
"""
|
||||
|
||||
from typing import Optional, Dict, Any
|
||||
|
||||
class RunwareWanAnimateAdvancedFeatureSettings:
|
||||
"""Runware Wan Animate Advanced Feature Settings Node"""
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {},
|
||||
"optional": {
|
||||
"useMode": ("BOOLEAN", {
|
||||
"tooltip": "Enable to include mode parameter in API request.",
|
||||
"default": True,
|
||||
}),
|
||||
"mode": (["animate", "replace"], {
|
||||
"tooltip": "Animation mode: 'animate' uses pose detector and skeleton retargeting to animate a character from a reference image. 'replace' uses pose detector and segmentation model to replace a character in existing footage. Only used when 'Use Mode' is enabled.",
|
||||
"default": "animate",
|
||||
}),
|
||||
"useRetargetPose": ("BOOLEAN", {
|
||||
"tooltip": "Enable to include retargetPose parameter in API request. Only available for animate mode.",
|
||||
"default": False,
|
||||
}),
|
||||
"retargetPose": ("BOOLEAN", {
|
||||
"tooltip": "Retarget the pose of video image to match the reference image initial pose. That is achieved moving the hands' bones according to video. Means if the character from video moves his hands it will take the initial reference image pose and change the position of bones according to video movements. Supported only for animate mode. Only used when 'Use Retarget Pose' is enabled.",
|
||||
"default": False,
|
||||
"label_on": "Enabled",
|
||||
"label_off": "Disabled",
|
||||
}),
|
||||
"usePrevSegCondFrames": ("BOOLEAN", {
|
||||
"tooltip": "Enable to include prevSegCondFrames parameter in API request.",
|
||||
"default": False,
|
||||
}),
|
||||
"prevSegCondFrames": ("INT", {
|
||||
"tooltip": "How many frames to take from previous segment to keep temporal consistency. Less -> less consistent between the segments. More -> more time for inference. Only used when 'Use Prev Seg Cond Frames' is enabled.",
|
||||
"default": 1,
|
||||
"min": 1,
|
||||
"max": 5,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("RUNWAREWANANIMATEADVANCEDFEATURESETTINGS",)
|
||||
RETURN_NAMES = ("Wan Animate Advanced Feature Settings",)
|
||||
FUNCTION = "create_wan_animate_settings"
|
||||
CATEGORY = "Runware/Advanced Feature Settings"
|
||||
DESCRIPTION = "Configure Wan Animate advanced feature settings including mode (animate/replace), pose retargeting, and previous segment conditioning frames for character animation and replacement."
|
||||
|
||||
def create_wan_animate_settings(self, **kwargs) -> tuple[Dict[str, Any]]:
|
||||
"""Create Wan Animate advanced feature settings"""
|
||||
|
||||
# Get use parameters
|
||||
useMode = kwargs.get("useMode", True)
|
||||
useRetargetPose = kwargs.get("useRetargetPose", False)
|
||||
usePrevSegCondFrames = kwargs.get("usePrevSegCondFrames", False)
|
||||
|
||||
# Get actual parameters
|
||||
mode = kwargs.get("mode", "animate")
|
||||
retargetPose = kwargs.get("retargetPose", False)
|
||||
prevSegCondFrames = kwargs.get("prevSegCondFrames", 1)
|
||||
|
||||
# Build settings dictionary
|
||||
wanAnimateSettings = {}
|
||||
|
||||
# Add mode only if useMode is enabled
|
||||
if useMode and mode is not None:
|
||||
wanAnimateSettings["mode"] = mode
|
||||
|
||||
# Add retargetPose only if useRetargetPose is enabled
|
||||
# Note: retargetPose is only supported for animate mode, but we'll let the API handle validation
|
||||
if useRetargetPose:
|
||||
wanAnimateSettings["retargetPose"] = retargetPose
|
||||
|
||||
# Add prevSegCondFrames only if usePrevSegCondFrames is enabled
|
||||
if usePrevSegCondFrames:
|
||||
wanAnimateSettings["prevSegCondFrames"] = prevSegCondFrames
|
||||
|
||||
# Clean up None values
|
||||
wanAnimateSettings = {k: v for k, v in wanAnimateSettings.items() if v is not None}
|
||||
|
||||
# Return the full structure with the feature name as key (similar to providerSettings pattern)
|
||||
if wanAnimateSettings:
|
||||
return ({"wanAnimate": wanAnimateSettings},)
|
||||
else:
|
||||
return ({},)
|
||||
|
||||
# Node class mappings
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"RunwareWanAnimateAdvancedFeatureSettings": RunwareWanAnimateAdvancedFeatureSettings,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"RunwareWanAnimateAdvancedFeatureSettings": "Runware Wan Animate Advanced Feature Settings",
|
||||
}
|
||||
|
||||
@@ -90,6 +90,14 @@ class RunwareKlingProviderSettings:
|
||||
"label_on": "Enabled",
|
||||
"label_off": "Disabled",
|
||||
}),
|
||||
"useCharacterOrientation": ("BOOLEAN", {
|
||||
"tooltip": "Enable to include characterOrientation parameter in provider settings",
|
||||
"default": False,
|
||||
}),
|
||||
"characterOrientation": (["image", "video"], {
|
||||
"tooltip": "Character orientation mode. 'image': has the same orientation as the person in the picture (reference video duration should not exceed 10 seconds). 'video': consistent with the orientation of the characters in the video (reference video duration should not exceed 30 seconds). Only used when 'Use Character Orientation' is enabled.",
|
||||
"default": "image",
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,6 +119,7 @@ class RunwareKlingProviderSettings:
|
||||
useAsmrMode = kwargs.get("useAsmrMode", False)
|
||||
useKeepOriginalSound = kwargs.get("useKeepOriginalSound", False)
|
||||
useSound = kwargs.get("useSound", False)
|
||||
useCharacterOrientation = kwargs.get("useCharacterOrientation", False)
|
||||
|
||||
# Get value parameters
|
||||
cameraControl = kwargs.get("cameraControl", "none")
|
||||
@@ -121,6 +130,7 @@ class RunwareKlingProviderSettings:
|
||||
asmrMode = kwargs.get("asmrMode", False)
|
||||
keepOriginalSound = kwargs.get("keepOriginalSound", True)
|
||||
sound = kwargs.get("sound", False)
|
||||
characterOrientation = kwargs.get("characterOrientation", "image")
|
||||
|
||||
# Build settings dictionary - only include what is enabled
|
||||
klingSettings: Dict[str, Any] = {}
|
||||
@@ -157,6 +167,10 @@ class RunwareKlingProviderSettings:
|
||||
if useSound:
|
||||
klingSettings["sound"] = sound
|
||||
|
||||
# Add characterOrientation only if useCharacterOrientation is enabled
|
||||
if useCharacterOrientation and characterOrientation:
|
||||
klingSettings["characterOrientation"] = characterOrientation
|
||||
|
||||
# Clean up None values
|
||||
klingSettings = {k: v for k, v in klingSettings.items() if v is not None}
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
from typing import List
|
||||
from .utils import runwareUtils as rwUtils
|
||||
|
||||
|
||||
class referenceVoices:
|
||||
"""Reference Voices node for configuring reference voice inputs"""
|
||||
|
||||
MAX_VOICES = 4
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
optionalInputs = {}
|
||||
|
||||
for i in range(1, cls.MAX_VOICES + 1):
|
||||
ordinal = rwUtils.getOrdinal(i)
|
||||
optionalInputs[f"Voice{i}"] = ("STRING", {
|
||||
"tooltip": f"URL or mediaUUID for the {ordinal} reference voice from Runware Media Upload node or external URL.",
|
||||
})
|
||||
|
||||
return {
|
||||
"required": {},
|
||||
"optional": optionalInputs
|
||||
}
|
||||
|
||||
DESCRIPTION = "Configure multiple reference voice inputs (URLs or mediaUUIDs) for video generation."
|
||||
FUNCTION = "createReferenceVoices"
|
||||
RETURN_TYPES = ("RUNWAREREFERENCEVOICES",)
|
||||
RETURN_NAMES = ("Reference Voices",)
|
||||
CATEGORY = "Runware"
|
||||
|
||||
def createReferenceVoices(self, **kwargs) -> tuple[List[str]]:
|
||||
"""Create list of reference voice URLs/UUIDs"""
|
||||
voiceList = []
|
||||
|
||||
for i in range(1, self.MAX_VOICES + 1):
|
||||
voiceKey = f"Voice{i}"
|
||||
voiceUrl = kwargs.get(voiceKey)
|
||||
|
||||
if voiceUrl and voiceUrl.strip() != "":
|
||||
voiceList.append(voiceUrl.strip())
|
||||
|
||||
return (voiceList,)
|
||||
|
||||
@@ -8,36 +8,59 @@ class videoAdvancedFeatureInputs:
|
||||
return {
|
||||
"required": {},
|
||||
"optional": {
|
||||
"useVideoCFGScale": ("BOOLEAN", {
|
||||
"tooltip": "Enable to include videoCFGScale parameter in API request.",
|
||||
"default": False,
|
||||
}),
|
||||
"videoCFGScale": ("FLOAT", {
|
||||
"tooltip": "CFG scale for video generation.",
|
||||
"tooltip": "CFG scale for video generation. Only used when 'Use Video CFG Scale' is enabled.",
|
||||
"default": None,
|
||||
"min": 0.0,
|
||||
"max": 30.0,
|
||||
"step": 0.1,
|
||||
}),
|
||||
"useAudioCFGScale": ("BOOLEAN", {
|
||||
"tooltip": "Enable to include audioCFGScale parameter in API request.",
|
||||
"default": False,
|
||||
}),
|
||||
"audioCFGScale": ("FLOAT", {
|
||||
"tooltip": "CFG scale for audio generation.",
|
||||
"tooltip": "CFG scale for audio generation. Only used when 'Use Audio CFG Scale' is enabled.",
|
||||
"default": None,
|
||||
"min": 0.0,
|
||||
"max": 30.0,
|
||||
"step": 0.1,
|
||||
}),
|
||||
"useVideoNegativePrompt": ("BOOLEAN", {
|
||||
"tooltip": "Enable to include videoNegativePrompt parameter in API request.",
|
||||
"default": False,
|
||||
}),
|
||||
"videoNegativePrompt": ("STRING", {
|
||||
"tooltip": "Negative prompt for video generation.",
|
||||
"tooltip": "Negative prompt for video generation. Only used when 'Use Video Negative Prompt' is enabled.",
|
||||
"multiline": True,
|
||||
"default": "",
|
||||
}),
|
||||
"useAudioNegativePrompt": ("BOOLEAN", {
|
||||
"tooltip": "Enable to include audioNegativePrompt parameter in API request.",
|
||||
"default": False,
|
||||
}),
|
||||
"audioNegativePrompt": ("STRING", {
|
||||
"tooltip": "Negative prompt for audio generation.",
|
||||
"tooltip": "Negative prompt for audio generation. Only used when 'Use Audio Negative Prompt' is enabled.",
|
||||
"multiline": True,
|
||||
"default": "",
|
||||
}),
|
||||
"useSlgLayer": ("BOOLEAN", {
|
||||
"tooltip": "Enable to include slgLayer parameter in API request.",
|
||||
"default": False,
|
||||
}),
|
||||
"slgLayer": ("INT", {
|
||||
"tooltip": "SLG layer setting.",
|
||||
"tooltip": "SLG layer setting. Only used when 'Use SLG Layer' is enabled.",
|
||||
"default": None,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
}),
|
||||
"Advanced Feature Settings": ("RUNWAREWANANIMATEADVANCEDFEATURESETTINGS", {
|
||||
"tooltip": "Connect Runware Wan Animate Advanced Feature Settings node to provide Wan Animate configuration for character animation and replacement.",
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,27 +73,41 @@ class videoAdvancedFeatureInputs:
|
||||
def create_advanced_feature_inputs(self, **kwargs) -> tuple[Dict[str, Any]]:
|
||||
"""Create video advanced feature inputs dictionary"""
|
||||
|
||||
# Get parameters
|
||||
# Get use parameters
|
||||
useVideoCFGScale = kwargs.get("useVideoCFGScale", False)
|
||||
useAudioCFGScale = kwargs.get("useAudioCFGScale", False)
|
||||
useVideoNegativePrompt = kwargs.get("useVideoNegativePrompt", False)
|
||||
useAudioNegativePrompt = kwargs.get("useAudioNegativePrompt", False)
|
||||
useSlgLayer = kwargs.get("useSlgLayer", False)
|
||||
|
||||
# Get actual parameters
|
||||
videoCFGScale = kwargs.get("videoCFGScale", None)
|
||||
audioCFGScale = kwargs.get("audioCFGScale", None)
|
||||
videoNegativePrompt = kwargs.get("videoNegativePrompt", None)
|
||||
audioNegativePrompt = kwargs.get("audioNegativePrompt", None)
|
||||
slgLayer = kwargs.get("slgLayer", None)
|
||||
wanAnimateSettings = kwargs.get("Advanced Feature Settings", None)
|
||||
|
||||
# Build advanced feature inputs dictionary - only include non-None values
|
||||
# Build advanced feature inputs dictionary - only include when use* is enabled
|
||||
advanced_inputs = {}
|
||||
|
||||
if videoCFGScale is not None:
|
||||
if useVideoCFGScale and videoCFGScale is not None:
|
||||
advanced_inputs["videoCFGScale"] = videoCFGScale
|
||||
if audioCFGScale is not None:
|
||||
if useAudioCFGScale and audioCFGScale is not None:
|
||||
advanced_inputs["audioCFGScale"] = audioCFGScale
|
||||
if videoNegativePrompt is not None and videoNegativePrompt.strip() != "":
|
||||
if useVideoNegativePrompt and videoNegativePrompt is not None and videoNegativePrompt.strip() != "":
|
||||
advanced_inputs["videoNegativePrompt"] = videoNegativePrompt.strip()
|
||||
if audioNegativePrompt is not None and audioNegativePrompt.strip() != "":
|
||||
if useAudioNegativePrompt and audioNegativePrompt is not None and audioNegativePrompt.strip() != "":
|
||||
advanced_inputs["audioNegativePrompt"] = audioNegativePrompt.strip()
|
||||
if slgLayer is not None:
|
||||
if useSlgLayer and slgLayer is not None:
|
||||
advanced_inputs["slgLayer"] = slgLayer
|
||||
|
||||
# Merge advanced feature settings if provided (similar to how providerSettings are merged)
|
||||
if wanAnimateSettings is not None and isinstance(wanAnimateSettings, dict) and len(wanAnimateSettings) > 0:
|
||||
# Merge the settings dictionary directly into advanced_inputs
|
||||
# The settings dict should already contain the correct structure (e.g., {"wanAnimate": {...}})
|
||||
advanced_inputs.update(wanAnimateSettings)
|
||||
|
||||
return (advanced_inputs,)
|
||||
|
||||
# Node class mappings
|
||||
|
||||
@@ -40,6 +40,9 @@ class videoInferenceInputs:
|
||||
"Reference Videos": ("RUNWAREREFERENCEVIDEOS", {
|
||||
"tooltip": "Connect the Reference Videos node to provide reference video mediaUUIDs."
|
||||
}),
|
||||
"Reference Voices": ("RUNWAREREFERENCEVOICES", {
|
||||
"tooltip": "Connect the Reference Voices node to provide reference voice URLs or mediaUUIDs."
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +64,7 @@ class videoInferenceInputs:
|
||||
frameImages = kwargs.get("Frame Images", None)
|
||||
references = kwargs.get("References", None)
|
||||
referenceVideos = kwargs.get("Reference Videos", None)
|
||||
referenceVoices = kwargs.get("Reference Voices", None)
|
||||
|
||||
inputs = {}
|
||||
|
||||
@@ -105,4 +109,7 @@ class videoInferenceInputs:
|
||||
if referenceVideos is not None and len(referenceVideos) > 0:
|
||||
inputs["referenceVideos"] = referenceVideos
|
||||
|
||||
if referenceVoices is not None and len(referenceVoices) > 0:
|
||||
inputs["referenceVoices"] = referenceVoices
|
||||
|
||||
return (inputs,)
|
||||
|
||||
@@ -58,6 +58,7 @@ class videoModelSearch:
|
||||
"runware:200@1 (Wan 2.1 1.3B)",
|
||||
"runware:200@2 (Wan 2.1 14B)",
|
||||
"runware:200@6 (Wan 2.2)",
|
||||
"runware:200@8 (Wan 2.2 A14B Animate)",
|
||||
"alibaba:wan@2.6 (Wan 2.6)",
|
||||
],
|
||||
"OpenAI": [
|
||||
@@ -148,6 +149,7 @@ class videoModelSearch:
|
||||
"runware:200@1": {"width": 853, "height": 480},
|
||||
"runware:200@2": {"width": 853, "height": 480},
|
||||
"runware:200@6": {"width": 1280, "height": 720},
|
||||
"runware:200@8": {"width": 1104, "height": 832},
|
||||
"alibaba:wan@2.6": {"width": 1280, "height": 720},
|
||||
|
||||
# OpenAI Models
|
||||
@@ -237,6 +239,7 @@ class videoModelSearch:
|
||||
"runware:200@1": "480p",
|
||||
"runware:200@2": "480p",
|
||||
"runware:200@6": "720p",
|
||||
"runware:200@8": "720p",
|
||||
"alibaba:wan@2.6": "720p",
|
||||
|
||||
# OpenAI Models
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user