feat: 新增启动欢迎通知、流式预览节点及多项功能更新

- 新增启动弹窗通知(绿色主题,支持关闭)
- 新增 StreamPreview 流式文本预览节点
- 新增 fileUpload、updateNotifier 前端 JS 模块
- 重构多个 client,统一错误处理
- 删除废弃节点 batch_nano_banana_v2、quan_neng_sheng_tu 等
- 将 .config 纳入版本控制(已清空密钥)
This commit is contained in:
Jony
2026-04-13 00:49:51 +08:00
parent bbc5f4a2c4
commit 92bcf65d14
28 changed files with 1381 additions and 3518 deletions
+104 -125
View File
@@ -1,146 +1,125 @@
"""
LoadFile 节点
ComfyUI 自定义节点,用于加载文件并转换为 FILE 类型数据
LoadFile 节点(增强版)
支持单文件路径和文件夹路径,输出 FILE_LIST 类型供全能LLM等节点使用
"""
import base64
import os
from pathlib import Path
from typing import Tuple
from typing import Tuple, List
from ..utils.file_types import FileData, DOCUMENT_MIME_TYPES, FILE_SIZE_LIMITS
from ..utils.file_types import FileData, FileList, DOCUMENT_MIME_TYPES, FILE_SIZE_LIMIT, TOTAL_FILE_SIZE_LIMIT
class LoadFile:
"""
LoadFile 节点
功能:
- 文件系统加载文件
- 支持 PDF 和 TXT 文件
- 转换为 FILE 类型数据(包含 base64 编码内容)
- 验证文件大小和格式
加载文件节点
- 单文件路径:加载指定文件
- 文件夹路径:加载文件夹内所有支持的文件(非递归)
- 两者可同时使用,结果合并输出
- 输出 FILE_LIST 类型,可直接连接到全能LLM对话助手
"""
@classmethod
def INPUT_TYPES(cls):
"""
定义输入参数
"""
return {
"required": {
"文件路径": ("STRING", {
"required": {},
"optional": {
"单文件路径": ("STRING", {
"default": "",
"multiline": False
})
}
"multiline": False,
"placeholder": "文件完整路径,多个文件用英文逗号分隔",
}),
"文件夹路径": ("STRING", {
"default": "",
"multiline": False,
"placeholder": "文件夹路径,自动读取其中所有支持的文件",
}),
},
}
# 返回值类型
RETURN_TYPES = ("FILE", "STRING")
RETURN_NAMES = ("文件", "文件信息")
# 执行函数名
RETURN_TYPES = ("FILE_LIST", "STRING")
RETURN_NAMES = ("文件列表", "文件信息")
FUNCTION = "load_file"
# 节点分类
CATEGORY = "file/input"
def load_file(self, 文件路径: str) -> Tuple[FileData, str]:
"""
加载文件并转换为 FILE 类型
Args:
文件路径: 文件的完整路径(支持绝对路径和相对路径)
Returns:
(FileData, 文件信息预览)
Raises:
ValueError: 文件不存在、不支持的文件类型或文件过大
"""
try:
# 清理路径(去除空格和引号)
file_path = 文件路径.strip().strip('"').strip("'")
if not file_path:
raise ValueError("文件路径不能为空")
# 转换为 Path 对象
path = Path(file_path)
# 如果是相对路径,转换为绝对路径
if not path.is_absolute():
# 相对于当前工作目录
path = Path.cwd() / path
# 验证文件是否存在
if not path.exists():
raise ValueError(f"文件不存在: {file_path}")
if not path.is_file():
raise ValueError(f"路径不是文件: {file_path}")
# 获取文件信息
extension = path.suffix.lower()
filename = path.stem
file_size = path.stat().st_size
# 验证文件类型
if extension not in DOCUMENT_MIME_TYPES:
supported_types = ", ".join(DOCUMENT_MIME_TYPES.keys())
def load_file(self, 文件路径: str = "", 文件夹路径: str = "") -> Tuple[FileList, str]:
collected: List[Path] = []
# 1. 单文件路径(逗号分隔,支持多个)
if 单文件路径.strip():
for raw in 单文件路径.split(","):
p = Path(raw.strip().strip('"').strip("'"))
if not p.is_absolute():
p = Path.cwd() / p
if not p.exists():
raise ValueError(f"文件不存在: {p}")
if not p.is_file():
raise ValueError(f"路径不是文件: {p}")
collected.append(p)
# 2. 文件路径
if 文件夹路径.strip():
folder = Path(文件夹路径.strip().strip('"').strip("'"))
if not folder.is_absolute():
folder = Path.cwd() / folder
if not folder.exists():
raise ValueError(f"文件夹不存在: {folder}")
if not folder.is_dir():
raise ValueError(f"路径不是文件夹: {folder}")
for p in sorted(folder.iterdir()):
if p.is_file() and p.suffix.lower() in DOCUMENT_MIME_TYPES:
collected.append(p)
if not collected:
raise ValueError(f"文件夹中没有支持的文件: {folder}")
if not collected:
raise ValueError("请至少提供一个文件路径或文件夹路径")
# 去重(保持顺序)
seen = set()
unique: List[Path] = []
for p in collected:
key = str(p.resolve())
if key not in seen:
seen.add(key)
unique.append(p)
# 大小检查 & 读取
total_size = 0
file_list: FileList = []
info_lines = []
for p in unique:
ext = p.suffix.lower()
if ext not in DOCUMENT_MIME_TYPES:
print(f"LoadFile: 跳过不支持的文件类型 {p.name}")
continue
file_size = p.stat().st_size
if file_size > FILE_SIZE_LIMIT:
raise ValueError(
f"不支持的文件类型: {extension}\n"
f"支持的类型: {supported_types}"
f"文件 {p.name} 大小 {file_size / 1024 / 1024:.1f}MB 超过单文件 50MB 限制"
)
# 获取 MIME 类型
mime_type = DOCUMENT_MIME_TYPES[extension]
# 验证文件大小
size_limit = FILE_SIZE_LIMITS.get(extension, 20 * 1024 * 1024)
if file_size > size_limit:
raise ValueError(
f"文件过大 ({file_size / 1024 / 1024:.2f}MB)"
f"最大支持 {size_limit / 1024 / 1024:.0f}MB"
)
# 读取文件并转换为 base64
print(f"LoadFile: 正在加载文件 {filename}{extension}")
print(f"LoadFile: 文件大小 = {file_size / 1024:.2f}KB")
with open(path, "rb") as f:
file_bytes = f.read()
# Base64 编码
b64_str = base64.b64encode(file_bytes).decode("utf-8")
# 创建 FileData 对象
file_data = FileData(
path=str(path),
filename=filename,
extension=extension,
mime_type=mime_type,
data=b64_str,
size=file_size
)
# 生成文件信息预览
file_info = (
f"文件名: {filename}{extension}\n"
f"类型: {mime_type}\n"
f"大小: {file_size / 1024:.2f}KB\n"
f"路径: {path}"
)
print(f"LoadFile: 加载成功")
return (file_data, file_info)
except ValueError as e:
print(f"LoadFile: 输入错误 - {str(e)}")
raise
except Exception as e:
print(f"LoadFile: 未知错误 - {str(e)}")
raise
total_size += file_size
if total_size > TOTAL_FILE_SIZE_LIMIT:
raise ValueError(f"所有文件总大小超过 50MB 限制")
mime = DOCUMENT_MIME_TYPES[ext]
with open(p, "rb") as f:
b64 = base64.b64encode(f.read()).decode("utf-8")
file_list.append(FileData(
path=str(p),
filename=p.stem,
extension=ext,
mime_type=mime,
data=b64,
size=file_size,
))
info_lines.append(f" {p.name} ({file_size / 1024:.1f}KB, {mime})")
print(f"LoadFile: 加载 {p.name} ({file_size / 1024:.1f}KB)")
info = f"{len(file_list)} 个文件,总大小 {total_size / 1024:.1f}KB\n" + "\n".join(info_lines)
return (file_list, info)