Merge pull request #135 from Runware/feature-skyreels

Added support for SkyReels
This commit is contained in:
Sirshendu Ganguly
2026-04-24 19:31:36 +05:30
committed by GitHub
10 changed files with 248 additions and 22 deletions
+11
View File
@@ -98,6 +98,7 @@ from .modules.vectorize import vectorize
from .modules.videoBgRemoval import videoBgRemoval
from .modules.videoUpscaler import videoUpscaler
from .modules.videoInputsReferences import videoInputsReferences
from .modules.videoInputsReferenceImagesMultipleImagesConnector import RunwareVideoInputsReferenceImagesMultipleImagesConnector
from .modules.videoInputsFrame import RunwareVideoInputsFrameImages
from .modules.safetyInputs import safetyInputs
from .modules.settings import RunwareSettings
@@ -229,6 +230,7 @@ NODE_CLASS_MAPPINGS = {
"Runware Video Background Removal": videoBgRemoval,
"Runware Video Upscaler": videoUpscaler,
"Runware Video Inference Inputs Reference Images": videoInputsReferences,
"Runware Video Inference Inputs Reference Images Multiple Images Connector": RunwareVideoInputsReferenceImagesMultipleImagesConnector,
"Runware Video Inputs Frame Images": RunwareVideoInputsFrameImages,
"Runware Safety Inputs": safetyInputs,
"Runware Settings": RunwareSettings,
@@ -260,6 +262,15 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"Runware Text Model Search": "Runware Text Model",
"Runware Image Inference Settings Color Palette": "Runware Image Inference Color Palette",
"Runware Video Transcription": "Runware Video Caption",
"Runware Video Inputs References": "Runware Video Inference Inputs Reference Images",
"Runware Video Inference Inputs Reference Images": "Runware Video Inference Inputs Reference Images",
"Runware Reference Voices": "Runware Video Inference Inputs Reference Voices",
"Runware Video Inference Inputs Reference Voices": "Runware Video Inference Inputs Reference Voices",
"Runware Reference Videos": "Runware Video Inference Inputs Reference Videos",
"Runware Video Inference Inputs Reference Videos": "Runware Video Inference Inputs Reference Videos",
"Runware Reference Audios": "Runware Video Inference Inputs Reference Audios",
"Runware Video Inference Inputs Reference Audios": "Runware Video Inference Inputs Reference Audios",
"Runware Video Inference Inputs Reference Images Multiple Images Connector": "Runware Video Inference Inputs Reference Images Multiple Images Connector",
}
WEB_DIRECTORY = "./clientlibs"
+4 -1
View File
@@ -1,6 +1,6 @@
import { app } from "../../scripts/app.js";
import { api } from "../../scripts/api.js";
import { promptEnhanceHandler, syncDimensionsNodeHandler, searchNodeHandler, APIKeyHandler, captionNodeHandler, saveTextHandler, mediaUUIDHandler, save3DFilepathHandler, videoTranscriptionHandler, videoOutputsHandler, handleCustomErrors, videoInferenceDimensionsHandler, videoModelSearchFilterHandler, audioModelSearchFilterHandler, textModelSearchFilterHandler, vectorizeModelSearchFilterHandler, vectorizeToggleHandler, useParameterToggleHandler, imageInferenceToggleHandler, imageInferenceAdvancedFeaturesToggleHandler, watermarkAdvancedFeatureToggleHandler, videoInferenceSpeechInputToggleHandler, regionalPromptingRegionsToggleHandler, upscalerToggleHandler, imageUpscalerSettingsToggleHandler, videoUpscalerToggleHandler, audioInferenceToggleHandler, audioInferenceSpeechToggleHandler, audioSettingsToggleHandler, textInferenceSettingsToggleHandler, videoSettingsToggleHandler, videoInferenceSettingsTtsToggleHandler, videoInferenceSettingsActiveSpeakerDetectionToggleHandler, videoInferenceSettingsActiveSpeakerBoundingBoxesToggleHandler, videoInferenceSettingsSegmentsToggleHandler, acceleratorOptionsToggleHandler, bytedanceProviderSettingsToggleHandler, xaiProviderSettingsToggleHandler, viduProviderSettingsToggleHandler, sourcefulProviderSettingsToggleHandler, sourcefulProviderSettingsFontsToggleHandler, threeDInferenceToggleHandler, threeDInferenceSettingsToggleHandler, threeDInferenceSettingsLatToggleHandler, ultralyticsProviderSettingsToggleHandler, openaiProviderSettingsToggleHandler, lightricksProviderSettingsToggleHandler, klingProviderSettingsToggleHandler, lumaProviderSettingsToggleHandler, briaProviderSettingsToggleHandler, pixverseProviderSettingsToggleHandler, alibabaProviderSettingsToggleHandler, mireloProviderSettingsToggleHandler, googleProviderSettingsToggleHandler, syncProviderSettingsToggleHandler, syncSegmentToggleHandler, settingsToggleHandler, safetyInputsToggleHandler, imageInferenceSettingsColorPaletteToggleHandler, audioInputToggleHandler, speechInputToggleHandler, briaProviderMaskToggleHandler, wanAnimateAdvancedFeatureSettingsToggleHandler, videoAdvancedFeatureInputsToggleHandler, audioInferenceInputsToggleHandler, audioInferenceSpeechVoicesToggleHandler } from "./utils.js";
import { promptEnhanceHandler, syncDimensionsNodeHandler, searchNodeHandler, APIKeyHandler, captionNodeHandler, saveTextHandler, mediaUUIDHandler, save3DFilepathHandler, videoTranscriptionHandler, videoOutputsHandler, handleCustomErrors, videoInferenceDimensionsHandler, videoModelSearchFilterHandler, audioModelSearchFilterHandler, textModelSearchFilterHandler, vectorizeModelSearchFilterHandler, vectorizeToggleHandler, useParameterToggleHandler, imageInferenceToggleHandler, imageInferenceAdvancedFeaturesToggleHandler, watermarkAdvancedFeatureToggleHandler, videoInferenceSpeechInputToggleHandler, regionalPromptingRegionsToggleHandler, upscalerToggleHandler, imageUpscalerSettingsToggleHandler, videoUpscalerToggleHandler, audioInferenceToggleHandler, audioInferenceSpeechToggleHandler, audioSettingsToggleHandler, textInferenceSettingsToggleHandler, videoSettingsToggleHandler, videoInferenceSettingsTtsToggleHandler, videoInferenceSettingsActiveSpeakerDetectionToggleHandler, videoInferenceSettingsActiveSpeakerBoundingBoxesToggleHandler, videoInferenceSettingsSegmentsToggleHandler, acceleratorOptionsToggleHandler, bytedanceProviderSettingsToggleHandler, xaiProviderSettingsToggleHandler, viduProviderSettingsToggleHandler, sourcefulProviderSettingsToggleHandler, sourcefulProviderSettingsFontsToggleHandler, threeDInferenceToggleHandler, threeDInferenceSettingsToggleHandler, threeDInferenceSettingsLatToggleHandler, ultralyticsProviderSettingsToggleHandler, openaiProviderSettingsToggleHandler, lightricksProviderSettingsToggleHandler, klingProviderSettingsToggleHandler, lumaProviderSettingsToggleHandler, briaProviderSettingsToggleHandler, pixverseProviderSettingsToggleHandler, alibabaProviderSettingsToggleHandler, mireloProviderSettingsToggleHandler, googleProviderSettingsToggleHandler, syncProviderSettingsToggleHandler, syncSegmentToggleHandler, settingsToggleHandler, safetyInputsToggleHandler, imageInferenceSettingsColorPaletteToggleHandler, audioInputToggleHandler, speechInputToggleHandler, briaProviderMaskToggleHandler, wanAnimateAdvancedFeatureSettingsToggleHandler, videoAdvancedFeatureInputsToggleHandler, audioInferenceInputsToggleHandler, audioInferenceSpeechVoicesToggleHandler, referenceVideosToggleHandler } from "./utils.js";
import { RUNWARE_NODE_TYPES, RUNWARE_NODE_PROPS, SEARCH_TERMS } from "./types.js";
const nodeInitList = [];
@@ -161,6 +161,9 @@ app.registerExtension({
videoAdvancedFeatureInputsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.AUDIOINFERENCEINPUTS) {
audioInferenceInputsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.REFERENCEVIDEOS ||
nodeClass === RUNWARE_NODE_TYPES.REFERENCEVIDEOS_LEGACY) {
referenceVideosToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.THREEDINFERENCE) {
threeDInferenceToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.THREEDINFERENCESETTINGS) {
+5
View File
@@ -112,6 +112,7 @@ const RUNWARE_NODE_TYPES = {
REFERENCEVOICES_LEGACY: "Runware Reference Voices",
REFERENCEAUDIOS: "Runware Video Inference Inputs Reference Audios",
VIDEOINPUTSREFERENCES_LEGACY: "Runware Video Inputs References",
VIDEOINPUTSREFERENCEMULTIIMAGES: "Runware Video Inference Inputs Reference Images Multiple Images Connector",
VIDEOINFERENCEINPUTS: "Runware Video Inference Inputs",
VIDEOINFERENCEELEMENTS: "Runware Video Inference Elements",
VIDEOINFERENCEELEMENTSCOMBINE: "Runware Video Inference Elements Combine",
@@ -434,6 +435,10 @@ const RUNWARE_NODE_PROPS = {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.VIDEOINPUTSREFERENCEMULTIIMAGES]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.VIDEOINFERENCEELEMENTS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
+74
View File
@@ -2838,6 +2838,9 @@ function videoModelSearchFilterHandler(videoModelSearchNode) {
"Pruna": [
"prunaai:p-video@0 (P-Video)",
],
"SkyReels": [
"skywork:skyreels@v4 (SkyReels V4)",
],
};
const MODEL_DIMENSIONS = {
@@ -2930,6 +2933,7 @@ function videoModelSearchFilterHandler(videoModelSearchNode) {
"prunaai:p-video@0": {"width": 1280, "height": 720},
"heygen:avatar@4": {"width": 1280, "height": 720},
"heygen:video-agent@0": {"width": 1280, "height": 720},
"skywork:skyreels@v4": {"width": 1280, "height": 720},
};
const MODEL_RESOLUTIONS = {
@@ -3022,6 +3026,7 @@ function videoModelSearchFilterHandler(videoModelSearchNode) {
"prunaai:p-video@0": "720p",
"heygen:avatar@4": "720p",
"heygen:video-agent@0": "720p",
"skywork:skyreels@v4": "720p",
};
const DEFAULT_DIMENSIONS = {"width": 1024, "height": 576};
@@ -4851,6 +4856,74 @@ function audioInferenceSpeechVoicesToggleHandler(voicesNode) {
}
}
function referenceVideosToggleHandler(referenceVideosNode) {
if (!referenceVideosNode?.widgets) return;
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;
}
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;
if (!paramWidget.inputEl && paramName) {
const nodeElement = referenceVideosNode.htmlElements?.widgetsContainer || referenceVideosNode.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";
}
}
}
}
referenceVideosNode.setDirtyCanvas(true);
}
appendWidgetCB(useWidget, () => {
setTimeout(toggleEnabled, 50);
});
setTimeout(toggleEnabled, 100);
}
for (let i = 1; i <= 4; i++) {
const useVideoWidget = referenceVideosNode.widgets.find((w) => w && w.name === `useVideo${i}`);
const videoWidget = referenceVideosNode.widgets.find((w) => w && w.name === `Video${i}`);
const tagWidget = referenceVideosNode.widgets.find((w) => w && w.name === `Tag${i}`);
const typeWidget = referenceVideosNode.widgets.find((w) => w && w.name === `Type${i}`);
if (useVideoWidget && videoWidget) {
toggleWidgetState(useVideoWidget, videoWidget, `Video${i}`);
}
if (useVideoWidget && tagWidget) {
toggleWidgetState(useVideoWidget, tagWidget, `Tag${i}`);
}
if (useVideoWidget && typeWidget) {
toggleWidgetState(useVideoWidget, typeWidget, `Type${i}`);
}
}
}
export {
notifyUser,
promptEnhanceHandler,
@@ -4919,6 +4992,7 @@ export {
videoAdvancedFeatureInputsToggleHandler,
audioInferenceInputsToggleHandler,
audioInferenceSpeechVoicesToggleHandler,
referenceVideosToggleHandler,
};
function googleProviderSettingsToggleHandler(googleNode) {
+44 -7
View File
@@ -1,4 +1,4 @@
from typing import List
from typing import Any, Dict, List
from .utils import runwareUtils as rwUtils
@@ -13,30 +13,67 @@ class referenceVideos:
for i in range(1, cls.MAX_VIDEOS + 1):
ordinal = rwUtils.getOrdinal(i)
optionalInputs[f"useVideo{i}"] = ("BOOLEAN", {
"default": False,
"tooltip": f"Enable to include the {ordinal} reference video.",
})
optionalInputs[f"Video{i}"] = ("STRING", {
"tooltip": f"MediaUUID for the {ordinal} reference video from Runware Media Upload node.",
})
optionalInputs[f"Tag{i}"] = ("STRING", {
"default": "",
"tooltip": f"Optional tag for the {ordinal} reference video (e.g., @video{i}). Must be used in the prompt.",
})
optionalInputs[f"Type{i}"] = ("STRING", {
"default": "reference",
"tooltip": (
f"Optional type for the {ordinal} reference video. "
"For example: reference or extend."
),
})
return {
"required": {},
"optional": optionalInputs
}
DESCRIPTION = "Configure multiple reference video inputs (mediaUUIDs) for video generation."
DESCRIPTION = (
"Configure multiple reference video inputs (mediaUUIDs) for video generation, "
"with optional tag and type per reference."
)
FUNCTION = "createReferenceVideos"
RETURN_TYPES = ("RUNWAREREFERENCEVIDEOS",)
RETURN_NAMES = ("Reference Videos",)
CATEGORY = "Runware"
def createReferenceVideos(self, **kwargs) -> tuple[List[str]]:
"""Create list of reference video UUIDs"""
videoList = []
def createReferenceVideos(self, **kwargs) -> tuple[List[Any]]:
"""Create list of reference video entries (string UUIDs or objects with tag/type)."""
videoList: List[Any] = []
for i in range(1, self.MAX_VIDEOS + 1):
useKey = f"useVideo{i}"
videoKey = f"Video{i}"
tagKey = f"Tag{i}"
typeKey = f"Type{i}"
useVideo = bool(kwargs.get(useKey, False))
videoUuid = kwargs.get(videoKey)
tagValue = (kwargs.get(tagKey) or "").strip()
typeValue = (kwargs.get(typeKey) or "reference").strip()
if videoUuid and videoUuid.strip() != "":
videoList.append(videoUuid.strip())
if useVideo and videoUuid and videoUuid.strip() != "":
normalized_video = videoUuid.strip()
# Backward compatible payload:
# - If tag/type are not provided, keep legacy string entry.
# - If tag or non-default type is provided, emit object entry.
if tagValue or typeValue != "reference":
entry: Dict[str, str] = {"video": normalized_video}
if tagValue:
entry["tag"] = tagValue
if typeValue:
entry["type"] = typeValue
videoList.append(entry)
else:
videoList.append(normalized_video)
return (videoList,)
@@ -0,0 +1,46 @@
from typing import List
from .utils import runwareUtils as rwUtils
class RunwareVideoInputsReferenceImagesMultipleImagesConnector:
"""Collect up to 4 images for one referenceImages entry."""
MAX_IMAGES = 4
@classmethod
def INPUT_TYPES(cls):
optionalInputs = {}
for i in range(1, cls.MAX_IMAGES + 1):
optionalInputs[f"Image{i}"] = ("IMAGE", {
"tooltip": f"Optional image {i} for this reference group.",
})
return {
"required": {},
"optional": optionalInputs,
}
DESCRIPTION = (
"Collect up to 4 images for one referenceImages item. "
"Connect this output to Images1/Images2/... on Runware Video Inference Inputs Reference Images."
)
FUNCTION = "createImages"
RETURN_TYPES = ("RUNWAREVIDEOINPUTSREFERENCEMULTIIMAGES",)
RETURN_NAMES = ("Images",)
CATEGORY = "Runware"
def createImages(self, **kwargs) -> tuple[List[str]]:
images: List[str] = []
for i in range(1, self.MAX_IMAGES + 1):
image = kwargs.get(f"Image{i}")
if image is not None:
images.append(rwUtils.convertTensor2IMG(image))
return (images,)
NODE_CLASS_MAPPINGS = {
"RunwareVideoInputsReferenceImagesMultipleImagesConnector": RunwareVideoInputsReferenceImagesMultipleImagesConnector,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareVideoInputsReferenceImagesMultipleImagesConnector": "Runware Video Inference Inputs Reference Images Multiple Images Connector",
}
+52 -14
View File
@@ -14,10 +14,21 @@ class videoInputsReferences:
for i in range(1, cls.MAX_REFERENCES + 1):
ordinal = rwUtils.getOrdinal(i)
optionalInputs[f"Image{i}"] = ("IMAGE", {
"tooltip": f"{ordinal.capitalize()} reference image.",
"tooltip": f"{ordinal.capitalize()} single reference image (mutually exclusive with Images{i}).",
})
optionalInputs[f"Images{i}"] = ("RUNWAREVIDEOINPUTSREFERENCEMULTIIMAGES", {
"tooltip": f"{ordinal.capitalize()} reference image group from the Multiple Images Connector (mutually exclusive with Image{i}).",
})
optionalInputs[f"Tag{i}"] = ("STRING", {
"tooltip": f"Optional tag for the {ordinal} reference (e.g., @image{i}, @Actor-1).",
"default": "",
})
optionalInputs[f"Type{i}"] = ("STRING", {
"tooltip": f"Type for the {ordinal} reference (e.g., 'asset'). Leave empty to omit.",
"tooltip": f"Type for the {ordinal} reference (e.g., image or grid). Leave empty to omit.",
"default": "",
})
optionalInputs[f"Audio{i}"] = ("STRING", {
"tooltip": f"Optional audio URL/mediaUUID for the {ordinal} reference.",
"default": "",
})
@@ -26,35 +37,62 @@ class videoInputsReferences:
"optional": optionalInputs
}
DESCRIPTION = "Configure multiple reference images with optional types for video inference inputs."
DESCRIPTION = (
"Configure multiple reference image entries for video inference inputs. "
"For each slot, provide either ImageN (single image) or ImagesN (connector output), not both."
)
FUNCTION = "createReferences"
RETURN_TYPES = ("RUNWAREVIDEOINPUTSREFERENCEIMAGES",)
RETURN_NAMES = ("Video Inputs Reference Images",)
CATEGORY = "Runware"
def createReferences(self, **kwargs) -> tuple[List[Dict[str, Any]]]:
"""Create list of reference objects from provided parameters"""
references = []
def createReferences(self, **kwargs) -> tuple[List[Any]]:
"""Create list of reference entries from provided parameters."""
references: List[Any] = []
for i in range(1, self.MAX_REFERENCES + 1):
imageKey = f"Image{i}"
imagesKey = f"Images{i}"
tagKey = f"Tag{i}"
typeKey = f"Type{i}"
audioKey = f"Audio{i}"
image = kwargs.get(imageKey)
images = kwargs.get(imagesKey)
refTag = (kwargs.get(tagKey) or "").strip()
refType = kwargs.get(typeKey, "")
refAudio = (kwargs.get(audioKey) or "").strip()
has_image = image is not None
has_images = isinstance(images, list) and len(images) > 0
if has_image and has_images:
raise ValueError(
f"Reference {i}: use either {imageKey} or {imagesKey}, not both."
)
if image is not None:
reference = self._createReference(image, refType)
if has_image or has_images:
reference = self._createReference(image, images, refTag, refType, refAudio)
references.append(reference)
return (references,)
def _createReference(self, image, refType):
"""Create reference entry (API expects direct strings)"""
imageUrl = rwUtils.convertTensor2IMG(image)
# API expects referenceImages to be a list of strings (UUID/URL/data URI)
# so we return the converted string directly.
return imageUrl
def _createReference(self, image, images, refTag: str, refType: str, refAudio: str):
"""Create reference entry."""
entry: Dict[str, Any] = {}
if image is not None:
entry["image"] = rwUtils.convertTensor2IMG(image)
elif isinstance(images, list) and len(images) > 0:
entry["images"] = images
else:
return entry
if refTag:
entry["tag"] = refTag
if isinstance(refType, str) and refType.strip():
entry["type"] = refType.strip()
if refAudio:
entry["audio"] = refAudio
return entry
# Node class mappings
+10
View File
@@ -134,6 +134,9 @@ class videoModelSearch:
"Pruna": [
"prunaai:p-video@0 (P-Video)",
],
"SkyReels": [
"skywork:skyreels@v4 (SkyReels V4)",
],
}
# Model dimensions mapping
@@ -268,6 +271,9 @@ class videoModelSearch:
# HeyGen Models (Model ID: heygen-avatar-iv)
"heygen:avatar@4": {"width": 1280, "height": 720},
"heygen:video-agent@0": {"width": 1280, "height": 720},
# SkyReels Models
"skywork:skyreels@v4": {"width": 1280, "height": 720},
}
# Model resolutions mapping
@@ -402,6 +408,9 @@ class videoModelSearch:
# HeyGen Models
"heygen:avatar@4": "720p",
"heygen:video-agent@0": "720p",
# SkyReels Models
"skywork:skyreels@v4": "720p",
}
MODEL_ARCHITECTURES = [
@@ -427,6 +436,7 @@ class videoModelSearch:
"xAI",
"VEED",
"Pruna",
"SkyReels",
]
DEFAULT_DIMENSIONS = {"width": 1024, "height": 576}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long