v1.3.3 Fixed errors due to audio encoding. Fixed minor widget issue.
- Fixed duration_seconds input - Both duration settings are now shown by default - Audio latent fix
This commit is contained in:
@@ -1642,34 +1642,20 @@ class TimelineEditor {
|
|||||||
const mode = this.displayModeWidget ? this.displayModeWidget.value : "seconds";
|
const mode = this.displayModeWidget ? this.displayModeWidget.value : "seconds";
|
||||||
|
|
||||||
if (this.durationFramesWidget) {
|
if (this.durationFramesWidget) {
|
||||||
const isVisible = mode === "frames";
|
// Always visible regardless of display mode
|
||||||
// We don't set type to "hidden" anymore because it breaks rendering in Nodes 2.0.
|
|
||||||
// We keep the type as "INT" and use computeSize to hide it.
|
|
||||||
this.durationFramesWidget.type = "INT";
|
this.durationFramesWidget.type = "INT";
|
||||||
if (!this.durationFramesWidget.options) this.durationFramesWidget.options = {};
|
if (!this.durationFramesWidget.options) this.durationFramesWidget.options = {};
|
||||||
this.durationFramesWidget.options.hidden = !isVisible;
|
this.durationFramesWidget.options.hidden = false;
|
||||||
this.durationFramesWidget.hidden = !isVisible;
|
this.durationFramesWidget.hidden = false;
|
||||||
|
|
||||||
if (isVisible) {
|
|
||||||
delete this.durationFramesWidget.computeSize;
|
delete this.durationFramesWidget.computeSize;
|
||||||
} else {
|
|
||||||
this.durationFramesWidget.computeSize = () => [0, 0];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (this.durationSecondsWidget) {
|
if (this.durationSecondsWidget) {
|
||||||
const isVisible = mode === "seconds";
|
// Always visible regardless of display mode
|
||||||
// We don't set type to "hidden" anymore because it breaks rendering in Nodes 2.0.
|
|
||||||
// We keep the type as "FLOAT" and use computeSize to hide it.
|
|
||||||
this.durationSecondsWidget.type = "FLOAT";
|
this.durationSecondsWidget.type = "FLOAT";
|
||||||
if (!this.durationSecondsWidget.options) this.durationSecondsWidget.options = {};
|
if (!this.durationSecondsWidget.options) this.durationSecondsWidget.options = {};
|
||||||
this.durationSecondsWidget.options.hidden = !isVisible;
|
this.durationSecondsWidget.options.hidden = false;
|
||||||
this.durationSecondsWidget.hidden = !isVisible;
|
this.durationSecondsWidget.hidden = false;
|
||||||
|
|
||||||
if (isVisible) {
|
|
||||||
delete this.durationSecondsWidget.computeSize;
|
delete this.durationSecondsWidget.computeSize;
|
||||||
} else {
|
|
||||||
this.durationSecondsWidget.computeSize = () => [0, 0];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force node resize and redraw deferred to next tick
|
// Force node resize and redraw deferred to next tick
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ class LoadAudioUI:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CATEGORY = "audio"
|
CATEGORY = "WhatDreamsCost"
|
||||||
RETURN_TYPES = ("AUDIO", "FLOAT")
|
RETURN_TYPES = ("AUDIO", "FLOAT")
|
||||||
RETURN_NAMES = ("audio", "duration")
|
RETURN_NAMES = ("audio", "duration")
|
||||||
FUNCTION = "load_audio"
|
FUNCTION = "load_audio"
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ class LoadVideoUI:
|
|||||||
RETURN_TYPES = ("IMAGE", "AUDIO", "FLOAT", "INT")
|
RETURN_TYPES = ("IMAGE", "AUDIO", "FLOAT", "INT")
|
||||||
RETURN_NAMES = ("images", "audio", "duration", "frame_count")
|
RETURN_NAMES = ("images", "audio", "duration", "frame_count")
|
||||||
FUNCTION = "load_video"
|
FUNCTION = "load_video"
|
||||||
CATEGORY = "Custom/Video"
|
CATEGORY = "WhatDreamsCost"
|
||||||
|
|
||||||
def load_video(self, video, frame_rate, display_mode, start_time, end_time, duration, start_frame, end_frame, duration_frames, custom_width=0, custom_height=0, resize_method="maintain aspect ratio", crop_x=0.0, crop_y=0.0, crop_w=1.0, crop_h=1.0, **kwargs):
|
def load_video(self, video, frame_rate, display_mode, start_time, end_time, duration, start_frame, end_frame, duration_frames, custom_width=0, custom_height=0, resize_method="maintain aspect ratio", crop_x=0.0, crop_y=0.0, crop_w=1.0, crop_h=1.0, **kwargs):
|
||||||
if not video:
|
if not video:
|
||||||
|
|||||||
@@ -378,7 +378,7 @@ class LTXDirector(io.ComfyNode):
|
|||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="LTXDirector",
|
node_id="LTXDirector",
|
||||||
display_name="LTX Director",
|
display_name="LTX Director",
|
||||||
category="conditioning/prompt_relay",
|
category="WhatDreamsCost",
|
||||||
description=(
|
description=(
|
||||||
"Same as Prompt Relay Encode, but local prompts and segment lengths are edited "
|
"Same as Prompt Relay Encode, but local prompts and segment lengths are edited "
|
||||||
"visually as draggable blocks on a timeline. The duration_frames input only sets the "
|
"visually as draggable blocks on a timeline. The duration_frames input only sets the "
|
||||||
@@ -584,9 +584,11 @@ class LTXDirector(io.ComfyNode):
|
|||||||
if audio_vae is not None:
|
if audio_vae is not None:
|
||||||
# Helper to generate empty latent
|
# Helper to generate empty latent
|
||||||
def get_empty_latent():
|
def get_empty_latent():
|
||||||
|
# Support both raw AudioVAE objects and ComfyUI VAE wrappers.
|
||||||
|
inner = getattr(audio_vae, "first_stage_model", audio_vae)
|
||||||
z_channels = audio_vae.latent_channels
|
z_channels = audio_vae.latent_channels
|
||||||
audio_freq = audio_vae.first_stage_model.latent_frequency_bins
|
audio_freq = inner.latent_frequency_bins
|
||||||
num_audio_latents = audio_vae.first_stage_model.num_of_latents_from_frames(ltxv_length, float(frame_rate))
|
num_audio_latents = inner.num_of_latents_from_frames(ltxv_length, float(frame_rate))
|
||||||
audio_latents = torch.zeros(
|
audio_latents = torch.zeros(
|
||||||
(1, z_channels, num_audio_latents, audio_freq),
|
(1, z_channels, num_audio_latents, audio_freq),
|
||||||
device=comfy.model_management.intermediate_device(),
|
device=comfy.model_management.intermediate_device(),
|
||||||
@@ -597,8 +599,23 @@ class LTXDirector(io.ComfyNode):
|
|||||||
try:
|
try:
|
||||||
if audio_out is not None:
|
if audio_out is not None:
|
||||||
# 1. Encode audio waveform into latent space
|
# 1. Encode audio waveform into latent space
|
||||||
# VAE expects shape (batch, samples, channels), so we move dim 1 (channels) to the end
|
waveform = audio_out["waveform"]
|
||||||
latent_samples = audio_vae.encode(audio_out["waveform"].movedim(1, -1))
|
if waveform.ndim == 2:
|
||||||
|
waveform = waveform.unsqueeze(0)
|
||||||
|
if waveform.ndim != 3:
|
||||||
|
raise ValueError(
|
||||||
|
f"Expected custom audio waveform with 2 or 3 dims, got shape {tuple(waveform.shape)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Wrapped ComfyUI VAE expects (batch, samples, channels);
|
||||||
|
# raw AudioVAE expects a dict with waveform in (batch, channels, samples).
|
||||||
|
if hasattr(audio_vae, "first_stage_model"):
|
||||||
|
latent_samples = audio_vae.encode(waveform.movedim(1, -1))
|
||||||
|
else:
|
||||||
|
latent_samples = audio_vae.encode({
|
||||||
|
"waveform": waveform,
|
||||||
|
"sample_rate": audio_out["sample_rate"],
|
||||||
|
})
|
||||||
|
|
||||||
if latent_samples.numel() == 0:
|
if latent_samples.numel() == 0:
|
||||||
raise ValueError("Encoded audio latent is empty (0 elements).")
|
raise ValueError("Encoded audio latent is empty (0 elements).")
|
||||||
@@ -629,7 +646,8 @@ class LTXDirector(io.ComfyNode):
|
|||||||
audio_latent = get_empty_latent()
|
audio_latent = get_empty_latent()
|
||||||
log.info("[PromptRelay] Auto-generated empty audio latent.")
|
log.info("[PromptRelay] Auto-generated empty audio latent.")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.warning("[PromptRelay] Could not generate empty audio latent: %s", e)
|
log.error("[PromptRelay] Could not generate empty audio latent: %s", e)
|
||||||
|
raise e
|
||||||
|
|
||||||
return io.NodeOutput(patched, conditioning, latent, audio_latent, guide_data, float(frame_rate), audio_out)
|
return io.NodeOutput(patched, conditioning, latent, audio_latent, guide_data, float(frame_rate), audio_out)
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ class LTXDirectorGuide(LTXVAddGuide):
|
|||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="LTXDirectorGuide",
|
node_id="LTXDirectorGuide",
|
||||||
display_name="LTX Director Guide",
|
display_name="LTX Director Guide",
|
||||||
category="LTXVCustom",
|
category="WhatDreamsCost",
|
||||||
description=(
|
description=(
|
||||||
"Applies guide images from a Prompt Relay Timeline node at the frame positions "
|
"Applies guide images from a Prompt Relay Timeline node at the frame positions "
|
||||||
"and strengths defined on the timeline. Connect guide_data from the timeline node."
|
"and strengths defined on the timeline. Connect guide_data from the timeline node."
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ class LTXKeyframer(io.ComfyNode):
|
|||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="LTXKeyframer",
|
node_id="LTXKeyframer",
|
||||||
display_name="LTX Keyframer",
|
display_name="LTX Keyframer",
|
||||||
category="LTXVCustom",
|
category="WhatDreamsCost",
|
||||||
description="Replaces video latent frames with the encoded input images. Number of widgets is dynamically configured.",
|
description="Replaces video latent frames with the encoded input images. Number of widgets is dynamically configured.",
|
||||||
inputs=inputs,
|
inputs=inputs,
|
||||||
outputs=[
|
outputs=[
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class LTXSequencer(LTXVAddGuide):
|
|||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="LTXSequencer",
|
node_id="LTXSequencer",
|
||||||
display_name="LTX Sequencer",
|
display_name="LTX Sequencer",
|
||||||
category="LTXVCustom",
|
category="WhatDreamsCost",
|
||||||
description="Add multiple guide images at specified frame indices or seconds with strengths. Number of widgets is dynamically configured.",
|
description="Add multiple guide images at specified frame indices or seconds with strengths. Number of widgets is dynamically configured.",
|
||||||
inputs=inputs,
|
inputs=inputs,
|
||||||
outputs=[
|
outputs=[
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class MultiImageLoader:
|
|||||||
RETURN_TYPES = ("IMAGE",) * 51
|
RETURN_TYPES = ("IMAGE",) * 51
|
||||||
RETURN_NAMES = ("multi_output",) + tuple(f"image_{i+1}" for i in range(50))
|
RETURN_NAMES = ("multi_output",) + tuple(f"image_{i+1}" for i in range(50))
|
||||||
FUNCTION = "load_images"
|
FUNCTION = "load_images"
|
||||||
CATEGORY = "image"
|
CATEGORY = "WhatDreamsCost"
|
||||||
|
|
||||||
def resize_image(self, image, width, height, resize_method="keep proportion", interpolation="nearest", multiple_of=0):
|
def resize_image(self, image, width, height, resize_method="keep proportion", interpolation="nearest", multiple_of=0):
|
||||||
MAX_RESOLUTION = 8192
|
MAX_RESOLUTION = 8192
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "WhatDreamsCost-ComfyUI"
|
name = "WhatDreamsCost-ComfyUI"
|
||||||
description = "A variety of custom ComfyUI nodes and workflows for creatives."
|
description = "A variety of custom ComfyUI nodes and workflows for creatives."
|
||||||
version = "1.3.2"
|
version = "1.3.3"
|
||||||
license = {file = "LICENSE"}
|
license = {file = "LICENSE"}
|
||||||
# classifiers = [
|
# classifiers = [
|
||||||
# # For OS-independent nodes (works on all operating systems)
|
# # For OS-independent nodes (works on all operating systems)
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class SpeechLengthCalculator:
|
|||||||
# Added "text" to RETURN_NAMES
|
# Added "text" to RETURN_NAMES
|
||||||
RETURN_NAMES = ("slow_frame_count", "average_frame_count", "fast_frame_count", "text")
|
RETURN_NAMES = ("slow_frame_count", "average_frame_count", "fast_frame_count", "text")
|
||||||
FUNCTION = "calculate_speech"
|
FUNCTION = "calculate_speech"
|
||||||
CATEGORY = "text/speech"
|
CATEGORY = "WhatDreamsCost"
|
||||||
|
|
||||||
def calculate_speech(self, text, fps, additional_time=0.0, text_input=None):
|
def calculate_speech(self, text, fps, additional_time=0.0, text_input=None):
|
||||||
# Prioritize the connected text_input if provided, otherwise fallback to the text widget
|
# Prioritize the connected text_input if provided, otherwise fallback to the text widget
|
||||||
|
|||||||
Reference in New Issue
Block a user