Merge pull request #54 from Runware/feature-resolution-addPixverseProviderSettingsZimage

async for imageInference; pixverseSupport
This commit is contained in:
Sirshendu Ganguly
2025-12-02 21:25:48 -05:00
committed by GitHub
27 changed files with 399 additions and 54 deletions
+3 -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, mediaUUIDHandler, videoTranscriptionHandler, handleCustomErrors, videoInferenceDimensionsHandler, videoModelSearchFilterHandler, audioModelSearchFilterHandler, useParameterToggleHandler, imageInferenceToggleHandler, upscalerToggleHandler, videoUpscalerToggleHandler, audioInferenceToggleHandler, acceleratorOptionsToggleHandler, bytedanceProviderSettingsToggleHandler, openaiProviderSettingsToggleHandler, lightricksProviderSettingsToggleHandler, klingProviderSettingsToggleHandler, lumaProviderSettingsToggleHandler, briaProviderSettingsToggleHandler } 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 } from "./utils.js";
import { RUNWARE_NODE_TYPES, RUNWARE_NODE_PROPS, SEARCH_TERMS } from "./types.js";
const nodeInitList = [];
@@ -69,6 +69,8 @@ app.registerExtension({
lumaProviderSettingsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.BRIAPROVIDERSETTINGS) {
briaProviderSettingsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.PIXVERSEPROVIDERSETTINGS) {
pixverseProviderSettingsToggleHandler(node);
}
if(crNodeProps.colorModeOnly === true) return;
+176 -1
View File
@@ -1246,6 +1246,114 @@ function openaiProviderSettingsToggleHandler(openaiNode) {
}
}
function pixverseProviderSettingsToggleHandler(pixverseNode) {
// Find all "use" parameter widgets for Pixverse Provider Settings
const useEffectWidget = pixverseNode.widgets.find(w => w.name === "useEffect");
const effectWidget = pixverseNode.widgets.find(w => w.name === "effect");
const useCameraMovementWidget = pixverseNode.widgets.find(w => w.name === "useCameraMovement");
const cameraMovementWidget = pixverseNode.widgets.find(w => w.name === "cameraMovement");
const useStyleWidget = pixverseNode.widgets.find(w => w.name === "useStyle");
const styleWidget = pixverseNode.widgets.find(w => w.name === "style");
const useMotionModeWidget = pixverseNode.widgets.find(w => w.name === "useMotionMode");
const motionModeWidget = pixverseNode.widgets.find(w => w.name === "motionMode");
const useSoundEffectSwitchWidget = pixverseNode.widgets.find(w => w.name === "useSoundEffectSwitch");
const soundEffectSwitchWidget = pixverseNode.widgets.find(w => w.name === "soundEffectSwitch");
const useSoundEffectContentWidget = pixverseNode.widgets.find(w => w.name === "useSoundEffectContent");
const soundEffectContentWidget = pixverseNode.widgets.find(w => w.name === "soundEffectContent");
const useAudioWidget = pixverseNode.widgets.find(w => w.name === "useAudio");
const audioWidget = pixverseNode.widgets.find(w => w.name === "audio");
const useMultiClipWidget = pixverseNode.widgets.find(w => w.name === "useMultiClip");
const multiClipWidget = pixverseNode.widgets.find(w => w.name === "multiClip");
const useThinkingWidget = pixverseNode.widgets.find(w => w.name === "useThinking");
const thinkingWidget = pixverseNode.widgets.find(w => w.name === "thinking");
// Helper function to toggle widget enabled state (exact same pattern)
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
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) {
const nodeElement = pixverseNode.htmlElements?.widgetsContainer || pixverseNode.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";
}
}
}
}
pixverseNode.setDirtyCanvas(true);
}
appendWidgetCB(useWidget, () => {
setTimeout(toggleEnabled, 50);
});
setTimeout(toggleEnabled, 100);
}
// Set up all toggle handlers
if (useEffectWidget && effectWidget) {
toggleWidgetState(useEffectWidget, effectWidget, "effect");
}
if (useCameraMovementWidget && cameraMovementWidget) {
toggleWidgetState(useCameraMovementWidget, cameraMovementWidget, "cameraMovement");
}
if (useStyleWidget && styleWidget) {
toggleWidgetState(useStyleWidget, styleWidget, "style");
}
if (useMotionModeWidget && motionModeWidget) {
toggleWidgetState(useMotionModeWidget, motionModeWidget, "motionMode");
}
if (useSoundEffectSwitchWidget && soundEffectSwitchWidget) {
toggleWidgetState(useSoundEffectSwitchWidget, soundEffectSwitchWidget, "soundEffectSwitch");
}
if (useSoundEffectContentWidget && soundEffectContentWidget) {
toggleWidgetState(useSoundEffectContentWidget, soundEffectContentWidget, "soundEffectContent");
}
if (useAudioWidget && audioWidget) {
toggleWidgetState(useAudioWidget, audioWidget, "audio");
}
if (useMultiClipWidget && multiClipWidget) {
toggleWidgetState(useMultiClipWidget, multiClipWidget, "multiClip");
}
if (useThinkingWidget && thinkingWidget) {
toggleWidgetState(useThinkingWidget, thinkingWidget, "thinking");
}
}
function lightricksProviderSettingsToggleHandler(lightricksNode) {
const useStartTimeWidget = lightricksNode.widgets.find(w => w.name === "useStartTime");
const startTimeWidget = lightricksNode.widgets.find(w => w.name === "startTime");
@@ -1334,6 +1442,11 @@ function imageInferenceToggleHandler(imageInferenceNode) {
const clipSkipWidget = imageInferenceNode.widgets.find(w => w.name === "clipSkip");
const maskMarginWidget = imageInferenceNode.widgets.find(w => w.name === "Mask Margin");
const maskMarginValueWidget = imageInferenceNode.widgets.find(w => w.name === "maskMargin");
const useResolutionWidget = imageInferenceNode.widgets.find(w => w.name === "useResolution");
const resolutionWidget = imageInferenceNode.widgets.find(w => w.name === "resolution");
const dimensionsWidget = imageInferenceNode.widgets.find(w => w.name === "dimensions");
const widthWidget = imageInferenceNode.widgets.find(w => w.name === "width");
const heightWidget = imageInferenceNode.widgets.find(w => w.name === "height");
// Helper function to toggle widget enabled state (exact same pattern as videoInferenceDimensionsHandler)
function toggleWidgetState(useWidget, paramWidget, paramName) {
@@ -1406,6 +1519,66 @@ function imageInferenceToggleHandler(imageInferenceNode) {
if (maskMarginWidget && maskMarginValueWidget) {
toggleWidgetState(maskMarginWidget, maskMarginValueWidget, "maskMargin");
}
// Handle Resolution
if (useResolutionWidget && resolutionWidget) {
toggleWidgetState(useResolutionWidget, resolutionWidget, "resolution");
}
// Handle Dimensions - disable width/height when dimensions is "None"
if (dimensionsWidget && widthWidget && heightWidget) {
function toggleDimensionsState() {
const dimensionsValue = dimensionsWidget.value;
const enabled = dimensionsValue !== "None";
// Toggle width widget
if (widthWidget.inputEl) {
widthWidget.inputEl.disabled = !enabled;
widthWidget.inputEl.style.opacity = enabled ? "1" : "0.5";
widthWidget.inputEl.style.cursor = enabled ? "text" : "not-allowed";
widthWidget.inputEl.readOnly = !enabled;
}
widthWidget.disabled = !enabled;
// Toggle height widget
if (heightWidget.inputEl) {
heightWidget.inputEl.disabled = !enabled;
heightWidget.inputEl.style.opacity = enabled ? "1" : "0.5";
heightWidget.inputEl.style.cursor = enabled ? "text" : "not-allowed";
heightWidget.inputEl.readOnly = !enabled;
}
heightWidget.disabled = !enabled;
// Fallback: try to find inputs via DOM if inputEl is not available
if (!widthWidget.inputEl || !heightWidget.inputEl) {
const nodeElement = imageInferenceNode.htmlElements?.widgetsContainer || imageInferenceNode.htmlElements;
if (nodeElement) {
const widthInput = nodeElement.querySelector(`input[name="width"]`);
const heightInput = nodeElement.querySelector(`input[name="height"]`);
if (widthInput) {
widthInput.disabled = !enabled;
widthInput.style.opacity = enabled ? "1" : "0.5";
widthInput.style.cursor = enabled ? "text" : "not-allowed";
widthInput.readOnly = !enabled;
}
if (heightInput) {
heightInput.disabled = !enabled;
heightInput.style.opacity = enabled ? "1" : "0.5";
heightInput.style.cursor = enabled ? "text" : "not-allowed";
heightInput.readOnly = !enabled;
}
}
}
imageInferenceNode.setDirtyCanvas(true);
}
appendWidgetCB(dimensionsWidget, () => {
setTimeout(toggleDimensionsState, 50);
});
setTimeout(toggleDimensionsState, 100);
}
}
function videoInferenceDimensionsHandler(videoInferenceNode) {
@@ -1517,7 +1690,7 @@ function videoModelSearchFilterHandler(videoModelSearchNode) {
],
"PixVerse": [
"pixverse:1@1 (PixVerse v3.5)", "pixverse:1@2 (PixVerse v4)",
"pixverse:1@3 (PixVerse v4.5)", "pixverse:lipsync@1 (PixVerse LipSync)",
"pixverse:1@3 (PixVerse v4.5)", "pixverse:1@6 (PixVerse v5.5)", "pixverse:lipsync@1 (PixVerse LipSync)",
],
"Vidu": [
"vidu:1@0 (Vidu Q1 Classic)", "vidu:1@1 (Vidu Q1)",
@@ -1579,6 +1752,7 @@ function videoModelSearchFilterHandler(videoModelSearchNode) {
"pixverse:1@1": {"width": 640, "height": 360},
"pixverse:1@2": {"width": 640, "height": 360},
"pixverse:1@3": {"width": 640, "height": 360},
"pixverse:1@6": {"width": 640, "height": 360},
"pixverse:lipsync@1": {"width": 640, "height": 360},
"vidu:1@0": {"width": 1920, "height": 1080},
"vidu:1@1": {"width": 1920, "height": 1080},
@@ -2044,4 +2218,5 @@ export {
klingProviderSettingsToggleHandler,
lumaProviderSettingsToggleHandler,
briaProviderSettingsToggleHandler,
pixverseProviderSettingsToggleHandler,
};
+91 -14
View File
@@ -1,4 +1,5 @@
from .utils import runwareUtils as rwUtils
import comfy.model_management
class txt2img:
@@ -52,6 +53,14 @@ class txt2img:
"max": 6048,
"step": 1,
}),
"useResolution": ("BOOLEAN", {
"tooltip": "Enable to include resolution parameter in API request. Disable if your model doesn't support resolution.",
"default": False,
}),
"resolution": (["960p", "1080p", "1k", "2k", "4k"], {
"tooltip": "Select a standard resolution for Image Inference. Only used when 'Use Resolution' is enabled.",
"default": "1k",
}),
"useSteps": ("BOOLEAN", {
"tooltip": "Enable to include steps parameter in API request. Disable if your model doesn't support steps (like nano banana).",
"default": True,
@@ -86,10 +95,10 @@ class txt2img:
"default": True,
}),
"seed": ("INT", {
"tooltip": "A value used to randomize the image generation. If you want to make images reproducible (generate the same image multiple times), you can use the same seed value. Set to 0 to auto-generate a random seed.",
"tooltip": "A value used to randomize the image generation. If you want to make images reproducible (generate the same image multiple times), you can use the same seed value.",
"default": 1,
"min": 0,
"max": 4294967295,
"min": 1,
"max": 2147483647,
}),
"useClipSkip": ("BOOLEAN", {
"tooltip": "Enable to include clipSkip parameter in API request. Disable if your model doesn't support clipSkip.",
@@ -134,6 +143,7 @@ class txt2img:
"tooltip": "Applies optimized acceleration presets that automatically configure multiple generation parameters for the best speed and quality balance. This parameter serves as an abstraction layer that intelligently adjusts acceleratorOptions, steps, scheduler, and other underlying settings.\n\nAvailable values:\n- none: No acceleration applied, uses default parameter values.\n- low: Minimal acceleration with optimized settings for lowest quality loss.\n- medium: Balanced acceleration preset with moderate speed improvements.\n- high: Maximum acceleration with caching and aggressive optimizations for fastest generation.",
"default": "none",
}),
},
"optional": {
"Accelerator": ("RUNWAREACCELERATOR", {
@@ -240,6 +250,8 @@ class txt2img:
outputFormat = kwargs.get("outputFormat", "WEBP")
batchSize = kwargs.get("batchSize", 1)
acceleration = kwargs.get("acceleration", "none")
useResolution = kwargs.get("useResolution", False)
resolution = kwargs.get("resolution", "1k")
if (maskImage is not None and seedImage is None):
raise Exception("Mask Image Requires Seed Image To Be Provided!")
@@ -249,10 +261,11 @@ class txt2img:
"taskType": "imageInference",
"taskUUID": rwUtils.genRandUUID(),
"model": runwareModel,
"outputType": "base64Data",
"outputType": "URL",
"outputFormat": outputFormat,
"outputQuality": rwUtils.OUTPUT_QUALITY,
"numberResults": batchSize,
"deliveryMethod": "async",
}
]
@@ -299,6 +312,10 @@ class txt2img:
if acceleration and acceleration != "none":
genConfig[0]["acceleration"] = acceleration
# Add resolution if enabled
if useResolution:
genConfig[0]["resolution"] = resolution
if (runwareLora is not None):
if (isinstance(runwareLora, list)):
genConfig[0]["lora"] = runwareLora
@@ -363,15 +380,75 @@ class txt2img:
if (multiInferenceMode):
return (None, genConfig)
else:
# Debug: Print the request being sent
print(f"[DEBUG] Sending Image Inference Request:")
print(f"[DEBUG] Request Payload: {rwUtils.safe_json_dumps(genConfig, indent=2)}")
try:
# Debug: Print the request being sent
print(f"[DEBUG] Sending Image Inference Request:")
print(f"[DEBUG] Request Payload: {rwUtils.safe_json_dumps(genConfig, indent=2)}")
genResult = rwUtils.inferenecRequest(genConfig)
# Debug: Print the response received
print(f"[DEBUG] Received Image Inference Response:")
print(f"[DEBUG] Response: {rwUtils.safe_json_dumps(genResult, indent=2)}")
except Exception as e:
# Re-raise the original error without modification
raise e
genResult = rwUtils.inferenecRequest(genConfig)
# Extract task UUID for polling (async delivery)
taskUUID = genConfig[0]["taskUUID"]
# Debug: Print the response received
print(f"[DEBUG] Received Image Inference Response:")
print(f"[DEBUG] Response: {rwUtils.safe_json_dumps(genResult, indent=2)}")
images = rwUtils.convertImageB64List(genResult)
return (images, None)
# Poll for image completion
while True:
# Check for interrupt before each poll
comfy.model_management.throw_exception_if_processing_interrupted()
# Poll for image result
pollResult = rwUtils.pollVideoResult(taskUUID) # Reuse pollVideoResult as it's generic
print(f"[Debugging] Poll result: {rwUtils.safe_json_dumps(pollResult, indent=2) if isinstance(pollResult, (dict, list)) else pollResult}")
# Check for errors first
if pollResult and "errors" in pollResult and len(pollResult["errors"]) > 0:
error_info = pollResult["errors"][0]
error_message = error_info.get("message", "Unknown error")
# Extract more detailed error info if available
if "responseContent" in error_info:
response_content = error_info["responseContent"]
# Handle both string and dict response content
if isinstance(response_content, str):
detailed_message = response_content
elif isinstance(response_content, dict):
detailed_message = response_content.get("message", str(response_content))
else:
detailed_message = str(response_content)
if detailed_message:
error_message = f"{error_message}\nProvider Error: {detailed_message}"
# Include taskUUID for debugging
task_uuid = error_info.get("taskUUID", "unknown")
raise Exception(f"Image generation failed (Task: {task_uuid}): {error_message}")
if pollResult and "data" in pollResult and len(pollResult["data"]) > 0:
image_data = pollResult["data"][0]
# Check status directly
if "status" in image_data:
status = image_data["status"]
if status == "success":
# Check for image data (imageURL, imageBase64Data, mediaURL)
if "imageURL" in image_data or "imageBase64Data" in image_data or "imageURL" in image_data:
# Convert and return images
images = rwUtils.convertImageB64List(pollResult)
return (images, None)
# If status is "processing", continue polling
# Check for interrupt before waiting
comfy.model_management.throw_exception_if_processing_interrupted()
# Wait before next poll (split into smaller chunks to allow more frequent interrupt checks)
for _ in range(10): # 10 x 0.1 second = 1 second total
comfy.model_management.throw_exception_if_processing_interrupted()
rwUtils.time.sleep(0.1)
@@ -6,32 +6,80 @@ class RunwarePixverseProviderSettings:
def INPUT_TYPES(cls):
return {
"required": {
"useEffect": ("BOOLEAN", {
"tooltip": "Enable to include effect parameter in API request.",
"default": False,
}),
"effect": (["none", "boom drop", "zoom in", "zoom out", "pan left", "pan right", "tilt up", "tilt down"], {
"default": "none",
"tooltip": "Camera effect for Pixverse videos.",
"tooltip": "Camera effect for Pixverse videos. Only used when 'Use Effect' is enabled.",
}),
"useCameraMovement": ("BOOLEAN", {
"tooltip": "Enable to include cameraMovement parameter in API request.",
"default": False,
}),
"cameraMovement": (["none", "static", "dynamic", "smooth"], {
"default": "none",
"tooltip": "Camera movement style for Pixverse videos.",
"tooltip": "Camera movement style for Pixverse videos. Only used when 'Use Camera Movement' is enabled.",
}),
"useStyle": ("BOOLEAN", {
"tooltip": "Enable to include style parameter in API request.",
"default": False,
}),
"style": (["anime", "realistic", "cinematic", "artistic", "vintage", "modern"], {
"default": "realistic",
"tooltip": "Visual style for Pixverse videos.",
"tooltip": "Visual style for Pixverse videos. Only used when 'Use Style' is enabled.",
}),
"useMotionMode": ("BOOLEAN", {
"tooltip": "Enable to include motionMode parameter in API request.",
"default": False,
}),
"motionMode": (["normal", "slow", "fast", "smooth", "dynamic"], {
"default": "normal",
"tooltip": "Motion mode for Pixverse videos.",
"tooltip": "Motion mode for Pixverse videos. Only used when 'Use Motion Mode' is enabled.",
}),
"useSoundEffectSwitch": ("BOOLEAN", {
"tooltip": "Enable to include soundEffectSwitch parameter in API request.",
"default": False,
}),
"soundEffectSwitch": ("BOOLEAN", {
"default": False,
"tooltip": "Enable or disable sound effects for Pixverse videos.",
"tooltip": "Enable or disable sound effects for Pixverse videos. Only used when 'Use Sound Effect Switch' is enabled.",
"label_on": "Enabled",
"label_off": "Disabled",
}),
"useSoundEffectContent": ("BOOLEAN", {
"tooltip": "Enable to include soundEffectContent parameter in API request.",
"default": False,
}),
"soundEffectContent": ("STRING", {
"multiline": True,
"placeholder": "Sound effect description or content",
"tooltip": "Sound effect content for Pixverse videos.",
"tooltip": "Sound effect content for Pixverse videos. Only used when 'Use Sound Effect Content' is enabled.",
}),
"useAudio": ("BOOLEAN", {
"tooltip": "Enable to include audio parameter in API request.",
"default": False,
}),
"audio": ("BOOLEAN", {
"tooltip": "Controls audio generation for Pixverse videos. Only used when 'Use Audio' is enabled.",
"default": False,
}),
"useMultiClip": ("BOOLEAN", {
"tooltip": "Enable to include multiClip parameter in API request.",
"default": False,
}),
"multiClip": ("BOOLEAN", {
"tooltip": "Multi-clip (Multi-shot) for Pixverse videos. Only used when 'Use Multi Clip' is enabled.",
"default": False,
}),
"useThinking": ("BOOLEAN", {
"tooltip": "Enable to include thinking parameter in API request.",
"default": False,
}),
"thinking": (["enabled", "disabled", "auto"], {
"tooltip": "Thinking option for Pixverse videos. Only used when 'Use Thinking' is enabled.",
"default": "auto",
}),
}
}
@@ -43,25 +91,46 @@ class RunwarePixverseProviderSettings:
CATEGORY = "Runware"
def create_pixverse_settings(self, **kwargs):
useEffect = kwargs.get("useEffect", False)
effect = kwargs.get("effect", "none")
useCameraMovement = kwargs.get("useCameraMovement", False)
cameraMovement = kwargs.get("cameraMovement", "none")
useStyle = kwargs.get("useStyle", False)
style = kwargs.get("style", "realistic")
useMotionMode = kwargs.get("useMotionMode", False)
motionMode = kwargs.get("motionMode", "normal")
useSoundEffectSwitch = kwargs.get("useSoundEffectSwitch", False)
soundEffectSwitch = kwargs.get("soundEffectSwitch", False)
useSoundEffectContent = kwargs.get("useSoundEffectContent", False)
soundEffectContent = kwargs.get("soundEffectContent", "")
useAudio = kwargs.get("useAudio", False)
audio = kwargs.get("audio", False)
useMultiClip = kwargs.get("useMultiClip", False)
multiClip = kwargs.get("multiClip", False)
useThinking = kwargs.get("useThinking", False)
thinking = kwargs.get("thinking", "auto")
# Create Pixverse settings dictionary
pixverse_settings = {
"effect": effect,
"cameraMovement": cameraMovement,
"style": style,
"motionMode": motionMode,
"soundEffectSwitch": soundEffectSwitch,
}
# Create Pixverse settings dictionary - only include parameters if their use flags are enabled
pixverse_settings = {}
# Only add soundEffectContent if it's not empty
if soundEffectContent and soundEffectContent.strip():
if useEffect:
pixverse_settings["effect"] = effect
if useCameraMovement:
pixverse_settings["cameraMovement"] = cameraMovement
if useStyle:
pixverse_settings["style"] = style
if useMotionMode:
pixverse_settings["motionMode"] = motionMode
if useSoundEffectSwitch:
pixverse_settings["soundEffectSwitch"] = soundEffectSwitch
if useSoundEffectContent and soundEffectContent and soundEffectContent.strip():
pixverse_settings["soundEffectContent"] = soundEffectContent
if useAudio:
pixverse_settings["audio"] = audio
if useMultiClip:
pixverse_settings["multiClip"] = multiClip
if useThinking:
pixverse_settings["thinking"] = thinking
# Remove "none" values to keep the settings clean
pixverse_settings = {k: v for k, v in pixverse_settings.items() if v != "none" and v is not None}
+19 -2
View File
@@ -561,8 +561,25 @@ def convertImageB64List(imageDataObject):
"maskImageBase64Data", result.get("guideImageBase64Data", False)
),
)
generatedImage = convertIMG2Tensor(generatedImage)
images += (generatedImage,)
if generatedImage:
# Handle base64 data
generatedImage = convertIMG2Tensor(generatedImage)
images += (generatedImage,)
else:
# If no base64 data, try to get image URL (check both imageURL and mediaURL)
imageURL = result.get("imageURL") or result.get("mediaURL")
if imageURL:
# Download image from URL and convert to tensor
try:
response = requests.get(imageURL, timeout=30)
response.raise_for_status()
image = Image.open(io.BytesIO(response.content))
imageNP = np.array(image).astype(np.float32) / 255.0
tensorImage = torch.from_numpy(imageNP).squeeze()
images += (tensorImage,)
except Exception as e:
print(f"[Error] Failed to download image from URL {imageURL}: {str(e)}")
raise Exception(f"Failed to download image from URL: {str(e)}")
images = torch.stack(images, dim=0)
return images
+3 -3
View File
@@ -50,10 +50,10 @@ class txt2vid:
"default": True,
}),
"seed": ("INT", {
"tooltip": "A value used to randomize the video generation. If you want to make videos reproducible (generate the same video multiple times), you can use the same seed value. Set to 0 to auto-generate a random seed.",
"tooltip": "A value used to randomize the video generation. If you want to make videos reproducible (generate the same video multiple times), you can use the same seed value.",
"default": 1,
"min": 0,
"max": 4294967295,
"min": 1,
"max": 2147483647,
}),
"useSteps": ("BOOLEAN", {
"tooltip": "Enable to include steps parameter in API request. Disable if your model doesn't support steps.",
+2
View File
@@ -42,6 +42,7 @@ class videoModelSearch:
"pixverse:1@1 (PixVerse v3.5)",
"pixverse:1@2 (PixVerse v4)",
"pixverse:1@3 (PixVerse v4.5)",
"pixverse:1@6 (PixVerse v5.5)",
"pixverse:lipsync@1 (PixVerse LipSync)",
],
"Vidu": [
@@ -119,6 +120,7 @@ class videoModelSearch:
"pixverse:1@1": {"width": 640, "height": 360},
"pixverse:1@2": {"width": 640, "height": 360},
"pixverse:1@3": {"width": 640, "height": 360},
"pixverse:1@6": {"width": 640, "height": 360},
"pixverse:lipsync@1": {"width": 640, "height": 360},
# Vidu 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
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
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
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
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
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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long