diff --git a/__init__.py b/__init__.py index 8abbb91..46644b6 100644 --- a/__init__.py +++ b/__init__.py @@ -3,7 +3,7 @@ from .custom_nodes.uvr import UVR5Node from .custom_nodes.rvc import RVCNode from .custom_nodes.loaders import DownloadAudio, LoadAudio, LoadWhisperModelNode, LoadRVCModelNode, LoadHubertModel, LoadPitchExtractionParams from .custom_nodes.output import PreviewAudio -from .custom_nodes.utils import AudioBatchValueNode, MergeImageBatches, MergeLatentBatches, ImageRepeatInterleavedNode, LatentRepeatInterleavedNode, MergeAudioNode, SimpleMathNode, SliceNode +from .custom_nodes.utils import Any2ListNode, AudioBatchValueNode, MergeImageBatches, MergeLatentBatches, ImageRepeatInterleavedNode, LatentRepeatInterleavedNode, MergeAudioNode, SimpleMathNode, SliceNode, ZipImagesNode # Set the web directory, any .js file in that directory will be loaded by the frontend as a frontend extension WEB_DIRECTORY = "./web" @@ -29,7 +29,9 @@ NODE_CLASS_MAPPINGS = { "DownloadAudio": DownloadAudio, "BatchedTranscriptionEncoderNode": BatchedTranscriptionEncoderNode, "SimpleMathNode": SimpleMathNode, - "SliceNode": SliceNode + "SliceNode": SliceNode, + "ZipNode": ZipImagesNode, + "Any2ListNode": Any2ListNode } # A dictionary that contains the friendly/humanly readable titles for the nodes @@ -52,5 +54,7 @@ NODE_DISPLAY_NAME_MAPPINGS = { "LatentRepeatInterleavedNode": "🌺Latent Repeat Interleaved", "BatchedTranscriptionEncoderNode": "🌺Batched CLIP Transcription Encode (Prompt)", "SimpleMathNode": "🌺Simple Math Operations", - "SliceNode": "🌺Slice Array" + "SliceNode": "🌺Slice Array", + "ZipNode": "🌺Zip Images", + "Any2ListNode": "🌺Any to List" } \ No newline at end of file diff --git a/custom_nodes/stt.py b/custom_nodes/stt.py index 4c25b8a..69e854a 100644 --- a/custom_nodes/stt.py +++ b/custom_nodes/stt.py @@ -264,7 +264,7 @@ class BatchedTranscriptionEncoderNode: pooled.append(pc.squeeze()) num_chunks = len(total_chunks) - duration_list = list(map(np.round,duration_list)) + duration_list = np.round(duration_list) num_frames = int(np.sum(duration_list))+1 final_pooled_output = torch.nested.to_padded_tensor(torch.nested.nested_tensor(pooled, dtype=torch.float32),0) final_conditioning = torch.nested.to_padded_tensor(torch.nested.nested_tensor(cond, dtype=torch.float32),0) @@ -278,4 +278,4 @@ class BatchedTranscriptionEncoderNode: print(f"{duration_list=}") print(f"{num_chunks=}, {max_chunks=}, {num_frames=}") - return (conditioning, batch_prompt_text, duration_list, num_chunks, num_frames) \ No newline at end of file + return (conditioning, batch_prompt_text, list(map(int,duration_list)), num_chunks, num_frames) \ No newline at end of file diff --git a/custom_nodes/utils.py b/custom_nodes/utils.py index 6675639..9d4baa1 100644 --- a/custom_nodes/utils.py +++ b/custom_nodes/utils.py @@ -392,6 +392,11 @@ class MergeAudioNode: del audios audio_name = os.path.basename(audio_path) return {"ui": {"preview": [{"filename": audio_name, "type": "temp", "subfolder": "preview", "widgetId": widgetId}]}, "result": (lambda: audio_to_bytes(*merged_audio),)} + + @classmethod + def IS_CHANGED(cls, audio1, audio2, sr="None", merge_type="median", normalize=False, audio3_opt=None, audio4_opt=None): + audios = [audio() for audio in [audio1, audio2, audio3_opt, audio4_opt] if audio is not None] + return get_hash(sr, merge_type, normalize, *audios) class SimpleMathNode: def __init__(self): @@ -401,8 +406,8 @@ class SimpleMathNode: def INPUT_TYPES(s): return { "optional": { - "n1": ("INT,FLOAT", { "default": 0.0, "step": 0.1 }), - "n2": ("INT,FLOAT", { "default": 0.0, "step": 0.1 }), + "n1": ("INT,FLOAT", { "default": None, "step": 0.1 }), + "n2": ("INT,FLOAT", { "default": None, "step": 0.1 }), "round_up": ("BOOLEAN", {"default": False}) }, "required": { @@ -410,11 +415,11 @@ class SimpleMathNode: }, } - RETURN_TYPES = ("INT", "FLOAT", ) + RETURN_TYPES = ("INT", "FLOAT") FUNCTION = "do_math" CATEGORY = CATEGORY - def do_math(self, operation, n1 = 0.0, n2 = 0.0, round_up=False): + def do_math(self, operation, n1 = None, n2 = None, round_up=False): a, b = np.array(n1).flatten(), np.array(n2).flatten() if operation=="ADD": number=a+b elif operation=="SUBTRACT": number=a-b @@ -425,7 +430,7 @@ class SimpleMathNode: elif operation=="MODULUS": number=a%b elif operation=="MIN": number=np.array(list(map(min,zip(a,b)))) elif operation=="MAX": number=np.array(list(map(max,zip(a,b)))) - else: number=np.array(n1 or n2).flatten() + else: number=a if n1 is not None else b print(f"{a=} \n{operation=} \n{b=} \n{number=}") @@ -465,4 +470,45 @@ class SliceNode: def slice(self, array, start=0, end=-1): if end==-1: end=len(array) - return (array[start:end],) \ No newline at end of file + return (array[start:end],) + +class ZipImagesNode: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "images1": ("IMAGE",), + "images2": ("IMAGE",), + }, + } + + RETURN_TYPES = ("IMAGE",) + OUTPUT_IS_LIST = (True, ) + FUNCTION = "dozip" + CATEGORY = CATEGORY + + def dozip(self, images1, images2): + return (list(map(torch.stack,zip(images1,images2))),) + +class Any2ListNode: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "any": (AlwaysEqualProxy("*"),), + }, + } + + RETURN_TYPES = (AlwaysEqualProxy("*"),) + OUTPUT_IS_LIST = (True, ) + FUNCTION = "to" + CATEGORY = CATEGORY + + def to(self, any): + return (list(any),) \ No newline at end of file diff --git a/web/js/ui_handlers.js b/web/js/ui_handlers.js index 6318191..2ec7a18 100644 --- a/web/js/ui_handlers.js +++ b/web/js/ui_handlers.js @@ -309,6 +309,13 @@ app.registerExtension({ }) break; + case "Any2ListNode": + chainCallback(nodeType.prototype, "onConnectInput", function (_, inputs) { + this.outputs[0].name = inputs; + this.outputs[0].type = inputs; + }) + break; + default: break