v3.2.5
This commit is contained in:
@@ -508,6 +508,12 @@ git clone https://github.com/1hew/ComfyUI-1hewNodes
|
||||
| Save Video | 保存 VIDEO 对象并返回路径,沿用容器扩展名并生成 Alpha 预览 |
|
||||
|
||||
|
||||
### 🎛️ 条件编码节点
|
||||
| 节点名称 | 功能描述 |
|
||||
|---------|----------|
|
||||
| Text Encode QwenImageEdit Keep Size | Qwen 图文编辑条件编码,支持多图视觉编码、尺寸保持策略与参考潜空间 |
|
||||
|
||||
|
||||
### 🔊 音频节点
|
||||
| 节点名称 | 功能描述 |
|
||||
|---------|----------|
|
||||
|
||||
@@ -508,6 +508,12 @@ git clone https://github.com/1hew/ComfyUI-1hewNodes
|
||||
| Save Video | Save a VIDEO object to disk with container extension preservation and alpha preview |
|
||||
|
||||
|
||||
### 🎛️ Conditioning Nodes
|
||||
| Node Name | Description |
|
||||
|-----------|-------------|
|
||||
| Text Encode QwenImageEdit Keep Size | Qwen image-edit conditioning encoder combining vision inputs and text, supports size preservation modes and reference latents |
|
||||
|
||||
|
||||
### 🔊 Audio Nodes
|
||||
| Node Name | Description |
|
||||
|-----------|-------------|
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ddeb1c5d300da986a4581874687164488759fb75ecf28e166ebf992c89f29329
|
||||
size 102142
|
||||
oid sha256:288350ffd657a77ea24f8c8e241184da8208fbbcadbc64f01b63b5443d96982e
|
||||
size 99239
|
||||
|
||||
+19
-11
@@ -37,7 +37,7 @@ class LoadImage(io.ComfyNode):
|
||||
category="1hewNodes/io",
|
||||
inputs=[
|
||||
io.Image.Input("get_image_size", optional=True),
|
||||
io.String.Input("path", default=""),
|
||||
io.String.Input("file", default=""),
|
||||
io.Int.Input("index", default=0, min=-8192, max=8192, step=1),
|
||||
io.Boolean.Input("include_subdir", default=True),
|
||||
io.Boolean.Input("all", default=False),
|
||||
@@ -227,11 +227,11 @@ class LoadImage(io.ComfyNode):
|
||||
return image_paths
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, path, include_subdir, **kwargs):
|
||||
if not path:
|
||||
def IS_CHANGED(cls, file, include_subdir, **kwargs):
|
||||
if not file:
|
||||
return float("nan")
|
||||
|
||||
image_paths = cls.get_image_paths(path, include_subdir)
|
||||
image_paths = cls.get_image_paths(file, include_subdir)
|
||||
if not image_paths:
|
||||
return float("nan")
|
||||
m = hashlib.sha256()
|
||||
@@ -281,13 +281,13 @@ class LoadImage(io.ComfyNode):
|
||||
@classmethod
|
||||
async def execute(
|
||||
cls,
|
||||
path: str,
|
||||
file: str,
|
||||
index: int,
|
||||
all: bool,
|
||||
include_subdir: bool,
|
||||
get_image_size: torch.Tensor | None = None,
|
||||
) -> io.NodeOutput:
|
||||
image_paths = cls.get_image_paths(path, include_subdir)
|
||||
image_paths = cls.get_image_paths(file, include_subdir)
|
||||
count = len(image_paths)
|
||||
|
||||
if count == 0:
|
||||
@@ -345,9 +345,9 @@ class LoadImage(io.ComfyNode):
|
||||
|
||||
else:
|
||||
idx = index % count
|
||||
path = image_paths[idx]
|
||||
selected_path = image_paths[idx]
|
||||
try:
|
||||
img, mask = cls.load_image(path)
|
||||
img, mask = cls.load_image(selected_path)
|
||||
tensor = cls.pil2tensor(img)
|
||||
mask_tensor = mask.unsqueeze(0).unsqueeze(-1)
|
||||
|
||||
@@ -359,7 +359,7 @@ class LoadImage(io.ComfyNode):
|
||||
output_image = tensor
|
||||
output_mask = mask_tensor.squeeze(-1)
|
||||
except Exception as e:
|
||||
print(f"Error loading image {path}: {e}")
|
||||
print(f"Error loading image {selected_path}: {e}")
|
||||
return io.NodeOutput(None, None)
|
||||
|
||||
return io.NodeOutput(
|
||||
@@ -479,7 +479,11 @@ async def upload_images(request):
|
||||
|
||||
@PromptServer.instance.routes.get("/1hew/view_image_from_folder")
|
||||
async def view_image_from_folder(request):
|
||||
folder = request.query.get("path") or request.query.get("folder")
|
||||
folder = (
|
||||
request.query.get("file")
|
||||
or request.query.get("path")
|
||||
or request.query.get("folder")
|
||||
)
|
||||
index_str = request.query.get("index", "0")
|
||||
include_subdir = (
|
||||
(request.query.get("include_subdir") or "true").lower()
|
||||
@@ -603,7 +607,11 @@ async def view_image_from_folder(request):
|
||||
|
||||
@PromptServer.instance.routes.get("/1hew/resolve_image_from_folder")
|
||||
async def resolve_image_from_folder(request):
|
||||
folder = request.query.get("path") or request.query.get("folder")
|
||||
folder = (
|
||||
request.query.get("file")
|
||||
or request.query.get("path")
|
||||
or request.query.get("folder")
|
||||
)
|
||||
index_str = request.query.get("index", "0")
|
||||
include_subdir = (
|
||||
(request.query.get("include_subdir") or "true").lower()
|
||||
|
||||
+17
-13
@@ -931,7 +931,7 @@ class LoadVideo(io.ComfyNode):
|
||||
display_name="Load Video",
|
||||
category="1hewNodes/io",
|
||||
inputs=[
|
||||
io.String.Input("path", default=""),
|
||||
io.String.Input("file", default=""),
|
||||
io.Int.Input("frame_limit", default=0, min=0, max=100000, step=1),
|
||||
io.Float.Input("fps", default=0.0, min=0.0, max=120.0, step=1.0),
|
||||
io.Int.Input("start_skip", default=0, min=0, max=100000, step=1),
|
||||
@@ -946,7 +946,7 @@ class LoadVideo(io.ComfyNode):
|
||||
@classmethod
|
||||
async def execute(
|
||||
cls,
|
||||
path: str,
|
||||
file: str,
|
||||
start_skip: int,
|
||||
end_skip: int,
|
||||
fps: float,
|
||||
@@ -955,8 +955,8 @@ class LoadVideo(io.ComfyNode):
|
||||
video_index: int,
|
||||
include_subdir: bool,
|
||||
) -> io.NodeOutput:
|
||||
path = (path or "").strip().strip('"').strip("'")
|
||||
video_paths = cls.get_video_paths(path, include_subdir)
|
||||
file = (file or "").strip().strip('"').strip("'")
|
||||
video_paths = cls.get_video_paths(file, include_subdir)
|
||||
count = len(video_paths)
|
||||
|
||||
if count == 0:
|
||||
@@ -1230,8 +1230,8 @@ class LoadVideo(io.ComfyNode):
|
||||
return video_paths
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, path, include_subdir, **kwargs):
|
||||
path = (path or "").strip().strip('"').strip("'")
|
||||
def IS_CHANGED(cls, file, include_subdir, **kwargs):
|
||||
file = (file or "").strip().strip('"').strip("'")
|
||||
start_skip = int(kwargs.get("start_skip") or 0)
|
||||
end_skip = int(kwargs.get("end_skip") or 0)
|
||||
fps = float(kwargs.get("fps") or 0.0)
|
||||
@@ -1242,19 +1242,19 @@ class LoadVideo(io.ComfyNode):
|
||||
f"video_index:{video_index}|start_skip:{start_skip}|end_skip:{end_skip}"
|
||||
f"|fps:{fps}|frame_limit:{frame_limit}|format:{format}"
|
||||
)
|
||||
if os.path.isfile(path):
|
||||
if os.path.isfile(file):
|
||||
try:
|
||||
mtime = os.path.getmtime(path)
|
||||
mtime = os.path.getmtime(file)
|
||||
return hashlib.sha256(
|
||||
f"{path}:{mtime}:{settings_key}".encode()
|
||||
f"{file}:{mtime}:{settings_key}".encode()
|
||||
).hexdigest()
|
||||
except OSError:
|
||||
return float("nan")
|
||||
|
||||
if not os.path.isdir(path):
|
||||
if not os.path.isdir(file):
|
||||
return float("nan")
|
||||
|
||||
video_paths = cls.get_video_paths(path, include_subdir)
|
||||
video_paths = cls.get_video_paths(file, include_subdir)
|
||||
m = hashlib.sha256()
|
||||
m.update(f"{settings_key}|include_subdir:{include_subdir}".encode())
|
||||
for video_path in video_paths:
|
||||
@@ -1469,7 +1469,9 @@ async def upload_videos(request):
|
||||
|
||||
@PromptServer.instance.routes.get("/1hew/video_info_from_folder")
|
||||
async def video_info_from_folder(request):
|
||||
path = request.query.get("path") or request.query.get("folder")
|
||||
path = request.query.get("file") or request.query.get("path") or request.query.get(
|
||||
"folder"
|
||||
)
|
||||
index_str = (
|
||||
request.query.get("index") or request.query.get("video_index") or "0"
|
||||
)
|
||||
@@ -1552,7 +1554,9 @@ async def video_info_from_folder(request):
|
||||
|
||||
@PromptServer.instance.routes.get("/1hew/view_video_from_folder")
|
||||
async def view_video_from_folder(request):
|
||||
path = request.query.get("path") or request.query.get("folder")
|
||||
path = request.query.get("file") or request.query.get("path") or request.query.get(
|
||||
"folder"
|
||||
)
|
||||
index_str = (
|
||||
request.query.get("index") or request.query.get("video_index") or "0"
|
||||
)
|
||||
|
||||
@@ -596,7 +596,7 @@ class LoadVideoToImage(io.ComfyNode):
|
||||
display_name="Load Video to Image",
|
||||
category="1hewNodes/io",
|
||||
inputs=[
|
||||
io.String.Input("path", default=""),
|
||||
io.String.Input("file", default=""),
|
||||
io.Int.Input("frame_limit", default=0, min=0, max=100000, step=1),
|
||||
io.Float.Input("fps", default=0.0, min=0.0, max=120.0, step=1.0),
|
||||
io.Int.Input("start_skip", default=0, min=0, max=100000, step=1),
|
||||
@@ -616,7 +616,7 @@ class LoadVideoToImage(io.ComfyNode):
|
||||
@classmethod
|
||||
async def execute(
|
||||
cls,
|
||||
path: str,
|
||||
file: str,
|
||||
fps: float,
|
||||
frame_limit: int,
|
||||
start_skip: int,
|
||||
@@ -625,8 +625,8 @@ class LoadVideoToImage(io.ComfyNode):
|
||||
video_index: int,
|
||||
include_subdir: bool,
|
||||
) -> io.NodeOutput:
|
||||
path = (path or "").strip().strip('"').strip("'")
|
||||
video_paths = cls.get_video_paths(path, include_subdir)
|
||||
file = (file or "").strip().strip('"').strip("'")
|
||||
video_paths = cls.get_video_paths(file, include_subdir)
|
||||
count = len(video_paths)
|
||||
|
||||
if count == 0:
|
||||
@@ -892,19 +892,19 @@ class LoadVideoToImage(io.ComfyNode):
|
||||
return video_paths
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, path, include_subdir, **kwargs):
|
||||
path = (path or "").strip().strip('"').strip("'")
|
||||
if os.path.isfile(path):
|
||||
def IS_CHANGED(cls, file, include_subdir, **kwargs):
|
||||
file = (file or "").strip().strip('"').strip("'")
|
||||
if os.path.isfile(file):
|
||||
try:
|
||||
mtime = os.path.getmtime(path)
|
||||
return hashlib.sha256(f"{path}:{mtime}".encode()).hexdigest()
|
||||
mtime = os.path.getmtime(file)
|
||||
return hashlib.sha256(f"{file}:{mtime}".encode()).hexdigest()
|
||||
except OSError:
|
||||
return float("nan")
|
||||
|
||||
if not os.path.isdir(path):
|
||||
if not os.path.isdir(file):
|
||||
return float("nan")
|
||||
|
||||
video_paths = cls.get_video_paths(path, include_subdir)
|
||||
video_paths = cls.get_video_paths(file, include_subdir)
|
||||
m = hashlib.sha256()
|
||||
for video_path in video_paths:
|
||||
try:
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "ComfyUI-1hewNodes"
|
||||
description = "This is a custom node collection for ComfyUI that provides some utility nodes."
|
||||
version = "3.2.4"
|
||||
version = "3.2.5"
|
||||
license = {file = "LICENSE"}
|
||||
|
||||
[project.urls]
|
||||
|
||||
+45
-14
@@ -72,8 +72,8 @@ app.registerExtension({
|
||||
nodeType.prototype.onConfigure = function () {
|
||||
const r = onConfigure ? onConfigure.apply(this, arguments) : undefined;
|
||||
if (this.widgets) {
|
||||
const pathWidget = this.widgets.find((w) => w.name === "path");
|
||||
if (pathWidget && pathWidget.value) {
|
||||
const fileWidget = this.widgets.find((w) => w.name === "file");
|
||||
if (fileWidget && fileWidget.value) {
|
||||
this._comfy1hewLoadImagePendingPreview = true;
|
||||
setTimeout(() => {
|
||||
const update = this.updatePreview;
|
||||
@@ -103,7 +103,7 @@ app.registerExtension({
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
const r = onNodeCreated ? onNodeCreated.apply(this, arguments) : undefined;
|
||||
|
||||
const pathWidget = this.widgets.find((w) => w.name === "path");
|
||||
const fileWidget = this.widgets.find((w) => w.name === "file");
|
||||
const indexWidget = this.widgets.find((w) => w.name === "index");
|
||||
const includeSubdirWidget = this.widgets.find(
|
||||
(w) => w.name === "include_subdir"
|
||||
@@ -175,10 +175,10 @@ app.registerExtension({
|
||||
}
|
||||
}
|
||||
|
||||
if (pathWidget) {
|
||||
pathWidget.value = finalPath;
|
||||
if (typeof pathWidget.callback === "function") {
|
||||
pathWidget.callback();
|
||||
if (fileWidget) {
|
||||
fileWidget.value = finalPath;
|
||||
if (typeof fileWidget.callback === "function") {
|
||||
fileWidget.callback();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,6 +248,37 @@ app.registerExtension({
|
||||
container.appendChild(imageEl);
|
||||
container.appendChild(infoEl);
|
||||
|
||||
const fileInputEl = document.createElement("input");
|
||||
fileInputEl.type = "file";
|
||||
fileInputEl.accept = "image/*";
|
||||
fileInputEl.style.display = "none";
|
||||
container.appendChild(fileInputEl);
|
||||
|
||||
const uploadWidget = this.addWidget(
|
||||
"button",
|
||||
"choose file to upload",
|
||||
"image",
|
||||
() => {
|
||||
app.canvas.node_widget = null;
|
||||
try {
|
||||
fileInputEl.value = "";
|
||||
} catch {}
|
||||
fileInputEl.click();
|
||||
}
|
||||
);
|
||||
uploadWidget.serialize = false;
|
||||
|
||||
fileInputEl.addEventListener("change", async () => {
|
||||
const file = fileInputEl.files && fileInputEl.files[0];
|
||||
if (!file) return;
|
||||
try {
|
||||
await uploadFilesAsFolder(
|
||||
[{ file: file, relativePath: file?.name }],
|
||||
false
|
||||
);
|
||||
} catch {}
|
||||
});
|
||||
|
||||
imageEl.addEventListener("contextmenu", (e) => {
|
||||
e.preventDefault();
|
||||
const node = this;
|
||||
@@ -287,7 +318,7 @@ app.registerExtension({
|
||||
this._comfy1hewLoadImageReqId = (this._comfy1hewLoadImageReqId || 0) + 1;
|
||||
const reqId = this._comfy1hewLoadImageReqId;
|
||||
|
||||
const path = pathWidget.value;
|
||||
const file = fileWidget.value;
|
||||
const index = indexWidget.value;
|
||||
const includeSubdir = includeSubdirWidget
|
||||
? includeSubdirWidget.value
|
||||
@@ -296,8 +327,8 @@ app.registerExtension({
|
||||
if (this.imageWidget) {
|
||||
this.imageWidget._comfy1hew_maxAspectRatio = all ? 1.25 : null;
|
||||
}
|
||||
const trimmedPath = String(path || "").trim();
|
||||
if (trimmedPath === "") {
|
||||
const trimmedFile = String(file || "").trim();
|
||||
if (trimmedFile === "") {
|
||||
this._comfy1hewLoadImageWasEmptyPath = true;
|
||||
const lastImgSrc = this._comfy1hewLoadImageLastImgSrc;
|
||||
|
||||
@@ -377,7 +408,7 @@ app.registerExtension({
|
||||
this._comfy1hewLoadImageWasEmptyPath = false;
|
||||
|
||||
const params = new URLSearchParams({
|
||||
path: path,
|
||||
file: file,
|
||||
include_subdir: includeSubdir,
|
||||
t: Date.now(),
|
||||
});
|
||||
@@ -476,13 +507,13 @@ app.registerExtension({
|
||||
};
|
||||
|
||||
// 监听 widget 变化
|
||||
if (pathWidget) pathWidget.callback = this.updatePreview;
|
||||
if (fileWidget) fileWidget.callback = this.updatePreview;
|
||||
if (indexWidget) indexWidget.callback = this.updatePreview;
|
||||
if (includeSubdirWidget) includeSubdirWidget.callback = this.updatePreview;
|
||||
if (allWidget) allWidget.callback = this.updatePreview;
|
||||
|
||||
const computeStateKey = () => {
|
||||
const p = String(pathWidget ? pathWidget.value : "");
|
||||
const p = String(fileWidget ? fileWidget.value : "");
|
||||
const i = String(indexWidget ? indexWidget.value : 0);
|
||||
const s = String(includeSubdirWidget ? includeSubdirWidget.value : true);
|
||||
const a = String(allWidget ? allWidget.value : false);
|
||||
@@ -564,7 +595,7 @@ app.registerExtension({
|
||||
setTimeout(ensurePreviewLayout, 500);
|
||||
|
||||
// 初始加载
|
||||
if (pathWidget && pathWidget.value) {
|
||||
if (fileWidget && fileWidget.value) {
|
||||
this.updatePreview();
|
||||
}
|
||||
if (this._comfy1hewLoadImagePendingPreview) {
|
||||
|
||||
+18
-18
@@ -28,8 +28,8 @@ app.registerExtension({
|
||||
? onConfigure.apply(this, arguments)
|
||||
: undefined;
|
||||
if (this.widgets) {
|
||||
const pathWidget = this.widgets.find((w) => w.name === "path");
|
||||
if (pathWidget && pathWidget.value) {
|
||||
const fileWidget = this.widgets.find((w) => w.name === "file");
|
||||
if (fileWidget && fileWidget.value) {
|
||||
setTimeout(() => {
|
||||
const update = this.updatePreview;
|
||||
if (update) update();
|
||||
@@ -52,7 +52,7 @@ app.registerExtension({
|
||||
? onNodeCreated.apply(this, arguments)
|
||||
: undefined;
|
||||
|
||||
const pathWidget = this.widgets.find((w) => w.name === "path");
|
||||
const fileWidget = this.widgets.find((w) => w.name === "file");
|
||||
const indexWidget = this.widgets.find((w) => w.name === "video_index");
|
||||
const includeSubdirWidget = this.widgets.find(
|
||||
(w) => w.name === "include_subdir"
|
||||
@@ -479,7 +479,7 @@ app.registerExtension({
|
||||
container.appendChild(fileInputEl);
|
||||
|
||||
// Add upload button widget
|
||||
const uploadWidget = this.addWidget("button", "choose video to upload", "image", () => {
|
||||
const uploadWidget = this.addWidget("button", "choose file to upload", "image", () => {
|
||||
app.canvas.node_widget = null; // Clear active widget
|
||||
fileInputEl.click();
|
||||
});
|
||||
@@ -508,10 +508,10 @@ app.registerExtension({
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathWidget) {
|
||||
pathWidget.value = newPath;
|
||||
if (typeof pathWidget.callback === "function") {
|
||||
pathWidget.callback();
|
||||
if (fileWidget) {
|
||||
fileWidget.value = newPath;
|
||||
if (typeof fileWidget.callback === "function") {
|
||||
fileWidget.callback();
|
||||
}
|
||||
}
|
||||
if (this.updatePreview) {
|
||||
@@ -549,10 +549,10 @@ app.registerExtension({
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathWidget) {
|
||||
pathWidget.value = folder;
|
||||
if (typeof pathWidget.callback === "function") {
|
||||
pathWidget.callback();
|
||||
if (fileWidget) {
|
||||
fileWidget.value = folder;
|
||||
if (typeof fileWidget.callback === "function") {
|
||||
fileWidget.callback();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -690,11 +690,11 @@ app.registerExtension({
|
||||
setTimeout(ensurePreviewLayout, 800);
|
||||
|
||||
this.updatePreview = async () => {
|
||||
const path = pathWidget.value;
|
||||
const file = fileWidget.value;
|
||||
const index = indexWidget.value;
|
||||
const includeSubdir = includeSubdirWidget.value;
|
||||
|
||||
if (!path) {
|
||||
if (!file) {
|
||||
this._comfy1hewVideoInfo = null;
|
||||
this.videoWidget.aspectRatio = undefined;
|
||||
infoEl.innerText = "";
|
||||
@@ -706,7 +706,7 @@ app.registerExtension({
|
||||
}
|
||||
|
||||
const params = new URLSearchParams({
|
||||
path: path,
|
||||
file: file,
|
||||
index: index,
|
||||
include_subdir: includeSubdir,
|
||||
t: Date.now(),
|
||||
@@ -725,7 +725,7 @@ app.registerExtension({
|
||||
|
||||
try {
|
||||
const infoParams = new URLSearchParams({
|
||||
path: path,
|
||||
file: file,
|
||||
index: index,
|
||||
include_subdir: includeSubdir,
|
||||
});
|
||||
@@ -774,11 +774,11 @@ app.registerExtension({
|
||||
return r2;
|
||||
};
|
||||
|
||||
if (pathWidget) pathWidget.callback = this.updatePreview;
|
||||
if (fileWidget) fileWidget.callback = this.updatePreview;
|
||||
if (indexWidget) indexWidget.callback = this.updatePreview;
|
||||
if (includeSubdirWidget) includeSubdirWidget.callback = this.updatePreview;
|
||||
|
||||
if (pathWidget && pathWidget.value) {
|
||||
if (fileWidget && fileWidget.value) {
|
||||
this.updatePreview();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user