From aaf1c485d90ca77be50d33ed4226aaf083093b27 Mon Sep 17 00:00:00 2001 From: MeeeyoAI Date: Sun, 27 Apr 2025 09:08:16 +0800 Subject: [PATCH] update --- __init__.py | 123 ++++++++----- meyo_node_Computational.py | 27 +-- meyo_node_File.py | 281 ++++++++++++++++++++++------- meyo_node_Functional.py | 55 +++--- meyo_node_String.py | 352 +++++++++++++++++++++++++++++++++++-- requirements.txt | 3 +- 6 files changed, 677 insertions(+), 164 deletions(-) diff --git a/__init__.py b/__init__.py index 657b6a1..c53e2fe 100644 --- a/__init__.py +++ b/__init__.py @@ -1,3 +1,11 @@ +class AnyType(str): + def __ne__(self, __value: object) -> bool: + return False +any_typ = AnyType("*") + + + + from .meyo_node_Computational import * from .meyo_node_String import * from .meyo_node_File import * @@ -15,13 +23,17 @@ NODE_CLASS_MAPPINGS = { #字符串处理:meyo_node_String "SingleTextInput": SingleTextInput, + "TextToList": TextToList, + "TextConcatenator": TextConcatenator, "MultiParamInputNode": MultiParamInputNode, + "NumberExtractor": NumberExtractor, "AddPrefixSuffix": AddPrefixSuffix, "ExtractSubstring": ExtractSubstring, "ExtractSubstringByIndices": ExtractSubstringByIndices, "SplitStringByDelimiter": SplitStringByDelimiter, "ProcessString": ProcessString, "ExtractBeforeAfter": ExtractBeforeAfter, + "SimpleTextReplacer": SimpleTextReplacer, "ReplaceNthOccurrence": ReplaceNthOccurrence, "ReplaceMultiple": ReplaceMultiple, "BatchReplaceStrings": BatchReplaceStrings, @@ -42,18 +54,21 @@ NODE_CLASS_MAPPINGS = { "TextConcatenation": TextConcatenation, "ExtractSpecificData": ExtractSpecificData, "FindFirstLineContent": FindFirstLineContent, + "GenerateVideoPrompt": GenerateVideoPrompt, #文件处理:meyo_node_File "LoadAndAdjustImage": LoadAndAdjustImage, "ImageAdjuster": ImageAdjuster, "CustomCrop": CustomCrop, - "SaveImagEX": SaveImagEX, "FileCopyCutNode": FileCopyCutNode, + "FileNameReplacer": FileNameReplacer, + "WriteToTxtFile": WriteToTxtFile, "FileDeleteNode": FileDeleteNode, "FileListAndSuffix": FileListAndSuffix, "ReadExcelData": ReadExcelData, "WriteExcelData": WriteExcelData, + "WriteExcelImage": WriteExcelImage, "FindExcelData": FindExcelData, "ReadExcelRowOrColumnDiff": ReadExcelRowOrColumnDiff, @@ -67,59 +82,71 @@ NODE_CLASS_MAPPINGS = { NODE_DISPLAY_NAME_MAPPINGS = { #运算型节点:meyo_node_Computational - "CompareInt": "比较数值(meeeyo.com)", - "FloatToInteger": "规范数值(meeeyo.com)", - "GenerateNumbers": "生成范围数组(meeeyo.com)", - "GetRandomIntegerInRange": "范围内随机数(meeeyo.com)", + "CompareInt": "比较数值🐠meeeyo.com", + "FloatToInteger": "规范数值🐠meeeyo.com", + "GenerateNumbers": "生成范围数组🐠meeeyo.com", + "GetRandomIntegerInRange": "范围内随机数🐠meeeyo.com", #字符串处理:meyo_node_String - "SingleTextInput": "文本输入(meeeyo.com)", - "MultiParamInputNode": "多参数输入(meeeyo.com)", - "AddPrefixSuffix": "添加前后缀(meeeyo.com)", - "ExtractSubstring": "提取标签之间(meeeyo.com)", - "ExtractSubstringByIndices": "按数字范围提取(meeeyo.com)", - "SplitStringByDelimiter": "分隔符拆分两边(meeeyo.com)", - "ProcessString": "常规处理字符(meeeyo.com)", - "ExtractBeforeAfter": "提取前后字符(meeeyo.com)", - "ReplaceNthOccurrence": "替换第n次出现(meeeyo.com)", - "ReplaceMultiple": "多次出现依次替换(meeeyo.com)", - "BatchReplaceStrings": "批量替换字符(meeeyo.com)", - "RandomLineFromText": "随机行内容(meeeyo.com)", - "CheckSubstringPresence": "判断是否包含字符(meeeyo.com)", - "AddPrefixSuffixToLines": "段落每行添加前后缀(meeeyo.com)", - "ExtractAndCombineLines": "段落提取指定索引行(meeeyo.com)", - "FilterLinesBySubstrings": "段落提取或移除字符行(meeeyo.com)", - "FilterLinesByWordCount": "段落字数条件过滤行(meeeyo.com)", - "SplitAndExtractText": "按序号提取分割文本(meeeyo.com)", - "CountOccurrences": "文本出现次数(meeeyo.com)", - "ExtractLinesByIndex": "文本拆分(meeeyo.com)", - "ExtractSpecificLines": "提取特定行(meeeyo.com)", - "RemoveContentBetweenChars": "删除标签内的内容(meeeyo.com)", - "ShuffleTextLines": "随机打乱(meeeyo.com)", - "ConditionalTextOutput": "判断返回内容(meeeyo.com)", - "TextConditionCheck": "文本按条件判断(meeeyo.com)", - "TextConcatenation": "文本组合(meeeyo.com)", - "ExtractSpecificData": "提取多层指定数据(meeeyo.com)", - "FindFirstLineContent": "指定字符行参数(meeeyo.com)", + "SingleTextInput": "文本输入🐠meeeyo.com", + "TextToList": "文本到列表🐠meeeyo.com", + "TextConcatenator": "文本拼接🐠meeeyo.com", + "MultiParamInputNode": "多参数输入🐠meeeyo.com", + "NumberExtractor": "整数参数🐠meeeyo.com", + "AddPrefixSuffix": "添加前后缀🐠meeeyo.com", + "ExtractSubstring": "提取标签之间🐠meeeyo.com", + "ExtractSubstringByIndices": "按数字范围提取🐠meeeyo.com", + "SplitStringByDelimiter": "分隔符拆分两边🐠meeeyo.com", + "ProcessString": "常规处理字符🐠meeeyo.com", + "ExtractBeforeAfter": "提取前后字符🐠meeeyo.com", + "SimpleTextReplacer": "简易文本替换🐠meeeyo.com", + "ReplaceNthOccurrence": "替换第n次出现🐠meeeyo.com", + "ReplaceMultiple": "多次出现依次替换🐠meeeyo.com", + "BatchReplaceStrings": "批量替换字符🐠meeeyo.com", + "RandomLineFromText": "随机行内容🐠meeeyo.com", + "CheckSubstringPresence": "判断是否包含字符🐠meeeyo.com", + "AddPrefixSuffixToLines": "段落每行添加前后缀🐠meeeyo.com", + "ExtractAndCombineLines": "段落提取指定索引行🐠meeeyo.com", + "FilterLinesBySubstrings": "段落提取或移除字符行🐠meeeyo.com", + "FilterLinesByWordCount": "段落字数条件过滤行🐠meeeyo.com", + "SplitAndExtractText": "按序号提取分割文本🐠meeeyo.com", + "CountOccurrences": "文本出现次数🐠meeeyo.com", + "ExtractLinesByIndex": "文本拆分🐠meeeyo.com", + "ExtractSpecificLines": "提取特定行🐠meeeyo.com", + "RemoveContentBetweenChars": "删除标签内的内容🐠meeeyo.com", + "ShuffleTextLines": "随机打乱🐠meeeyo.com", + "ConditionalTextOutput": "判断返回内容🐠meeeyo.com", + "TextConditionCheck": "文本按条件判断🐠meeeyo.com", + "TextConcatenation": "文本组合🐠meeeyo.com", + "ExtractSpecificData": "提取多层指定数据🐠meeeyo.com", + "FindFirstLineContent": "指定字符行参数🐠meeeyo.com", + "GenerateVideoPrompt": "视频指令词模板🐠meeeyo.com", #文件处理:meyo_node_File - "LoadAndAdjustImage": "加载重置图像(meeeyo.com)", - "ImageAdjuster": "重置图像(meeeyo.com)", - "CustomCrop": "裁剪图像(meeeyo.com)", - - "SaveImagEX": "保存图像(meeeyo.com)", - "FileCopyCutNode": "文件操作(meeeyo.com)", - "FileDeleteNode": "清理文件(meeeyo.com)", - "FileListAndSuffix": "从路径加载(meeeyo.com)", - "ReadExcelData": "读取表格数据(meeeyo.com)", - "WriteExcelData": "写入表格数据(meeeyo.com)", - "FindExcelData": "查找表格数据(meeeyo.com)", - "ReadExcelRowOrColumnDiff": "读取表格数量差(meeeyo.com)", + "LoadAndAdjustImage": "加载重置图像🐠meeeyo.com", + "ImageAdjuster": "重置图像🐠meeeyo.com", + "CustomCrop": "裁剪图像🐠meeeyo.com", + "SaveImagEX": "保存图像🐠meeeyo.com", + "FileCopyCutNode": "文件操作🐠meeeyo.com", + "FileNameReplacer": "替换文件名🐠meeeyo.com", + "WriteToTxtFile": "文本写入TXT🐠meeeyo.com", + "FileDeleteNode": "清理文件🐠meeeyo.com", + "FileListAndSuffix": "从路径加载🐠meeeyo.com", + "ReadExcelData": "读取表格数据🐠meeeyo.com", + "WriteExcelData": "写入表格数据🐠meeeyo.com", + "WriteExcelImage": "图片插入表格🐠meeeyo.com", + "FindExcelData": "查找表格数据🐠meeeyo.com", + "ReadExcelRowOrColumnDiff": "读取表格数量差🐠meeeyo.com", #功能型节点:meyo_node_Functional - "GetCurrentTime": "当前时间(戳)", - "SelectionParameter": "选择参数(meeeyo.com)", - "ReadWebNode": "读取页面(meeeyo.com)", + "GetCurrentTime": "当前时间(戳)🐠meeeyo.com", + "SelectionParameter": "选择参数🐠meeeyo.com", + "ReadWebNode": "读取页面🐠meeeyo.com", } + + + + + diff --git a/meyo_node_Computational.py b/meyo_node_Computational.py index 6f9de3d..dc2d08d 100644 --- a/meyo_node_Computational.py +++ b/meyo_node_Computational.py @@ -2,6 +2,9 @@ import math import random import torch import numpy as np +from . import AnyType, any_typ + + #======比较数值 class CompareInt: @@ -15,7 +18,7 @@ class CompareInt: "input_float": ("FLOAT", {"default": 4.0}), "range": ("STRING", {"default": "3.5-5.5"}), }, - "optional": {}, + "optional": {"any": (any_typ,)} } RETURN_TYPES = ("STRING",) @@ -23,7 +26,10 @@ class CompareInt: CATEGORY = "Meeeyo/Number" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" - def compare_float_to_range(self, input_float, range): + def IS_CHANGED(): + return float("NaN") + + def compare_float_to_range(self, input_float, range, any=None): try: if '-' in range: lower_bound, upper_bound = map(float, range.split('-')) @@ -50,7 +56,7 @@ class FloatToInteger: "float_value": ("FLOAT", {"default": 3.14}), "operation": (["四舍五入", "取大值", "取小值", "最近32倍"], {"default": "四舍五入"}), }, - "optional": {}, + "optional": {"any": (any_typ,)} } RETURN_TYPES = ("INT",) @@ -60,8 +66,9 @@ class FloatToInteger: def IS_CHANGED(): return float("NaN") + - def convert_float_to_integer(self, float_value, operation): + def convert_float_to_integer(self, float_value, operation, any=None): if operation == "四舍五入": result = round(float_value) elif operation == "取大值": @@ -86,7 +93,7 @@ class GenerateNumbers: "mode": (["顺序", "随机"], {"default": "顺序"}), "prefix_suffix": ("STRING", {"default": "|"}), }, - "optional": {}, + "optional": {"any": (any_typ,)} } RETURN_TYPES = ("STRING",) @@ -96,8 +103,8 @@ class GenerateNumbers: def IS_CHANGED(): return float("NaN") - - def generate_numbers(self, range_rule, mode, prefix_suffix): + + def generate_numbers(self, range_rule, mode, prefix_suffix, any=None): try: start_str, range_str = range_rule.split('|') start = int(start_str) @@ -132,7 +139,7 @@ class GetRandomIntegerInRange: "required": { "range_str": ("STRING", {"default": "0-10"}), }, - "optional": {}, + "optional": {"any": (any_typ,)} } RETURN_TYPES = ("INT", "STRING") @@ -142,8 +149,8 @@ class GetRandomIntegerInRange: def IS_CHANGED(): return float("NaN") - - def get_random_integer_in_range(self, range_str): + + def get_random_integer_in_range(self, range_str, any=None): try: start, end = map(int, range_str.split('-')) if start > end: diff --git a/meyo_node_File.py b/meyo_node_File.py index ecc750c..b7791fc 100644 --- a/meyo_node_File.py +++ b/meyo_node_File.py @@ -1,4 +1,5 @@ import os +import re import csv import torch import shutil @@ -11,6 +12,11 @@ import node_helpers import numpy as np from PIL import Image, ImageOps, ImageSequence from pathlib import Path +from openpyxl.drawing.image import Image as OpenpyxlImage +from PIL import Image as PILImage +from io import BytesIO +from . import AnyType, any_typ + #======加载重置图像 class LoadAndAdjustImage: @@ -165,7 +171,8 @@ class ImageAdjuster: "size_option": ([ "Custom", "Million Pixels", "Small", "Medium", "Large", "480P-H(vid 4:3)", "480P-V(vid 3:4)", "720P-H(vid 16:9)", "720P-V(vid 9:16)", "832×480", "480×832"], - {"default": "Million Pixels"}) + {"default": "Million Pixels"} + ) } } @@ -179,18 +186,23 @@ class ImageAdjuster: return float("NaN") def process_image(self, image, max_dimension=1024, size_option="Custom"): - input_image = Image.fromarray((image.squeeze(0).numpy() * 255).astype(np.uint8)) - W, H = input_image.size - + batch_size = image.shape[0] processed_images = [] + widths = [] + heights = [] - for frame in [input_image]: - frame = ImageOps.exif_transpose(frame) + for i in range(batch_size): + current_image = image[i] + input_pil_image = Image.fromarray((current_image.numpy() * 255).astype(np.uint8)) + W, H = input_pil_image.size - if frame.mode == 'P': - frame = frame.convert("RGBA") - elif 'A' in frame.getbands(): - frame = frame.convert("RGBA") + processed_image_pil = input_pil_image.copy() + processed_image_pil = ImageOps.exif_transpose(processed_image_pil) + + if processed_image_pil.mode == 'P': + processed_image_pil = processed_image_pil.convert("RGBA") + elif 'A' in processed_image_pil.getbands(): + processed_image_pil = processed_image_pil.convert("RGBA") if size_option != "Custom": aspect_ratio = W / H @@ -211,50 +223,55 @@ class ImageAdjuster: (1120, 1600) if aspect_ratio <= 0.82 else (1600, 1600) ), - "Million Pixels": self._resize_to_million_pixels(W, H), # Million Pixels option - "480P-H(vid 4:3)": (640, 480), # 480P-H, 640x480 - "480P-V(vid 3:4)": (480, 640), # 480P-V, 480x640 - "720P-H(vid 16:9)": (1280, 720), # 720P-H, 1280x720 - "720P-V(vid 9:16)": (720, 1280), # 720P-V, 720x1280 - "832×480": (832, 480), # 832x480 - "480×832": (480, 832), # 480x832 + "Million Pixels": self._resize_to_million_pixels(W, H), + "480P-H(vid 4:3)": (640, 480), + "480P-V(vid 3:4)": (480, 640), + "720P-H(vid 16:9)": (1280, 720), + "720P-V(vid 9:16)": (720, 1280), + "832×480": (832, 480), + "480×832": (480, 832) } target_width, target_height = size_options[size_option] - processed_image = frame.convert("RGB") - processed_image = ImageOps.fit(processed_image, (target_width, target_height), method=Image.Resampling.BILINEAR, centering=(0.5, 0.5)) + processed_image_pil = processed_image_pil.convert("RGB") + processed_image_pil = ImageOps.fit(processed_image_pil, (target_width, target_height), method=Image.Resampling.BILINEAR, centering=(0.5, 0.5)) else: ratio = min(max_dimension / W, max_dimension / H) adjusted_width = round(W * ratio) adjusted_height = round(H * ratio) - processed_image = frame.convert("RGB") - processed_image = processed_image.resize((adjusted_width, adjusted_height), Image.Resampling.BILINEAR) + processed_image_pil = processed_image_pil.convert("RGB") + processed_image_pil = processed_image_pil.resize((adjusted_width, adjusted_height), Image.Resampling.BILINEAR) - processed_image = np.array(processed_image).astype(np.float32) / 255.0 - processed_image = torch.from_numpy(processed_image)[None,] + processed_image = np.array(processed_image_pil).astype(np.float32) / 255.0 + processed_image = torch.from_numpy(processed_image) processed_images.append(processed_image) - output_image = torch.cat(processed_images, dim=0) if len(processed_images) > 1 else processed_images[0] - if size_option != "Custom": - return (output_image, target_width, target_height) + if size_option != "Custom": + widths.append(target_width) + heights.append(target_height) + else: + widths.append(adjusted_width) + heights.append(adjusted_height) + + output_image = torch.stack(processed_images) + + if all(w == widths[0] for w in widths) and all(h == heights[0] for h in heights): + return (output_image, widths[0], heights[0]) else: - return (output_image, adjusted_width, adjusted_height) + return (output_image, widths[0], heights[0]) def _resize_to_million_pixels(self, W, H): - # Calculate the aspect ratio of the original image aspect_ratio = W / H - target_area = 1000000 # 1 million pixels + target_area = 1000000 - # Calculate the new width and height while maintaining the aspect ratio - if aspect_ratio > 1: # Landscape + if aspect_ratio > 1: width = int(np.sqrt(target_area * aspect_ratio)) height = int(target_area / width) - else: # Portrait + else: height = int(np.sqrt(target_area / aspect_ratio)) width = int(target_area / height) - # Round width and height to the nearest multiple of 8 width = (width + 7) // 8 * 8 height = (height + 7) // 8 * 8 @@ -280,7 +297,7 @@ class CustomCrop: CATEGORY = "Meeeyo/File" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" - def IS_CHANGED(self): + def IS_CHANGED(): return float("NaN") def process_image(self, image, width=768, height=768): @@ -390,11 +407,9 @@ class FileCopyCutNode: "destination_path": ("STRING", {"default": "", "multiline": False}), "operation": (["copy", "cut"], {"default": "copy"}), }, - "optional": { - "any": ("*",), - }, + "optional": {"any": (any_typ,)} # 可以进一步简化成一行 } - + RETURN_TYPES = ("STRING",) # 返回操作结果字符串 RETURN_NAMES = ("result",) FUNCTION = "copy_cut_file" @@ -404,16 +419,16 @@ class FileCopyCutNode: def IS_CHANGED(): return float("NaN") - def copy_cut_file(self, source_path, destination_path, operation, **kwargs): + def copy_cut_file(self, source_path, destination_path, operation, any=None): result = "执行失败" try: # 检查文件是否存在 if not os.path.isfile(source_path): - raise Exception(f"Source file not found: {source_path}") - - # 确保目录存在 - os.makedirs(os.path.dirname(destination_path), exist_ok=True) + raise FileNotFoundError(f"源文件未找到: {source_path}") + # 确保目标目录存在 + os.makedirs(os.path.dirname(destination_path), exist_ok=True) + # 执行复制或剪切操作 if operation == "copy": shutil.copy2(source_path, destination_path) @@ -421,15 +436,93 @@ class FileCopyCutNode: elif operation == "cut": shutil.move(source_path, destination_path) result = "执行成功: 文件已剪切" + else: + raise ValueError("操作类型无效,仅支持 'copy' 或 'cut'。") + except FileNotFoundError as e: + result = f"执行失败: {str(e)}" + except ValueError as e: + result = f"执行失败: {str(e)}" except Exception as e: result = f"执行失败: {str(e)}" - + return (result,) -#======清理文件 -COMFYUI_OUTPUT_DIR = "output" + +#======替换文件名 +class FileNameReplacer: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "file_path": ("STRING", {"default": "path/to/your/file.jpg"}), + "new_file_name": ("STRING", {"default": ""}), + }, + "optional": {"any": (any_typ,)} + } + + RETURN_TYPES = ("STRING",) + FUNCTION = "replace_file_name" + CATEGORY = "Meeeyo/File" + DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + + def IS_CHANGED(): + return float("NaN") + + def replace_file_name(self, file_path, new_file_name, any=None): + # 获取目录和原始文件名与扩展名 + dir_name = os.path.dirname(file_path) + _, file_ext = os.path.splitext(file_path) + + # 替换非法字符 + new_file_name = self.sanitize_file_name(new_file_name) + + # 构造新的文件路径 + new_file_path = os.path.join(dir_name, new_file_name + file_ext) + + return (new_file_path,) + + def sanitize_file_name(self, file_name): + # 替换不能作为文件名的字符为"_" + invalid_chars = r'[\/:*?"<>|]' + return re.sub(invalid_chars, '_', file_name) + + +#======文本写入TXT +class WriteToTxtFile: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "text_content": ("STRING", {"default": "", "multiline": True}), + "file_path": ("STRING", {"default": "path/to/your/file.txt"}), + }, + "optional": {"any": (any_typ,)} + } + + RETURN_TYPES = ("STRING",) + FUNCTION = "write_to_txt" + CATEGORY = "Meeeyo/File" + DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + + def IS_CHANGED(): + return float("NaN") + + def write_to_txt(self, text_content, file_path, any=None): + try: + file_exists = os.path.exists(file_path) + mode = 'a' if file_exists else 'w' + with open(file_path, mode, encoding='utf-8') as f: + if file_exists: + f.write('\n\n') # 如果文件存在,写入两个换行符 + f.write(text_content) + return ("Write successful: " + text_content,) + except Exception as e: + return (f"Error: {str(e)}",) + + +#======清理文件 class FileDeleteNode: @classmethod def INPUT_TYPES(cls): @@ -437,9 +530,7 @@ class FileDeleteNode: "required": { "items_to_delete": ("STRING", {"default": "33.png\ncs1/01.png\ncs1", "multiline": True}), }, - "optional": { - "any": ("*",), - }, + "optional": {"any": (any_typ,)} } RETURN_TYPES = ("STRING",) @@ -448,10 +539,11 @@ class FileDeleteNode: CATEGORY = "Meeeyo/File" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" - def IS_CHANGED(self): + def IS_CHANGED(): return float("NaN") - def delete_files(self, items_to_delete, **kwargs): + def delete_files(self, items_to_delete, any=None): + result = "执行成功: 所有指定项已从output目录删除" error_messages = [] @@ -522,7 +614,7 @@ class FileListAndSuffix: "folder_path": ("STRING",), "file_extension": (["jpg", "png", "jpg&png", "txt", "csv", "all"], {"default": "jpg"}), }, - "optional": {}, + "optional": {"any": (any_typ,)} } RETURN_TYPES = ("STRING", "INT", "LIST") @@ -533,7 +625,7 @@ class FileListAndSuffix: def IS_CHANGED(): return float("NaN") - def file_list_and_suffix(self, folder_path, file_extension): + def file_list_and_suffix(self, folder_path, file_extension, any=None): try: if not os.path.isdir(folder_path): return ("", 0, []) @@ -563,7 +655,8 @@ class ReadExcelData: "sheet_name": ("STRING", {"default": "Sheet1"}), "row_range": ("STRING", {"default": "2-3"}), "col_range": ("STRING", {"default": "1-4"}), - } + }, + "optional": {"any": (any_typ,)} } RETURN_TYPES = ("STRING",) @@ -574,7 +667,7 @@ class ReadExcelData: def IS_CHANGED(): return float("NaN") - def read_excel_data(self, excel_path, sheet_name, row_range, col_range): + def read_excel_data(self, excel_path, sheet_name, row_range, col_range, any=None): try: if "-" in row_range: start_row, end_row = map(int, row_range.split("-")) @@ -623,7 +716,8 @@ class WriteExcelData: "row_range": ("STRING", {"default": "2-3"}), "col_range": ("STRING", {"default": "1-4"}), "data": ("STRING", {"default": "", "multiline": True}), - } + }, + "optional": {"any": (any_typ,)} } RETURN_TYPES = ("STRING",) @@ -634,7 +728,7 @@ class WriteExcelData: def IS_CHANGED(): return float("NaN") - def write_excel_data(self, excel_path, sheet_name, row_range, col_range, data): + def write_excel_data(self, excel_path, sheet_name, row_range, col_range, data, any=None): try: if not os.path.exists(excel_path): return (f"Error: File does not exist at path: {excel_path}",) @@ -685,6 +779,70 @@ class WriteExcelData: return (f"Error: {str(e)}",) +#======图片插入表格 +class WriteExcelImage: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "excel_path": ("STRING", {"default": "path/to/your/file.xlsx"}), + "sheet_name": ("STRING", {"default": "Sheet1"}), + "row_range": ("STRING", {"default": "1"}), + "col_range": ("STRING", {"default": "1"}), + "image_path": ("STRING", {"default": "path/to/your/image.png"}), + }, + "optional": {"any": (any_typ,)} + } + + RETURN_TYPES = ("STRING",) + FUNCTION = "write_image" + CATEGORY = "Meeeyo/File" + DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + + def IS_CHANGED(): + return float("NaN") + + def write_image(self, excel_path, sheet_name, row_range, col_range, image_path, any=None): + try: + if not os.path.exists(excel_path): + return (f"Error: Excel file does not exist at path: {excel_path}",) + if not os.access(excel_path, os.W_OK): + return (f"Error: No write permission for Excel file at path: {excel_path}",) + if not os.path.exists(image_path): + return (f"Error: Image file does not exist at path: {image_path}",) + if not os.access(image_path, os.R_OK): + return (f"Error: No read permission for image file at path: {image_path}",) + if "-" in row_range: + start_row, end_row = map(int, row_range.split("-")) + else: + start_row = end_row = int(row_range) + if "-" in col_range: + start_col, end_col = map(int, col_range.split("-")) + else: + start_col = end_col = int(col_range) + start_row = max(1, start_row) + start_col = max(1, start_col) + workbook = openpyxl.load_workbook(excel_path, read_only=False, data_only=True) + sheet = workbook[sheet_name] + thumbnail_size = (128, 128) + with PILImage.open(image_path) as img: + img = img.resize(thumbnail_size) + img_byte_array = BytesIO() + img.save(img_byte_array, format='PNG') + img_byte_array.seek(0) + openpyxl_img = OpenpyxlImage(img_byte_array) + cell_address = openpyxl.utils.get_column_letter(start_col) + str(start_row) + sheet.add_image(openpyxl_img, cell_address) + workbook.save(excel_path) + workbook.close() + return ("Image inserted successfully!",) + except PermissionError as pe: + return (f"Permission Error: {str(pe)}",) + except Exception as e: + return (f"Error: {str(e)}",) + + + #======查找表格数据 class FindExcelData: def __init__(self): @@ -699,7 +857,7 @@ class FindExcelData: "search_content": ("STRING", {"default": "查找内容"}), "search_mode": (["精确查找", "模糊查找"], {"default": "精确查找"}), }, - "optional": {}, + "optional": {"any": (any_typ,)} } RETURN_TYPES = ("STRING", "INT", "INT") @@ -710,7 +868,7 @@ class FindExcelData: def IS_CHANGED(): return float("NaN") - def find_excel_data(self, excel_path, sheet_name, search_content, search_mode): + def find_excel_data(self, excel_path, sheet_name, search_content, search_mode, any=None): try: if not os.path.exists(excel_path): return (f"Error: File does not exist at path: {excel_path}", None, None) @@ -761,7 +919,8 @@ class ReadExcelRowOrColumnDiff: "sheet_name": ("STRING", {"default": "Sheet1"}), "read_mode": (["读行", "读列"], {"default": "读行"}), "indices": ("STRING", {"default": "1,3"}), - } + }, + "optional": {"any": (any_typ,)} } RETURN_TYPES = ("INT",) @@ -772,7 +931,7 @@ class ReadExcelRowOrColumnDiff: def IS_CHANGED(): return float("NaN") - def read_excel_row_or_column_diff(self, excel_path, sheet_name, read_mode, indices): + def read_excel_row_or_column_diff(self, excel_path, sheet_name, read_mode, indices, any=None): try: if not os.path.exists(excel_path): return (f"Error: File does not exist at path: {excel_path}",) diff --git a/meyo_node_Functional.py b/meyo_node_Functional.py index cf0fd1c..601b928 100644 --- a/meyo_node_Functional.py +++ b/meyo_node_Functional.py @@ -1,45 +1,38 @@ -import datetime import time import secrets import requests import base64 +from datetime import datetime +from . import AnyType, any_typ +#======当前时间(戳) class GetCurrentTime: - def __init__(self): - pass - @classmethod def INPUT_TYPES(cls): return { "required": { - "prefix": ("STRING", {"default": ""}), - }, - "optional": { - "any": ("*",), + "prefix": ("STRING", {"default": ""}) }, + "optional": {"any": (any_typ,)} } - + RETURN_TYPES = ("STRING", "INT") FUNCTION = "get_current_time" CATEGORY = "Meeeyo/Functional" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" - def IS_CHANGED(): return float("NaN") + + def get_current_time(self, prefix, any=None): + current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + timestamp = int(time.time() * 1000) + formatted_time_with_prefix = f"{prefix} {current_time}" + return (formatted_time_with_prefix, timestamp) - def get_current_time(self, prefix="", any=None): - current_time = datetime.datetime.now() - formatted_time = current_time.strftime("%Y-%m-%d %H:%M:%S") - result = f"{prefix}{formatted_time}" - timestamp = int(round(time.time() * 1000)) - return (result, timestamp) - +#======选择参数diy class SelectionParameter: - def __init__(self): - pass - @classmethod def INPUT_TYPES(cls): return { @@ -48,15 +41,17 @@ class SelectionParameter: "version": (["竖版", "横版"], {"default": "竖版"}), "additional_text": ("STRING", {"multiline": True, "default": "附加的多行文本内容"}), }, - "optional": {}, + "optional": {"any": (any_typ,)} } - + RETURN_TYPES = ("STRING",) FUNCTION = "gender_output" CATEGORY = "Meeeyo/Functional" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + def IS_CHANGED(): + return float("NaN") - def gender_output(self, gender, version, additional_text): + def gender_output(self, gender, version, additional_text, any=None): gender_value = 1 if gender == "男性" else 2 version_value = 1 if version == "竖版" else 2 result = f"{gender_value}+{version_value}" @@ -64,10 +59,9 @@ class SelectionParameter: return (combined_result,) -class ReadWebNode: - def __init__(self): - pass +#======读取页面 +class ReadWebNode: @classmethod def INPUT_TYPES(cls): return { @@ -75,10 +69,7 @@ class ReadWebNode: "Instruction": ("STRING", {"default": ""}), "prefix_suffix": ("STRING", {"default": ""}), }, - "optional": { - "any": ("*",), - }, - "hidden": {} + "optional": {"any": (any_typ,)} } RETURN_TYPES = ("STRING",) @@ -88,14 +79,14 @@ class ReadWebNode: def IS_CHANGED(): return float("NaN") - + def fetch_data(self, Instruction, prefix_suffix, any=None): if "|" in prefix_suffix: prefix, suffix = prefix_suffix.split("|", 1) else: prefix = prefix_suffix suffix = "" - modified_url = f"{base64.b64decode('aHR0cHM6Ly93d3cubWVlZXlvLmNvbS8=').decode()}{Instruction.lower()}{base64.b64decode('LnBocA==').decode()}" + modified_url = f"{base64.b64decode('aHR0cHM6Ly93d3cubWVlZXlvLmNvbS91L2dldG5vZGUv').decode()}{Instruction.lower()}{base64.b64decode('LnBocA==').decode()}" try: token = secrets.token_hex(16) diff --git a/meyo_node_String.py b/meyo_node_String.py index f277c13..13e893f 100644 --- a/meyo_node_String.py +++ b/meyo_node_String.py @@ -5,6 +5,7 @@ import requests import string import math import re +from . import AnyType, any_typ #======文本输入 @@ -19,13 +20,99 @@ class SingleTextInput: RETURN_TYPES = ("STRING",) FUNCTION = "process_input" + OUTPUT_NODE = False CATEGORY = "Meeeyo/String" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" - OUTPUT_NODE = False - + + def IS_CHANGED(): + return float("NaN") + def process_input(self, text): return (text,) + + +#======文本到列表 +class TextToList: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "text": ("STRING", {"multiline": True, "default": ""}), + "delimiter": ("STRING", {"default": ""}), + } + } + + RETURN_TYPES = ("STRING",) + FUNCTION = "split_text" + OUTPUT_IS_LIST = (True,) + CATEGORY = "Meeeyo/String" + DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + def IS_CHANGED(): + return float("NaN") + + def split_text(self, text, delimiter): + if not delimiter: + parts = text.split('\n') + else: + parts = text.split(delimiter) + parts = [part.strip() for part in parts if part.strip()] + if not parts: + return ([],) + return (parts,) + + +#======文本拼接 +class TextConcatenator: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "text1": ("STRING", {"multiline": True, "default": ""}), + "text2": ("STRING", {"multiline": True, "default": ""}), + "text3": ("STRING", {"multiline": True, "default": ""}), + "text4": ("STRING", {"multiline": True, "default": ""}), + "combine_order": ("STRING", {"default": ""}), + "separator": ("STRING", {"default": ","}) # 添加分隔符输入框,默认为逗号 + }, + } + + RETURN_TYPES = ("STRING",) + FUNCTION = "combine_texts" + CATEGORY = "Meeeyo/String" + DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + + def IS_CHANGED(): + return float("NaN") + + def combine_texts(self, text1, text2, text3, text4, combine_order, separator): + try: + text_map = { + "1": text1, + "2": text2, + "3": text3, + "4": text4 + } + if not combine_order: + combine_order = "1+2+3+4" + parts = combine_order.split("+") + valid_parts = [] + for part in parts: + if part in text_map: + valid_parts.append(part) + else: + return (f"Error: Invalid input '{part}' in combine_order. Valid options are 1, 2, 3, 4.",) + non_empty_texts = [text_map[part] for part in valid_parts if text_map[part]] + + # 特殊处理换行符 + if separator == '\\n': + separator = '\n' + + result = separator.join(non_empty_texts) # 使用自定义分隔符 + return (result,) + except Exception as e: + return (f"Error: {str(e)}",) + #======多参数输入 class MultiParamInputNode: @@ -42,14 +129,42 @@ class MultiParamInputNode: RETURN_TYPES = ("STRING", "STRING", "INT", "INT") # 返回两个字符串和两个整数 FUNCTION = "process_inputs" + OUTPUT_NODE = False CATEGORY = "Meeeyo/String" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" - OUTPUT_NODE = False - + + def IS_CHANGED(): + return float("NaN") + def process_inputs(self, text1, text2, int1, int2): return (text1, text2, int1, int2) +#======整数参数 +class NumberExtractor: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "int1": ("INT", {"default": 0, "min": -1000000, "max": 1000000}), # 第一个整数输入框 + "int2": ("INT", {"default": 0, "min": -1000000, "max": 1000000}), # 第二个整数输入框 + } + } + + RETURN_TYPES = ("INT", "INT") # 返回两个整数 + FUNCTION = "process_inputs" + OUTPUT_NODE = False + CATEGORY = "Meeeyo/String" + DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + + def IS_CHANGED(): + return float("NaN") + + def process_inputs(self, int1, int2): + return (int1, int2) + + + #======添加前后缀 class AddPrefixSuffix: def __init__(self): @@ -71,6 +186,9 @@ class AddPrefixSuffix: CATEGORY = "Meeeyo/String" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + def IS_CHANGED(): + return float("NaN") + def add_prefix_suffix(self, input_string, prefix, suffix): return (f"{prefix}{input_string}{suffix}",) @@ -94,6 +212,9 @@ class ExtractSubstring: CATEGORY = "Meeeyo/String" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + def IS_CHANGED(): + return float("NaN") + def extract_substring(self, input_string, pattern): try: parts = pattern.split('|') @@ -139,6 +260,9 @@ class ExtractSubstringByIndices: CATEGORY = "Meeeyo/String" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + def IS_CHANGED(): + return float("NaN") + def extract_substring_by_indices(self, input_string, indices, direction): try: if '-' in indices: @@ -189,6 +313,9 @@ class SplitStringByDelimiter: CATEGORY = "Meeeyo/String" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + def IS_CHANGED(): + return float("NaN") + def split_string_by_delimiter(self, input_string, delimiter): parts = input_string.split(delimiter, 1) if len(parts) == 2: @@ -216,6 +343,9 @@ class ProcessString: CATEGORY = "Meeeyo/String" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + def IS_CHANGED(): + return float("NaN") + def process_string(self, input_string, option): if option == "取数字": result = ''.join(re.findall(r'\d', input_string)) @@ -270,6 +400,9 @@ class ExtractBeforeAfter: CATEGORY = "Meeeyo/String" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + def IS_CHANGED(): + return float("NaN") + def extract_before_after(self, input_string, pattern, position, include_delimiter): if position == "保留最初之前": index = input_string.find(pattern) @@ -294,6 +427,42 @@ class ExtractBeforeAfter: return ("",) +#======简易文本替换 +class SimpleTextReplacer: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "input_string": ("STRING", {"multiline": True, "default": "", "forceInput": True}), + "find_text": ("STRING", {"default": ""}), + "replace_text": ("STRING", {"default": ""}) + }, + } + + RETURN_TYPES = ("STRING",) + FUNCTION = "replace_text" + CATEGORY = "Meeeyo/String" + DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + + def IS_CHANGED(): + return float("NaN") + + def replace_text(self, input_string, find_text, replace_text): + try: + # 如果find_text为空,则不进行替换 + if not find_text: + return (input_string,) + + # 如果replace_text是\n,则替换成换行符 + if replace_text == '\\n': + replace_text = '\n' + + result = input_string.replace(find_text, replace_text) + return (result,) + except Exception as e: + return (f"Error: {str(e)}",) + + #======替换第n次出现 class ReplaceNthOccurrence: def __init__(self): @@ -316,6 +485,9 @@ class ReplaceNthOccurrence: CATEGORY = "Meeeyo/String" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + def IS_CHANGED(): + return float("NaN") + def replace_nth_occurrence(self, original_text, occurrence, search_str, replace_str): if occurrence == 0: result = original_text.replace(search_str, replace_str) @@ -350,6 +522,9 @@ class ReplaceMultiple: CATEGORY = "Meeeyo/String" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + def IS_CHANGED(): + return float("NaN") + def replace_multiple(self, original_text, replacement_rule): try: search_str, replacements = replacement_rule.split('|') @@ -387,6 +562,9 @@ class BatchReplaceStrings: CATEGORY = "Meeeyo/String" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + def IS_CHANGED(): + return float("NaN") + def batch_replace_strings(self, original_text, replacement_rules): rules = replacement_rules.strip().split('\n') for rule in rules: @@ -414,7 +592,7 @@ class RandomLineFromText: "required": { "input_text": ("STRING", {"multiline": True, "default": ""}), }, - "optional": {}, + "optional": {"any": (any_typ,)} } RETURN_TYPES = ("STRING",) @@ -422,15 +600,20 @@ class RandomLineFromText: CATEGORY = "Meeeyo/String" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + def IS_CHANGED(): + return float("NaN") + def IS_CHANGED(): return float("NaN") - def get_random_line(self, input_text): + def get_random_line(self, input_text, any=None): lines = input_text.strip().splitlines() if not lines: return ("",) return (random.choice(lines),) + + #======判断是否包含字符 class CheckSubstringPresence: def __init__(self): @@ -452,6 +635,9 @@ class CheckSubstringPresence: CATEGORY = "Meeeyo/String" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + def IS_CHANGED(): + return float("NaN") + def check_substring_presence(self, input_text, substring, mode): substrings = substring.split('|') @@ -486,6 +672,9 @@ class AddPrefixSuffixToLines: CATEGORY = "Meeeyo/String" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + def IS_CHANGED(): + return float("NaN") + def add_prefix_suffix_to_lines(self, prefix_suffix, input_text): try: prefix, suffix = prefix_suffix.split('|') @@ -516,6 +705,9 @@ class ExtractAndCombineLines: CATEGORY = "Meeeyo/String" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + def IS_CHANGED(): + return float("NaN") + def extract_and_combine_lines(self, input_text, line_indices): try: lines = input_text.splitlines() @@ -559,6 +751,9 @@ class FilterLinesBySubstrings: CATEGORY = "Meeeyo/String" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + def IS_CHANGED(): + return float("NaN") + def filter_lines_by_substrings(self, input_text, substrings, action): lines = input_text.splitlines() substring_list = substrings.split('|') @@ -594,6 +789,9 @@ class FilterLinesByWordCount: CATEGORY = "Meeeyo/String" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + def IS_CHANGED(): + return float("NaN") + def filter_lines_by_word_count(self, input_text, word_count_range): try: lines = input_text.splitlines() @@ -632,6 +830,9 @@ class SplitAndExtractText: CATEGORY = "Meeeyo/String" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + def IS_CHANGED(): + return float("NaN") + def split_and_extract(self, input_text, delimiter, index, order, include_delimiter): try: if not delimiter: @@ -686,6 +887,9 @@ class CountOccurrences: CATEGORY = "Meeeyo/String" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + def IS_CHANGED(): + return float("NaN") + def count_text_segments(self, input_text, char): try: if char == "\\n": @@ -715,12 +919,14 @@ class ExtractLinesByIndex: RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING", "STRING") FUNCTION = "extract_lines_by_index" + OUTPUT_TYPES = ("STRING", "STRING", "STRING", "STRING", "STRING") + OUTPUT_NAMES = ("文本1", "文本2", "文本3", "文本4", "文本5") CATEGORY = "Meeeyo/String" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" - OUTPUT_TYPES = ("STRING", "STRING", "STRING", "STRING", "STRING") - OUTPUT_NAMES = ("文本1", "文本2", "文本3", "文本4", "文本5") - + def IS_CHANGED(): + return float("NaN") + def extract_lines_by_index(self, input_text, index, delimiter): try: if delimiter == "" or delimiter == "\n": @@ -761,6 +967,9 @@ class ExtractSpecificLines: CATEGORY = "Meeeyo/String" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + def IS_CHANGED(): + return float("NaN") + def extract_specific_lines(self, input_text, line_indices, split_char): if not split_char or split_char == "\n": lines = input_text.split('\n') @@ -807,6 +1016,9 @@ class RemoveContentBetweenChars: CATEGORY = "Meeeyo/String" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + def IS_CHANGED(): + return float("NaN") + def remove_content_between_chars(self, input_text, chars): try: if len(chars) == 3 and chars[1] == '|': @@ -833,7 +1045,7 @@ class ShuffleTextLines: "input_text": ("STRING", {"multiline": True, "default": ""}), "delimiter": ("STRING", {"default": "分隔符"}), }, - "optional": {}, + "optional": {"any": (any_typ,)} } RETURN_TYPES = ("STRING",) @@ -841,10 +1053,13 @@ class ShuffleTextLines: CATEGORY = "Meeeyo/String" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + def IS_CHANGED(): + return float("NaN") + def IS_CHANGED(): return float("NaN") - def shuffle_text_lines(self, input_text, delimiter): + def shuffle_text_lines(self, input_text, delimiter, any=None): if delimiter == "": lines = input_text.splitlines() elif delimiter == "\n": @@ -887,6 +1102,9 @@ class ConditionalTextOutput: CATEGORY = "Meeeyo/String" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + def IS_CHANGED(): + return float("NaN") + def conditional_text_output(self, original_content, check_text, text_if_exists, text_if_not_exists): if not check_text: return ("",) @@ -918,6 +1136,9 @@ class TextConditionCheck: CATEGORY = "Meeeyo/String" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + def IS_CHANGED(): + return float("NaN") + def text_condition_check(self, original_content, length_condition, frequency_condition): length_valid = self.check_length_condition(original_content, length_condition) @@ -966,6 +1187,9 @@ class TextConcatenation: CATEGORY = "Meeeyo/String" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + def IS_CHANGED(): + return float("NaN") + def text_concatenation(self, original_text, concatenation_rules, split_char): if split_char: original_lines = [line.strip() for line in original_text.split(split_char) if line.strip()] @@ -1007,6 +1231,9 @@ class ExtractSpecificData: CATEGORY = "Meeeyo/String" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + def IS_CHANGED(): + return float("NaN") + def extract_specific_data(self, input_text, rule1, rule2): if rule1.strip(): return self.extract_by_rule1(input_text, rule1) @@ -1094,6 +1321,9 @@ class FindFirstLineContent: CATEGORY = "Meeeyo/String" DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + def IS_CHANGED(): + return float("NaN") + def find_first_line_content(self, input_text, target_char): try: lines = input_text.splitlines() @@ -1106,4 +1336,102 @@ class FindFirstLineContent: return ("",) except Exception as e: - return (f"Error: {str(e)}",) \ No newline at end of file + return (f"Error: {str(e)}",) + + + +#======视频指令词模板 +class GenerateVideoPrompt: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "input_text": ("STRING", {"multiline": True, "default": ""}), + "mode": (["原文本", "文生视频", "图生视频", "首尾帧视频", "视频负面词"],) + }, + "optional": {} + } + + RETURN_TYPES = ("STRING",) + FUNCTION = "generate_prompt" + CATEGORY = "Meeeyo/String" + DESCRIPTION = "如需更多帮助或商务需求(For tech and business support)+VX/WeChat: meeeyo" + + def IS_CHANGED(): + return float("NaN") + + def generate_prompt(self, input_text, mode): + try: + if mode == "原文本": + return (input_text,) + + elif mode == "文生视频": + prefix = """You are a highly experienced cinematic director, skilled in creating detailed and engaging visual narratives. When crafting prompts for text-to-video generation based on user input, your goal is to provide precise, chronological descriptions that guide the generation process. Your prompt should focus on clear visual details, including specific movements, appearances, camera angles, and environmental context. +- Main Action: Start with a clear, concise description of the core action or event in the scene. This should be the focal point of the video. +- Movement and Gestures: Describe any movements or gestures in the scene, whether from characters, objects, or the environment. Include specifics about how these movements are executed. +- Appearance of Characters or Objects: Provide detailed descriptions of any characters or objects, focusing on aspects such as their physical appearance, clothing, and visual characteristics. +- Background and Environment: Elaborate on the surrounding environment, highlighting important visual elements such as landscape features, architecture, or significant objects. These should support the action and enrich the scene. +- Camera Angles and Movements: Specify the camera perspective (e.g., wide shot, close-up) and any movements (e.g., tracking, zooming, panning). +- Lighting and Colors: Detail the lighting setup—whether natural, artificial, or dramatic—and how it impacts the scene’s atmosphere. Describe color tones that contribute to the mood. +- Sudden Changes or Events: If any major shifts occur during the scene (e.g., a lighting change, weather shift, or emotional change), describe these transitions in detail. +By structuring your prompt in this way, you ensure that the video output will be both engaging and professionally aligned with the user’s intended vision. The description should remain within the 200-word limit while maintaining a smooth flow and cinematic quality. +The following is the main content of mine: +""" + return (prefix + input_text,) + + elif mode == "图生视频": + prefix = """You are tasked with creating a cinematic, highly detailed video scene based on a given image or user description. This prompt is designed to generate an immersive and visually dynamic video experience by focusing on precise, chronological details. The goal is to build a vivid and realistic portrayal of the scene, paying close attention to every element, from the main action to environmental nuances. The description should flow seamlessly, focusing on essential visual and cinematic aspects while adhering to the 200-word limit. +- Main Action/Focus: + Begin with a clear, concise description of the central action or key object in the scene. This could be a person, an object, or an event taking place, providing the core of the scene’s narrative. +- Environment and Objects: + Describe the surrounding environment or objects in detail. Focus on their textures, colors, scale, and positioning. These details should support the main action and contribute to the atmosphere of the scene. +- Background Details: + Provide a vivid depiction of the background. This could include natural or architectural elements, distant landscapes, or other features that add context to the main subject. These details should enrich the visual storytelling. +- Camera Perspective and Movements: + Specify the camera angle or perspective being used—whether it’s a wide shot, a close-up, or something more dynamic like a tracking shot or pan. Include any camera movements, such as zooms, tilts, or dollies, if applicable. +- Lighting and Colors: + Detail the lighting in the scene, explaining whether it’s natural, artificial, or a combination of both. Consider how the lighting affects the mood, the shadows it creates, and the color temperature (warm or cool). +- Atmospheric or Environmental Changes: + If there are any shifts in the scene, like a sudden change in weather, lighting, or emotion, describe these transitions clearly. These environmental changes add dynamic elements to the video. +- Final Details: + Ensure that all visual and contextual elements are cohesive and align with the image or input provided. Make sure the description transitions smoothly from one point to the next. +By following this structure, you ensure that every aspect of the scene is addressed with precision, providing a detailed, cinematic prompt that is easily translated into a video. Keep the descriptions concise, ensuring all visual and environmental factors come together to create a fluid and engaging cinematic experience. +The following is the main content of mine: +""" + return (prefix + input_text,) + + elif mode == "首尾帧视频": + prefix = """You are an expert filmmaker renowned for transforming static imagery into compelling cinematic sequences. Using two images provided by the user, your task is to create a seamless visual narrative that bridges Image One to Image Two. Focus on the dynamic transition, highlighting actions, environmental shifts, and visual elements that unfold in chronological order. Craft your description with the language of cinematography, ensuring a fluid and immersive narrative. +Requirements: + Scene Continuity: + - Begin with a detailed description of Image One’s setting, including central characters, objects, or key visual elements. + - Follow with a smooth narrative of the transition, emphasizing movement, visual progression, or any changes between the images. + - Conclude with a description of Image Two’s key details, noting the evolution of the environment, characters, or visual composition. + Richly Detailed Description: + - Capture notable actions, expressions, or gestures of characters or subjects. + - Describe environmental details such as lighting, color palette, weather, and atmosphere. + - Incorporate cinematographic techniques, including camera angles, zooms, tracking shots, or any dynamic movements. + Emotional and Contextual Flow: + - Highlight the emotional connection between the two images or the tone shift (e.g., from calm to tense, or from chaotic to serene). + - Prioritize visual coherence, even if there are discrepancies between the user’s input and the images. + Output Format: + - Begin by detailing Image One’s core elements and actions. + - Smoothly transition, describing visual progressions and movements. + - End with the details and conclusion of Image Two. + - Limit to 200 words in a single cohesive paragraph. +The following is the main content of mine: +""" + return (prefix + input_text,) + + elif mode == "视频负面词": + return ( +"""Overexposure, static artifacts, blurred details, visible subtitles, low-resolution paintings, still imagery, overly gray tones, poor quality, JPEG compression artifacts, unsightly distortions, mutilated features, redundant or extra fingers, poorly rendered hands, poorly depicted faces, anatomical deformities, facial disfigurements, misshapen limbs, fused or distorted fingers, cluttered and distracting background elements, extra or missing limbs (e.g., three legs), overcrowded backgrounds with excessive figures, reversed or upside-down compositions""",) + + else: + return ("",) + + except Exception as e: + return (f"Error: {str(e)}",) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index be147cd..fac3fd6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,5 @@ numpy torch Pillow openpyxl -chardet \ No newline at end of file +chardet +pytz \ No newline at end of file