Files
rui40000-RUI-Nodes/utf8_converter_node.py
T
rui40000andClaude Opus 4.8 5d3834503e fix: 八方向拆分不再切断角色;全仓库参数补齐中文 tooltip
【修复】角色被格线切断(脚、手杖、飘起的斗篷被削掉)
新增 expand_beyond_cell(默认开启):格子只用来判定「这是哪个方向」,
角色的实际范围由它自身的连通区域决定,按质心归属确保邻居不混入。
实测 8/8 方向的裁剪框边缘 alpha 从 1.00(内容顶到边界=被切断)
降到 0.00,S 方向高度 326→356、E 方向宽度 150→188 把缺的部分找了回来。
代价是需要两遍扫描(先求全序列并集框再提取),耗时 4.7s→14.9s。

【规则】每个参数都必须有中文 tooltip,作为以后的统一约定
全仓库 26 个节点 169 个参数,此前缺 115 个,现已 100% 覆盖。
tooltip 写「怎么调」而不只是「是什么」:给取值区间的实际影响、
推荐值与踩坑提示(如 OpenAI/ZenMux 的地址栏不能带 :// ,
素材拆分节点用于动画序列时顺序会漂移等)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-29 19:07:01 +08:00

179 lines
6.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import os
def is_binary_file(path):
try:
with open(path, "rb") as f:
chunk = f.read(1024)
if b"\x00" in chunk:
return True
except:
return True
return False
def try_decode(data):
"""
尝试多种编码格式解码二进制数据 (对齐 fix_encoding.py)
"""
# 1. UTF-8
try:
return data.decode("utf-8"), "utf-8"
except:
pass
# 2. UTF-16 with BOM
if data.startswith(b'\xff\xfe') or data.startswith(b'\xfe\xff'):
try:
return data.decode("utf-16"), "utf-16"
except:
pass
# 3. GB18030 (covers GBK and more)
try:
return data.decode("gb18030"), "gb18030"
except:
pass
# 4. Fallback
return data.decode("utf-8", errors="ignore"), "fallback-ignore"
def clean_text(text, remove_emoji=True):
"""
严格清洗文本 (对齐 fix_encoding.py):
- 删除控制字符 (保留换行和制表)
- 删除 surrogate 区
- 删除 emoji (默认强制删除 > 0xFFFF 的所有 4 字节字符)
- 强制统一换行符为 Unix 风格 (\n)
"""
cleaned = []
for c in text:
code = ord(c)
# 删除控制字符(保留换行和制表)
if 0 <= code < 32 and c not in ("\n", "\t"):
continue
# 删除 surrogate 区
if 0xD800 <= code <= 0xDFFF:
continue
# 删除 emoji(默认删除所有 > 0xFFFF 字符)
if remove_emoji and code > 0xFFFF:
continue
cleaned.append(c)
result_text = "".join(cleaned)
# 强制统一换行符为 Unix 风格 (\n),防止 \r 导致打标工具报错
result_text = result_text.replace("\r\n", "\n").replace("\r", "\n")
return result_text
class UTF8ConverterNode:
"""
UTF-8 编码转换与文本清洗节点 (工业级强力重构版):
完全对齐 fix_encoding.py 的逻辑,使用鲁棒的解码策略并执行极其严格的字符清洗。
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"input_text": ("STRING", {
"multiline": True,
"default": "",
"placeholder": "输入文本 / Input Text",
"tooltip": "待转码的文本。用于把从各处复制来的乱码文本\n"
"(GBK/UTF-16 等被误读的内容)修正为正常 UTF-8。\n"
"若下方填了文件路径,则以文件内容为准,本框会被忽略。"
}),
"remove_emoji": ("BOOLEAN", {
"default": True,
"label_on": "是 / Yes",
"label_off": "否 / No",
"tooltip": "是否强制删除所有 4 字节字符(包含绝大多数 Emoji)"
}),
},
"optional": {
"input_file": ("STRING", {
"multiline": False,
"default": "",
"placeholder": "文件路径 / File Path (优先读取 / Overrides input_text)",
"tooltip": "文本文件路径。填了它就优先读文件,忽略上面的输入框。\n"
"会依次尝试 UTF-8 → UTF-16(带BOM) → GB18030 解码,\n"
"都失败则忽略非法字节强行读出,尽量不让整个文件报废。"
}),
}
}
RETURN_TYPES = ("STRING", "STRING")
RETURN_NAMES = ("filtered_text", "log")
FUNCTION = "run"
CATEGORY = "Rui-Node🐶/文本处理📝"
def run(self, input_text, remove_emoji, input_file=""):
log = []
text_content = ""
encoding_used = "none"
# 1. 获取原始内容 (文件优先)
if input_file and input_file.strip():
if os.path.exists(input_file):
log.append(f"正在读取文件: {input_file}")
# 检查是否为二进制文件
if is_binary_file(input_file):
log.append("警告: 检测到可能的二进制文件 (跳过)")
return ("", "\n".join(log))
try:
with open(input_file, "rb") as f:
raw_data = f.read()
text_content, encoding_used = try_decode(raw_data)
log.append(f"解码成功: 使用编码 {encoding_used}")
except Exception as e:
return ("", f"读取或解码文件时发生错误: {str(e)}")
else:
log.append(f"错误: 文件不存在 -> {input_file}")
log.append("回退到使用 input_text")
text_content = input_text
encoding_used = "input_text"
else:
log.append("使用直接输入的文本")
text_content = input_text
encoding_used = "input_text"
# 2. 清洗文本
log.append("-" * 20)
log.append("开始工业级强力清洗文本...")
original_len = len(text_content)
cleaned_content = clean_text(text_content, remove_emoji=remove_emoji)
final_len = len(cleaned_content)
removed_count = original_len - final_len
# 3. 生成日志
log.append(f"原始长度: {original_len}")
log.append(f"输出长度: {final_len}")
log.append(f"移除字符数: {removed_count}")
if removed_count > 0:
log.append(f"清洗详情: 已移除非法控制字符、代理对,并统一换行符为 \\n。")
if remove_emoji:
log.append("已开启 remove_emoji: 删除了所有 4 字节字符。")
else:
log.append("结果: 文本已符合规范,无需修改。")
return (cleaned_content, "\n".join(log))
# 节点映射字典
NODE_CLASS_MAPPINGS = {
"UTF8Converter": UTF8ConverterNode
}
# 节点显示名称映射
NODE_DISPLAY_NAME_MAPPINGS = {
"UTF8Converter": "转化为utf-8编码 / Convert to UTF-8"
}