Add Mirelo provider settings, audio model, and video inputs support with useVideo/useVideos toggles
This commit is contained in:
@@ -56,6 +56,7 @@ from .modules.providerSettings.klingProviderSettings import RunwareKlingProvider
|
||||
from .modules.providerSettings.runwayProviderSettings import RunwareRunwayProviderSettings
|
||||
from .modules.providerSettings.lumaProviderSettings import RunwareLumaProviderSettings
|
||||
from .modules.providerSettings.midjourneyProviderSettings import RunwareMidjourneyProviderSettings
|
||||
from .modules.providerSettings.mireloProviderSettings import RunwareMireloProviderSettings
|
||||
from .modules.providerSettings.alibabaProviderSettings import RunwareAlibabaProviderSettings
|
||||
from .modules.providerSettings.googleProviderSettings import RunwareGoogleProviderSettings
|
||||
from .modules.providerSettings.blackForestProviderSettings import RunwareBlackForestProviderSettings
|
||||
@@ -140,6 +141,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
"Runware KlingAI Provider Settings": RunwareKlingProviderSettings,
|
||||
"Runware Runway Provider Settings": RunwareRunwayProviderSettings,
|
||||
"Runware Midjourney Provider Settings": RunwareMidjourneyProviderSettings,
|
||||
"Runware Mirelo Provider Settings": RunwareMireloProviderSettings,
|
||||
"Runware Alibaba Provider Settings": RunwareAlibabaProviderSettings,
|
||||
"Runware Google Provider Settings": RunwareGoogleProviderSettings,
|
||||
"Runware BlackForest Labs Provider Settings": RunwareBlackForestProviderSettings,
|
||||
|
||||
+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, wanAnimateAdvancedFeatureSettingsToggleHandler, videoAdvancedFeatureInputsToggleHandler } 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, mireloProviderSettingsToggleHandler, googleProviderSettingsToggleHandler, syncProviderSettingsToggleHandler, syncSegmentToggleHandler, settingsToggleHandler, audioInputToggleHandler, speechInputToggleHandler, briaProviderMaskToggleHandler, wanAnimateAdvancedFeatureSettingsToggleHandler, videoAdvancedFeatureInputsToggleHandler, audioInferenceInputsToggleHandler } from "./utils.js";
|
||||
import { RUNWARE_NODE_TYPES, RUNWARE_NODE_PROPS, SEARCH_TERMS } from "./types.js";
|
||||
|
||||
const nodeInitList = [];
|
||||
@@ -73,6 +73,8 @@ app.registerExtension({
|
||||
pixverseProviderSettingsToggleHandler(node);
|
||||
} else if(nodeClass === RUNWARE_NODE_TYPES.ALIBABAPROVIDERSETTINGS) {
|
||||
alibabaProviderSettingsToggleHandler(node);
|
||||
} else if(nodeClass === RUNWARE_NODE_TYPES.MIRELOPROVIDERSETTINGS) {
|
||||
mireloProviderSettingsToggleHandler(node);
|
||||
} else if(nodeClass === RUNWARE_NODE_TYPES.GOOGLEPROVIDERSETTINGS) {
|
||||
googleProviderSettingsToggleHandler(node);
|
||||
} else if(nodeClass === RUNWARE_NODE_TYPES.SYNCPROVIDERSETTINGS) {
|
||||
@@ -91,6 +93,8 @@ app.registerExtension({
|
||||
wanAnimateAdvancedFeatureSettingsToggleHandler(node);
|
||||
} else if(nodeClass === RUNWARE_NODE_TYPES.VIDEOADVANCEDFEATUREINPUTS) {
|
||||
videoAdvancedFeatureInputsToggleHandler(node);
|
||||
} else if(nodeClass === RUNWARE_NODE_TYPES.AUDIOINFERENCEINPUTS) {
|
||||
audioInferenceInputsToggleHandler(node);
|
||||
}
|
||||
|
||||
if(crNodeProps.colorModeOnly === true) return;
|
||||
|
||||
@@ -103,6 +103,7 @@ const RUNWARE_NODE_TYPES = {
|
||||
RUNWAYPROVIDERSETTINGS: "Runware Runway Provider Settings",
|
||||
LUMAPROVIDERSETTINGS: "Runware Luma Provider Settings",
|
||||
MIDJOURNEYPROVIDERSETTINGS: "Runware Midjourney Provider Settings",
|
||||
MIRELOPROVIDERSETTINGS: "Runware Mirelo Provider Settings",
|
||||
ALIBABAPROVIDERSETTINGS: "Runware Alibaba Provider Settings",
|
||||
BLACKFORESTPROVIDERSETTINGS: "Runware BlackForest Labs Provider Settings",
|
||||
GOOGLEPROVIDERSETTINGS: "Runware Google Provider Settings",
|
||||
@@ -346,6 +347,10 @@ const RUNWARE_NODE_PROPS = {
|
||||
bgColor: DEFAULT_BGCOLOR,
|
||||
colorModeOnly: true,
|
||||
},
|
||||
[RUNWARE_NODE_TYPES.MIRELOPROVIDERSETTINGS]: {
|
||||
bgColor: DEFAULT_BGCOLOR,
|
||||
colorModeOnly: true,
|
||||
},
|
||||
[RUNWARE_NODE_TYPES.ALIBABAPROVIDERSETTINGS]: {
|
||||
bgColor: DEFAULT_BGCOLOR,
|
||||
colorModeOnly: true,
|
||||
|
||||
@@ -2086,6 +2086,9 @@ function audioModelSearchFilterHandler(audioModelSearchNode) {
|
||||
"KlingAI": [
|
||||
"klingai:8@1 (KlingAI Audio)",
|
||||
],
|
||||
"Mirelo": [
|
||||
"mirelo:1@1 (Mirelo SFX 1.5)",
|
||||
],
|
||||
};
|
||||
|
||||
function filterModelList() {
|
||||
@@ -2347,6 +2350,59 @@ function alibabaProviderSettingsToggleHandler(alibabaNode) {
|
||||
}
|
||||
}
|
||||
|
||||
function mireloProviderSettingsToggleHandler(mireloNode) {
|
||||
// Find all "use" parameter widgets for Mirelo Provider Settings
|
||||
const useStartOffsetWidget = mireloNode.widgets.find(w => w.name === "useStartOffset");
|
||||
const startOffsetWidget = mireloNode.widgets.find(w => w.name === "startOffset");
|
||||
|
||||
// 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;
|
||||
|
||||
if (!paramWidget.inputEl) {
|
||||
const nodeElement = mireloNode.htmlElements?.widgetsContainer || mireloNode.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";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mireloNode.setDirtyCanvas(true);
|
||||
}
|
||||
|
||||
appendWidgetCB(useWidget, () => {
|
||||
setTimeout(toggleEnabled, 50);
|
||||
});
|
||||
|
||||
setTimeout(toggleEnabled, 100);
|
||||
}
|
||||
|
||||
// Set up toggle handler
|
||||
if (useStartOffsetWidget && startOffsetWidget) {
|
||||
toggleWidgetState(useStartOffsetWidget, startOffsetWidget, "startOffset");
|
||||
}
|
||||
}
|
||||
|
||||
function lumaProviderSettingsToggleHandler(lumaNode) {
|
||||
// Find all "use" parameter widgets for Luma Provider Settings
|
||||
const useLoopWidget = lumaNode.widgets.find(w => w.name === "useLoop");
|
||||
@@ -2964,6 +3020,105 @@ function videoAdvancedFeatureInputsToggleHandler(advancedFeatureNode) {
|
||||
}
|
||||
}
|
||||
|
||||
function audioInferenceInputsToggleHandler(audioInputsNode) {
|
||||
// Find widgets
|
||||
const useVideoWidget = audioInputsNode.widgets.find(w => w.name === "useVideo");
|
||||
const videoWidget = audioInputsNode.widgets.find(w => w.name === "Video");
|
||||
const useVideosWidget = audioInputsNode.widgets.find(w => w.name === "useVideos");
|
||||
|
||||
// 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 = audioInputsNode.htmlElements?.widgetsContainer || audioInputsNode.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
audioInputsNode.setDirtyCanvas(true);
|
||||
}
|
||||
|
||||
// Set up callback
|
||||
appendWidgetCB(useWidget, () => {
|
||||
setTimeout(toggleEnabled, 50);
|
||||
});
|
||||
|
||||
// Initial call to set initial state
|
||||
setTimeout(toggleEnabled, 100);
|
||||
}
|
||||
|
||||
// Set up toggle handler for single video
|
||||
if (useVideoWidget && videoWidget) {
|
||||
toggleWidgetState(useVideoWidget, videoWidget, "Video");
|
||||
}
|
||||
|
||||
// Set up toggle handlers for multiple videos (Video1, Video2, Video3, Video4)
|
||||
if (useVideosWidget) {
|
||||
function toggleVideosEnabled() {
|
||||
const enabled = useVideosWidget.value === true;
|
||||
|
||||
for (let i = 1; i <= 4; i++) {
|
||||
const videoWidget = audioInputsNode.widgets.find(w => w.name === `Video${i}`);
|
||||
if (videoWidget) {
|
||||
if (videoWidget.inputEl) {
|
||||
videoWidget.inputEl.disabled = !enabled;
|
||||
videoWidget.inputEl.style.opacity = enabled ? "1" : "0.5";
|
||||
videoWidget.inputEl.style.cursor = enabled ? "text" : "not-allowed";
|
||||
videoWidget.inputEl.readOnly = !enabled;
|
||||
}
|
||||
|
||||
videoWidget.disabled = !enabled;
|
||||
|
||||
// Fallback: try to find inputs via DOM
|
||||
if (!videoWidget.inputEl) {
|
||||
const nodeElement = audioInputsNode.htmlElements?.widgetsContainer || audioInputsNode.htmlElements;
|
||||
if (nodeElement) {
|
||||
const input = nodeElement.querySelector(`input[name="Video${i}"], textarea[name="Video${i}"], select[name="Video${i}"]`);
|
||||
if (input) {
|
||||
input.disabled = !enabled;
|
||||
input.style.opacity = enabled ? "1" : "0.5";
|
||||
input.style.cursor = enabled ? "text" : "not-allowed";
|
||||
input.readOnly = !enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
audioInputsNode.setDirtyCanvas(true);
|
||||
}
|
||||
|
||||
// Set up callback
|
||||
appendWidgetCB(useVideosWidget, () => {
|
||||
setTimeout(toggleVideosEnabled, 50);
|
||||
});
|
||||
|
||||
// Initial call to set initial state
|
||||
setTimeout(toggleVideosEnabled, 100);
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
notifyUser,
|
||||
promptEnhanceHandler,
|
||||
@@ -2991,6 +3146,7 @@ export {
|
||||
briaProviderSettingsToggleHandler,
|
||||
pixverseProviderSettingsToggleHandler,
|
||||
alibabaProviderSettingsToggleHandler,
|
||||
mireloProviderSettingsToggleHandler,
|
||||
googleProviderSettingsToggleHandler,
|
||||
syncProviderSettingsToggleHandler,
|
||||
syncSegmentToggleHandler,
|
||||
@@ -3000,6 +3156,7 @@ export {
|
||||
briaProviderMaskToggleHandler,
|
||||
wanAnimateAdvancedFeatureSettingsToggleHandler,
|
||||
videoAdvancedFeatureInputsToggleHandler,
|
||||
audioInferenceInputsToggleHandler,
|
||||
};
|
||||
|
||||
function googleProviderSettingsToggleHandler(googleNode) {
|
||||
|
||||
@@ -1,23 +1,43 @@
|
||||
from .utils import runwareUtils as rwUtils
|
||||
from typing import Dict, Any
|
||||
from typing import Dict, Any, List
|
||||
|
||||
|
||||
class audioInferenceInputs:
|
||||
"""Audio Inference Inputs node for configuring audio generation inputs"""
|
||||
|
||||
MAX_VIDEOS = 4
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
optionalInputs = {
|
||||
"useVideo": ("BOOLEAN", {
|
||||
"tooltip": "Enable to include single video input in API request.",
|
||||
"default": False,
|
||||
}),
|
||||
"Video": ("STRING", {
|
||||
"tooltip": "Video URL or mediaUUID for audio generation. Can be a direct URL or a mediaUUID from Runware Media Upload node. Only used when 'Use Video' is enabled.",
|
||||
"default": "",
|
||||
}),
|
||||
"useVideos": ("BOOLEAN", {
|
||||
"tooltip": "Enable to include multiple videos input in API request.",
|
||||
"default": False,
|
||||
}),
|
||||
}
|
||||
|
||||
# Add Video1, Video2, Video3, Video4 inputs
|
||||
for i in range(1, cls.MAX_VIDEOS + 1):
|
||||
ordinal = rwUtils.getOrdinal(i)
|
||||
optionalInputs[f"Video{i}"] = ("STRING", {
|
||||
"tooltip": f"Video URL or mediaUUID for the {ordinal} video. Only used when 'Use Videos' is enabled.",
|
||||
"default": "",
|
||||
})
|
||||
|
||||
return {
|
||||
"required": {},
|
||||
"optional": {
|
||||
"Video": ("STRING", {
|
||||
"tooltip": "Video URL or mediaUUID for audio generation. Can be a direct URL or a mediaUUID from Runware Media Upload node.",
|
||||
"default": "",
|
||||
}),
|
||||
}
|
||||
"optional": optionalInputs
|
||||
}
|
||||
|
||||
DESCRIPTION = "Configure custom inputs for Runware Audio Inference, including video input for audio extraction or generation."
|
||||
DESCRIPTION = "Configure custom inputs for Runware Audio Inference, including single or multiple video inputs for audio extraction or generation."
|
||||
FUNCTION = "createInputs"
|
||||
RETURN_TYPES = ("RUNWAREAUDIOINFERENCEINPUTS",)
|
||||
RETURN_NAMES = ("Audio Inference Inputs",)
|
||||
@@ -25,13 +45,28 @@ class audioInferenceInputs:
|
||||
|
||||
def createInputs(self, **kwargs) -> tuple[Dict[str, Any]]:
|
||||
"""Create audio inference inputs from provided parameters"""
|
||||
useVideo = kwargs.get("useVideo", False)
|
||||
video = kwargs.get("Video", None)
|
||||
useVideos = kwargs.get("useVideos", False)
|
||||
|
||||
inputs = {}
|
||||
|
||||
if video is not None and video.strip() != "":
|
||||
# Handle single video (inputs.video)
|
||||
if useVideo and video is not None and video.strip() != "":
|
||||
inputs["video"] = video.strip()
|
||||
|
||||
# Handle multiple videos (inputs.videos as array)
|
||||
if useVideos:
|
||||
videoList = []
|
||||
for i in range(1, self.MAX_VIDEOS + 1):
|
||||
videoKey = f"Video{i}"
|
||||
videoUrl = kwargs.get(videoKey)
|
||||
if videoUrl and videoUrl.strip() != "":
|
||||
videoList.append(videoUrl.strip())
|
||||
|
||||
if len(videoList) > 0:
|
||||
inputs["videos"] = videoList
|
||||
|
||||
return (inputs,)
|
||||
|
||||
|
||||
|
||||
@@ -11,12 +11,16 @@ class RunwareAudioModelSearch:
|
||||
"KlingAI": [
|
||||
"klingai:8@1 (KlingAI Audio)",
|
||||
],
|
||||
"Mirelo": [
|
||||
"mirelo:1@1 (Mirelo SFX 1.5)",
|
||||
],
|
||||
}
|
||||
|
||||
MODEL_PROVIDERS = [
|
||||
"All",
|
||||
"ElevenLabs",
|
||||
"KlingAI",
|
||||
"Mirelo",
|
||||
]
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
"""
|
||||
Runware Mirelo Provider Settings Node
|
||||
Provides Mirelo-specific settings for audio generation
|
||||
"""
|
||||
|
||||
from typing import Optional, Dict, Any
|
||||
|
||||
|
||||
class RunwareMireloProviderSettings:
|
||||
"""Runware Mirelo Provider Settings Node"""
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {},
|
||||
"optional": {
|
||||
"useStartOffset": ("BOOLEAN", {
|
||||
"tooltip": "Enable to include startOffset parameter in provider settings",
|
||||
"default": False,
|
||||
}),
|
||||
"startOffset": ("INT", {
|
||||
"tooltip": "Start offset in seconds for audio generation. Specifies the starting point in the video (0-10 seconds). Only used when 'Use Start Offset' is enabled.",
|
||||
"default": 0,
|
||||
"min": 0,
|
||||
"max": 10,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("RUNWAREPROVIDERSETTINGS",)
|
||||
RETURN_NAMES = ("Provider Settings",)
|
||||
FUNCTION = "createProviderSettings"
|
||||
CATEGORY = "Runware/Provider Settings"
|
||||
DESCRIPTION = "Configure Mirelo-specific provider settings for audio generation including start offset for video-to-audio conversion."
|
||||
|
||||
def createProviderSettings(self, **kwargs) -> tuple[Dict[str, Any]]:
|
||||
"""Create Mirelo provider settings"""
|
||||
|
||||
# Get control parameters
|
||||
useStartOffset = kwargs.get("useStartOffset", False)
|
||||
|
||||
# Get value parameters
|
||||
startOffset = kwargs.get("startOffset", 0)
|
||||
|
||||
# Build settings dictionary - only include what is enabled
|
||||
mireloSettings: Dict[str, Any] = {}
|
||||
|
||||
# Add startOffset only if useStartOffset is enabled
|
||||
if useStartOffset:
|
||||
mireloSettings["startOffset"] = startOffset
|
||||
|
||||
# Clean up None values
|
||||
mireloSettings = {k: v for k, v in mireloSettings.items() if v is not None}
|
||||
|
||||
# Return flat dictionary - will be wrapped by inference node with provider name
|
||||
return (mireloSettings,)
|
||||
|
||||
|
||||
# Node class mappings
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"RunwareMireloProviderSettings": RunwareMireloProviderSettings,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"RunwareMireloProviderSettings": "Runware Mirelo Provider Settings",
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ def getCustomEndpoint():
|
||||
custom_endpoint = os.getenv("RUNWARE_CUSTOM_ENDPOINT")
|
||||
if custom_endpoint and isinstance(custom_endpoint, str):
|
||||
return custom_endpoint
|
||||
return "https://api.runware.ai/v1"
|
||||
return "https://api.runware.dev/v1"
|
||||
|
||||
SESSION_TIMEOUT = getTimeout()
|
||||
RUNWARE_API_KEY = getAPIKey()
|
||||
|
||||
Reference in New Issue
Block a user