Add GetTrackRange, SimpleCalculatorKJ

This commit is contained in:
kijai 2025-12-14 17:12:03 +02:00
parent ff7f876c09
commit 1b9af2322c
2 changed files with 123 additions and 2 deletions

View File

@ -129,10 +129,10 @@ NODE_CONFIG = {
"AppendStringsToList": {"class": AppendStringsToList, "name": "Append Strings To List"},
"JoinStrings": {"class": JoinStrings, "name": "Join Strings"},
"JoinStringMulti": {"class": JoinStringMulti, "name": "Join String Multi"},
"SimpleCalculatorKJ": {"class": SimpleCalculatorKJ, "name": "Simple Calculator KJ"},
"SomethingToString": {"class": SomethingToString, "name": "Something To String"},
"Sleep": {"class": Sleep, "name": "Sleep"},
"VRAM_Debug": {"class": VRAM_Debug, "name": "VRAM Debug"},
"SomethingToString": {"class": SomethingToString, "name": "Something To String"},
"EmptyLatentImagePresets": {"class": EmptyLatentImagePresets, "name": "Empty Latent Image Presets"},
"EmptyLatentImageCustomPresets": {"class": EmptyLatentImageCustomPresets, "name": "Empty Latent Image Custom Presets"},
"ModelPassThrough": {"class": ModelPassThrough, "name": "ModelPass"},
@ -223,7 +223,10 @@ NODE_CONFIG = {
#lora
"LoraExtractKJ": {"class": LoraExtractKJ, "name": "LoraExtractKJ"},
"LoraReduceRankKJ": {"class": LoraReduceRank, "name": "LoraReduceRank"}
"LoraReduceRankKJ": {"class": LoraReduceRank, "name": "LoraReduceRank"},
#tracks
"GetTrackRange": {"class": GetTrackRange, "name": "Get Track Range"},
}
def generate_node_mappings(node_config):

View File

@ -10,6 +10,7 @@ import folder_paths
from nodes import MAX_RESOLUTION
from comfy.utils import common_upscale, ProgressBar, load_torch_file
from comfy.comfy_types.node_typing import IO
from comfy_api.latest import io
script_directory = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
folder_paths.add_model_folder_path("kjnodes_fonts", os.path.join(script_directory, "fonts"))
@ -2703,3 +2704,120 @@ class LatentInpaintTTM:
m = model.clone()
m.add_wrapper_with_key(WrappersMP.SAMPLER_SAMPLE, "TTM_SampleWrapper", TTM_SampleWrapper(mask, steps))
return (m, )
class SimpleCalculatorKJ:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"expression": ("STRING", {"default": "a + b", "multiline": True}),
},
"optional": {
"a": (IO.ANY, {"default": 0.0, "min": -1e10, "max": 1e10, "step": 0.01, "forceInput": True}),
"b": (IO.ANY, {"default": 0.0, "min": -1e10, "max": 1e10, "step": 0.01, "forceInput": True}),
}
}
RETURN_TYPES = ("FLOAT", "INT",)
FUNCTION = "calculate"
CATEGORY = "KJNodes/misc"
DESCRIPTION = "Calculator node that evaluates a mathematical expression using inputs a and b."
def calculate(self, expression, a=None, b=None):
import ast
import operator
import math
# Allowed operations
allowed_operators = {ast.Add: operator.add, ast.Sub: operator.sub, ast.Mult: operator.mul, ast.Div: operator.truediv,
ast.Pow: operator.pow, ast.USub: operator.neg, ast.UAdd: operator.pos,
}
# Allowed functions
allowed_functions = {
'abs': abs, 'round': round, 'min': min, 'max': max,
'pow': pow, 'sqrt': math.sqrt, 'sin': math.sin,
'cos': math.cos, 'tan': math.tan, 'log': math.log,
'log10': math.log10, 'exp': math.exp, 'floor': math.floor,
'ceil': math.ceil
}
# Allowed constants
allowed_names = {'a': a, 'b': b, 'pi': math.pi, 'e': math.e}
def eval_node(node):
if isinstance(node, ast.Constant): # Numbers
return node.value
elif isinstance(node, ast.Name): # Variables
if node.id in allowed_names:
return allowed_names[node.id]
raise ValueError(f"Name '{node.id}' is not allowed")
elif isinstance(node, ast.BinOp): # Binary operations
if type(node.op) not in allowed_operators:
raise ValueError(f"Operator {type(node.op).__name__} is not allowed")
left = eval_node(node.left)
right = eval_node(node.right)
return allowed_operators[type(node.op)](left, right)
elif isinstance(node, ast.UnaryOp): # Unary operations
if type(node.op) not in allowed_operators:
raise ValueError(f"Operator {type(node.op).__name__} is not allowed")
operand = eval_node(node.operand)
return allowed_operators[type(node.op)](operand)
elif isinstance(node, ast.Call): # Function calls
if not isinstance(node.func, ast.Name):
raise ValueError("Only simple function calls are allowed")
if node.func.id not in allowed_functions:
raise ValueError(f"Function '{node.func.id}' is not allowed")
args = [eval_node(arg) for arg in node.args]
return allowed_functions[node.func.id](*args)
else:
raise ValueError(f"Node type {type(node).__name__} is not allowed")
try:
tree = ast.parse(expression, mode='eval')
result = eval_node(tree.body)
return (float(result), int(result))
except Exception as e:
print(f"CalculatorKJ Error: {str(e)}")
return (0.0, 0)
class GetTrackRange(io.ComfyNode):
@classmethod
def define_schema(cls):
return io.Schema(
node_id="GetTrackRange",
category="conditioning/video_models",
inputs=[
io.Tracks.Input("tracks"),
io.Int.Input("start_index", default=24, min=-10000, max=10000, step=1),
io.Int.Input("num_frames", default=10, min=1, max=10000, step=1),
],
outputs=[
io.Tracks.Output(),
],
)
@classmethod
def execute(cls, tracks, start_index, num_frames) -> io.NodeOutput:
track_path = tracks["track_path"]
mask = tracks["track_visibility"]
total_frames = track_path.shape[0]
if start_index < 0:
start_index = total_frames + start_index
start_index = max(0, min(start_index, total_frames))
# Clamp end_index
end_index = max(0, min(start_index + num_frames, total_frames))
tracks_out = track_path[start_index:end_index, ...]
mask_out = mask[start_index:end_index, ...]
out_track = {
"track_path": tracks_out,
"track_visibility": mask_out,
}
return io.NodeOutput(out_track)