4 Commits
Author SHA1 Message Date
niknah 312e285e47 A long fix for the warning message when running with image input/output
nodes
It removes the nodes inside the image input/output nodes before running.
2026-08-16 22:27:05 +10:00
niknah 385ee20f78 Update changes list 2026-08-04 08:43:20 +10:00
niknah 487b3237ee Add column node 2026-08-04 08:42:02 +10:00
niknah 145e15a6fd Revert changes for validation warning when running. KJ get/set nodes
were not working with it.
2026-07-24 23:51:32 +10:00
10 changed files with 612 additions and 1630 deletions
+5
View File
@@ -0,0 +1,5 @@
node_modules
package-lock.json
__pycache__
eslint.config.js
package.json
+3
View File
@@ -117,6 +117,9 @@ Using flux klein edit(to make new objects appear) + wan video.
## Changes
* v1.0.9: Fixed up warning message for nodes inside the image input / output nodes.
* v1.0.8: Add column node. Adds a column, repeats values until it fills the source spreadsheet's rows
* v1.0.7: Reverted changes for errors/warnings when running. It wasn't working for KJ get/set nodes.
* v1.0.6: Fix missing first image in video. Remove checking errors/warnings when running.
* v1.0.5: File list node now uses the input folder as source. Don't crash when numbers in the columns are not correct.
* v1.0.4: Added sequence, file list nodes.
+312 -137
View File
@@ -6,6 +6,8 @@ from server import PromptServer
import sys
import csv
import hashlib
import base64
import pickle
from io import StringIO
import numpy as np
from PIL import Image
@@ -62,6 +64,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 +145,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
@@ -223,8 +238,10 @@ class Spreadsheet2VideoInputImage(io.ComfyNode):
def check_lazy_status(cls, COLUMN1=None):
return []
# Disabled: If this is enabled the workflow will validate.
# But it will run the original nodes that are not in any loop
# Hacky fix for node not validating
RETURN_TYPES=("IMAGE","*","*","*","*","*","*","*","*","*","*","*","*","*","*")
# RETURN_TYPES=("IMAGE","*","*","*","*","*","*","*","*","*","*","*","*","*","*")
class GroupInfo():
@@ -233,6 +250,7 @@ class GroupInfo():
self.group_name = group_name
self.first_input_node_id = first_input_node_id
self.output_image_link = None
self.restored_nodes = {}
def change_out_node(self, graph):
@@ -250,111 +268,6 @@ class GroupInfo():
# logging.warning("S2V: Could not find output image node")
return found_node
@classmethod
def map_children(cls, prompt):
input_node_ids = []
child_node_ids = {}
for node_id, node in prompt.items():
if "inputs" not in node:
continue
for k, v in node["inputs"].items():
if not is_link(v):
continue
parent_node_id = v[0]
if parent_node_id not in child_node_ids:
child_node_ids[parent_node_id] = {"in":[], "out":[]}
child_node_ids[parent_node_id]["out"].append(node_id)
if node_id not in child_node_ids:
child_node_ids[node_id] = {"in":[], "out":[]}
child_node_ids[node_id]["in"].append(parent_node_id)
if node["class_type"] == "Spreadsheet2VideoInputImage":
input_node_ids.append(node_id)
return (child_node_ids,input_node_ids)
@classmethod
def add_map_output_by_group_name(cls, group_name_by_output_id, child_node_ids, children, group_name):
for child_id in children["out"]:
if child_id in group_name_by_output_id:
existing_group_name = group_name_by_output_id[child_id]
if existing_group_name != group_name:
raise ValueError(f"S2V: Node is in both column1 groups, node id:{child_id}, names:{existing_group_name}, {group_name}")
group_name_by_output_id[child_id] = group_name
if child_id not in child_node_ids:
logging.error(f"S2V: Cannot find output node id: {child_id}")
continue
cls.add_map_output_by_group_name(
group_name_by_output_id,
child_node_ids,
child_node_ids[child_id],
group_name
)
@classmethod
def map_output_by_group_name(cls, group_name_by_output_id, prompt):
(child_node_ids, input_node_ids) = cls.map_children(prompt)
for input_node_id in input_node_ids:
inputImageNode = prompt[input_node_id]
group_name = cls.get_group_name_from_node(inputImageNode)
group_name_by_output_id[input_node_id] = group_name
cls.add_map_output_by_group_name(
group_name_by_output_id,
child_node_ids,
child_node_ids[input_node_id],
group_name
)
return group_name_by_output_id
@classmethod
def get_group_name_from_node(cls, inputImageNode):
if inputImageNode is None or "inputs" not in inputImageNode:
return None
group_name = inputImageNode["inputs"]["COLUMN1"].strip()
if group_name == "":
return None
return group_name
@classmethod
def map_outputs(cls, prompt):
(child_node_ids, input_node_ids) = cls.map_children(prompt)
group_name_by_output_id = {}
cls.map_output_by_group_name(group_name_by_output_id, prompt)
by_group_name = {}
for input_node_id in input_node_ids:
inputImageNode = prompt[input_node_id]
group_name = cls.get_group_name_from_node(inputImageNode)
if group_name is None:
continue
if group_name in by_group_name:
raise ValueError(f"S2V: Duplicate group names: {group_name}")
group_info = GroupInfo(input_node_id, group_name)
by_group_name[group_name] = group_info
group_info.add_orig_node_ids(
prompt,
child_node_ids[input_node_id],
child_node_ids,
group_name_by_output_id,
0
)
logging.info(f"S2V: group: {group_name}, nodes: {group_info.get_orig_node_ids_count()}")
return by_group_name
def add_orig_node_ids(
self,
@@ -362,10 +275,12 @@ class GroupInfo():
children,
child_node_ids,
group_name_by_output_id,
parent_is_out,
depth
):
for child_id in (children["in"] + children["out"]):
for (child_id, node_is_out) in ([ [i,False] for i in children["in"]] + [ [i,True] for i in children["out"]]):
must_be_group = group_name_by_output_id[child_id] if child_id in group_name_by_output_id else None
is_out = parent_is_out and node_is_out
class_type = prompt[child_id]["class_type"]
if (
@@ -376,8 +291,8 @@ class GroupInfo():
(must_be_group is not None and must_be_group != self.group_name)
):
pass
elif (child_id not in self.orig_node_ids):
self.orig_node_ids[child_id] = True
elif(child_id not in self.orig_node_ids):
self.orig_node_ids[child_id] = is_out
if child_id in child_node_ids:
self.add_orig_node_ids(
@@ -385,8 +300,13 @@ class GroupInfo():
child_node_ids[child_id],
child_node_ids,
group_name_by_output_id,
is_out,
depth+1
)
elif(is_out is True):
# if the node is on the output path ever. It is out.
# Cause the path is not processed in any order.
self.orig_node_ids[child_id] = is_out
def get_orig_node_ids_count(self):
return len(self.orig_node_ids.keys())
@@ -396,7 +316,11 @@ class GroupInfo():
node_id_map = {}
for node_id in contained:
original_node = prompt[node_id]
if node_id in prompt:
original_node = prompt[node_id]
else:
original_node = self.restored_nodes[node_id]
new_node = graph.node(original_node["class_type"])
new_node.set_override_display_id(node_id)
node_id_map[node_id] = (original_node, new_node)
@@ -485,9 +409,188 @@ 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']={}
@classmethod
def restore_group_infos_from_prompt(cls, prompt):
by_group_name = {}
for node_id, node in prompt.items():
if "_meta" not in node:
continue
meta = node["_meta"]
if "__group_info" in meta:
group_info = pickle.loads(base64.b64decode(meta["__group_info"]))
if "__removed" in meta:
group_info.restored_nodes = meta["__removed"]
by_group_name[group_info.group_name] = group_info
if(len(by_group_name.keys()) == 0):
return None
return by_group_name
def remove_nodes_from_prompt(self, prompt, exclude_ids):
removed = {}
for node_id, is_out in self.orig_node_ids.items():
if node_id in prompt and node_id not in exclude_ids:
removed[node_id]= prompt[node_id]
del prompt[node_id]
return removed
@classmethod
def remove_nodes_in_group_infos(cls, prompt, by_group_name, exclude_ids):
for name, group_info in by_group_name.items():
input_node = prompt[group_info.first_input_node_id]
raw_bytes = pickle.dumps(group_info)
# base64 encodes the binary data into a clean, portable text string
if "_meta" not in input_node:
input_node["_meta"] = {}
input_node["_meta"]["__group_info"] = base64.b64encode(raw_bytes).decode("utf-8")
removed = group_info.remove_nodes_from_prompt(prompt, exclude_ids)
input_node["_meta"]["__removed"] = removed
class MapOutputs():
def __init__(self):
self.child_node_ids = {}
self.input_node_ids = []
def map_children(self, prompt):
input_node_ids = []
child_node_ids = {}
for node_id, node in prompt.items():
if "inputs" not in node or "class_type" not in node:
continue
for k, v in node["inputs"].items():
if not is_link(v):
continue
parent_node_id = v[0]
if parent_node_id not in child_node_ids:
child_node_ids[parent_node_id] = {"in":[], "out":[]}
child_node_ids[parent_node_id]["out"].append(node_id)
if node_id not in child_node_ids:
child_node_ids[node_id] = {"in":[], "out":[]}
child_node_ids[node_id]["in"].append(parent_node_id)
if node["class_type"] == "Spreadsheet2VideoInputImage":
input_node_ids.append(node_id)
self.child_node_ids = child_node_ids
self.input_node_ids = input_node_ids
return (child_node_ids,input_node_ids)
def get_group_name_from_node(self, inputImageNode):
if inputImageNode is None or "inputs" not in inputImageNode:
return None
group_name = inputImageNode["inputs"]["COLUMN1"].strip()
if group_name == "":
return None
return group_name
def add_map_output_by_group_name(self, group_name_by_output_id, child_node_ids, children, group_name):
for child_id in children["out"]:
if child_id in group_name_by_output_id:
existing_group_name = group_name_by_output_id[child_id]
if existing_group_name != group_name:
raise ValueError(f"S2V: Node is in both column1 groups, node id:{child_id}, names:{existing_group_name}, {group_name}")
group_name_by_output_id[child_id] = group_name
if child_id not in child_node_ids:
logging.error(f"S2V: Cannot find output node id: {child_id}")
continue
self.add_map_output_by_group_name(
group_name_by_output_id,
child_node_ids,
child_node_ids[child_id],
group_name
)
def map_output_by_group_name(self, group_name_by_output_id, prompt):
(child_node_ids, input_node_ids) = self.map_children(prompt)
for input_node_id in input_node_ids:
inputImageNode = prompt[input_node_id]
group_name = self.get_group_name_from_node(inputImageNode)
group_name_by_output_id[input_node_id] = group_name
self.add_map_output_by_group_name(
group_name_by_output_id,
child_node_ids,
child_node_ids[input_node_id],
group_name
)
return group_name_by_output_id
def map_outputs(self, prompt):
(child_node_ids, input_node_ids) = self.map_children(prompt)
group_name_by_output_id = {}
self.map_output_by_group_name(group_name_by_output_id, prompt)
by_group_name = {}
for input_node_id in input_node_ids:
inputImageNode = prompt[input_node_id]
group_name = self.get_group_name_from_node(inputImageNode)
if group_name is None:
continue
if group_name in by_group_name:
raise ValueError(f"S2V: Duplicate group names: {group_name}")
group_info = GroupInfo(input_node_id, group_name)
by_group_name[group_name] = group_info
group_info.add_orig_node_ids(
prompt,
child_node_ids[input_node_id],
child_node_ids,
group_name_by_output_id,
True,
0
)
logging.info(f"S2V: group: {group_name}, nodes: {group_info.get_orig_node_ids_count()}")
return by_group_name
def mark_parent_nodes(self, mark_dict, node):
for parent_id in node["in"]:
if parent_id in mark_dict:
continue
mark_dict[parent_id] = True
for node_id in self.child_node_ids:
node = self.child_node_ids[node_id]
self.mark_parent_nodes(mark_dict, node)
def nodes_used_by_other_nodes(self, prompt, by_group_name) -> dict[str, bool]:
used_by_s2v_ids : dict[str, bool] = {}
not_used_by_s2v_ids : dict[str, bool] = {}
used_by_other_nodes : dict[str, bool] = {}
for name, group_info in by_group_name.items():
for node_id in group_info.orig_node_ids.keys():
used_by_s2v_ids[node_id] = True
not_used_by_s2v_ids = { k: True for k in prompt.keys() if k not in used_by_s2v_ids }
for node_id in not_used_by_s2v_ids:
for node_id in self.child_node_ids:
node = self.child_node_ids[node_id]
self.mark_parent_nodes(used_by_other_nodes, node)
return used_by_other_nodes
class Spreadsheet2VideoFinalVideo(io.ComfyNode):
@@ -576,6 +679,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)
@@ -673,6 +777,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:
@@ -698,9 +853,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)
@@ -861,7 +1014,9 @@ class Spreadsheet2VideoNode(io.ComfyNode):
# Use StringIO to treat the string as a file
f = StringIO(spreadsheet)
by_group_name = GroupInfo.map_outputs(cls.hidden.prompt)
by_group_name = GroupInfo.restore_group_infos_from_prompt(cls.hidden.prompt)
if by_group_name is None:
by_group_name = MapOutputs().map_outputs(cls.hidden.prompt)
used_groups = {}
reader = csv.reader(f, delimiter=',')
@@ -928,9 +1083,11 @@ class Spreadsheet2VideoNode(io.ComfyNode):
previous = lastProcessImageNode.out(0)
)
for group_name in used_groups:
used_group = by_group_name[group_name]
used_group.replace_with_do_nothing(cls.hidden.prompt)
# Disabled: If we make the original nodes that are not in the loop DoNothing
# 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),
@@ -953,3 +1110,21 @@ class Spreadsheet2VideoNode(io.ComfyNode):
return str(random.random())
def prompt_handler(json_data):
try:
prompt = json_data["prompt"]
map_outputs = MapOutputs()
by_group_name = map_outputs.map_outputs(prompt)
# remove
used_by_other_nodes = map_outputs.nodes_used_by_other_nodes(prompt, by_group_name)
GroupInfo.remove_nodes_in_group_infos(prompt, by_group_name, used_by_other_nodes)
except Exception:
logging.exception("Problem with prompt")
return json_data
return json_data
PromptServer.instance.add_on_prompt_handler(prompt_handler)
+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,
]
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "spreadsheet2video-comfyui",
"version": "1.0.0",
"version": "1.0.1",
"description": "",
"license": "ISC",
"author": "",
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "Spreadsheet2Video-ComfyUI"
version = "1.0.6"
version = "1.0.9"
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,
+36 -56
View File
@@ -1,7 +1,7 @@
{
"id": "8d94793d-f68f-467c-b91c-5a1bad8692ca",
"revision": 0,
"last_node_id": 11,
"last_node_id": 13,
"last_link_id": 5,
"nodes": [
{
@@ -12,8 +12,8 @@
4133.395402282243
],
"size": [
301.040625,
106
301.03125,
142
],
"flags": {},
"order": 0,
@@ -43,12 +43,12 @@
"id": 7,
"type": "Spreadsheet2VideoSequence",
"pos": [
-830.0402038757348,
-832.3603875607462,
4853.891607434261
],
"size": [
301.040625,
106
301.015625,
142
],
"flags": {},
"order": 1,
@@ -82,8 +82,8 @@
4400
],
"size": [
301.040625,
106
301.03125,
142
],
"flags": {},
"order": 2,
@@ -117,8 +117,8 @@
4101.516095855725
],
"size": [
247.78345320808046,
238.2740203378844
247.765625,
238.25
],
"flags": {},
"order": 5,
@@ -142,11 +142,7 @@
"ver": "0.24.0",
"Node name for S&R": "PreviewAny"
},
"widgets_values": [
null,
null,
false
]
"widgets_values": []
},
{
"id": 5,
@@ -156,8 +152,8 @@
4430
],
"size": [
226.80773384673182,
212.1823816679189
226.796875,
212.171875
],
"flags": {
"collapsed": false
@@ -183,22 +179,18 @@
"ver": "0.24.0",
"Node name for S&R": "PreviewAny"
},
"widgets_values": [
null,
null,
null
]
"widgets_values": []
},
{
"id": 8,
"type": "Spreadsheet2VideoSequence",
"pos": [
-827.5216964337976,
-829.841880118809,
5156.965602783869
],
"size": [
301.040625,
106
301.015625,
142
],
"flags": {},
"order": 3,
@@ -228,11 +220,11 @@
"id": 11,
"type": "PreviewAny",
"pos": [
-469.36544276581486,
-471.6856264508262,
5404.420565428257
],
"size": [
210,
225,
166
],
"flags": {},
@@ -257,22 +249,18 @@
"ver": "0.24.0",
"Node name for S&R": "PreviewAny"
},
"widgets_values": [
null,
null,
null
]
"widgets_values": []
},
{
"id": 10,
"type": "Spreadsheet2VideoSequence",
"pos": [
-827.1824057491543,
-829.5025894341657,
5402.336211698525
],
"size": [
301.040625,
106
301.015625,
142
],
"flags": {},
"order": 4,
@@ -302,11 +290,11 @@
"id": 9,
"type": "PreviewAny",
"pos": [
-469.70473345045866,
-472.02491713547,
5159.049956513601
],
"size": [
210,
225,
166
],
"flags": {},
@@ -331,21 +319,17 @@
"ver": "0.24.0",
"Node name for S&R": "PreviewAny"
},
"widgets_values": [
null,
null,
null
]
"widgets_values": []
},
{
"id": 6,
"type": "PreviewAny",
"pos": [
-472.22324089239635,
-474.5434245774077,
4855.975961163992
],
"size": [
210,
225,
166
],
"flags": {},
@@ -370,11 +354,7 @@
"ver": "0.24.0",
"Node name for S&R": "PreviewAny"
},
"widgets_values": [
null,
null,
null
]
"widgets_values": []
}
],
"links": [
@@ -422,14 +402,14 @@
"groups": [
{
"id": 1,
"title": "Will Error",
"title": "Should Error",
"bounding": [
-891.0076443313377,
-893.327828016349,
4744.143303391399,
1146.6399876260243,
883.1031726327501
],
"color": "#3f789e",
"color": "#A88",
"flags": {}
},
{
@@ -448,13 +428,13 @@
"config": {},
"extra": {
"ds": {
"scale": 0.5131581182307067,
"scale": 0.31863081771035706,
"offset": [
2097.969476454836,
-3598.220857052559
2004.6716575603311,
-3393.3513028960815
]
},
"frontendVersion": "1.45.15",
"frontendVersion": "1.48.7",
"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
}