Add column node

This commit is contained in:
niknah
2026-08-04 08:42:02 +10:00
parent 145e15a6fd
commit 487b3237ee
5 changed files with 347 additions and 131 deletions
+92 -27
View File
@@ -62,6 +62,14 @@ class S2VComfy():
return cls.get_prompt_dir() / ("%s%06d" % (prefix, max_num+1) )
# Add empty columns if the row does not have the same amount of columns as the header
@classmethod
def fill_empty_columns(cls, headerRow, from_rows):
for from_row in from_rows:
if len(from_row) < len(headerRow):
from_row += [None] * (len(headerRow) - len(from_row))
last_processed_time = None
# Executed after each row
class Spreadsheet2VideoProcessImage(io.ComfyNode):
@@ -135,24 +143,29 @@ class Spreadsheet2VideoProcessImage(io.ComfyNode):
# The original nodes are replaced with this one so they won't do anything.
class Spreadsheet2VideoDoNothing(io.ComfyNode):
@classmethod
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id="Spreadsheet2VideoDoNothing",
display_name="Internal S2V Do nothing",
description="internal node, ignore",
category="S2V Internal",
inputs=[
],
outputs=[
],
hidden=[],
)
# class Spreadsheet2VideoDoNothing(io.ComfyNode):
# @classmethod
# def define_schema(cls) -> io.Schema:
# return io.Schema(
# node_id="Spreadsheet2VideoDoNothing",
# display_name="Internal S2V Do nothing",
# description="internal node, ignore",
# category="S2V Internal",
# inputs=[
# ],
# outputs=[
# ],
# hidden=[],
# )
@classmethod
def execute(cls, **kwargs) -> io.NodeOutput:
return io.NodeOutput()
# @classmethod
# def execute(cls, **kwargs) -> io.NodeOutput:
# return io.NodeOutput(
# ExecutionBlocker(None)
# )
# def check_lazy_status(self):
# return []
# Not executed
@@ -487,9 +500,11 @@ class GroupInfo():
logging.warning(f"S2V: not enough columns in row, column: {v[1]}, row: {','.join(row)}")
return foundImageInput
def replace_with_do_nothing(self, prompt):
for remove_node_id in self.orig_node_ids.keys():
prompt[remove_node_id]['class_type']="Spreadsheet2VideoDoNothing"
# def replace_with_do_nothing(self, prompt):
# for remove_node_id in self.orig_node_ids.keys():
# node = prompt[remove_node_id]
# node['class_type']="Spreadsheet2VideoDoNothing"
# node['inputs']={}
class Spreadsheet2VideoFinalVideo(io.ComfyNode):
@@ -578,6 +593,7 @@ class Spreadsheet2VideoFinalVideo(io.ComfyNode):
if len(waveforms)==0:
output_audio["waveform"] = torch.empty(1, 1, 0)
output_audio["sample_rate"] = 44100
else:
output_audio["waveform"] = torch.cat(waveforms, dim=1).unsqueeze(0)
@@ -675,6 +691,57 @@ class Spreadsheet2VideoFilesList(io.ComfyNode):
)
class Spreadsheet2VideoAddColumnSpreadsheet(io.ComfyNode):
@classmethod
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id="Spreadsheet2VideoAddColumnSpreadsheet",
display_name="Spreadsheet2Video Add column to Spreadsheet",
category="Spreadsheet2Video",
description="Adds columns to a spreadsheet. Repeats rows if there are not enough. Maximum rows to add is the source spreadsheet",
search_aliases=["spreadsheet", "loop", "add", "column"],
inputs=[
io.String.Input("spreadsheet", multiline=True, tooltip="Comma separated values with a header row. Can use Spreadsheet2Video Load Spreadsheet node."),
io.String.Input("spreadsheet_to_add", multiline=True, tooltip="Comma separated values with a header row. The columns to add"),
],
outputs=[
io.String.Output(),
],
)
@classmethod
def execute(cls, spreadsheet, spreadsheet_to_add) -> io.NodeOutput:
f = StringIO(spreadsheet)
reader = csv.reader(f, delimiter=',')
headerRow = next(reader)
from_rows = list(reader)
S2VComfy.fill_empty_columns(headerRow, from_rows)
f = StringIO(spreadsheet_to_add)
reader = csv.reader(f, delimiter=',')
output = StringIO()
writer = csv.writer(output)
addHeaderRow = next(reader)
add_rows = list(reader)
writer.writerow(headerRow + addHeaderRow)
add_row_upto = 0
for from_row in from_rows:
add_row_upto = add_row_upto % len(add_rows)
add_row = add_rows[add_row_upto]
writer.writerow(from_row + add_row)
add_row_upto += 1
return io.NodeOutput(
output.getvalue()
)
class Spreadsheet2VideoMultiplySpreadsheet(io.ComfyNode):
@classmethod
def define_schema(cls) -> io.Schema:
@@ -700,9 +767,7 @@ class Spreadsheet2VideoMultiplySpreadsheet(io.ComfyNode):
headerRow = next(reader)
from_rows = list(reader)
for from_row in from_rows:
if len(from_row) < len(headerRow):
from_row += [None] * (len(headerRow) - len(from_row))
S2VComfy.fill_empty_columns(headerRow, from_rows)
f = StringIO(spreadsheet_to_add)
@@ -931,10 +996,10 @@ class Spreadsheet2VideoNode(io.ComfyNode):
)
# Disabled: If we make the original nodes that are not in the loop DoNothing
# If there are KJ get / set disconnected nodes. The get nodes will return None instead of the value
# for group_name in used_groups:
# used_group = by_group_name[group_name]
# used_group.replace_with_do_nothing(cls.hidden.prompt)
# Some workflows won't work with this.
# for group_name in used_groups:
# used_group = by_group_name[group_name]
# used_group.replace_with_do_nothing(cls.hidden.prompt)
return io.NodeOutput(
finalVideoNode.out(0),
+4 -2
View File
@@ -10,7 +10,8 @@ from .Spreadsheet2VideoNodes import (
Spreadsheet2VideoLoadText,
Spreadsheet2VideoSequence,
Spreadsheet2VideoFilesList,
Spreadsheet2VideoDoNothing,
# Spreadsheet2VideoDoNothing,
Spreadsheet2VideoAddColumnSpreadsheet,
Spreadsheet2VideoMultiplySpreadsheet
)
@@ -41,7 +42,8 @@ class Spreadsheet2VideoExtension(ComfyExtension):
Spreadsheet2VideoLoadText,
Spreadsheet2VideoSequence,
Spreadsheet2VideoFilesList,
Spreadsheet2VideoDoNothing,
# Spreadsheet2VideoDoNothing,
Spreadsheet2VideoAddColumnSpreadsheet,
Spreadsheet2VideoMultiplySpreadsheet,
]
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "Spreadsheet2Video-ComfyUI"
version = "1.0.7"
version = "1.0.8"
description = "Use a spreadsheet as input for ComfyUI execution"
license = { file = "LICENSE" }
dependencies = [
+69 -101
View File
@@ -12,8 +12,8 @@
1166.8738027901068
],
"size": [
312.483984375,
158
312.453125,
193.984375
],
"flags": {},
"order": 0,
@@ -54,6 +54,8 @@
}
],
"properties": {
"cnr_id": "Spreadsheet2Video-ComfyUI",
"ver": "145e15a6fd7c2f746444869550ee3c7a25e17126",
"Node name for S&R": "Spreadsheet2VideoInputImage"
},
"widgets_values": [
@@ -69,7 +71,7 @@
],
"size": [
270,
78
106
],
"flags": {},
"order": 4,
@@ -120,23 +122,32 @@
1234.79316455834
],
"size": [
264.0162109375,
26
264,
78
],
"flags": {},
"order": 8,
"order": 7,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 72
},
{
"name": "audio",
"shape": 7,
"type": "AUDIO",
"link": null
}
],
"outputs": [],
"properties": {
"cnr_id": "Spreadsheet2Video-ComfyUI",
"ver": "145e15a6fd7c2f746444869550ee3c7a25e17126",
"Node name for S&R": "Spreadsheet2VideoOutputImage"
}
},
"widgets_values": []
},
{
"id": 6,
@@ -146,8 +157,8 @@
1046.018577649322
],
"size": [
300.440416046162,
58
300.40625,
93.984375
],
"flags": {},
"order": 1,
@@ -179,8 +190,8 @@
1254.2865711858988
],
"size": [
286.73896268632825,
58.36471886178333
286.71875,
93.984375
],
"flags": {},
"order": 2,
@@ -239,40 +250,6 @@
}
],
"properties": {
"proxyWidgets": [
[
"27",
"text"
],
[
"13",
"width"
],
[
"13",
"height"
],
[
"28",
"unet_name"
],
[
"30",
"clip_name"
],
[
"29",
"vae_name"
],
[
"3",
"steps"
],
[
"3",
"control_after_generate"
]
],
"cnr_id": "comfy-core",
"ver": "0.3.73",
"enableTabs": false,
@@ -281,44 +258,33 @@
"hasSecondTab": false,
"secondTabText": "Send Back",
"secondTabOffset": 80,
"secondTabWidth": 65
},
"widgets_values": []
},
{
"id": 7,
"type": "EmptyImage",
"pos": [
1207.965006424334,
719.2113395590205
],
"size": [
270,
130
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
3
]
}
],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.19.3",
"Node name for S&R": "EmptyImage"
"secondTabWidth": 65,
"proxyWidgetErrorQuarantine": [
{
"originalEntry": [
"3",
"control_after_generate"
],
"reason": "missingSubgraphInput",
"attemptedAtVersion": 1
}
],
"previewExposures": [
{
"name": "$$canvas-image-preview",
"sourceNodeId": "3",
"sourcePreviewName": "$$canvas-image-preview"
}
]
},
"widgets_values": [
"Latina female with thick wavy hair, harbor boats and pastel houses behind. Breezy seaside light, warm tones, cinematic close-up. ",
1024,
1024,
1,
0
"z_image_turbo_bf16.safetensors",
"qwen_3_4b.safetensors",
"FLUX1\\ae.safetensors",
8
]
},
{
@@ -329,11 +295,11 @@
704.5518187413677
],
"size": [
140,
246
225,
290
],
"flags": {},
"order": 7,
"order": 5,
"mode": 0,
"inputs": [
{
@@ -342,7 +308,13 @@
"link": 4
}
],
"outputs": [],
"outputs": [
{
"name": "images",
"type": "IMAGE",
"links": null
}
],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.19.3",
@@ -362,14 +334,14 @@
200
],
"flags": {},
"order": 5,
"order": 3,
"mode": 0,
"inputs": [
{
"name": "first_image",
"shape": 7,
"type": "IMAGE",
"link": 3
"link": null
}
],
"outputs": [
@@ -379,9 +351,16 @@
"links": [
4
]
},
{
"name": "AUDIO",
"type": "AUDIO",
"links": null
}
],
"properties": {
"cnr_id": "Spreadsheet2Video-ComfyUI",
"ver": "145e15a6fd7c2f746444869550ee3c7a25e17126",
"Node name for S&R": "Spreadsheet2Video"
},
"widgets_values": [
@@ -390,14 +369,6 @@
}
],
"links": [
[
3,
7,
0,
1,
0,
"IMAGE"
],
[
4,
1,
@@ -1045,7 +1016,7 @@
"secondTabWidth": 65
},
"widgets_values": [
1007068742280709,
1045136092634709,
"randomize",
8,
1,
@@ -1124,7 +1095,6 @@
200
],
"color": "#3f789e",
"font_size": 24,
"flags": {}
},
{
@@ -1137,7 +1107,6 @@
540
],
"color": "#3f789e",
"font_size": 24,
"flags": {}
},
{
@@ -1150,7 +1119,6 @@
413.6
],
"color": "#3f789e",
"font_size": 24,
"flags": {}
}
],
@@ -1301,13 +1269,13 @@
"config": {},
"extra": {
"ds": {
"scale": 1.1556864978176087,
"scale": 1.0506240889250995,
"offset": [
-1268.0988326073784,
-547.481125026469
-1381.2506133337172,
-628.9646201878417
]
},
"frontendVersion": "1.42.11",
"frontendVersion": "1.47.12",
"VHS_latentpreview": false,
"VHS_latentpreviewrate": 0,
"VHS_MetadataImage": true,
+181
View File
@@ -0,0 +1,181 @@
{
"id": "844d351e-ec61-4bdf-b8d9-26dfd091434b",
"revision": 0,
"last_node_id": 96,
"last_link_id": 189,
"nodes": [
{
"id": 91,
"type": "PreviewAny",
"pos": [
-1.841804357108301,
1074.0426332603915
],
"size": [
364.28125,
330.375
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"name": "source",
"type": "*",
"link": 178
}
],
"outputs": [
{
"name": "STRING",
"type": "STRING",
"links": null
}
],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.19.3",
"Node name for S&R": "PreviewAny"
},
"widgets_values": []
},
{
"id": 89,
"type": "Spreadsheet2VideoMultiplySpreadsheet",
"pos": [
-585.6675584600498,
1069.7731994235164
],
"size": [
400,
200
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "STRING",
"type": "STRING",
"links": [
178
]
}
],
"properties": {
"cnr_id": "Spreadsheet2Video-ComfyUI",
"ver": "7342d3ec4657599baab5be5dd4414663e9d0da1a",
"Node name for S&R": "Spreadsheet2VideoMultiplySpreadsheet"
},
"widgets_values": [
"column1,prompt\nworkflow1,One\nworkflow1,Two\nworkflow1,Three\nworkflow1",
"sampler\neuler\nheun"
]
},
{
"id": 95,
"type": "PreviewAny",
"pos": [
85.70426079452947,
615.0534104526303
],
"size": [
406.703125,
308.359375
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "source",
"type": "*",
"link": 189
}
],
"outputs": [
{
"name": "STRING",
"type": "STRING",
"links": null
}
],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.19.3",
"Node name for S&R": "PreviewAny"
},
"widgets_values": []
},
{
"id": 96,
"type": "Spreadsheet2VideoAddColumnSpreadsheet",
"pos": [
-569.2349843700941,
598.1665514472247
],
"size": [
433.046875,
327.765625
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "STRING",
"type": "STRING",
"links": [
189
]
}
],
"properties": {
"cnr_id": "Spreadsheet2Video-ComfyUI",
"ver": "145e15a6fd7c2f746444869550ee3c7a25e17126",
"Node name for S&R": "Spreadsheet2VideoAddColumnSpreadsheet"
},
"widgets_values": [
"column1,prompt\nworkflow1,One\nworkflow1,Two\nworkflow1,Three\nworkflow1,Four\nworkflow1",
"sampler\neuler\nheun"
]
}
],
"links": [
[
178,
89,
0,
91,
0,
"STRING"
],
[
189,
96,
0,
95,
0,
"STRING"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 0.7751878709002787,
"offset": [
835.409880916017,
-212.2218877684238
]
},
"frontendVersion": "1.47.12",
"VHS_latentpreview": false,
"VHS_latentpreviewrate": 0,
"VHS_MetadataImage": true,
"VHS_KeepIntermediate": true
},
"version": 0.4
}