diff --git a/JDCN_AnyFileList.py b/classes/JDCN_AnyFileList.py similarity index 96% rename from JDCN_AnyFileList.py rename to classes/JDCN_AnyFileList.py index da57c57..ae72351 100644 --- a/JDCN_AnyFileList.py +++ b/classes/JDCN_AnyFileList.py @@ -1,144 +1,144 @@ -import os -import glob - -from .shared import FILE_EXTENSIONS - -def get_files_in_folder(folder_path): - if not os.path.isdir(folder_path): - print("Folder does not exist.") - return [] - files = glob.glob(os.path.join(folder_path, "*")) - return files - - -def getSubDirectories(folder_path): - try: - root, dirs, _ = next(os.walk(folder_path)) - return dirs - except StopIteration: - return [] - - -class JDCN_AnyFileList: - - def __init__(self): - pass - - @classmethod - def INPUT_TYPES(s): - - FilterBy = [ - "*", - "images", - "audio", - "video", - "text", - "tensors", - "folder" - ] - - FileExtensions = ["*"] - FileExtensions.extend(FILE_EXTENSIONS["tensors"]) - FileExtensions.extend(FILE_EXTENSIONS["images"]) - FileExtensions.extend(FILE_EXTENSIONS["audio"]) - FileExtensions.extend(FILE_EXTENSIONS["video"]) - FileExtensions.extend(FILE_EXTENSIONS["text"]) - - return { - "required": { - "folder_path": ("STRING", { - "multiline": False, - "default": "undefined" - }), - "filter_by": (FilterBy,), - "extension": (FileExtensions,), - }, - } - - RETURN_TYPES = ("STRING", "STRING", "INT",) - RETURN_NAMES = ("PathList", "NameList", "Total") - OUTPUT_IS_LIST = (True, True, False) - OUTPUT_NODE = True - FUNCTION = "make_list" - - CATEGORY = "🔵 JDCN 🔵" - - def make_list(self, folder_path, filter_by, extension): - - if not os.path.exists(folder_path): - print(f"The folder '{folder_path}' does not exist.") - return ([""], [""], 0) - - search_mode = 0 - extension_search_mode = 0 - - if filter_by == "folder": - search_mode = 1 - elif filter_by == "*": - search_mode = 3 - else: - search_mode = 2 - - if extension == "*": - extension_search_mode = 1 - else: - extension_search_mode = 2 - - path_names = [] - path_list = [] - total = 0 - - if search_mode == 1: - - dirs = [folder for folder in os.listdir(folder_path) if os.path.isdir(os.path.join(folder_path, folder))] - for dir_name in dirs: - full_path = os.path.join(folder_path, dir_name) - path_names.append(dir_name) - path_list.append(full_path) - total = total + 1 - - return (path_list, path_names, total,) - - elif search_mode == 2: - - for root, dirs, files in os.walk(folder_path): - for file in files: - file_name, file_extension = os.path.splitext(file) - if file_extension in FILE_EXTENSIONS[filter_by]: - if extension_search_mode == 2: - if file.endswith(extension): - path_names.append(file_name) - path_list.append(os.path.join(root, file)) - total = total+1 - else: - path_names.append(file_name) - path_list.append(os.path.join(root, file)) - total = total+1 - - return (path_list, path_names, total,) - - elif search_mode == 3: - - dirs = [folder for folder in os.listdir(folder_path)] - for dir_name in dirs: - full_path = os.path.join(folder_path, dir_name) - if extension_search_mode == 2: - if dir_name.endswith(extension): - path_names.append(dir_name) - path_list.append(full_path) - total = total+1 - else: - path_names.append(dir_name) - path_list.append(full_path) - total = total+1 - - return (path_list, path_names, total,) - - -N_CLASS_MAPPINGS = { - "JDCN_AnyFileList": JDCN_AnyFileList, -} - -N_DISPLAY_NAME_MAPPINGS = { - "JDCN_AnyFileList": "JDCN_AnyFileList", -} +import os +import glob + +from .shared import FILE_EXTENSIONS + +def get_files_in_folder(folder_path): + if not os.path.isdir(folder_path): + print("Folder does not exist.") + return [] + files = glob.glob(os.path.join(folder_path, "*")) + return files + + +def getSubDirectories(folder_path): + try: + root, dirs, _ = next(os.walk(folder_path)) + return dirs + except StopIteration: + return [] + + +class JDCN_AnyFileList: + + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + + FilterBy = [ + "*", + "images", + "audio", + "video", + "text", + "tensors", + "folder" + ] + + FileExtensions = ["*"] + FileExtensions.extend(FILE_EXTENSIONS["tensors"]) + FileExtensions.extend(FILE_EXTENSIONS["images"]) + FileExtensions.extend(FILE_EXTENSIONS["audio"]) + FileExtensions.extend(FILE_EXTENSIONS["video"]) + FileExtensions.extend(FILE_EXTENSIONS["text"]) + + return { + "required": { + "folder_path": ("STRING", { + "multiline": False, + "default": "undefined" + }), + "filter_by": (FilterBy,), + "extension": (FileExtensions,), + }, + } + + RETURN_TYPES = ("STRING", "STRING", "INT",) + RETURN_NAMES = ("PathList", "NameList", "Total") + OUTPUT_IS_LIST = (True, True, False) + OUTPUT_NODE = True + FUNCTION = "make_list" + + CATEGORY = "🔵 JDCN 🔵" + + def make_list(self, folder_path, filter_by, extension): + + if not os.path.exists(folder_path): + print(f"The folder '{folder_path}' does not exist.") + return ([""], [""], 0) + + search_mode = 0 + extension_search_mode = 0 + + if filter_by == "folder": + search_mode = 1 + elif filter_by == "*": + search_mode = 3 + else: + search_mode = 2 + + if extension == "*": + extension_search_mode = 1 + else: + extension_search_mode = 2 + + path_names = [] + path_list = [] + total = 0 + + if search_mode == 1: + + dirs = [folder for folder in os.listdir(folder_path) if os.path.isdir(os.path.join(folder_path, folder))] + for dir_name in dirs: + full_path = os.path.join(folder_path, dir_name) + path_names.append(dir_name) + path_list.append(full_path) + total = total + 1 + + return (path_list, path_names, total,) + + elif search_mode == 2: + + for root, dirs, files in os.walk(folder_path): + for file in files: + file_name, file_extension = os.path.splitext(file) + if file_extension in FILE_EXTENSIONS[filter_by]: + if extension_search_mode == 2: + if file.endswith(extension): + path_names.append(file_name) + path_list.append(os.path.join(root, file)) + total = total+1 + else: + path_names.append(file_name) + path_list.append(os.path.join(root, file)) + total = total+1 + + return (path_list, path_names, total,) + + elif search_mode == 3: + + dirs = [folder for folder in os.listdir(folder_path)] + for dir_name in dirs: + full_path = os.path.join(folder_path, dir_name) + if extension_search_mode == 2: + if dir_name.endswith(extension): + path_names.append(dir_name) + path_list.append(full_path) + total = total+1 + else: + path_names.append(dir_name) + path_list.append(full_path) + total = total+1 + + return (path_list, path_names, total,) + + +N_CLASS_MAPPINGS = { + "JDCN_AnyFileList": JDCN_AnyFileList, +} + +N_DISPLAY_NAME_MAPPINGS = { + "JDCN_AnyFileList": "JDCN_AnyFileList", +} diff --git a/JDCN_AnyFileListHelper.py b/classes/JDCN_AnyFileListHelper.py similarity index 100% rename from JDCN_AnyFileListHelper.py rename to classes/JDCN_AnyFileListHelper.py diff --git a/JDCN_AnyFileListRandom.py b/classes/JDCN_AnyFileListRandom.py similarity index 100% rename from JDCN_AnyFileListRandom.py rename to classes/JDCN_AnyFileListRandom.py diff --git a/JDCN_AnyFileSelector.py b/classes/JDCN_AnyFileSelector.py similarity index 95% rename from JDCN_AnyFileSelector.py rename to classes/JDCN_AnyFileSelector.py index 13b91e4..d57dd5f 100644 --- a/JDCN_AnyFileSelector.py +++ b/classes/JDCN_AnyFileSelector.py @@ -1,58 +1,58 @@ -import os -import numpy as np - - -class AlwaysEqualProxy(str): - def __eq__(self, _): - return True - - def __ne__(self, _): - return False - - -class JDCN_AnyFileSelector: - - def __init__(self): - pass - - @classmethod - def INPUT_TYPES(s): - - return { - "required": { - "PathList": ("STRING", {"forceInput": True}), - "Index": ("INT", {"default": 1, "min": 1, "max": 9999}), - "Change": (['fixed', 'increment', 'decrement'],) - }, - } - - INPUT_IS_LIST = True - RETURN_TYPES = ("STRING",) - RETURN_NAMES = ("out",) - OUTPUT_NODE = True - FUNCTION = "make_list" - - CATEGORY = "🔵 JDCN 🔵" - - def make_list(self, PathList, Index, Change): - - Index = Index[0] - 1 - - if len(PathList) == 0: - print("Error in List Variable") - return None - - if Index < 0 or Index >= len(PathList): - print("Error in List Variable") - return None - - return (PathList[Index],) - - -N_CLASS_MAPPINGS = { - "JDCN_AnyFileSelector": JDCN_AnyFileSelector, -} - -N_DISPLAY_NAME_MAPPINGS = { - "JDCN_AnyFileSelector": "JDCN_AnyFileSelector", -} +import os +import numpy as np + + +class AlwaysEqualProxy(str): + def __eq__(self, _): + return True + + def __ne__(self, _): + return False + + +class JDCN_AnyFileSelector: + + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + + return { + "required": { + "PathList": ("STRING", {"forceInput": True}), + "Index": ("INT", {"default": 1, "min": 1, "max": 9999}), + "Change": (['fixed', 'increment', 'decrement'],) + }, + } + + INPUT_IS_LIST = True + RETURN_TYPES = ("STRING",) + RETURN_NAMES = ("out",) + OUTPUT_NODE = True + FUNCTION = "make_list" + + CATEGORY = "🔵 JDCN 🔵" + + def make_list(self, PathList, Index, Change): + + Index = Index[0] - 1 + + if len(PathList) == 0: + print("Error in List Variable") + return None + + if Index < 0 or Index >= len(PathList): + print("Error in List Variable") + return None + + return (PathList[Index],) + + +N_CLASS_MAPPINGS = { + "JDCN_AnyFileSelector": JDCN_AnyFileSelector, +} + +N_DISPLAY_NAME_MAPPINGS = { + "JDCN_AnyFileSelector": "JDCN_AnyFileSelector", +} diff --git a/JDCN_BatchCounter.py b/classes/JDCN_BatchCounter.py similarity index 100% rename from JDCN_BatchCounter.py rename to classes/JDCN_BatchCounter.py diff --git a/JDCN_BatchImageLoadFromDir.py b/classes/JDCN_BatchImageLoadFromDir.py similarity index 100% rename from JDCN_BatchImageLoadFromDir.py rename to classes/JDCN_BatchImageLoadFromDir.py diff --git a/JDCN_BatchImageLoadFromList.py b/classes/JDCN_BatchImageLoadFromList.py similarity index 96% rename from JDCN_BatchImageLoadFromList.py rename to classes/JDCN_BatchImageLoadFromList.py index 9db38e9..9fb2896 100644 --- a/JDCN_BatchImageLoadFromList.py +++ b/classes/JDCN_BatchImageLoadFromList.py @@ -1,150 +1,150 @@ -import random -import os -import torch -import numpy as np -from PIL import Image, ImageSequence - - -def pilToImage(image): - return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0) - - -def extract_file_names(file_paths): - """ - Extract file names without extensions from a list of file paths. - - Parameters: - - file_paths: A list of file paths. - - Returns: - - A list of file names without extensions. - """ - file_names = [] - - for file_path in file_paths: - base_name, _ = os.path.splitext(os.path.basename(file_path)) - file_names.append(base_name) - - return file_names - - -def load_images(image_paths): - """ - Load a list of images using the provided code for reading. - - Parameters: - - image_paths: A list of file paths for the images. - - Returns: - - A list of loaded images. - """ - loaded_images = [] - - for image_path in image_paths: - try: - img = Image.open(image_path) - image = img.convert("RGB") - loaded_images.append(pilToImage(image)) - except Exception as e: - # Catching exceptions to ensure the code doesn't stop in the middle - print(f"Error loading image from '{image_path}': {e}") - - return loaded_images - - -def get_batch_from_list(input_list, batch_size, page_number, direction): - """ - Get a batch of elements from a list based on the batch size, page number, and direction. - - Parameters: - - input_list: The input list to extract batches from. - - batch_size: The size of each batch. - - page_number: The page number to retrieve (1-indexed). - - direction: "TOPTOBOTTOM", "BOTTOMTOTOP", or "RANDOM". - - Returns: - - A list containing the elements of the specified batch. - """ - try: - # Validate the direction parameter - valid_directions = {"TOPTOBOTTOM", "BOTTOMTOTOP", "RANDOM"} - if direction not in valid_directions: - raise ValueError(f"Direction must be one of {valid_directions}.") - - # Ensure input_list is not empty - if not input_list: - raise ValueError("Input list is empty.") - - # Calculate the starting index for the specified page - start_index = (page_number - 1) * batch_size - - # Check for out-of-range conditions - if start_index < 0 or start_index >= len(input_list): - raise ValueError("Start index is out of range.") - - # Define a dictionary to map directions to extraction functions - direction_actions = { - "TOPTOBOTTOM": lambda start, size: input_list[start:start + size], - "BOTTOMTOTOP": lambda start, size: input_list[-start - size:-start][::-1], - "RANDOM": lambda start, size: random.sample(input_list, size) - } - - # Extract the batch based on the direction - batch_extraction = direction_actions[direction] - batch = batch_extraction(start_index, batch_size) - - return batch - - except ValueError as ve: - print(f"Error in get_batch_from_list: {ve}") - return [] - -# Example usage: -# input_list = [1, 2, 3, 4, 5, 6, 7, 8, 9] -# batch_size = 3 -# page_number = 2 -# direction = "RANDOM" -# result = get_batch_from_list(input_list, batch_size, page_number, direction) -# print(result) - - -class JDCN_BatchImageLoadFromList: - - def __init__(self): - pass - - @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "PathList": ("STRING", {"forceInput": True}), - "Index": ("INT", {"default": 1, "min": 1, "max": 9999}), - "BatchSize": ("INT", {"default": 5, "min": 0, "max": 9999}), - "BatchDirection": (["TOPTOBOTTOM", "BOTTOMTOTOP", "RANDOM"],), - }, - } - - INPUT_IS_LIST = True - RETURN_TYPES = ("IMAGE", "STRING", "STRING", "INT") - RETURN_NAMES = ("Images", "ImageNames", "ImagePaths", "Index") - FUNCTION = "doit" - OUTPUT_NODE = True - OUTPUT_IS_LIST = (True, True, True, False) - CATEGORY = "🔵 JDCN 🔵" - - def doit(self, PathList, Index, BatchSize, BatchDirection): - paths = get_batch_from_list(PathList, BatchSize[0], Index[0], BatchDirection[0]) - names = extract_file_names(paths) - images = load_images(paths) - # print(PathList,Index,BatchSize,BatchDirection) - return (images, names, paths, Index) - # return ([],[],[],0) - - -N_CLASS_MAPPINGS = { - "JDCN_BatchImageLoadFromList": JDCN_BatchImageLoadFromList, -} - -N_DISPLAY_NAME_MAPPINGS = { - "JDCN_BatchImageLoadFromList": "JDCN_BatchImageLoadFromList", -} +import random +import os +import torch +import numpy as np +from PIL import Image, ImageSequence + + +def pilToImage(image): + return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0) + + +def extract_file_names(file_paths): + """ + Extract file names without extensions from a list of file paths. + + Parameters: + - file_paths: A list of file paths. + + Returns: + - A list of file names without extensions. + """ + file_names = [] + + for file_path in file_paths: + base_name, _ = os.path.splitext(os.path.basename(file_path)) + file_names.append(base_name) + + return file_names + + +def load_images(image_paths): + """ + Load a list of images using the provided code for reading. + + Parameters: + - image_paths: A list of file paths for the images. + + Returns: + - A list of loaded images. + """ + loaded_images = [] + + for image_path in image_paths: + try: + img = Image.open(image_path) + image = img.convert("RGB") + loaded_images.append(pilToImage(image)) + except Exception as e: + # Catching exceptions to ensure the code doesn't stop in the middle + print(f"Error loading image from '{image_path}': {e}") + + return loaded_images + + +def get_batch_from_list(input_list, batch_size, page_number, direction): + """ + Get a batch of elements from a list based on the batch size, page number, and direction. + + Parameters: + - input_list: The input list to extract batches from. + - batch_size: The size of each batch. + - page_number: The page number to retrieve (1-indexed). + - direction: "TOPTOBOTTOM", "BOTTOMTOTOP", or "RANDOM". + + Returns: + - A list containing the elements of the specified batch. + """ + try: + # Validate the direction parameter + valid_directions = {"TOPTOBOTTOM", "BOTTOMTOTOP", "RANDOM"} + if direction not in valid_directions: + raise ValueError(f"Direction must be one of {valid_directions}.") + + # Ensure input_list is not empty + if not input_list: + raise ValueError("Input list is empty.") + + # Calculate the starting index for the specified page + start_index = (page_number - 1) * batch_size + + # Check for out-of-range conditions + if start_index < 0 or start_index >= len(input_list): + raise ValueError("Start index is out of range.") + + # Define a dictionary to map directions to extraction functions + direction_actions = { + "TOPTOBOTTOM": lambda start, size: input_list[start:start + size], + "BOTTOMTOTOP": lambda start, size: input_list[-start - size:-start][::-1], + "RANDOM": lambda start, size: random.sample(input_list, size) + } + + # Extract the batch based on the direction + batch_extraction = direction_actions[direction] + batch = batch_extraction(start_index, batch_size) + + return batch + + except ValueError as ve: + print(f"Error in get_batch_from_list: {ve}") + return [] + +# Example usage: +# input_list = [1, 2, 3, 4, 5, 6, 7, 8, 9] +# batch_size = 3 +# page_number = 2 +# direction = "RANDOM" +# result = get_batch_from_list(input_list, batch_size, page_number, direction) +# print(result) + + +class JDCN_BatchImageLoadFromList: + + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "PathList": ("STRING", {"forceInput": True}), + "Index": ("INT", {"default": 1, "min": 1, "max": 9999}), + "BatchSize": ("INT", {"default": 5, "min": 0, "max": 9999}), + "BatchDirection": (["TOPTOBOTTOM", "BOTTOMTOTOP", "RANDOM"],), + }, + } + + INPUT_IS_LIST = True + RETURN_TYPES = ("IMAGE", "STRING", "STRING", "INT") + RETURN_NAMES = ("Images", "ImageNames", "ImagePaths", "Index") + FUNCTION = "doit" + OUTPUT_NODE = True + OUTPUT_IS_LIST = (True, True, True, False) + CATEGORY = "🔵 JDCN 🔵" + + def doit(self, PathList, Index, BatchSize, BatchDirection): + paths = get_batch_from_list(PathList, BatchSize[0], Index[0], BatchDirection[0]) + names = extract_file_names(paths) + images = load_images(paths) + # print(PathList,Index,BatchSize,BatchDirection) + return (images, names, paths, Index) + # return ([],[],[],0) + + +N_CLASS_MAPPINGS = { + "JDCN_BatchImageLoadFromList": JDCN_BatchImageLoadFromList, +} + +N_DISPLAY_NAME_MAPPINGS = { + "JDCN_BatchImageLoadFromList": "JDCN_BatchImageLoadFromList", +} diff --git a/JDCN_BatchLatentLoadFromDir.py b/classes/JDCN_BatchLatentLoadFromDir.py similarity index 96% rename from JDCN_BatchLatentLoadFromDir.py rename to classes/JDCN_BatchLatentLoadFromDir.py index 3491f6a..a403d0a 100644 --- a/JDCN_BatchLatentLoadFromDir.py +++ b/classes/JDCN_BatchLatentLoadFromDir.py @@ -1,125 +1,125 @@ -import os -import safetensors.torch - - -def extract_file_name(file_path): - base_name, _ = os.path.splitext(os.path.basename(file_path)) - return base_name - -def extract_file_names(file_paths, skip, load): - file_names = [] - - start_index = skip - end_index = skip + load - - if start_index == end_index: - subset_file_paths = file_paths[start_index:len(file_paths)] - else: - subset_file_paths = file_paths[start_index:end_index] - - for file_path in subset_file_paths: - file_names.append(extract_file_name(file_path)) - return file_names - -def extract_file_paths(file_paths, skip, load): - - start_index = skip - end_index = skip + load - - if start_index == end_index: - subset_file_paths = file_paths[start_index:len(file_paths)] - else: - subset_file_paths = file_paths[start_index:end_index] - - return subset_file_paths - - -def get_files_by_extension(directory_path, extension): - try: - if not os.path.exists(directory_path): - return [] - file_paths = [] - for root, dirs, files in os.walk(directory_path): - for file in files: - if file.endswith(extension): - file_path = os.path.join(root, file) - file_paths.append(file_path) - return file_paths - - except Exception as e: - print(f"Error: {e}") - return [] - - -def read_latent_files(file_paths, skip, load): - - try: - start_index = skip - end_index = skip + load - - if start_index == end_index: - subset_file_paths = file_paths[start_index:len(file_paths)] - else: - subset_file_paths = file_paths[start_index:end_index] - - # print(f"Processing: start index: {start_index}, end index: {end_index}, paths: {subset_file_paths}") - - latents = [] - for file_path in subset_file_paths: - try: - latent = safetensors.torch.load_file(file_path, device="cpu") - multiplier = 1.0 - if "latent_format_version_0" not in latent: - multiplier = 1.0 / 0.18215 - samples = {"samples": latent["latent_tensor"].float() * multiplier} - latents.append(samples) - except Exception as e: - print(f"Error loading latent from file {file_path}: {e}") - return latents - except Exception as e: - print(f"Error: {e}") - return [] - - -class JDCN_BatchLatentLoadFromDir: - def __init__(self): - pass - - @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "Directory": ("STRING", {"default": "directory path"}), - "Load_Cap": ("INT", {"default": 1, "min": 1, "max": 9999}), - "Skip_Frame": ("INT", {"default": 0, "min": 0, "max": 9999}), - }, - } - # INPUT_IS_LIST = True - RETURN_TYPES = ("LATENT", "STRING", "STRING", "INT", "INT", "INT") - RETURN_NAMES = ("Latent", "Latent_Names", "Latent_Paths", "Load_Cap", "Skip_Frame", "Count") - FUNCTION = "doit" - OUTPUT_NODE = True - OUTPUT_IS_LIST = (True, True, True, False, False, False) - CATEGORY = "🔵 JDCN 🔵" - - def doit(self, Directory, Load_Cap, Skip_Frame): - - file_paths = get_files_by_extension(Directory, ".latent") - - if (len(file_paths) == 0): - return ([], [], [], []) - else: - latents = read_latent_files(file_paths, Skip_Frame, Load_Cap) - file_names = extract_file_names(file_paths, Skip_Frame, Load_Cap) - file_paths = extract_file_paths(file_paths, Skip_Frame, Load_Cap) - - return (latents, file_names, file_paths, Load_Cap, Skip_Frame, len(file_names)) - - -N_CLASS_MAPPINGS = { - "JDCN_BatchLatentLoadFromDir": JDCN_BatchLatentLoadFromDir, -} - -N_DISPLAY_NAME_MAPPINGS = { - "JDCN_BatchLatentLoadFromDir": "JDCN_BatchLatentLoadFromDir", -} +import os +import safetensors.torch + + +def extract_file_name(file_path): + base_name, _ = os.path.splitext(os.path.basename(file_path)) + return base_name + +def extract_file_names(file_paths, skip, load): + file_names = [] + + start_index = skip + end_index = skip + load + + if start_index == end_index: + subset_file_paths = file_paths[start_index:len(file_paths)] + else: + subset_file_paths = file_paths[start_index:end_index] + + for file_path in subset_file_paths: + file_names.append(extract_file_name(file_path)) + return file_names + +def extract_file_paths(file_paths, skip, load): + + start_index = skip + end_index = skip + load + + if start_index == end_index: + subset_file_paths = file_paths[start_index:len(file_paths)] + else: + subset_file_paths = file_paths[start_index:end_index] + + return subset_file_paths + + +def get_files_by_extension(directory_path, extension): + try: + if not os.path.exists(directory_path): + return [] + file_paths = [] + for root, dirs, files in os.walk(directory_path): + for file in files: + if file.endswith(extension): + file_path = os.path.join(root, file) + file_paths.append(file_path) + return file_paths + + except Exception as e: + print(f"Error: {e}") + return [] + + +def read_latent_files(file_paths, skip, load): + + try: + start_index = skip + end_index = skip + load + + if start_index == end_index: + subset_file_paths = file_paths[start_index:len(file_paths)] + else: + subset_file_paths = file_paths[start_index:end_index] + + # print(f"Processing: start index: {start_index}, end index: {end_index}, paths: {subset_file_paths}") + + latents = [] + for file_path in subset_file_paths: + try: + latent = safetensors.torch.load_file(file_path, device="cpu") + multiplier = 1.0 + if "latent_format_version_0" not in latent: + multiplier = 1.0 / 0.18215 + samples = {"samples": latent["latent_tensor"].float() * multiplier} + latents.append(samples) + except Exception as e: + print(f"Error loading latent from file {file_path}: {e}") + return latents + except Exception as e: + print(f"Error: {e}") + return [] + + +class JDCN_BatchLatentLoadFromDir: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "Directory": ("STRING", {"default": "directory path"}), + "Load_Cap": ("INT", {"default": 1, "min": 1, "max": 9999}), + "Skip_Frame": ("INT", {"default": 0, "min": 0, "max": 9999}), + }, + } + # INPUT_IS_LIST = True + RETURN_TYPES = ("LATENT", "STRING", "STRING", "INT", "INT", "INT") + RETURN_NAMES = ("Latent", "Latent_Names", "Latent_Paths", "Load_Cap", "Skip_Frame", "Count") + FUNCTION = "doit" + OUTPUT_NODE = True + OUTPUT_IS_LIST = (True, True, True, False, False, False) + CATEGORY = "🔵 JDCN 🔵" + + def doit(self, Directory, Load_Cap, Skip_Frame): + + file_paths = get_files_by_extension(Directory, ".latent") + + if (len(file_paths) == 0): + return ([], [], [], []) + else: + latents = read_latent_files(file_paths, Skip_Frame, Load_Cap) + file_names = extract_file_names(file_paths, Skip_Frame, Load_Cap) + file_paths = extract_file_paths(file_paths, Skip_Frame, Load_Cap) + + return (latents, file_names, file_paths, Load_Cap, Skip_Frame, len(file_names)) + + +N_CLASS_MAPPINGS = { + "JDCN_BatchLatentLoadFromDir": JDCN_BatchLatentLoadFromDir, +} + +N_DISPLAY_NAME_MAPPINGS = { + "JDCN_BatchLatentLoadFromDir": "JDCN_BatchLatentLoadFromDir", +} diff --git a/JDCN_BatchLatentLoadFromList.py b/classes/JDCN_BatchLatentLoadFromList.py similarity index 100% rename from JDCN_BatchLatentLoadFromList.py rename to classes/JDCN_BatchLatentLoadFromList.py diff --git a/JDCN_BatchSaveLatent.py b/classes/JDCN_BatchSaveLatent.py similarity index 95% rename from JDCN_BatchSaveLatent.py rename to classes/JDCN_BatchSaveLatent.py index cea7e19..5e1110a 100644 --- a/JDCN_BatchSaveLatent.py +++ b/classes/JDCN_BatchSaveLatent.py @@ -1,75 +1,75 @@ -import os -import torch -import comfy.utils -from comfy.cli_args import args - - -def count_files_in_folder(folder_path): - file_count = 0 - for _, _, files in os.walk(folder_path): - file_count += len(files) - return file_count - -def save_latent(index,prefix,output_dir,latent): - - padding = str(index).zfill(4) - file_name = f"{prefix}_{padding}.latent" - file_path = os.path.join(output_dir, file_name) - - output = {} - output["latent_tensor"] = latent - output["latent_format_version_0"] = torch.tensor([]) - - comfy.utils.save_torch_file(output, file_path, None) - - -class JDCN_BatchSaveLatent: - - def __init__(self): - pass - - @classmethod - def INPUT_TYPES(s): - return { - "required": { - "Latents": ("LATENT",), - "Directory": ("STRING", {}), - "FilenamePrefix": ("STRING", {"default": "Latent"}) - }, - } - - INPUT_IS_LIST = True - RETURN_TYPES = () - FUNCTION = "BatchSave" - OUTPUT_NODE = True - CATEGORY = "🔵 JDCN 🔵" - - def BatchSave(self, Latents, Directory, FilenamePrefix): - try: - - Directory = Directory[0] - FilenamePrefix = FilenamePrefix[0] - - if not os.path.exists(Directory): - os.makedirs(Directory) - - lastIndex = count_files_in_folder(Directory) - index = 1 - - for latent in Latents: - save_latent(lastIndex+index,FilenamePrefix,Directory,latent['samples']) - index=index+1 - - except Exception as e: - print(f"Error saving latent: {e}") - - return () - - -N_CLASS_MAPPINGS = { - "JDCN_BatchSaveLatent": JDCN_BatchSaveLatent, -} - -N_DISPLAY_NAME_MAPPINGS = { - "JDCN_BatchSaveLatent": "JDCN_BatchSaveLatent", -} +import os +import torch +import comfy.utils +from comfy.cli_args import args + + +def count_files_in_folder(folder_path): + file_count = 0 + for _, _, files in os.walk(folder_path): + file_count += len(files) + return file_count + +def save_latent(index,prefix,output_dir,latent): + + padding = str(index).zfill(4) + file_name = f"{prefix}_{padding}.latent" + file_path = os.path.join(output_dir, file_name) + + output = {} + output["latent_tensor"] = latent + output["latent_format_version_0"] = torch.tensor([]) + + comfy.utils.save_torch_file(output, file_path, None) + + +class JDCN_BatchSaveLatent: + + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "Latents": ("LATENT",), + "Directory": ("STRING", {}), + "FilenamePrefix": ("STRING", {"default": "Latent"}) + }, + } + + INPUT_IS_LIST = True + RETURN_TYPES = () + FUNCTION = "BatchSave" + OUTPUT_NODE = True + CATEGORY = "🔵 JDCN 🔵" + + def BatchSave(self, Latents, Directory, FilenamePrefix): + try: + + Directory = Directory[0] + FilenamePrefix = FilenamePrefix[0] + + if not os.path.exists(Directory): + os.makedirs(Directory) + + lastIndex = count_files_in_folder(Directory) + index = 1 + + for latent in Latents: + save_latent(lastIndex+index,FilenamePrefix,Directory,latent['samples']) + index=index+1 + + except Exception as e: + print(f"Error saving latent: {e}") + + return () + + +N_CLASS_MAPPINGS = { + "JDCN_BatchSaveLatent": JDCN_BatchSaveLatent, +} + +N_DISPLAY_NAME_MAPPINGS = { + "JDCN_BatchSaveLatent": "JDCN_BatchSaveLatent", +} diff --git a/JDCN_FileMover.py b/classes/JDCN_FileMover.py similarity index 96% rename from JDCN_FileMover.py rename to classes/JDCN_FileMover.py index 77041a8..63d35a7 100644 --- a/JDCN_FileMover.py +++ b/classes/JDCN_FileMover.py @@ -1,102 +1,102 @@ -import glob -import os -import shutil - - -def create_folder_if_not_exists(folder_path): - if not os.path.exists(folder_path): - os.makedirs(folder_path) - print(f"Folder '{folder_path}' created.") - else: - print(f"Folder '{folder_path}' already exists.") - -def move_it(source_path, destination_dir, overwrite=False): - try: - create_folder_if_not_exists(destination_dir) - filename = os.path.basename(source_path) - destination_path = os.path.join(destination_dir, filename) - - if os.path.exists(destination_path): - if overwrite: - shutil.move(source_path, destination_path) - else: - base, ext = os.path.splitext(filename) - i = 1 - while True: - new_filename = f"{base}_{i}{ext}" - new_destination_path = os.path.join(destination_dir, new_filename) - if not os.path.exists(new_destination_path): - destination_path = new_destination_path - break - i += 1 - shutil.move(source_path, destination_path) - else: - shutil.move(source_path, destination_path) - - except Exception as e: - print(f"Error: {e}") - - -def create_folder_if_not_exists(folder_path): - if not os.path.exists(folder_path): - os.makedirs(folder_path) - print(f"Folder '{folder_path}' created.") - else: - print(f"Folder '{folder_path}' already exists.") - - -def get_files_in_folder(folder_path): - if not os.path.isdir(folder_path): - print("Folder does not exist.") - return [] - files = glob.glob(os.path.join(folder_path, "*")) - return files - - -class JDCN_FileMover: - - def __init__(self): - pass - - @classmethod - def INPUT_TYPES(s): - - return { - "required": { - "FilePaths": ("STRING", {"forceInput": True}), - "OutputDirectory": ("STRING", {"default": "directory path"}), - "OverwriteFile": ("BOOLEAN", {"default": False}), - }, - } - - INPUT_IS_LIST = True - RETURN_TYPES = ("STRING",) - RETURN_NAMES = ("NewFilePaths",) - OUTPUT_IS_LIST = (True,) - OUTPUT_NODE = True - FUNCTION = "make_list" - CATEGORY = "🔵 JDCN 🔵" - - def make_list(self, FilePaths, OutputDirectory, OverwriteFile): - - if len(FilePaths) == 0: - print("Empty FileName string") - return ([],) - - create_folder_if_not_exists(OutputDirectory[0]) - - for file in FilePaths: - move_it(file, OutputDirectory[0], OverwriteFile[0]) - - file_paths_new = get_files_in_folder(OutputDirectory[0]) - - return (file_paths_new,) - - -N_CLASS_MAPPINGS = { - "JDCN_FileMover": JDCN_FileMover, -} - -N_DISPLAY_NAME_MAPPINGS = { - "JDCN_FileMover": "JDCN_FileMover", -} +import glob +import os +import shutil + + +def create_folder_if_not_exists(folder_path): + if not os.path.exists(folder_path): + os.makedirs(folder_path) + print(f"Folder '{folder_path}' created.") + else: + print(f"Folder '{folder_path}' already exists.") + +def move_it(source_path, destination_dir, overwrite=False): + try: + create_folder_if_not_exists(destination_dir) + filename = os.path.basename(source_path) + destination_path = os.path.join(destination_dir, filename) + + if os.path.exists(destination_path): + if overwrite: + shutil.move(source_path, destination_path) + else: + base, ext = os.path.splitext(filename) + i = 1 + while True: + new_filename = f"{base}_{i}{ext}" + new_destination_path = os.path.join(destination_dir, new_filename) + if not os.path.exists(new_destination_path): + destination_path = new_destination_path + break + i += 1 + shutil.move(source_path, destination_path) + else: + shutil.move(source_path, destination_path) + + except Exception as e: + print(f"Error: {e}") + + +def create_folder_if_not_exists(folder_path): + if not os.path.exists(folder_path): + os.makedirs(folder_path) + print(f"Folder '{folder_path}' created.") + else: + print(f"Folder '{folder_path}' already exists.") + + +def get_files_in_folder(folder_path): + if not os.path.isdir(folder_path): + print("Folder does not exist.") + return [] + files = glob.glob(os.path.join(folder_path, "*")) + return files + + +class JDCN_FileMover: + + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + + return { + "required": { + "FilePaths": ("STRING", {"forceInput": True}), + "OutputDirectory": ("STRING", {"default": "directory path"}), + "OverwriteFile": ("BOOLEAN", {"default": False}), + }, + } + + INPUT_IS_LIST = True + RETURN_TYPES = ("STRING",) + RETURN_NAMES = ("NewFilePaths",) + OUTPUT_IS_LIST = (True,) + OUTPUT_NODE = True + FUNCTION = "make_list" + CATEGORY = "🔵 JDCN 🔵" + + def make_list(self, FilePaths, OutputDirectory, OverwriteFile): + + if len(FilePaths) == 0: + print("Empty FileName string") + return ([],) + + create_folder_if_not_exists(OutputDirectory[0]) + + for file in FilePaths: + move_it(file, OutputDirectory[0], OverwriteFile[0]) + + file_paths_new = get_files_in_folder(OutputDirectory[0]) + + return (file_paths_new,) + + +N_CLASS_MAPPINGS = { + "JDCN_FileMover": JDCN_FileMover, +} + +N_DISPLAY_NAME_MAPPINGS = { + "JDCN_FileMover": "JDCN_FileMover", +} diff --git a/JDCN_ImageSaver.py b/classes/JDCN_ImageSaver.py similarity index 100% rename from JDCN_ImageSaver.py rename to classes/JDCN_ImageSaver.py diff --git a/JDCN_ListToString.py b/classes/JDCN_ListToString.py similarity index 95% rename from JDCN_ListToString.py rename to classes/JDCN_ListToString.py index 0310ed6..3108d5f 100644 --- a/JDCN_ListToString.py +++ b/classes/JDCN_ListToString.py @@ -1,39 +1,39 @@ -class JDCN_ListToString: - - def __init__(self): - pass - - @classmethod - def INPUT_TYPES(s): - - return { - "required": { - "list": ("STRING", {"forceInput": True}), - }, - } - - INPUT_IS_LIST = True - RETURN_TYPES = ("STRING",) - RETURN_NAMES = ("string",) - OUTPUT_NODE = True - FUNCTION = "make_list" - CATEGORY = "🔵 JDCN 🔵" - - def make_list(self, list): - - if len(list) == 0: - print("Error in List Variable") - return ("",) - - file_string_list = '\n'.join(list) - - return (file_string_list,) - - -N_CLASS_MAPPINGS = { - "JDCN_ListToString": JDCN_ListToString, -} - -N_DISPLAY_NAME_MAPPINGS = { - "JDCN_ListToString": "JDCN_ListToString", -} +class JDCN_ListToString: + + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + + return { + "required": { + "list": ("STRING", {"forceInput": True}), + }, + } + + INPUT_IS_LIST = True + RETURN_TYPES = ("STRING",) + RETURN_NAMES = ("string",) + OUTPUT_NODE = True + FUNCTION = "make_list" + CATEGORY = "🔵 JDCN 🔵" + + def make_list(self, list): + + if len(list) == 0: + print("Error in List Variable") + return ("",) + + file_string_list = '\n'.join(list) + + return (file_string_list,) + + +N_CLASS_MAPPINGS = { + "JDCN_ListToString": JDCN_ListToString, +} + +N_DISPLAY_NAME_MAPPINGS = { + "JDCN_ListToString": "JDCN_ListToString", +} diff --git a/JDCN_ReBatch.py b/classes/JDCN_ReBatch.py similarity index 96% rename from JDCN_ReBatch.py rename to classes/JDCN_ReBatch.py index 3ba3c17..f804c2b 100644 --- a/JDCN_ReBatch.py +++ b/classes/JDCN_ReBatch.py @@ -1,62 +1,62 @@ -def split_into_batches(list, batch_size): - num_batches = len(list) // batch_size + (len(list) % batch_size != 0) - batches = [] - for i in range(num_batches): - start_idx = i * batch_size - end_idx = min(start_idx + batch_size, len(list)) - batch = list[start_idx:end_idx] - batches.append(batch) - return batches - -def batches_to_string(batches): - string_representation = [] - for batch in batches: - string_representation.append("\n".join(batch)) - return string_representation - - -class JDCN_ReBatch: - - def __init__(self): - pass - - @classmethod - def INPUT_TYPES(s): - - return { - "required": { - "FileNames": ("STRING", {"forceInput": True}), - "BatchSize": ("INT", {"default": 1, "min": 1, "max": 9999}), - "TextList": ("BOOLEAN", {"default": False}), - }, - } - - INPUT_IS_LIST = True - RETURN_TYPES = ("STRING",) - RETURN_NAMES = ("FilePaths",) - OUTPUT_IS_LIST = (True,) - OUTPUT_NODE = True - FUNCTION = "make_it" - CATEGORY = "🔵 JDCN 🔵" - - def make_it(self, FileNames, BatchSize, TextList): - - if len(FileNames) == 0: - print("Empty FileName") - return ("",) - - batches = split_into_batches(FileNames,BatchSize[0]) - - if(TextList[0]): - batches = batches_to_string(batches) - - return (batches,) - - -N_CLASS_MAPPINGS = { - "JDCN_ReBatch": JDCN_ReBatch, -} - -N_DISPLAY_NAME_MAPPINGS = { - "JDCN_ReBatch": "JDCN_ReBatch", -} +def split_into_batches(list, batch_size): + num_batches = len(list) // batch_size + (len(list) % batch_size != 0) + batches = [] + for i in range(num_batches): + start_idx = i * batch_size + end_idx = min(start_idx + batch_size, len(list)) + batch = list[start_idx:end_idx] + batches.append(batch) + return batches + +def batches_to_string(batches): + string_representation = [] + for batch in batches: + string_representation.append("\n".join(batch)) + return string_representation + + +class JDCN_ReBatch: + + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + + return { + "required": { + "FileNames": ("STRING", {"forceInput": True}), + "BatchSize": ("INT", {"default": 1, "min": 1, "max": 9999}), + "TextList": ("BOOLEAN", {"default": False}), + }, + } + + INPUT_IS_LIST = True + RETURN_TYPES = ("STRING",) + RETURN_NAMES = ("FilePaths",) + OUTPUT_IS_LIST = (True,) + OUTPUT_NODE = True + FUNCTION = "make_it" + CATEGORY = "🔵 JDCN 🔵" + + def make_it(self, FileNames, BatchSize, TextList): + + if len(FileNames) == 0: + print("Empty FileName") + return ("",) + + batches = split_into_batches(FileNames,BatchSize[0]) + + if(TextList[0]): + batches = batches_to_string(batches) + + return (batches,) + + +N_CLASS_MAPPINGS = { + "JDCN_ReBatch": JDCN_ReBatch, +} + +N_DISPLAY_NAME_MAPPINGS = { + "JDCN_ReBatch": "JDCN_ReBatch", +} diff --git a/JDCN_SeamlessExperience.py b/classes/JDCN_SeamlessExperience.py similarity index 96% rename from JDCN_SeamlessExperience.py rename to classes/JDCN_SeamlessExperience.py index 9b75ee7..c881379 100644 --- a/JDCN_SeamlessExperience.py +++ b/classes/JDCN_SeamlessExperience.py @@ -1,216 +1,216 @@ -import os -import glob -import shutil -from PIL import Image, ImageEnhance -# from server import PromptServer -# from aiohttp import web - -def get_files_in_folder(folder_path): - if not os.path.isdir(folder_path): - print("Folder does not exist.") - return [] - files = glob.glob(os.path.join(folder_path, "*")) - return files - -def create_folder_if_not_exists(folder_path): - if not os.path.exists(folder_path): - os.makedirs(folder_path) - print(f"Folder '{folder_path}' created.") - else: - print(f"Folder '{folder_path}' already exists.") - -def delete_files_in_folder(folder_path): - for file in os.listdir(folder_path): - file_path = os.path.join(folder_path, file) - try: - if os.path.isfile(file_path): - os.unlink(file_path) - except Exception as e: - print(f"Failed to delete file '{file_path}': {e}") - -def copy_images_and_delete_folder(file_paths, destination_folder, deleting_folder): - - if len(file_paths) <= 0: - print("Source image path list is empty.") - return - - if not os.path.isdir(destination_folder): - print("Destination folder does not exist.") - return - - delete_files_in_folder(destination_folder) - - for file_path in file_paths: - file_name = os.path.basename(file_path) - destination_file_path = os.path.join(destination_folder, file_name) - shutil.copyfile(file_path, destination_file_path) - # print(f"Image '{file_name}' copied to '{destination_folder}'.") - - delete_files_in_folder(deleting_folder) - - -def copy_images(file_paths, destination_folder): - - if len(file_paths) <= 0: - print("Source image path list is empty.") - return - - if not os.path.isdir(destination_folder): - print("Destination folder does not exist.") - return - - delete_files_in_folder(destination_folder) - - for file_path in file_paths: - file_name = os.path.basename(file_path) - destination_file_path = os.path.join(destination_folder, file_name) - shutil.copyfile(file_path, destination_file_path) - # print(f"Image '{file_name}' copied to '{destination_folder}'.") - -def change_opacity(im, opacity): - assert opacity >= 0 and opacity <= 1 - if im.mode != 'RGBA': - im = im.convert('RGBA') - else: - im = im.copy() - alpha = im.split()[3] - alpha = ImageEnhance.Brightness(alpha).enhance(opacity) - im.putalpha(alpha) - return im - - -def merge_images(image1, image2): - # Convert images to RGBA mode if not already - if image1.mode != 'RGBA': - image1 = image1.convert('RGBA') - if image2.mode != 'RGBA': - image2 = image2.convert('RGBA') - - merged_image = Image.alpha_composite(image1, image2) - merged_image = merged_image.convert('RGB') - - return merged_image - -def updateProgress(current,max): - pass - # client_id = PromptServer.instance.client_id - # PromptServer.instance.send_sync("jdcnse/progress", { "details": { "value": current, "max": max } }, client_id) - # print(f"current: {current} max: {max} left: {(current/max)*100}") - - -def seamless(arr, batch_size, overlap_size, output_folder): - - affected = [] - steps = (len(arr) // batch_size) - - affected.append(f"TOTAL SEAMLESS PROCESSING ROUNDS: {steps-1}") - - for i in range(1, steps): - affected.append("") - affected.append("=============================================================================") - affected.append("") - affected.append(f"ROUND: {i}/{steps-1}") - affected.append("") - select_start_a = (batch_size * i) + (overlap_size * (i-1)) + 1 - select_end_a = select_start_a + overlap_size - - select_start_b = select_end_a - select_end_b = select_start_b + overlap_size - - merge_this = arr[select_start_a-1:select_end_a-1] - merge_that = arr[select_start_b-1:select_end_b-1] - - affected.append(f"SET 1: {merge_this[0]} - {merge_this[len(merge_this)-1]}") - affected.append(f"SET 2: {merge_that[0]} - {merge_that[len(merge_that)-1]}") - - for j in range(len(merge_this)): - image_path_1 = merge_this[j] - image_path_2 = merge_that[j] - - image1 = Image.open(image_path_1) - image2 = Image.open(image_path_2) - - if image1.mode != 'RGBA': - image1 = image1.convert('RGBA') - if image2.mode != 'RGBA': - image2 = image2.convert('RGBA') - - opacity = ((j+1) / (overlap_size)) - - image_a = change_opacity(image1, 1 - opacity) - - merge = merge_images(image2, image_a) - - image2_filename = os.path.basename(image_path_2) - output_path = os.path.join(output_folder, image2_filename) - - os.remove(image_path_1) - os.remove(image_path_2) - merge.save(output_path, quality=95) - current = i+(j/overlap_size) - max = steps - updateProgress(current,max) - - - # print(f"Batch {i}/{steps-1} completed") - - return affected - - -class JDCN_SeamlessExperience: - def __init__(self): - pass - - @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "ImagePaths": ("STRING", {"forceInput": True}), - "OutputDirectory": ("STRING", {"default": "directory path"}), - "BatchSize": ("INT", {"default": 0, "min": 0, "max": 9999}), - "OverlapSize": ("INT", {"default": 0, "min": 0, "max": 9999}), - }, - } - - FUNCTION = "doit" - - INPUT_IS_LIST = True - RETURN_TYPES = ("STRING", "STRING") - RETURN_NAMES = ("NewImagePaths", "Log") - OUTPUT_IS_LIST = (True, False) - OUTPUT_NODE = True - CATEGORY = "🔵 JDCN 🔵" - - def doit(self, ImagePaths, OutputDirectory, BatchSize, OverlapSize): - - input_folder = "./input/jdcn" - - create_folder_if_not_exists(input_folder) - create_folder_if_not_exists(OutputDirectory[0]) - - copy_images(ImagePaths, input_folder) - input_file_paths = get_files_in_folder(input_folder) - log = seamless(input_file_paths, BatchSize[0], OverlapSize[0], input_folder) - file_paths = get_files_in_folder(input_folder) - copy_images_and_delete_folder(file_paths, OutputDirectory[0], input_folder) - file_paths = get_files_in_folder(OutputDirectory[0]) - - log.append("") - log.append(f"IMAGES BEFORE: {len(input_file_paths)}") - log.append("") - log.append(f"IMAGES AFTER: {len(file_paths)}") - - log_string = '\n'.join(log) - - updateProgress(1,1) - - return (file_paths, log_string) - - -N_CLASS_MAPPINGS = { - "JDCN_SeamlessExperience": JDCN_SeamlessExperience, -} - -N_DISPLAY_NAME_MAPPINGS = { - "JDCN_SeamlessExperience": "JDCN_SeamlessExperience", -} +import os +import glob +import shutil +from PIL import Image, ImageEnhance +# from server import PromptServer +# from aiohttp import web + +def get_files_in_folder(folder_path): + if not os.path.isdir(folder_path): + print("Folder does not exist.") + return [] + files = glob.glob(os.path.join(folder_path, "*")) + return files + +def create_folder_if_not_exists(folder_path): + if not os.path.exists(folder_path): + os.makedirs(folder_path) + print(f"Folder '{folder_path}' created.") + else: + print(f"Folder '{folder_path}' already exists.") + +def delete_files_in_folder(folder_path): + for file in os.listdir(folder_path): + file_path = os.path.join(folder_path, file) + try: + if os.path.isfile(file_path): + os.unlink(file_path) + except Exception as e: + print(f"Failed to delete file '{file_path}': {e}") + +def copy_images_and_delete_folder(file_paths, destination_folder, deleting_folder): + + if len(file_paths) <= 0: + print("Source image path list is empty.") + return + + if not os.path.isdir(destination_folder): + print("Destination folder does not exist.") + return + + delete_files_in_folder(destination_folder) + + for file_path in file_paths: + file_name = os.path.basename(file_path) + destination_file_path = os.path.join(destination_folder, file_name) + shutil.copyfile(file_path, destination_file_path) + # print(f"Image '{file_name}' copied to '{destination_folder}'.") + + delete_files_in_folder(deleting_folder) + + +def copy_images(file_paths, destination_folder): + + if len(file_paths) <= 0: + print("Source image path list is empty.") + return + + if not os.path.isdir(destination_folder): + print("Destination folder does not exist.") + return + + delete_files_in_folder(destination_folder) + + for file_path in file_paths: + file_name = os.path.basename(file_path) + destination_file_path = os.path.join(destination_folder, file_name) + shutil.copyfile(file_path, destination_file_path) + # print(f"Image '{file_name}' copied to '{destination_folder}'.") + +def change_opacity(im, opacity): + assert opacity >= 0 and opacity <= 1 + if im.mode != 'RGBA': + im = im.convert('RGBA') + else: + im = im.copy() + alpha = im.split()[3] + alpha = ImageEnhance.Brightness(alpha).enhance(opacity) + im.putalpha(alpha) + return im + + +def merge_images(image1, image2): + # Convert images to RGBA mode if not already + if image1.mode != 'RGBA': + image1 = image1.convert('RGBA') + if image2.mode != 'RGBA': + image2 = image2.convert('RGBA') + + merged_image = Image.alpha_composite(image1, image2) + merged_image = merged_image.convert('RGB') + + return merged_image + +def updateProgress(current,max): + pass + # client_id = PromptServer.instance.client_id + # PromptServer.instance.send_sync("jdcnse/progress", { "details": { "value": current, "max": max } }, client_id) + # print(f"current: {current} max: {max} left: {(current/max)*100}") + + +def seamless(arr, batch_size, overlap_size, output_folder): + + affected = [] + steps = (len(arr) // batch_size) + + affected.append(f"TOTAL SEAMLESS PROCESSING ROUNDS: {steps-1}") + + for i in range(1, steps): + affected.append("") + affected.append("=============================================================================") + affected.append("") + affected.append(f"ROUND: {i}/{steps-1}") + affected.append("") + select_start_a = (batch_size * i) + (overlap_size * (i-1)) + 1 + select_end_a = select_start_a + overlap_size + + select_start_b = select_end_a + select_end_b = select_start_b + overlap_size + + merge_this = arr[select_start_a-1:select_end_a-1] + merge_that = arr[select_start_b-1:select_end_b-1] + + affected.append(f"SET 1: {merge_this[0]} - {merge_this[len(merge_this)-1]}") + affected.append(f"SET 2: {merge_that[0]} - {merge_that[len(merge_that)-1]}") + + for j in range(len(merge_this)): + image_path_1 = merge_this[j] + image_path_2 = merge_that[j] + + image1 = Image.open(image_path_1) + image2 = Image.open(image_path_2) + + if image1.mode != 'RGBA': + image1 = image1.convert('RGBA') + if image2.mode != 'RGBA': + image2 = image2.convert('RGBA') + + opacity = ((j+1) / (overlap_size)) + + image_a = change_opacity(image1, 1 - opacity) + + merge = merge_images(image2, image_a) + + image2_filename = os.path.basename(image_path_2) + output_path = os.path.join(output_folder, image2_filename) + + os.remove(image_path_1) + os.remove(image_path_2) + merge.save(output_path, quality=95) + current = i+(j/overlap_size) + max = steps + updateProgress(current,max) + + + # print(f"Batch {i}/{steps-1} completed") + + return affected + + +class JDCN_SeamlessExperience: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "ImagePaths": ("STRING", {"forceInput": True}), + "OutputDirectory": ("STRING", {"default": "directory path"}), + "BatchSize": ("INT", {"default": 0, "min": 0, "max": 9999}), + "OverlapSize": ("INT", {"default": 0, "min": 0, "max": 9999}), + }, + } + + FUNCTION = "doit" + + INPUT_IS_LIST = True + RETURN_TYPES = ("STRING", "STRING") + RETURN_NAMES = ("NewImagePaths", "Log") + OUTPUT_IS_LIST = (True, False) + OUTPUT_NODE = True + CATEGORY = "🔵 JDCN 🔵" + + def doit(self, ImagePaths, OutputDirectory, BatchSize, OverlapSize): + + input_folder = "./input/jdcn" + + create_folder_if_not_exists(input_folder) + create_folder_if_not_exists(OutputDirectory[0]) + + copy_images(ImagePaths, input_folder) + input_file_paths = get_files_in_folder(input_folder) + log = seamless(input_file_paths, BatchSize[0], OverlapSize[0], input_folder) + file_paths = get_files_in_folder(input_folder) + copy_images_and_delete_folder(file_paths, OutputDirectory[0], input_folder) + file_paths = get_files_in_folder(OutputDirectory[0]) + + log.append("") + log.append(f"IMAGES BEFORE: {len(input_file_paths)}") + log.append("") + log.append(f"IMAGES AFTER: {len(file_paths)}") + + log_string = '\n'.join(log) + + updateProgress(1,1) + + return (file_paths, log_string) + + +N_CLASS_MAPPINGS = { + "JDCN_SeamlessExperience": JDCN_SeamlessExperience, +} + +N_DISPLAY_NAME_MAPPINGS = { + "JDCN_SeamlessExperience": "JDCN_SeamlessExperience", +} diff --git a/JDCN_SplitString.py b/classes/JDCN_SplitString.py similarity index 100% rename from JDCN_SplitString.py rename to classes/JDCN_SplitString.py diff --git a/JDCN_StringToList.py b/classes/JDCN_StringToList.py similarity index 95% rename from JDCN_StringToList.py rename to classes/JDCN_StringToList.py index 7534cc9..57f9d1d 100644 --- a/JDCN_StringToList.py +++ b/classes/JDCN_StringToList.py @@ -1,40 +1,40 @@ -class JDCN_StringToList: - - def __init__(self): - pass - - @classmethod - def INPUT_TYPES(s): - - return { - "required": { - "string": ("STRING", {"forceInput": True}), - }, - } - - # INPUT_IS_LIST = True - RETURN_TYPES = ("STRING",) - RETURN_NAMES = ("list",) - OUTPUT_IS_LIST = (True,) - OUTPUT_NODE = True - FUNCTION = "make_list" - CATEGORY = "🔵 JDCN 🔵" - - def make_list(self, string): - - if len(string) == 0: - print("Error in string Variable") - return ("",) - - file_paths = string.split('\n') - - return (file_paths,) - - -N_CLASS_MAPPINGS = { - "JDCN_StringToList": JDCN_StringToList, -} - -N_DISPLAY_NAME_MAPPINGS = { - "JDCN_StringToList": "JDCN_StringToList", -} +class JDCN_StringToList: + + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + + return { + "required": { + "string": ("STRING", {"forceInput": True}), + }, + } + + # INPUT_IS_LIST = True + RETURN_TYPES = ("STRING",) + RETURN_NAMES = ("list",) + OUTPUT_IS_LIST = (True,) + OUTPUT_NODE = True + FUNCTION = "make_list" + CATEGORY = "🔵 JDCN 🔵" + + def make_list(self, string): + + if len(string) == 0: + print("Error in string Variable") + return ("",) + + file_paths = string.split('\n') + + return (file_paths,) + + +N_CLASS_MAPPINGS = { + "JDCN_StringToList": JDCN_StringToList, +} + +N_DISPLAY_NAME_MAPPINGS = { + "JDCN_StringToList": "JDCN_StringToList", +} diff --git a/JDCN_TXTFileSaver.py b/classes/JDCN_TXTFileSaver.py similarity index 100% rename from JDCN_TXTFileSaver.py rename to classes/JDCN_TXTFileSaver.py diff --git a/JDCN_VHSFileMover.py b/classes/JDCN_VHSFileMover.py similarity index 96% rename from JDCN_VHSFileMover.py rename to classes/JDCN_VHSFileMover.py index 61b4af4..7cb7e7e 100644 --- a/JDCN_VHSFileMover.py +++ b/classes/JDCN_VHSFileMover.py @@ -1,80 +1,80 @@ -import os -import shutil - -def create_folder_if_not_exists(folder_path): - if not os.path.exists(folder_path): - os.makedirs(folder_path) - print(f"Folder '{folder_path}' created.") - else: - print(f"Folder '{folder_path}' already exists.") - -def move_it(source_path, destination_dir, overwrite=False): - try: - create_folder_if_not_exists(destination_dir) - - filename = os.path.basename(source_path) - destination_path = os.path.join(destination_dir, filename) - - if os.path.exists(destination_path): - if overwrite: - shutil.move(source_path, destination_path) - else: - base, ext = os.path.splitext(filename) - i = 1 - while True: - new_filename = f"{base}_{i}{ext}" - new_destination_path = os.path.join(destination_dir, new_filename) - if not os.path.exists(new_destination_path): - destination_path = new_destination_path - break - i += 1 - shutil.move(source_path, destination_path) - else: - shutil.move(source_path, destination_path) - - except Exception as e: - print(f"Error: {e}") - -class JDCN_VHSFileMover: - - def __init__(self): - pass - - @classmethod - def INPUT_TYPES(s): - - return { - "required": { - "FileNames": ("VHS_FILENAMES", {}), - "OutputDirectory": ("STRING", {"default": "directory path"}), - "OverwriteFile": ("BOOLEAN", {"default": False}), - }, - } - - INPUT_IS_LIST = True - RETURN_TYPES = ("STRING",) - RETURN_NAMES = ("FilePaths",) - OUTPUT_IS_LIST = (True,) - OUTPUT_NODE = True - FUNCTION = "make_list" - - CATEGORY = "🔵 JDCN 🔵" - - def make_list(self, FileNames, OutputDirectory, OverwriteFile): - - file_paths = FileNames[0][1] - - if len(file_paths) > 0: - for file in file_paths: - move_it(file, OutputDirectory[0], OverwriteFile[0]) - - return (file_paths,) - - -N_CLASS_MAPPINGS = { - "JDCN_VHSFileMover": JDCN_VHSFileMover, -} - -N_DISPLAY_NAME_MAPPINGS = { - "JDCN_VHSFileMover": "JDCN_VHSFileMover", -} +import os +import shutil + +def create_folder_if_not_exists(folder_path): + if not os.path.exists(folder_path): + os.makedirs(folder_path) + print(f"Folder '{folder_path}' created.") + else: + print(f"Folder '{folder_path}' already exists.") + +def move_it(source_path, destination_dir, overwrite=False): + try: + create_folder_if_not_exists(destination_dir) + + filename = os.path.basename(source_path) + destination_path = os.path.join(destination_dir, filename) + + if os.path.exists(destination_path): + if overwrite: + shutil.move(source_path, destination_path) + else: + base, ext = os.path.splitext(filename) + i = 1 + while True: + new_filename = f"{base}_{i}{ext}" + new_destination_path = os.path.join(destination_dir, new_filename) + if not os.path.exists(new_destination_path): + destination_path = new_destination_path + break + i += 1 + shutil.move(source_path, destination_path) + else: + shutil.move(source_path, destination_path) + + except Exception as e: + print(f"Error: {e}") + +class JDCN_VHSFileMover: + + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + + return { + "required": { + "FileNames": ("VHS_FILENAMES", {}), + "OutputDirectory": ("STRING", {"default": "directory path"}), + "OverwriteFile": ("BOOLEAN", {"default": False}), + }, + } + + INPUT_IS_LIST = True + RETURN_TYPES = ("STRING",) + RETURN_NAMES = ("FilePaths",) + OUTPUT_IS_LIST = (True,) + OUTPUT_NODE = True + FUNCTION = "make_list" + + CATEGORY = "🔵 JDCN 🔵" + + def make_list(self, FileNames, OutputDirectory, OverwriteFile): + + file_paths = FileNames[0][1] + + if len(file_paths) > 0: + for file in file_paths: + move_it(file, OutputDirectory[0], OverwriteFile[0]) + + return (file_paths,) + + +N_CLASS_MAPPINGS = { + "JDCN_VHSFileMover": JDCN_VHSFileMover, +} + +N_DISPLAY_NAME_MAPPINGS = { + "JDCN_VHSFileMover": "JDCN_VHSFileMover", +} diff --git a/shared.py b/classes/shared.py similarity index 100% rename from shared.py rename to classes/shared.py