From bfc5d50b6cf4847336ae4ebb9c7b51393b4b39cf Mon Sep 17 00:00:00 2001 From: kijai <40791699+kijai@users.noreply.github.com> Date: Wed, 13 May 2026 22:55:16 +0300 Subject: [PATCH] Make audio processing nodes handle None -inputs --- comfy_extras/nodes_audio.py | 52 ++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/comfy_extras/nodes_audio.py b/comfy_extras/nodes_audio.py index 6382dd618..fcc1c34d5 100644 --- a/comfy_extras/nodes_audio.py +++ b/comfy_extras/nodes_audio.py @@ -82,6 +82,8 @@ class VAEEncodeAudio(IO.ComfyNode): @classmethod def execute(cls, vae, audio) -> IO.NodeOutput: + if audio is None: + raise ValueError("VAEEncodeAudio: input audio is None (source video may have no audio track).") sample_rate = audio["sample_rate"] vae_sample_rate = getattr(vae, "audio_sample_rate", 44100) if vae_sample_rate != sample_rate: @@ -171,6 +173,8 @@ class SaveAudio(IO.ComfyNode): @classmethod def execute(cls, audio, filename_prefix="ComfyUI", format="flac") -> IO.NodeOutput: + if audio is None: + raise ValueError("SaveAudio: input audio is None (source video may have no audio track).") return IO.NodeOutput( ui=UI.AudioSaveHelper.get_save_audio_ui(audio, filename_prefix=filename_prefix, cls=cls, format=format) ) @@ -198,6 +202,8 @@ class SaveAudioMP3(IO.ComfyNode): @classmethod def execute(cls, audio, filename_prefix="ComfyUI", format="mp3", quality="128k") -> IO.NodeOutput: + if audio is None: + raise ValueError("SaveAudioMP3: input audio is None (source video may have no audio track).") return IO.NodeOutput( ui=UI.AudioSaveHelper.get_save_audio_ui( audio, filename_prefix=filename_prefix, cls=cls, format=format, quality=quality @@ -226,6 +232,8 @@ class SaveAudioOpus(IO.ComfyNode): @classmethod def execute(cls, audio, filename_prefix="ComfyUI", format="opus", quality="V3") -> IO.NodeOutput: + if audio is None: + raise ValueError("SaveAudioOpus: input audio is None (source video may have no audio track).") return IO.NodeOutput( ui=UI.AudioSaveHelper.get_save_audio_ui( audio, filename_prefix=filename_prefix, cls=cls, format=format, quality=quality @@ -252,6 +260,8 @@ class PreviewAudio(IO.ComfyNode): @classmethod def execute(cls, audio) -> IO.NodeOutput: + if audio is None: + raise ValueError("PreviewAudio: input audio is None (source video may have no audio track).") return IO.NodeOutput(ui=UI.PreviewAudio(audio, cls=cls)) save_flac = execute # TODO: remove @@ -392,21 +402,26 @@ class TrimAudioDuration(IO.ComfyNode): @classmethod def execute(cls, audio, start_index, duration) -> IO.NodeOutput: + if audio is None: + return IO.NodeOutput(None) waveform = audio["waveform"] sample_rate = audio["sample_rate"] audio_length = waveform.shape[-1] + if audio_length == 0: + return IO.NodeOutput(audio) + if start_index < 0: start_frame = audio_length + int(round(start_index * sample_rate)) else: start_frame = int(round(start_index * sample_rate)) - start_frame = max(0, min(start_frame, audio_length - 1)) + start_frame = max(0, min(start_frame, audio_length)) end_frame = start_frame + int(round(duration * sample_rate)) end_frame = max(0, min(end_frame, audio_length)) if start_frame >= end_frame: - raise ValueError("AudioTrim: Start time must be less than end time and be within the audio length.") + raise ValueError("TrimAudioDuration: Start time must be less than end time and be within the audio length.") return IO.NodeOutput({"waveform": waveform[..., start_frame:end_frame], "sample_rate": sample_rate}) @@ -433,11 +448,13 @@ class SplitAudioChannels(IO.ComfyNode): @classmethod def execute(cls, audio) -> IO.NodeOutput: + if audio is None: + return IO.NodeOutput(None, None) waveform = audio["waveform"] sample_rate = audio["sample_rate"] if waveform.shape[1] != 2: - raise ValueError("AudioSplit: Input audio has only one channel.") + raise ValueError(f"AudioSplit: Input audio must be stereo (2 channels), got {waveform.shape[1]} channel(s).") left_channel = waveform[..., 0:1, :] right_channel = waveform[..., 1:2, :] @@ -465,6 +482,12 @@ class JoinAudioChannels(IO.ComfyNode): @classmethod def execute(cls, audio_left, audio_right) -> IO.NodeOutput: + if audio_left is None and audio_right is None: + return IO.NodeOutput(None) + if audio_left is None: + return IO.NodeOutput(audio_right) + if audio_right is None: + return IO.NodeOutput(audio_left) waveform_left = audio_left["waveform"] sample_rate_left = audio_left["sample_rate"] waveform_right = audio_right["waveform"] @@ -538,6 +561,12 @@ class AudioConcat(IO.ComfyNode): @classmethod def execute(cls, audio1, audio2, direction) -> IO.NodeOutput: + if audio1 is None and audio2 is None: + return IO.NodeOutput(None) + if audio1 is None: + return IO.NodeOutput(audio2) + if audio2 is None: + return IO.NodeOutput(audio1) waveform_1 = audio1["waveform"] waveform_2 = audio2["waveform"] sample_rate_1 = audio1["sample_rate"] @@ -585,6 +614,12 @@ class AudioMerge(IO.ComfyNode): @classmethod def execute(cls, audio1, audio2, merge_method) -> IO.NodeOutput: + if audio1 is None and audio2 is None: + return IO.NodeOutput(None) + if audio1 is None: + return IO.NodeOutput(audio2) + if audio2 is None: + return IO.NodeOutput(audio1) waveform_1 = audio1["waveform"] waveform_2 = audio2["waveform"] sample_rate_1 = audio1["sample_rate"] @@ -595,6 +630,9 @@ class AudioMerge(IO.ComfyNode): length_1 = waveform_1.shape[-1] length_2 = waveform_2.shape[-1] + if length_1 == 0 or length_2 == 0: + return IO.NodeOutput({"waveform": waveform_1, "sample_rate": output_sample_rate}) + if length_2 > length_1: logging.info(f"AudioMerge: Trimming audio2 from {length_2} to {length_1} samples to match audio1 length.") waveform_2 = waveform_2[..., :length_1] @@ -646,6 +684,8 @@ class AudioAdjustVolume(IO.ComfyNode): @classmethod def execute(cls, audio, volume) -> IO.NodeOutput: + if audio is None: + return IO.NodeOutput(None) if volume == 0: return IO.NodeOutput(audio) waveform = audio["waveform"] @@ -729,8 +769,14 @@ class AudioEqualizer3Band(IO.ComfyNode): @classmethod def execute(cls, audio, low_gain_dB, low_freq, mid_gain_dB, mid_freq, mid_q, high_gain_dB, high_freq) -> IO.NodeOutput: + if audio is None: + return IO.NodeOutput(None) waveform = audio["waveform"] sample_rate = audio["sample_rate"] + + if waveform.shape[-1] == 0: + return IO.NodeOutput(audio) + eq_waveform = waveform.clone() # 1. Apply Low Shelf (Bass)