Merge pull request #11 from fchangjun/codex/url-download-subdir

Codex/url download subdir
This commit is contained in:
方长君
2026-04-15 10:37:19 +08:00
committed by GitHub
5 changed files with 73 additions and 32 deletions
+10 -2
View File
@@ -9,7 +9,7 @@
### Overview
Baidu Meux ComfyTools is a collection of custom nodes for ComfyUI used by the Baidu Meux asset platform.
Current version: **1.7.0**
Current version: **1.7.1**
Main nodes:
@@ -38,6 +38,9 @@ Workflow-compatible aliases:
### Changelog
- **v1.7.1**
- Updated URL persistence so downloaded files are stored under `ComfyUI/input/meux_downloads`.
- Added `text_align` and `vertical_align` controls to `MeuxArtisticTextPreview`.
- **v1.7.0**
- Added `MeuxArtisticTextPreview` for transparent text preview rendering with fill, outline, spacing, mask output, and preset-based effects.
- Added a project-level `fonts/` directory and made it the first font lookup location for the preview node.
@@ -93,6 +96,7 @@ Workflow-compatible aliases:
- `source_type = local`: select a file from `ComfyUI/input`
- `source_type = url`: download from an HTTP/HTTPS URL
- Downloaded URL files are persisted under `ComfyUI/input/meux_downloads` when `persist_to_input = true`
- Outputs `IMAGE` and the derived mask from alpha or grayscale when present
#### MeuxMaskLoader
@@ -252,7 +256,7 @@ Baidu_Meux_ComfyTools/
### 概述
Baidu Meux ComfyTools 是一组用于 ComfyUI 的自定义节点,服务于百度 Meux 资产平台相关工作流。
当前版本:**1.7.0**
当前版本:**1.7.1**
主要节点:
@@ -281,6 +285,9 @@ Baidu Meux ComfyTools 是一组用于 ComfyUI 的自定义节点,服务于百
### 更新日志
- **v1.7.1**
- 调整 URL 落盘位置,下载文件现在保存到 `ComfyUI/input/meux_downloads`
- 为 `MeuxArtisticTextPreview` 新增 `text_align` 和 `vertical_align` 对齐控制
- **v1.7.0**
- 新增 `MeuxArtisticTextPreview`,用于透明背景文字预览,支持填充、描边、字距、遮罩输出和预设效果。
- 新增项目级 `fonts/` 目录,并作为预览节点的第一字体读取位置。
@@ -336,6 +343,7 @@ Baidu Meux ComfyTools 是一组用于 ComfyUI 的自定义节点,服务于百
- `source_type = local`:从 `ComfyUI/input` 选择本地文件
- `source_type = url`:运行时下载 HTTP/HTTPS 图片
- `persist_to_input = true` 时,URL 下载文件会保存到 `ComfyUI/input/meux_downloads`
- 输出 `IMAGE`,以及图片自带 alpha/灰度派生的 `MASK`
#### MeuxMaskLoader
+1 -1
View File
@@ -62,7 +62,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
}
# 可选:添加版本和作者信息
__version__ = "1.7.0"
__version__ = "1.7.1"
__author__ = "fangchangjun"
# 调试信息 - 可以帮助确认导入是否成功
+29 -7
View File
@@ -41,6 +41,8 @@ class MeuxArtisticTextPreview:
"font_size": ("INT", {"default": 128, "min": 8, "max": 1024, "step": 1}),
"char_spacing": ("INT", {"default": 0, "min": -20, "max": 200, "step": 1}),
"line_spacing": ("INT", {"default": 0, "min": -512, "max": 512, "step": 1}),
"text_align": (["left", "center", "right"], {"default": "center"}),
"vertical_align": (["top", "middle", "bottom"], {"default": "middle"}),
"bold": ("BOOLEAN", {"default": False}),
"italic": ("BOOLEAN", {"default": False}),
"background_color": ("STRING", {"default": "#000000"}),
@@ -83,6 +85,8 @@ class MeuxArtisticTextPreview:
font_size,
char_spacing,
line_spacing,
text_align,
vertical_align,
bold,
italic,
background_color,
@@ -132,6 +136,8 @@ class MeuxArtisticTextPreview:
font=font,
char_spacing=char_spacing,
line_spacing=line_spacing,
text_align=text_align,
vertical_align=vertical_align,
safe_area=safe_area,
fake_bold=fake_bold,
)
@@ -299,18 +305,17 @@ class MeuxArtisticTextPreview:
return int(line_spacing)
return max(6, font.size // 5 if hasattr(font, "size") else 6)
def _render_text_mask(self, text, width, height, font, char_spacing, line_spacing, safe_area, fake_bold):
def _render_text_mask(
self, text, width, height, font, char_spacing, line_spacing, text_align, vertical_align, safe_area, fake_bold
):
mask_image = Image.new("L", (width, height), 0)
draw = ImageDraw.Draw(mask_image)
line_spacing = self._resolve_line_spacing(font, line_spacing)
block_width, block_height = self._text_block_size(font, text, char_spacing, line_spacing)
_, block_height = self._text_block_size(font, text, char_spacing, line_spacing)
left, top, right, bottom = safe_area
safe_width = right - left
safe_height = bottom - top
start_x = left + (safe_width - block_width) / 2
start_y = top + (safe_height - block_height) / 2
start_y = self._block_start_y(block_height, vertical_align, top, bottom)
offsets = ((0, 0), (1, 0), (0, 1), (1, 1)) if fake_bold else ((0, 0),)
lines = text.splitlines() or [text]
@@ -319,7 +324,8 @@ class MeuxArtisticTextPreview:
current_y = start_y
for line in lines:
current_x = start_x
line_width = self._line_width(font, line, char_spacing)
current_x = self._line_start_x(line_width, text_align, left, right)
for index, char in enumerate(line):
for dx, dy in offsets:
draw.text((current_x + dx, current_y + dy), char, fill=255, font=font)
@@ -330,6 +336,22 @@ class MeuxArtisticTextPreview:
return mask_image
def _line_start_x(self, line_width, text_align, left, right):
safe_width = right - left
if text_align == "left":
return left
if text_align == "right":
return right - line_width
return left + (safe_width - line_width) / 2
def _block_start_y(self, block_height, vertical_align, top, bottom):
safe_height = bottom - top
if vertical_align == "top":
return top
if vertical_align == "bottom":
return bottom - block_height
return top + (safe_height - block_height) / 2
def _apply_fake_italic(self, mask_image, shear=0.25):
bbox = mask_image.getbbox()
if bbox is None:
+32 -21
View File
@@ -17,6 +17,8 @@ class ImageLoader:
Hybrid image loader that mirrors ComfyUI's Load Image node while adding URL download support.
"""
URL_DOWNLOAD_SUBDIR = "meux_downloads"
RETURN_TYPES = ("IMAGE", "MASK")
RETURN_NAMES = ("image", "mask")
FUNCTION = "load_image"
@@ -180,24 +182,21 @@ class ImageLoader:
return mask
def _persist_image(self, image: Image.Image, filename_hint: str, url: str, overwrite: bool):
try:
input_dir = folder_paths.get_input_directory()
except KeyError:
raise ValueError("未检测到 ComfyUI 的 input 目录,请在根目录创建 input 文件夹后重启。")
input_dir = self._get_input_directory()
persist_dir = self._get_url_persist_directory(create=True)
extension = self._infer_extension(image, url)
filename = self._build_filename(filename_hint or os.path.basename(urlparse(url).path), extension, url)
full_path = os.path.abspath(os.path.join(input_dir, filename))
input_dir_abs = os.path.abspath(input_dir)
if not (full_path.startswith(input_dir_abs + os.sep) or full_path == input_dir_abs):
raise ValueError("无法写入到 input 目录之外。")
filename = self._build_cached_filename(filename_hint, extension, url)
full_path = os.path.abspath(os.path.join(persist_dir, filename))
persist_dir_abs = os.path.abspath(persist_dir)
if not (full_path.startswith(persist_dir_abs + os.sep) or full_path == persist_dir_abs):
raise ValueError("无法写入到 URL 下载缓存目录之外。")
if os.path.exists(full_path) and not overwrite:
stem, ext = os.path.splitext(filename)
counter = 1
while True:
candidate = f"{stem}_{counter}{ext}"
candidate_path = os.path.join(input_dir, candidate)
candidate_path = os.path.join(persist_dir, candidate)
if not os.path.exists(candidate_path):
full_path = candidate_path
break
@@ -210,19 +209,31 @@ class ImageLoader:
else:
image.save(full_path)
print(f"[MeuxImageLoader] 已保存到 {os.path.relpath(full_path, input_dir)}")
return full_path
def _build_filename(self, hint: str, extension: str, url: str) -> str:
sanitized = self._sanitize_filename(hint) if hint else ""
if sanitized.endswith(extension):
base = sanitized[: -len(extension)]
else:
base = sanitized or ""
def _build_cached_filename(self, filename_hint: str, extension: str, url: str) -> str:
digest = hashlib.sha1(url.encode("utf-8")).hexdigest()[:16]
path_name = os.path.basename(urlparse(url).path)
label_source = filename_hint or path_name or ""
label = self._sanitize_filename(label_source)
if label.endswith(extension):
label = label[: -len(extension)]
if label:
return f"meux_url_{digest}__{label}{extension}"
return f"meux_url_{digest}{extension}"
if not base:
digest = hashlib.sha1(url.encode("utf-8")).hexdigest()[:12]
base = f"meux_url_{digest}"
def _get_input_directory(self) -> str:
try:
return folder_paths.get_input_directory()
except KeyError:
raise ValueError("未检测到 ComfyUI 的 input 目录,请在根目录创建 input 文件夹后重启。")
return f"{base}{extension}"
def _get_url_persist_directory(self, create: bool) -> str:
input_dir = self._get_input_directory()
persist_dir = os.path.join(input_dir, self.URL_DOWNLOAD_SUBDIR)
if create:
os.makedirs(persist_dir, exist_ok=True)
return persist_dir
def _infer_extension(self, image: Image.Image, url: str) -> str:
parsed = urlparse(url)
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "baidu_meux_comfy_tools"
description = "Baidu Meux ComfyTools is a collection of custom nodes that streamline common workflow chores in ComfyUI for the Baidu Meux asset platform.."
version = "1.7.0"
version = "1.7.1"
license = {file = "LICENSE"}
# classifiers = [
# # For OS-independent nodes (works on all operating systems)