39 Commits
Author SHA1 Message Date
Morgan Johansson b418aad910 Foreach filename is now recursive 2026-01-14 16:40:22 +01:00
alt-key-project 666f6a7d0e Merge pull request #13 from barakyo/main
Add support for glob patterns
2026-01-14 16:36:00 +01:00
Barak d1b6d36b9c Add support for glob patterns 2025-08-06 16:49:40 -07:00
Morgan Johansson c4e2b4b2e3 install fix. 2025-02-23 11:28:36 +01:00
Morgan Johansson 35f3b27082 install fix. 2025-02-18 06:57:22 +01:00
Morgan Johansson 3604dd7b7c Removed resampling from pillow access. 2025-02-17 22:25:27 +01:00
Morgan Johansson 9db7b8640b Removed resampling from pillow access. 2025-02-17 22:25:07 +01:00
Morgan Johansson 4dd1e94f2c Removed resampling from pillow access. 2025-02-16 15:47:34 +01:00
Morgan Johansson 5bb45568a5 Removed useless IS_CHANGED functions. 2025-02-15 19:42:34 +01:00
Morgan Johansson 8ff285984b update check fix. 2025-02-12 18:10:25 +01:00
Morgan Johansson 16acde23cd update check fix. 2025-02-12 18:05:01 +01:00
Morgan Johansson 59c955d6c9 update check fix. 2025-02-11 21:48:02 +01:00
Morgan Johansson 9d1f0cbf24 Install fix. 2025-02-11 21:36:09 +01:00
Morgan Johansson 72060cdc3f Version bump 2025-02-11 21:04:19 +01:00
Morgan Johansson 2d839ef3dd Version bump 2025-02-11 20:41:21 +01:00
Morgan Johansson 5087440b2d numpy req versions 2025-02-11 19:47:24 +01:00
Morgan Johansson c50b4846f3 Fix for change check warnings. 2025-02-11 19:46:26 +01:00
Morgan Johansson 8566fb427f Foreach fix. 2025-02-08 21:38:59 +01:00
Morgan Johansson 118c5d23da Loader will always add alpha. 2025-02-08 20:51:11 +01:00
Morgan Johansson 0410126ec1 Foreach fix. 2025-02-08 17:41:51 +01:00
Morgan Johansson 4828d7baa2 Version bump 2024-12-07 21:06:37 +01:00
Morgan Johansson 8fb8720626 Version bump 2024-12-07 21:05:58 +01:00
Morgan Johansson eca0e02942 Fixed requirements.txt that could cause parse error on some pip versions. 2024-12-07 21:05:28 +01:00
Morgan Johansson a177672695 Fix for transitions example. 2024-11-17 07:54:08 +01:00
Morgan Johansson b1e0c426ce Added examples. 2024-11-17 07:49:54 +01:00
alt-key-project bae863ea81 Merge pull request #7 from ComfyNodePRs/licence-update
Update PyProject Toml - License
2024-11-17 07:15:37 +01:00
alt-key-project a32c26ceef Merge pull request #6 from ComfyNodePRs/licence-update
Update PyProject Toml - License
2024-11-17 07:15:14 +01:00
snomiao 7f7427c1d4 chore(licence-update): Update PyProject Toml - License 2024-08-14 01:26:16 +00:00
snomiao 8b5421c993 chore(licence-update): Update PyProject Toml - License 2024-07-31 15:02:42 +00:00
Morgan Johansson f644507af4 fixed github action branch 2024-07-01 07:37:00 +02:00
Morgan Johansson 3979e1d653 fixed github action branch 2024-07-01 07:35:09 +02:00
Morgan Johansson 23beb93040 Version bump 2024-07-01 07:29:45 +02:00
alt-key-project ebdfa3d4b9 Merge pull request #3 from haohaocreates/publish
Add Github Action for Publishing to Comfy Registry
2024-07-01 07:25:00 +02:00
Morgan Johansson 1718b9eb01 publisher id added 2024-07-01 07:21:14 +02:00
alt-key-project 692c079b6a Merge pull request #4 from haohaocreates/pyproject
Add pyproject.toml for Custom Node Registry
2024-07-01 07:20:21 +02:00
haohaocreates ebd67759ba chore(pyproject): Add pyproject.toml for Custom Node Registry 2024-05-22 16:51:59 -04:00
haohaocreates 26f270a4c1 chore(publish): Add Github Action for Publishing to Comfy Registry 2024-05-22 16:51:55 -04:00
alt-key-project fcc56e1d3f Update README.md 2023-12-03 11:31:55 +01:00
alt-key-project d552870611 Update README.md 2023-12-03 11:18:12 +01:00
32 changed files with 3266 additions and 516 deletions
+21
View File
@@ -0,0 +1,21 @@
name: Publish to Comfy registry
on:
workflow_dispatch:
push:
branches:
- main
paths:
- "pyproject.toml"
jobs:
publish-node:
name: Publish Custom Node to registry
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Publish Custom Node
uses: Comfy-Org/publish-node-action@main
with:
## Add your own personal access token to your Github Repository secrets and reference it here.
personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }}
+1
View File
@@ -1,2 +1,3 @@
__pycache__
.idea
*.cmd
+3 -3
View File
@@ -56,7 +56,7 @@ the input (to allow for camera "movement").
Fades from one frame set to another over a specified number of overlapping frames.
### Calculation [DVB]
Maths node providing arithmetic operators and most common mathenatical functions (as defined in python math module).
Maths node providing arithmetic operators and most common mathematical functions (as defined in Python math module).
### Create Frame Set [DVB]
Creates a frame set from an image batch and a specified frame rate.
@@ -140,7 +140,7 @@ Cropping utility to perform a camera pan within a frame set. Outputs a frame set
### Sine Camera Roll [DVB]
Rolls the camera along z axis - sine oscillation.
### Sine Camera Zoom [DVB]"
### Sine Camera Zoom [DVB]
Oscillating (sine wave) zoom through crop.
### String Input [DVB]
@@ -150,4 +150,4 @@ User input node for string values.
User input node for text(string) values.
### Unwrap Frame Set [DVB]
Extracts contents of frame set (required for use with other custom node packs and/or output nodes).
Extracts contents of frame set (required for use with other custom node packs and/or output nodes).
+1 -1
View File
@@ -53,7 +53,7 @@ _SIGNATURE_SUFFIX = " [DVB]"
MANIFEST = {
"name": "Dream Video Batches",
"version": (1, 0, 0),
"version": (1, 1, 4),
"author": "Dream Project",
"project": "https://github.com/alt-key-project/comfyui-dream-video-batches",
"description": "Various utility nodes for working with video batches in ComfyUI",
-12
View File
@@ -25,10 +25,6 @@ class DVB_Multiply:
}
}
@classmethod
def IS_CHANGED(cls, *values):
return hashed_as_strings(*values)
def result(self, multiple, **kwargs):
f = kwargs.get("float", 0.0)
i = kwargs.get("int", 0)
@@ -54,10 +50,6 @@ class DVB_Divide:
}
}
@classmethod
def IS_CHANGED(cls, *values):
return hashed_as_strings(*values)
def result(self, divisor, **kwargs):
f = kwargs.get("float", 0.0)
i = kwargs.get("int", 0)
@@ -89,10 +81,6 @@ class DVB_Calculation:
}
}
@classmethod
def IS_CHANGED(cls, *values):
return hashed_as_strings(*values)
def _make_model(self):
funcs = self._make_functions()
m = base_eval_model.clone()
+1 -26
View File
@@ -2,8 +2,6 @@
import math
from PIL.Image import Resampling
from .categories import *
from .core import *
@@ -43,8 +41,7 @@ def zoom_frame(image: DVB_Image, f, o_width, o_height):
h = (image.height - o_height) * f + o_height
c = image.quad.center
return image.crop(c.x - w * 0.5, c.y - h * 0.5, c.x + w * 0.5, c.y + h * 0.5).resize(o_width,
o_height,
Resampling.BILINEAR)
o_height)
def make_pan_function(direction_x: float, direction_y: float):
@@ -99,10 +96,6 @@ class DVB_Zoom:
RETURN_NAMES = ("frames",)
FUNCTION = "result"
@classmethod
def IS_CHANGED(cls, *values):
return ALWAYS_CHANGED_FLAG
def result(self, frames: FrameSet, output_width: int, output_height: int, direction):
def motion(f):
if direction == "in":
@@ -135,10 +128,6 @@ class DVB_LinearCameraPan:
RETURN_NAMES = ("frames",)
FUNCTION = "result"
@classmethod
def IS_CHANGED(cls, *values):
return ALWAYS_CHANGED_FLAG
def result(self, frames: FrameSet, output_width: int, output_height: int, direction_x: float,
direction_y: float, pan_mode: str):
def motion(f):
@@ -173,9 +162,6 @@ class DVB_LinearCameraRoll:
RETURN_NAMES = ("frames",)
FUNCTION = "result"
@classmethod
def IS_CHANGED(cls, *values):
return ALWAYS_CHANGED_FLAG
def result(self, frames: FrameSet, output_width: int, output_height: int, degrees: float):
def motion(f):
@@ -214,10 +200,6 @@ class DVB_ZoomSine:
RETURN_NAMES = ("frames",)
FUNCTION = "result"
@classmethod
def IS_CHANGED(cls, *values):
return ALWAYS_CHANGED_FLAG
def result(self, frames: FrameSet, output_width: int, output_height: int, period_seconds, phase_seconds):
def motion(f):
f = _recalc_motion_factor_to_loopable(f, frames.indexed_length)
@@ -253,9 +235,6 @@ class DVB_SineCameraPan:
RETURN_NAMES = ("frames",)
FUNCTION = "result"
@classmethod
def IS_CHANGED(cls, *values):
return ALWAYS_CHANGED_FLAG
def result(self, frames: FrameSet, output_width: int, output_height: int, direction_x: float,
direction_y: float, pan_mode: str, period_seconds, phase_seconds):
@@ -294,10 +273,6 @@ class DVB_SineCameraRoll:
RETURN_NAMES = ("frames",)
FUNCTION = "result"
@classmethod
def IS_CHANGED(cls, *values):
return ALWAYS_CHANGED_FLAG
def result(self, frames: FrameSet, output_width: int, output_height: int, degrees: float, period_seconds,
phase_seconds):
def motion(f):
-4
View File
@@ -25,9 +25,5 @@
"transitions": "\ud83c\udfac",
"io": "\ud83d\udcbe"
}
},
"paths": {
"default_input": "I:\\AI\\ComfyUI\\ComfyUI\\input",
"default_output": "I:\\AI\\ComfyUI\\ComfyUI\\output"
}
}
-1
View File
@@ -1,6 +1,5 @@
from .batch_processing import DVB_ImageBatchProcessor
from .dvb_image import DVB_Image
from .node_support import ALWAYS_CHANGED_FLAG
from .frameset import FrameSet
from .framerate import FrameRate
from .config import DVB_Config
-7
View File
@@ -2,15 +2,8 @@
import json
import os
import folder_paths as comfy_paths
_EMBEDDED_CONFIGURATION = {
"debug": False,
"paths": {
"default_input": comfy_paths.input_directory,
"default_output": comfy_paths.output_directory
},
"ui": {
"top_category": "DVB",
"prepend_icon_to_category": True,
+3 -4
View File
@@ -4,7 +4,6 @@ from functools import cache
import numpy
import torch
from PIL import Image, ImageFilter, ImageEnhance
from PIL.Image import Resampling
from PIL.ImageDraw import ImageDraw
from torch import Tensor
from .vector import *
@@ -99,7 +98,7 @@ class DVB_Image:
return DVB_Image(pil_image=self.pil_image.crop((left, top, right, bottom)))
def rotate(self, degrees_cw):
return DVB_Image(pil_image=self.pil_image.rotate(degrees_cw, Resampling.BILINEAR))
return DVB_Image(pil_image=self.pil_image.rotate(degrees_cw))
@property
def width(self):
@@ -162,13 +161,13 @@ class DVB_Image:
def from_file(cls, file_path):
return DVB_Image(pil_image=Image.open(file_path))
def resize(self, resize_width=0, resize_height=0, resampling=Resampling.NEAREST):
def resize(self, resize_width=0, resize_height=0):
if resize_width > 0 or resize_height > 0:
ratio = self.width / self.height
if resize_height <= 0:
resize_height = round(resize_width / ratio)
elif resize_width <= 0:
resize_width = round(resize_height * ratio)
return DVB_Image(pil_image=self.pil_image.resize((resize_width, resize_height), resampling))
return DVB_Image(pil_image=self.pil_image.resize((resize_width, resize_height)))
else:
return self
-1
View File
@@ -1,3 +1,2 @@
# -*- coding: utf-8 -*-
ALWAYS_CHANGED_FLAG = float("NaN")
+4 -3
View File
@@ -3,13 +3,16 @@ import json
import os
def hashed_as_strings(*items):
def hashed_as_strings(*items, **kwargs):
tokens = "|".join(list(map(str, items)))
m = hashlib.sha256()
m.update(tokens.encode(encoding="utf-8"))
for pair in kwargs.items():
m.update(str(pair).encode(encoding="utf-8"))
return m.digest().hex()
class ForEachState:
def __init__(self, filepath):
self._filepath = os.path.abspath(filepath)
@@ -21,7 +24,6 @@ class ForEachState:
self._data = dict()
def add_files_to_process(self, files):
files = list(map(lambda f: os.path.basename(f), files))
all_files = set(self._data.keys())
all_files.update(files)
for filename in all_files:
@@ -32,7 +34,6 @@ class ForEachState:
f.write(txt)
def mark_done(self, filename):
filename = os.path.basename(filename)
self._data[filename] = True
with open(self._filepath, "w", encoding="utf8") as f:
txt = json.dumps(self._data, indent=2)
-36
View File
@@ -24,10 +24,6 @@ class DVB_FrameSetReindex:
RETURN_NAMES = ("frames",)
FUNCTION = "result"
@classmethod
def IS_CHANGED(cls, *values):
return ALWAYS_CHANGED_FLAG
def result(self, frames: FrameSet, start, step):
return (frames.reindexed(start, step),)
@@ -50,10 +46,6 @@ class DVB_FrameSetOffset:
RETURN_NAMES = ("frames",)
FUNCTION = "result"
@classmethod
def IS_CHANGED(cls, *values):
return ALWAYS_CHANGED_FLAG
def result(self, frames: FrameSet, offset):
return (frames.reindexed(frames.first_index + offset),)
@@ -78,10 +70,6 @@ class DVB_ConcatFrameSets:
RETURN_NAMES = ("frames",)
FUNCTION = "result"
@classmethod
def IS_CHANGED(cls, *values):
return ALWAYS_CHANGED_FLAG
def result(self, a: FrameSet, b: FrameSet, offset_from_end, step):
first_index = a.last_index + step + offset_from_end
@@ -112,10 +100,6 @@ class DVB_MergeFrames:
RETURN_NAMES = ("frames",)
FUNCTION = "result"
@classmethod
def IS_CHANGED(cls, *values):
return ALWAYS_CHANGED_FLAG
def result(self, a: FrameSet, b: FrameSet, priority: str):
if priority == "use_b_when_possible":
t = a
@@ -146,10 +130,6 @@ class DVB_Splitter:
RETURN_NAMES = ("first_half", "second_half")
FUNCTION = "result"
@classmethod
def IS_CHANGED(cls, *values):
return ALWAYS_CHANGED_FLAG
def result(self, frames: FrameSet, overlap: int):
images = frames.indexed_images
n = len(images) // 2
@@ -186,10 +166,6 @@ class DVB_Reverse:
RETURN_NAMES = ("frames", )
FUNCTION = "result"
@classmethod
def IS_CHANGED(cls, *values):
return ALWAYS_CHANGED_FLAG
def result(self, frames: FrameSet):
return (FrameSet.from_images(list(reversed(frames.images)), frames.framerate, frames.indices),)
@@ -212,10 +188,6 @@ class DVB_FrameSetRepeat:
RETURN_NAMES = ("frames", )
FUNCTION = "result"
@classmethod
def IS_CHANGED(cls, *values):
return ALWAYS_CHANGED_FLAG
def result(self, frames: FrameSet, repetitions, step):
original_frames = frames.indexed_images
result = list()
@@ -246,10 +218,6 @@ class DVB_FrameSetSplitBeginning:
RETURN_NAMES = ("beginning", "other")
FUNCTION = "result"
@classmethod
def IS_CHANGED(cls, *values):
return ALWAYS_CHANGED_FLAG
def result(self, frames: FrameSet, num_entries):
num_entries = min(len(frames), num_entries)
images = frames.indexed_images
@@ -277,10 +245,6 @@ class DVB_FrameSetSplitEnd:
RETURN_NAMES = ("beginning", "other")
FUNCTION = "result"
@classmethod
def IS_CHANGED(cls, *values):
return ALWAYS_CHANGED_FLAG
def result(self, frames: FrameSet, num_entries):
num_entries = min(len(frames), num_entries)
n = len(frames) - num_entries
View File
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

+500
View File
@@ -0,0 +1,500 @@
{
"last_node_id": 13,
"last_link_id": 15,
"nodes": [
{
"id": 8,
"type": "Unwrap Frame Set [DVB]",
"pos": [
1243,
697
],
"size": [
315,
198
],
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"name": "frames",
"type": "FRAME_SET",
"link": 8
}
],
"outputs": [
{
"name": "images",
"type": "IMAGE",
"links": [
9
],
"slot_index": 0
},
{
"name": "framerate_float",
"type": "FLOAT",
"links": null
},
{
"name": "framerate_rounded",
"type": "INT",
"links": [
10
],
"slot_index": 2
},
{
"name": "framerate_base",
"type": "INT",
"links": null
},
{
"name": "framerate_divisor",
"type": "INT",
"links": null
},
{
"name": "first_index",
"type": "INT",
"links": null
},
{
"name": "indexed_length",
"type": "INT",
"links": null
},
{
"name": "frame_count",
"type": "INT",
"links": null
}
],
"properties": {
"Node name for S&R": "Unwrap Frame Set [DVB]"
},
"widgets_values": [
"BLEND"
]
},
{
"id": 9,
"type": "VHS_VideoCombine",
"pos": [
1622,
688
],
"size": [
315,
535
],
"flags": {},
"order": 8,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 9
},
{
"name": "frame_rate",
"type": "INT",
"link": 10,
"widget": {
"name": "frame_rate"
}
}
],
"outputs": [],
"properties": {
"Node name for S&R": "VHS_VideoCombine"
},
"widgets_values": {
"frame_rate": 8,
"loop_count": 0,
"filename_prefix": "AnimateDiff",
"format": "video/h265-mp4",
"pingpong": false,
"save_image": true,
"crf": 20,
"save_metadata": true,
"audio_file": "",
"videopreview": {
"hidden": false,
"paused": false,
"params": {
"filename": "AnimateDiff_00004.mp4",
"subfolder": "",
"type": "output",
"format": "video/h265-mp4"
}
}
}
},
{
"id": 5,
"type": "Generate Inbetween Frames [DVB]",
"pos": [
856,
697
],
"size": [
352.79998779296875,
58
],
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "frames",
"type": "FRAME_SET",
"link": 15
}
],
"outputs": [
{
"name": "frames",
"type": "FRAME_SET",
"links": [
8
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "Generate Inbetween Frames [DVB]"
},
"widgets_values": [
"BLEND"
]
},
{
"id": 4,
"type": "Create Frame Set [DVB]",
"pos": [
499,
698
],
"size": [
315,
130
],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 5
},
{
"name": "framerate_base",
"type": "INT",
"link": 12,
"widget": {
"name": "framerate_base"
}
},
{
"name": "step",
"type": "INT",
"link": 14,
"widget": {
"name": "step"
}
}
],
"outputs": [
{
"name": "frames",
"type": "FRAME_SET",
"links": [
15
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "Create Frame Set [DVB]"
},
"widgets_values": [
0,
1,
24,
1
]
},
{
"id": 6,
"type": "ImageBatch",
"pos": [
226,
705
],
"size": [
210,
46
],
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "image1",
"type": "IMAGE",
"link": 3
},
{
"name": "image2",
"type": "IMAGE",
"link": 4
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
5
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "ImageBatch"
},
"widgets_values": []
},
{
"id": 2,
"type": "LoadImage",
"pos": [
-158,
691
],
"size": [
315,
314
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
4
],
"slot_index": 0
},
{
"name": "MASK",
"type": "MASK",
"links": null
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"b.jpg",
"image"
]
},
{
"id": 11,
"type": "Int Input [DVB]",
"pos": [
-146,
1073
],
"size": [
315,
58
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "INT",
"type": "INT",
"links": [
12
]
}
],
"title": "✍ Framerate",
"properties": {
"Node name for S&R": "Int Input [DVB]"
},
"widgets_values": [
8
]
},
{
"id": 12,
"type": "Int Input [DVB]",
"pos": [
-155,
1202
],
"size": [
315,
58
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "INT",
"type": "INT",
"links": [
14
],
"slot_index": 0
}
],
"title": "✍ Total frames",
"properties": {
"Node name for S&R": "Int Input [DVB]"
},
"widgets_values": [
80
]
},
{
"id": 3,
"type": "LoadImage",
"pos": [
-167,
310
],
"size": [
315,
314
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
3
],
"slot_index": 0
},
{
"name": "MASK",
"type": "MASK",
"links": null
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"a.jpg",
"image"
]
}
],
"links": [
[
3,
3,
0,
6,
0,
"IMAGE"
],
[
4,
2,
0,
6,
1,
"IMAGE"
],
[
5,
6,
0,
4,
0,
"IMAGE"
],
[
8,
5,
0,
8,
0,
"FRAME_SET"
],
[
9,
8,
0,
9,
0,
"IMAGE"
],
[
10,
8,
2,
9,
1,
"INT"
],
[
12,
11,
0,
4,
1,
"INT"
],
[
14,
12,
0,
4,
2,
"INT"
],
[
15,
4,
0,
5,
0,
"FRAME_SET"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 0.6209213230591556,
"offset": [
599.5462387889024,
146.43580096031252
]
}
},
"version": 0.4
}
+590
View File
@@ -0,0 +1,590 @@
{
"last_node_id": 22,
"last_link_id": 28,
"nodes": [
{
"id": 18,
"type": "UpscaleModelLoader",
"pos": [
-441,
-23
],
"size": [
315,
58
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "UPSCALE_MODEL",
"type": "UPSCALE_MODEL",
"links": [
22
]
}
],
"properties": {
"Node name for S&R": "UpscaleModelLoader"
},
"widgets_values": [
"4x_foolhardy_Remacri.pth"
]
},
{
"id": 11,
"type": "Int Input [DVB]",
"pos": [
-420,
486
],
"size": [
315,
58
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "INT",
"type": "INT",
"links": [
12
]
}
],
"title": "✍ Framerate",
"properties": {
"Node name for S&R": "Int Input [DVB]"
},
"widgets_values": [
8
]
},
{
"id": 16,
"type": "Int Input [DVB]",
"pos": [
-421,
626
],
"size": [
315,
58
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "INT",
"type": "INT",
"links": [
19
],
"slot_index": 0
}
],
"title": "✍ Total Frames",
"properties": {
"Node name for S&R": "Int Input [DVB]"
},
"widgets_values": [
100
]
},
{
"id": 3,
"type": "LoadImage",
"pos": [
-446,
91
],
"size": [
315,
314
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
21
],
"slot_index": 0
},
{
"name": "MASK",
"type": "MASK",
"links": null
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"b.jpg",
"image"
]
},
{
"id": 17,
"type": "ImageUpscaleWithModel",
"pos": [
-39,
13
],
"size": [
340.20001220703125,
46
],
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "upscale_model",
"type": "UPSCALE_MODEL",
"link": 22
},
{
"name": "image",
"type": "IMAGE",
"link": 21
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
27
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "ImageUpscaleWithModel"
}
},
{
"id": 22,
"type": "ImageScale",
"pos": [
332,
13
],
"size": [
315,
130
],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "image",
"type": "IMAGE",
"link": 27
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
28
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "ImageScale"
},
"widgets_values": [
"nearest-exact",
1024,
1024,
"disabled"
]
},
{
"id": 4,
"type": "Create Frame Set [DVB]",
"pos": [
673,
118
],
"size": [
315,
130
],
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 28
},
{
"name": "framerate_base",
"type": "INT",
"link": 12,
"widget": {
"name": "framerate_base"
}
}
],
"outputs": [
{
"name": "frames",
"type": "FRAME_SET",
"links": [
18
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "Create Frame Set [DVB]"
},
"widgets_values": [
0,
1,
24,
1
]
},
{
"id": 15,
"type": "Frame Set Repeat [DVB]",
"pos": [
1007,
239
],
"size": [
315,
82
],
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"name": "frames",
"type": "FRAME_SET",
"link": 18
},
{
"name": "repetitions",
"type": "INT",
"link": 19,
"widget": {
"name": "repetitions"
}
}
],
"outputs": [
{
"name": "frames",
"type": "FRAME_SET",
"links": [
20
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "Frame Set Repeat [DVB]"
},
"widgets_values": [
2,
1
]
},
{
"id": 14,
"type": "Linear Camera Roll [DVB]",
"pos": [
1343,
243
],
"size": [
315,
106
],
"flags": {},
"order": 8,
"mode": 0,
"inputs": [
{
"name": "frames",
"type": "FRAME_SET",
"link": 20
}
],
"outputs": [
{
"name": "frames",
"type": "FRAME_SET",
"links": [
24
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "Linear Camera Roll [DVB]"
},
"widgets_values": [
512,
512,
45
]
},
{
"id": 8,
"type": "Unwrap Frame Set [DVB]",
"pos": [
1679,
241
],
"size": [
315,
198
],
"flags": {},
"order": 9,
"mode": 0,
"inputs": [
{
"name": "frames",
"type": "FRAME_SET",
"link": 24
}
],
"outputs": [
{
"name": "images",
"type": "IMAGE",
"links": [
9
],
"slot_index": 0
},
{
"name": "framerate_float",
"type": "FLOAT",
"links": null
},
{
"name": "framerate_rounded",
"type": "INT",
"links": [
10
],
"slot_index": 2
},
{
"name": "framerate_base",
"type": "INT",
"links": null
},
{
"name": "framerate_divisor",
"type": "INT",
"links": null
},
{
"name": "first_index",
"type": "INT",
"links": null
},
{
"name": "indexed_length",
"type": "INT",
"links": null
},
{
"name": "frame_count",
"type": "INT",
"links": null
}
],
"properties": {
"Node name for S&R": "Unwrap Frame Set [DVB]"
},
"widgets_values": [
"BLEND"
]
},
{
"id": 9,
"type": "VHS_VideoCombine",
"pos": [
2024,
267
],
"size": [
315,
535
],
"flags": {},
"order": 10,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 9
},
{
"name": "frame_rate",
"type": "INT",
"link": 10,
"widget": {
"name": "frame_rate"
}
}
],
"outputs": [],
"properties": {
"Node name for S&R": "VHS_VideoCombine"
},
"widgets_values": {
"frame_rate": 8,
"loop_count": 0,
"filename_prefix": "AnimateDiff",
"format": "video/h265-mp4",
"pingpong": false,
"save_image": true,
"crf": 20,
"save_metadata": true,
"audio_file": "",
"videopreview": {
"hidden": false,
"paused": false,
"params": {
"filename": "AnimateDiff_00008.mp4",
"subfolder": "",
"type": "output",
"format": "video/h265-mp4"
}
}
}
}
],
"links": [
[
9,
8,
0,
9,
0,
"IMAGE"
],
[
10,
8,
2,
9,
1,
"INT"
],
[
12,
11,
0,
4,
1,
"INT"
],
[
18,
4,
0,
15,
0,
"FRAME_SET"
],
[
19,
16,
0,
15,
1,
"INT"
],
[
20,
15,
0,
14,
0,
"FRAME_SET"
],
[
21,
3,
0,
17,
1,
"IMAGE"
],
[
22,
18,
0,
17,
0,
"UPSCALE_MODEL"
],
[
24,
14,
0,
8,
0,
"FRAME_SET"
],
[
27,
17,
0,
22,
0,
"IMAGE"
],
[
28,
22,
0,
4,
0,
"IMAGE"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 0.5644739300537787,
"offset": [
498.9422532390306,
313.94385501548663
]
}
},
"version": 0.4
}
+419
View File
@@ -0,0 +1,419 @@
{
"last_node_id": 25,
"last_link_id": 32,
"nodes": [
{
"id": 4,
"type": "Create Frame Set [DVB]",
"pos": [
230,
-368
],
"size": [
315,
130
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 29
},
{
"name": "framerate_base",
"type": "INT",
"link": 12,
"widget": {
"name": "framerate_base"
}
}
],
"outputs": [
{
"name": "frames",
"type": "FRAME_SET",
"links": [
30
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "Create Frame Set [DVB]"
},
"widgets_values": [
0,
1,
24,
1
]
},
{
"id": 24,
"type": "Frame Set Reindex [DVB]",
"pos": [
617,
-358
],
"size": [
315,
82
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "frames",
"type": "FRAME_SET",
"link": 30
}
],
"outputs": [
{
"name": "frames",
"type": "FRAME_SET",
"links": [
31
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "Frame Set Reindex [DVB]"
},
"widgets_values": [
0,
2
]
},
{
"id": 25,
"type": "Generate Inbetween Frames [DVB]",
"pos": [
977,
-357
],
"size": [
352.79998779296875,
58
],
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "frames",
"type": "FRAME_SET",
"link": 31
}
],
"outputs": [
{
"name": "frames",
"type": "FRAME_SET",
"links": [
32
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "Generate Inbetween Frames [DVB]"
},
"widgets_values": [
"BLEND"
]
},
{
"id": 8,
"type": "Unwrap Frame Set [DVB]",
"pos": [
1376,
-358
],
"size": [
315,
198
],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "frames",
"type": "FRAME_SET",
"link": 32
}
],
"outputs": [
{
"name": "images",
"type": "IMAGE",
"links": [
9
],
"slot_index": 0
},
{
"name": "framerate_float",
"type": "FLOAT",
"links": null
},
{
"name": "framerate_rounded",
"type": "INT",
"links": [
10
],
"slot_index": 2
},
{
"name": "framerate_base",
"type": "INT",
"links": null
},
{
"name": "framerate_divisor",
"type": "INT",
"links": null
},
{
"name": "first_index",
"type": "INT",
"links": null
},
{
"name": "indexed_length",
"type": "INT",
"links": null
},
{
"name": "frame_count",
"type": "INT",
"links": null
}
],
"properties": {
"Node name for S&R": "Unwrap Frame Set [DVB]"
},
"widgets_values": [
"BLEND"
]
},
{
"id": 9,
"type": "VHS_VideoCombine",
"pos": [
1790,
-345
],
"size": [
315,
535
],
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 9
},
{
"name": "frame_rate",
"type": "INT",
"link": 10,
"widget": {
"name": "frame_rate"
}
}
],
"outputs": [],
"properties": {
"Node name for S&R": "VHS_VideoCombine"
},
"widgets_values": {
"frame_rate": 8,
"loop_count": 0,
"filename_prefix": "AnimateDiff",
"format": "video/h265-mp4",
"pingpong": false,
"save_image": true,
"crf": 20,
"save_metadata": true,
"audio_file": "",
"videopreview": {
"hidden": false,
"paused": false,
"params": {
"filename": "AnimateDiff_00009.mp4",
"subfolder": "",
"type": "output",
"format": "video/h265-mp4"
}
}
}
},
{
"id": 23,
"type": "VHS_LoadVideo",
"pos": [
-172,
-449
],
"size": [
235.1999969482422,
431.20001220703125
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
29
],
"slot_index": 0
},
{
"name": "frame_count",
"type": "INT",
"links": null
}
],
"properties": {
"Node name for S&R": "VHS_LoadVideo"
},
"widgets_values": {
"video": "AnimateDiff_00008.mp4",
"force_rate": 0,
"force_size": "Disabled",
"custom_width": 512,
"custom_height": 512,
"frame_load_cap": 0,
"skip_first_frames": 0,
"select_every_nth": 1,
"choose video to upload": "image",
"videopreview": {
"hidden": false,
"paused": false,
"params": {
"filename": "AnimateDiff_00008.mp4",
"type": "input",
"format": "video"
}
}
}
},
{
"id": 11,
"type": "Int Input [DVB]",
"pos": [
-205,
46
],
"size": [
315,
58
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "INT",
"type": "INT",
"links": [
12
]
}
],
"title": "✍ Framerate",
"properties": {
"Node name for S&R": "Int Input [DVB]"
},
"widgets_values": [
8
]
}
],
"links": [
[
9,
8,
0,
9,
0,
"IMAGE"
],
[
10,
8,
2,
9,
1,
"INT"
],
[
12,
11,
0,
4,
1,
"INT"
],
[
29,
23,
0,
4,
0,
"IMAGE"
],
[
30,
4,
0,
24,
0,
"FRAME_SET"
],
[
31,
24,
0,
25,
0,
"FRAME_SET"
],
[
32,
25,
0,
8,
0,
"FRAME_SET"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 0.6697434796608355,
"offset": [
1128.3921823557682,
1176.070734278846
]
}
},
"version": 0.4
}
File diff suppressed because it is too large Load Diff
-4
View File
@@ -20,10 +20,6 @@ class DVB_InbetweenFrames:
RETURN_NAMES = ("frames",)
FUNCTION = "result"
@classmethod
def IS_CHANGED(cls, *values):
return ALWAYS_CHANGED_FLAG
def result(self, frames: FrameSet, fill_mode):
assert isinstance(frames, FrameSet)
gc_comfyui()
-16
View File
@@ -18,10 +18,6 @@ class DVB_InputText:
RETURN_NAMES = ("STRING",)
FUNCTION = "noop"
@classmethod
def IS_CHANGED(cls, *values):
return hashed_as_strings(*values)
def noop(self, value):
return (value,)
@@ -43,10 +39,6 @@ class DVB_InputString:
RETURN_NAMES = ("STRING",)
FUNCTION = "noop"
@classmethod
def IS_CHANGED(cls, *values):
return hashed_as_strings(*values)
def noop(self, value):
return (value,)
@@ -68,10 +60,6 @@ class DVB_InputFloat:
RETURN_NAMES = ("FLOAT",)
FUNCTION = "noop"
@classmethod
def IS_CHANGED(cls, *values):
return hashed_as_strings(*values)
def noop(self, value):
return (value,)
@@ -93,10 +81,6 @@ class DVB_InputInt:
RETURN_NAMES = ("INT",)
FUNCTION = "noop"
@classmethod
def IS_CHANGED(cls, *values):
return hashed_as_strings(*values)
def noop(self, value):
return (value,)
+2 -8
View File
@@ -1,14 +1,8 @@
# -*- coding: utf-8 -*-
from .shared import DreamConfig
def setup_default_config():
DreamConfig()
from core import DVB_Config
def run_install():
setup_default_config()
DVB_Config()
if __name__ == "__main__":
run_install()
+10 -138
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
import os
import random
import hashlib
from .categories import NodeCategories
from .core import *
@@ -23,143 +23,15 @@ class DVB_LoadImageFromPath:
}
@classmethod
def IS_CHANGED(cls, *values):
return ALWAYS_CHANGED_FLAG
def IS_CHANGED(cls, *values, **kwargs):
image_path = kwargs.get("image_path", "None")
if (not image_path) or (not os.path.isfile(image_path)):
return ""
m = hashlib.sha256()
with open(image_path, "rb") as f:
m.update(f.read())
return m.digest().hex()
def result(self, image_path, **other):
return (DVB_Image.join_to_tensor_data([DVB_Image(file_path=image_path)]),)
return (DVB_Image.join_to_tensor_data([DVB_Image(file_path=image_path, with_alpha=True)]),)
class DreamRandomBatchLoader:
CATEGORY = NodeCategories.IO
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("image",)
FUNCTION = "result"
NODE_NAME = "Image Batch Random Loader"
ICON = "⚅"
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"directory_path": ("STRING", {"default": '', "multiline": False}),
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
"pattern": ("STRING", {"default": '*', "multiline": False}),
"mode": (["RGB", "RGBA", "GRAY"],),
"load_count": ("INT", {"default": 2, "min": 1, "max": 100000})
},
"optional": {
"resize_width": ("INT", {"default": 0, "min": 0, "max": 8192}),
"resize_height": ("INT", {"default": 0, "min": 0, "max": 8192})
}
}
@classmethod
def IS_CHANGED(cls, *values):
return ALWAYS_CHANGED_FLAG
def result(self, directory_path, pattern, load_count, seed, mode, **other):
entries = list_images_in_directory(directory_path, pattern, True)
keys_in_order = list(sorted(entries.keys()))
random.Random(seed).shuffle(keys_in_order)
if len(keys_in_order) > load_count:
keys_in_order = list(keys_in_order[0:load_count])
resize_width = other.get("resize_width", 0)
resize_height = other.get("resize_height", 0)
images = list()
for k in keys_in_order:
images.append(_load_and_resize(entries[k].pop(), resize_width, resize_height, mode))
if len(images) == 0:
images.append(DreamImage.empty(512, 512, "RGB").resize(resize_width, resize_height).convert(mode))
return (DreamImage.join_to_tensor_data(images),)
class DreamImageBatchLoader:
CATEGORY = NodeCategories.IO
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("image",)
FUNCTION = "result"
NODE_NAME = "Image Big Batch Loader"
ICON = "💾"
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"directory_path": ("STRING", {"default": '', "multiline": False}),
"pattern": ("STRING", {"default": '*', "multiline": False}),
"max_loaded": ("INT", {"default": 100, "min": 1, "max": 100000}),
"mode": (["RGB", "RGBA", "GRAY"],),
"indexing": (["numeric", "alphabetic order"],)
},
"optional": {
"resize_width": ("INT", {"default": 0, "min": 0, "max": 8192}),
"resize_height": ("INT", {"default": 0, "min": 0, "max": 8192})
}
}
@classmethod
def IS_CHANGED(cls, *values):
return ALWAYS_CHANGED_FLAG
def result(self, directory_path, pattern, indexing, max_loaded, mode, **other):
entries = list_images_in_directory(directory_path, pattern, indexing == "alphabetic order")
keys_in_order = list(sorted(entries.keys()))
if len(keys_in_order) > max_loaded:
keys_in_order = list(keys_in_order[0:max_loaded])
resize_width = other.get("resize_width", 0)
resize_height = other.get("resize_height", 0)
images = list()
i = 0
for k in keys_in_order:
images.append(_load_and_resize(entries[k].pop(), resize_width, resize_height, mode))
i += 1
if (i % 100) == 0:
print("Loaded image {}/{}".format(i, len(entries)))
if len(images) == 0:
images.append(DreamImage.empty(512, 512, "RGB").resize(resize_width, resize_height).convert(mode))
return (DreamImage.join_to_tensor_data(images),)
class DreamImageSequenceInputWithDefaultFallback:
NODE_NAME = "Image Sequence Loader"
ICON = "💾"
@classmethod
def INPUT_TYPES(cls):
return {
"required": SharedTypes.frame_counter | {
"directory_path": ("STRING", {"default": '', "multiline": False}),
"pattern": ("STRING", {"default": '*', "multiline": False}),
"indexing": (["numeric", "alphabetic order"],)
},
"optional": {
"default_image": ("IMAGE", {"default": None})
}
}
CATEGORY = NodeCategories.IMAGE_ANIMATION
RETURN_TYPES = ("IMAGE", "STRING")
RETURN_NAMES = ("image", "frame_name")
FUNCTION = "result"
@classmethod
def IS_CHANGED(cls, *values):
return ALWAYS_CHANGED_FLAG
def result(self, frame_counter, directory_path, pattern, indexing, **other):
default_image = other.get("default_image", None)
entries = list_images_in_directory(directory_path, pattern, indexing == "alphabetic order")
entry = entries.get(frame_counter.current_frame, None)
if not entry:
return (default_image, "")
else:
image_names = [os.path.basename(file_path) for file_path in entry]
images = map(lambda f: DreamImage(file_path=f), entry)
return (DreamImage.join_to_tensor_data(images), image_names[0])
-116
View File
@@ -1,116 +0,0 @@
# -*- coding: utf-8 -*-
import json
import os
import folder_paths as comfy_paths
from PIL.PngImagePlugin import PngInfo
from .categories import NodeCategories
from .shared import hashed_as_strings, DreamImageProcessor, DreamImage, \
list_images_in_directory, DreamConfig
from .dreamtypes import SharedTypes, FrameCounter, AnimationSequence, LogEntry
CONFIG = DreamConfig()
def _save_png(pil_image, filepath, embed_info, prompt, extra_pnginfo):
info = PngInfo()
if extra_pnginfo is not None:
for item in extra_pnginfo:
info.add_text(item, json.dumps(extra_pnginfo[item]))
if prompt is not None:
info.add_text("prompt", json.dumps(prompt))
if embed_info:
pil_image.save(filepath, pnginfo=info, optimize=True)
else:
pil_image.save(filepath, optimize=True)
def _save_jpg(pil_image, filepath, quality):
pil_image.save(filepath, quality=quality, optimize=True)
class DreamImageSequenceOutput:
NODE_NAME = "Image Sequence Saver"
ICON = "💾"
@classmethod
def INPUT_TYPES(cls):
return {
"required": SharedTypes.frame_counter | {
"image": ("IMAGE",),
"directory_path": ("STRING", {"default": comfy_paths.output_directory, "multiline": False}),
"prefix": ("STRING", {"default": 'frame', "multiline": False}),
"digits": ("INT", {"default": 5}),
"at_end": (["stop output", "raise error", "keep going"],),
"filetype": (['png with embedded workflow', "png", 'jpg'],),
},
"hidden": {
"prompt": "PROMPT",
"extra_pnginfo": "EXTRA_PNGINFO"
},
}
CATEGORY = NodeCategories.IMAGE_ANIMATION
RETURN_TYPES = (AnimationSequence.ID, LogEntry.ID)
OUTPUT_NODE = True
RETURN_NAMES = ("sequence", "log_entry")
FUNCTION = "save"
@classmethod
def IS_CHANGED(cls, *values):
return hashed_as_strings(*values)
def _get_new_filename(self, current_frame, prefix, digits, filetype):
return prefix + "_" + str(current_frame).zfill(digits) + "." + filetype.split(" ")[0]
def _save_single_image(self, dream_image: DreamImage, batch_counter, frame_counter: FrameCounter,
directory_path,
prefix, digits, filetype, prompt, extra_pnginfo, at_end, logger):
if at_end == "stop output" and frame_counter.is_after_last_frame:
logger("Reached end of animation - not saving output!")
return ()
if at_end == "raise error" and frame_counter.is_after_last_frame:
logger("Reached end of animation - raising error to stop processing!")
raise Exception("Reached end of animation!")
filename = self._get_new_filename(frame_counter.current_frame, prefix, digits, filetype)
if batch_counter >= 0:
filepath = os.path.join(directory_path, "batch_" + (str(batch_counter).zfill(4)), filename)
else:
filepath = os.path.join(directory_path, filename)
save_dir = os.path.dirname(filepath)
if not os.path.isdir(save_dir):
os.makedirs(save_dir)
if filetype.startswith("png"):
dream_image.save_png(filepath, filetype == 'png with embedded workflow', prompt, extra_pnginfo)
elif filetype == "jpg":
dream_image.save_jpg(filepath, int(CONFIG.get("encoding.jpeg_quality", 95)))
logger("Saved {} in {}".format(filename, os.path.abspath(save_dir)))
return ()
def _generate_animation_sequence(self, filetype, directory_path, frame_counter):
if filetype.startswith("png"):
pattern = "*.png"
else:
pattern = "*.jpg"
frames = list_images_in_directory(directory_path, pattern, False)
return AnimationSequence(frame_counter, frames)
def save(self, image, **args):
log_texts = list()
logger = lambda s: log_texts.append(s)
if not args.get("directory_path", ""):
args["directory_path"] = comfy_paths.output_directory
args["logger"] = logger
proc = DreamImageProcessor(image, **args)
proc.process(self._save_single_image)
frame_counter: FrameCounter = args["frame_counter"]
log_entry = LogEntry([])
for text in log_texts:
log_entry = log_entry.add(text)
if frame_counter.is_final_frame:
return (self._generate_animation_sequence(args["filetype"], args["directory_path"],
frame_counter), log_entry)
else:
return (AnimationSequence(frame_counter), log_entry)
+1 -13
View File
@@ -1,6 +1,5 @@
from .categories import NodeCategories
from .dreamtypes import PartialPrompt
from .shared import hashed_as_strings
from .core.partial_prompt import PartialPrompt
class RandomPromptScheduleGenerator:
@@ -29,10 +28,6 @@ class RandomPromptScheduleGenerator:
RETURN_NAMES = ("prompt_schedule", "positive_prompt_schedule", "negative_prompt_schedule")
FUNCTION = "result"
@classmethod
def IS_CHANGED(cls, *values):
return hashed_as_strings(*values)
def result(self, partial_prompt: PartialPrompt, fixed_partial_prompt, total_frames, prompt_interval, seed,
num_positive, num_negative, adjustment, adjustment_reference, clamp):
text = list()
@@ -83,9 +78,6 @@ class DreamWeightedPromptBuilder:
RETURN_NAMES = ("partial_prompt",)
FUNCTION = "result"
@classmethod
def IS_CHANGED(cls, *values):
return hashed_as_strings(*values)
def result(self, added_prompt, weight, **args):
input = args.get("partial_prompt", PartialPrompt())
@@ -113,10 +105,6 @@ class DreamPromptFinalizer:
RETURN_NAMES = ("positive", "negative")
FUNCTION = "result"
@classmethod
def IS_CHANGED(cls, *values):
return hashed_as_strings(*values)
def result(self, partial_prompt: PartialPrompt, adjustment, adjustment_reference, clamp):
if adjustment == "raw" or partial_prompt.is_empty():
return partial_prompt.finalize(clamp)
+15
View File
@@ -0,0 +1,15 @@
[project]
name = "comfyui-dream-video-batches"
description = "Provide utilities for batch based video generation workflows (s.a. AnimateDiff and Stable Video Diffusion)."
version = "1.2.0"
license = { file = "LICENSE" }
dependencies = ["imageio", "pilgram", "scipy", "numpy<2.0,>=1.18", "torchvision", "evalidate"]
[project.urls]
Repository = "https://github.com/alt-key-project/comfyui-dream-video-batches"
# Used by Comfy Registry https://comfyregistry.org
[tool.comfy]
PublisherId = "altkeyproject"
DisplayName = "comfyui-dream-video-batches"
Icon = ""
+1 -1
View File
@@ -1,6 +1,6 @@
imageio
pilgram
scipy
numpy<1.24>=1.18
numpy<2.0.0,>=1.18
torchvision
evalidate
-81
View File
@@ -1,29 +1,15 @@
# -*- coding: utf-8 -*-
import json
import os
import random
from typing import Dict, Tuple, List
import folder_paths as comfy_paths
import glob
import numpy
import torch
NODE_FILE = os.path.abspath(__file__)
DREAM_NODES_SOURCE_ROOT = os.path.dirname(NODE_FILE)
TEMP_PATH = os.path.join(os.path.abspath(comfy_paths.temp_directory), "Dream_Anim")
_config_data = None
def pick_random_by_weight(data: List[Tuple[float, object]], rng: random.Random):
total_weight = sum(map(lambda item: item[0], data))
r = rng.random()
@@ -34,21 +20,6 @@ def pick_random_by_weight(data: List[Tuple[float, object]], rng: random.Random):
return data[0][1]
class DreamMask:
def __init__(self, tensor_image=None, pil_image=None):
if pil_image:
self.pil_image = pil_image
else:
self.pil_image = convertTensorImageToPIL(tensor_image)
if self.pil_image.mode != "L":
self.pil_image = self.pil_image.convert("L")
def create_tensor_image(self):
return torch.from_numpy(numpy.array(self.pil_image).astype(numpy.float32) / 255.0)
def list_files_in_directory(directory_path: str, pattern: str, alphabetic_index: bool,
endings=('.jpeg', '.jpg', '.png', '.tiff', '.gif', '.bmp', '.webp')) -> Dict[int, List[str]]:
if not os.path.isdir(directory_path):
@@ -97,55 +68,3 @@ def list_images_in_directory(directory_path: str, pattern: str, alphabetic_index
('.jpeg', '.jpg', '.png', '.tiff', '.gif', '.bmp', '.webp'))
#
#
# class MpegEncoderUtility:
# def __init__(self, video_path: str, bit_rate_factor: float, width: int, height: int, files: List[str],
# fps: float, encoding_threads: int, codec_name, max_b_frame):
# import mpegCoder
# self._files = files
# self._logger = get_logger()
# self._enc = mpegCoder.MpegEncoder()
# bit_rate = self._calculate_bit_rate(width, height, fps, bit_rate_factor)
# self._logger.info("Bitrate " + str(bit_rate))
# self._enc.setParameter(
# videoPath=video_path, codecName=codec_name,
# nthread=encoding_threads, bitRate=bit_rate, width=width, height=height, widthSrc=width,
# heightSrc=height,
# GOPSize=len(files), maxBframe=max_b_frame, frameRate=self._fps_to_tuple(fps))
#
# def _calculate_bit_rate(self, width: int, height: int, fps: float, bit_rate_factor: float):
# bits_per_pixel_base = 0.5
# return round(max(10, float(width * height * fps * bits_per_pixel_base * bit_rate_factor * 0.001)))
#
# def encode(self):
# if not self._enc.FFmpegSetup():
# raise Exception("Failed to setup MPEG Encoder - check parameters!")
# try:
# t = time.time()
#
# for filepath in self._files:
# self._logger.debug("Encoding frame {}", filepath)
# image = DreamImage.from_file(filepath).convert("RGB")
# self._enc.EncodeFrame(image.numpy_array())
# self._enc.FFmpegClose()
# self._logger.info("Completed video encoding of {n} frames in {t} seconds", n=len(self._files),
# t=round(time.time() - t))
# finally:
# self._enc.clear()
#
# def _fps_to_tuple(self, fps: float):
# def _is_almost_int(f: float):
# return abs(f - int(f)) < 0.001
#
# a = fps
# b = 1
# while not _is_almost_int(a) and b < 100:
# a /= 10
# b *= 10
# a = round(a)
# b = round(b)
# self._logger.info("Video specified as {fps} fps - encoder framerate {a}/{b}", fps=fps, a=a, b=b)
# return (a, b)
-12
View File
@@ -52,10 +52,6 @@ class DVB_FadeToBlack:
RETURN_NAMES = ("frames",)
FUNCTION = "result"
@classmethod
def IS_CHANGED(cls, *values):
return ALWAYS_CHANGED_FLAG
def result(self, frames: FrameSet, fade_seconds: float):
assert isinstance(frames, FrameSet)
gc_comfyui()
@@ -83,10 +79,6 @@ class DVB_FadeFromBlack:
RETURN_NAMES = ("frames",)
FUNCTION = "result"
@classmethod
def IS_CHANGED(cls, *values):
return ALWAYS_CHANGED_FLAG
def result(self, frames: FrameSet, fade_seconds: float):
assert isinstance(frames, FrameSet)
gc_comfyui()
@@ -115,10 +107,6 @@ class DVB_BlendedTransition:
RETURN_NAMES = ("frames",)
FUNCTION = "result"
@classmethod
def IS_CHANGED(cls, *values):
return ALWAYS_CHANGED_FLAG
def result(self, frames_first: FrameSet, frames_after: FrameSet, fade_seconds):
assert isinstance(frames_first, FrameSet)
assert isinstance(frames_after, FrameSet)
+12 -21
View File
@@ -7,6 +7,7 @@ import glob
from .categories import NodeCategories
from .core import *
from pathlib import Path
class DVB_IntToString:
@@ -89,8 +90,8 @@ class DVB_ForEachFilename:
FUNCTION = "exec"
@classmethod
def IS_CHANGED(cls, *values):
return ALWAYS_CHANGED_FLAG
def IS_CHANGED(cls, *values, **kwargs):
return float("NaN")
@classmethod
def INPUT_TYPES(cls):
@@ -104,14 +105,16 @@ class DVB_ForEachFilename:
def exec(self, id: str, directory: str, pattern: str):
directory = directory.strip('"')
if not os.path.isdir(directory):
on_node_error(DVB_ForEachFilename, "Not a directory: {}".format(directory))
statefile = os.path.normpath(os.path.abspath(os.path.join(directory, "foreach_" + id + ".json")))
p = Path(directory)
parts = p.parts
idx = next((i for i, part in enumerate(parts) if any(ch in part for ch in "*?[]")), len(parts))
base2 = Path(*parts[:idx])
foreach_filename = "foreach_" + id + ".json"
statefile = os.path.normpath(os.path.abspath(os.path.join(base2, foreach_filename)))
search_path = os.path.normpath(os.path.abspath(directory))
state = ForEachState(statefile)
files = list(glob.glob(os.path.join(search_path, pattern), recursive=False))
files = list(filter(lambda f: not f.endswith(foreach_filename), glob.glob(os.path.join(search_path, pattern), recursive=True)))
state.add_files_to_process(files)
next_path = state.pop()
@@ -140,10 +143,6 @@ class DVB_FrameSetDimensionsScaled:
}
}
@classmethod
def IS_CHANGED(cls, *values):
return ALWAYS_CHANGED_FLAG
def exec(self, frames :FrameSet, factor : float):
d = frames.image_dimensions
@@ -168,10 +167,6 @@ class DVB_ForEachCheckpoint:
}
}
@classmethod
def IS_CHANGED(cls, *values):
return ALWAYS_CHANGED_FLAG
def exec(self, image, foreach):
state = ForEachState(foreach)
next_file = state.pop()
@@ -237,10 +232,6 @@ class DVB_FrameDimensions:
RETURN_NAMES = ("width", "height", "final_width", "final_height")
FUNCTION = "result"
@classmethod
def IS_CHANGED(cls, *values):
return hashed_as_strings(*values)
def result(self, size, aspect_ratio, orientation, divisor, alignment, alignment_type):
ratio = tuple(map(int, aspect_ratio.split(":")))
final_width = int(size)
@@ -271,8 +262,8 @@ class DVB_TraceMalloc:
FUNCTION = "result"
@classmethod
def IS_CHANGED(cls, *values):
return ALWAYS_CHANGED_FLAG
def IS_CHANGED(cls, *values, **kwargs):
return float("NaN")
def result(self, string):
import tracemalloc, gc
-8
View File
@@ -23,10 +23,6 @@ class DVB_ImagesToFrameSet:
RETURN_NAMES = ("frames",)
FUNCTION = "work"
@classmethod
def IS_CHANGED(cls, *values):
return ALWAYS_CHANGED_FLAG
def work(self, images, first_frame_index, step, framerate_base, framerate_divisor):
fps = FrameRate(framerate_base, framerate_divisor)
indices = []
@@ -54,10 +50,6 @@ class DVB_UnwrapFrameSet:
"first_index", "indexed_length", "frame_count")
FUNCTION = "work"
@classmethod
def IS_CHANGED(cls, *values):
return ALWAYS_CHANGED_FLAG
def work(self, frames: FrameSet, gap_mode: str):
images = frames.images
if gap_mode == "FAIL" and frames.has_index_gaps():