Merge pull request #64 from Runware/feature-KlingAvatar
Add Kling Avatar support and fix enable/disbaling id on speech/audio
This commit is contained in:
+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 } 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 } from "./utils.js";
|
||||
import { RUNWARE_NODE_TYPES, RUNWARE_NODE_PROPS, SEARCH_TERMS } from "./types.js";
|
||||
|
||||
const nodeInitList = [];
|
||||
@@ -81,6 +81,10 @@ app.registerExtension({
|
||||
syncSegmentToggleHandler(node);
|
||||
} else if(nodeClass === RUNWARE_NODE_TYPES.SETTINGS) {
|
||||
settingsToggleHandler(node);
|
||||
} else if(nodeClass === RUNWARE_NODE_TYPES.AUDIOINPUT) {
|
||||
audioInputToggleHandler(node);
|
||||
} else if(nodeClass === RUNWARE_NODE_TYPES.SPEECHINPUT) {
|
||||
speechInputToggleHandler(node);
|
||||
}
|
||||
|
||||
if(crNodeProps.colorModeOnly === true) return;
|
||||
|
||||
@@ -1684,6 +1684,8 @@ function videoModelSearchFilterHandler(videoModelSearchNode) {
|
||||
"klingai:7@1 (KlingAI Lip-Sync)",
|
||||
"klingai:kling@o1 (Kling VIDEO O1)",
|
||||
"klingai:kling-video@2.6-pro (Kling VIDEO 2.6 Pro)",
|
||||
"klingai:avatar@2.0-standard (KlingAI Avatar 2.0 Standard)",
|
||||
"klingai:avatar@2.0-pro (KlingAI Avatar 2.0 Pro)",
|
||||
],
|
||||
"Veo": [
|
||||
"google:2@0 (Veo 2.0)", "google:3@0 (Veo 3.0)", "google:3@1 (Veo 3.0 Fast)",
|
||||
@@ -1752,6 +1754,8 @@ function videoModelSearchFilterHandler(videoModelSearchNode) {
|
||||
"klingai:7@1": {"width": 0, "height": 0},
|
||||
"klingai:kling@o1": {"width": 1440, "height": 1440},
|
||||
"klingai:kling-video@2.6-pro": {"width": 1920, "height": 1080},
|
||||
"klingai:avatar@2.0-standard": {"width": 0, "height": 0},
|
||||
"klingai:avatar@2.0-pro": {"width": 0, "height": 0},
|
||||
"google:2@0": {"width": 1280, "height": 720},
|
||||
"google:3@0": {"width": 1280, "height": 720},
|
||||
"google:3@1": {"width": 1280, "height": 720},
|
||||
@@ -1811,6 +1815,8 @@ function videoModelSearchFilterHandler(videoModelSearchNode) {
|
||||
"klingai:7@1": null, // No resolution support
|
||||
"klingai:kling@o1": null, // No standard resolution
|
||||
"klingai:kling-video@2.6-pro": "1080p",
|
||||
"klingai:avatar@2.0-standard": null, // No resolution support
|
||||
"klingai:avatar@2.0-pro": null, // No resolution support
|
||||
"google:2@0": "720p",
|
||||
"google:3@0": "720p",
|
||||
"google:3@1": "720p",
|
||||
@@ -2517,6 +2523,120 @@ function briaProviderSettingsToggleHandler(briaNode) {
|
||||
}
|
||||
}
|
||||
|
||||
function audioInputToggleHandler(audioInputNode) {
|
||||
// Find "use" parameter widget and "id" widget for Audio Input
|
||||
const useIdWidget = audioInputNode.widgets.find(w => w.name === "useId");
|
||||
const idWidget = audioInputNode.widgets.find(w => w.name === "id");
|
||||
|
||||
// Helper function to toggle widget enabled state
|
||||
function toggleWidgetState(useWidget, paramWidget, paramName) {
|
||||
if (!useWidget || !paramWidget) return;
|
||||
|
||||
function toggleEnabled() {
|
||||
const enabled = useWidget.value === true;
|
||||
|
||||
// Disable/enable widgets using inputEl if available
|
||||
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;
|
||||
}
|
||||
// Also set widget property
|
||||
paramWidget.disabled = !enabled;
|
||||
|
||||
// Fallback: try to find inputs via DOM if inputEl is not available
|
||||
if (!paramWidget.inputEl) {
|
||||
const nodeElement = audioInputNode.htmlElements?.widgetsContainer || audioInputNode.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";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
audioInputNode.setDirtyCanvas(true);
|
||||
}
|
||||
|
||||
// Set up callback
|
||||
appendWidgetCB(useWidget, () => {
|
||||
setTimeout(toggleEnabled, 50);
|
||||
});
|
||||
|
||||
// Initial call to set initial state
|
||||
setTimeout(toggleEnabled, 100);
|
||||
}
|
||||
|
||||
// Set up toggle handler for "useId" parameter
|
||||
if (useIdWidget && idWidget) {
|
||||
toggleWidgetState(useIdWidget, idWidget, "id");
|
||||
}
|
||||
}
|
||||
|
||||
function speechInputToggleHandler(speechInputNode) {
|
||||
// Find "use" parameter widget and "id" widget for Speech Input
|
||||
const useIdWidget = speechInputNode.widgets.find(w => w.name === "useId");
|
||||
const idWidget = speechInputNode.widgets.find(w => w.name === "id");
|
||||
|
||||
// Helper function to toggle widget enabled state
|
||||
function toggleWidgetState(useWidget, paramWidget, paramName) {
|
||||
if (!useWidget || !paramWidget) return;
|
||||
|
||||
function toggleEnabled() {
|
||||
const enabled = useWidget.value === true;
|
||||
|
||||
// Disable/enable widgets using inputEl if available
|
||||
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;
|
||||
}
|
||||
// Also set widget property
|
||||
paramWidget.disabled = !enabled;
|
||||
|
||||
// Fallback: try to find inputs via DOM if inputEl is not available
|
||||
if (!paramWidget.inputEl) {
|
||||
const nodeElement = speechInputNode.htmlElements?.widgetsContainer || speechInputNode.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";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
speechInputNode.setDirtyCanvas(true);
|
||||
}
|
||||
|
||||
// Set up callback
|
||||
appendWidgetCB(useWidget, () => {
|
||||
setTimeout(toggleEnabled, 50);
|
||||
});
|
||||
|
||||
// Initial call to set initial state
|
||||
setTimeout(toggleEnabled, 100);
|
||||
}
|
||||
|
||||
// Set up toggle handler for "useId" parameter
|
||||
if (useIdWidget && idWidget) {
|
||||
toggleWidgetState(useIdWidget, idWidget, "id");
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
notifyUser,
|
||||
promptEnhanceHandler,
|
||||
@@ -2548,6 +2668,8 @@ export {
|
||||
syncProviderSettingsToggleHandler,
|
||||
syncSegmentToggleHandler,
|
||||
settingsToggleHandler,
|
||||
audioInputToggleHandler,
|
||||
speechInputToggleHandler,
|
||||
};
|
||||
|
||||
function googleProviderSettingsToggleHandler(googleNode) {
|
||||
|
||||
+16
-7
@@ -13,16 +13,21 @@ class RunwareAudioInput:
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"id": ("STRING", {
|
||||
"tooltip": "Reference ID used to map audio input. Required when using segments array.",
|
||||
"default": "",
|
||||
}),
|
||||
"source": ("STRING", {
|
||||
"tooltip": "URL, base64, data URI, or UUID for the audio file. Audio formats: WAV, MP3, OGG, M4A, M3A, AAC, WMA, FLAC, MP4. Recommended: WAV/MP3 at 44.1kHz or 48kHz sampling rate.",
|
||||
"default": "",
|
||||
}),
|
||||
},
|
||||
"optional": {}
|
||||
"optional": {
|
||||
"useId": ("BOOLEAN", {
|
||||
"tooltip": "Enable to include reference ID for mapping audio input. Required when using segments array.",
|
||||
"default": False,
|
||||
}),
|
||||
"id": ("STRING", {
|
||||
"tooltip": "Reference ID used to map audio input. Required when using segments array.",
|
||||
"default": "",
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("RUNWAREAUDIOINPUT",)
|
||||
@@ -34,17 +39,21 @@ class RunwareAudioInput:
|
||||
def createAudioInput(self, **kwargs) -> tuple[Dict[str, Any]]:
|
||||
"""Create audio input configuration"""
|
||||
|
||||
useId = kwargs.get("useId", False)
|
||||
audio_id = kwargs.get("id", "").strip()
|
||||
source = kwargs.get("source", "").strip()
|
||||
|
||||
if not audio_id or not source:
|
||||
if not source:
|
||||
return ({},)
|
||||
|
||||
audioInput: Dict[str, Any] = {
|
||||
"id": audio_id,
|
||||
"source": source,
|
||||
}
|
||||
|
||||
# Only include id if useId is enabled and id is provided
|
||||
if useId and audio_id:
|
||||
audioInput["id"] = audio_id
|
||||
|
||||
return (audioInput,)
|
||||
|
||||
|
||||
|
||||
+16
-7
@@ -13,10 +13,6 @@ class RunwareSpeechInput:
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"id": ("STRING", {
|
||||
"tooltip": "Reference ID used to map speech input. Required when using segments array.",
|
||||
"default": "",
|
||||
}),
|
||||
"provider": ("STRING", {
|
||||
"tooltip": "TTS provider name. Currently supports: elevenlabs",
|
||||
"default": "elevenlabs",
|
||||
@@ -31,7 +27,16 @@ class RunwareSpeechInput:
|
||||
"default": "",
|
||||
}),
|
||||
},
|
||||
"optional": {}
|
||||
"optional": {
|
||||
"useId": ("BOOLEAN", {
|
||||
"tooltip": "Enable to include reference ID for mapping speech input. Required when using segments array.",
|
||||
"default": False,
|
||||
}),
|
||||
"id": ("STRING", {
|
||||
"tooltip": "Reference ID used to map speech input. Required when using segments array.",
|
||||
"default": "",
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("RUNWARESPEECHINPUT",)
|
||||
@@ -43,21 +48,25 @@ class RunwareSpeechInput:
|
||||
def createSpeechInput(self, **kwargs) -> tuple[Dict[str, Any]]:
|
||||
"""Create speech input configuration"""
|
||||
|
||||
useId = kwargs.get("useId", False)
|
||||
speech_id = kwargs.get("id", "").strip()
|
||||
provider = kwargs.get("provider", "elevenlabs").strip()
|
||||
voiceId = kwargs.get("voiceId", "").strip()
|
||||
text = kwargs.get("text", "").strip()
|
||||
|
||||
if not speech_id or not provider or not voiceId or not text:
|
||||
if not provider or not voiceId or not text:
|
||||
return ({},)
|
||||
|
||||
speechInput: Dict[str, Any] = {
|
||||
"id": speech_id,
|
||||
"provider": provider,
|
||||
"voiceId": voiceId,
|
||||
"text": text,
|
||||
}
|
||||
|
||||
# Only include id if useId is enabled and id is provided
|
||||
if useId and speech_id:
|
||||
speechInput["id"] = speech_id
|
||||
|
||||
return (speechInput,)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user