Merge pull request #54 from Runware/feature-resolution-addPixverseProviderSettingsZimage
async for imageInference; pixverseSupport
This commit is contained in:
+3
-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 } 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
@@ -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
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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
|
||||
|
||||
+1
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
Reference in New Issue
Block a user