2025-02-19 07:11:49 -05:00
import os
import av
import torch
import folder_paths
import json
2025-08-31 06:19:54 +03:00
from typing import Optional
from typing_extensions import override
2025-02-19 07:11:49 -05:00
from fractions import Fraction
2025-12-08 11:27:02 +02:00
from comfy_api . latest import ComfyExtension , io , ui , Input , InputImpl , Types
2025-04-29 02:58:00 -07:00
from comfy . cli_args import args
2025-02-19 07:11:49 -05:00
2025-08-31 06:19:54 +03:00
class SaveWEBM ( io . ComfyNode ) :
2025-02-19 07:11:49 -05:00
@classmethod
2025-08-31 06:19:54 +03:00
def define_schema ( cls ) :
return io . Schema (
node_id = " SaveWEBM " ,
add search aliases to all nodes (#12035)
* feat: Add search_aliases field to node schema
Adds `search_aliases` field to improve node discoverability. Users can define alternative search terms for nodes (e.g., "text concat" → StringConcatenate).
Changes:
- Add `search_aliases: list[str]` to V3 Schema
- Add `SEARCH_ALIASES` support for V1 nodes
- Include field in `/object_info` response
- Add aliases to high-priority core nodes
V1 usage:
```python
class MyNode:
SEARCH_ALIASES = ["alt name", "synonym"]
```
V3 usage:
```python
io.Schema(
node_id="MyNode",
search_aliases=["alt name", "synonym"],
...
)
```
## Related PRs
- Frontend: Comfy-Org/ComfyUI_frontend#XXXX (draft - merge after this)
- Docs: Comfy-Org/docs#XXXX (draft - merge after stable)
* Propagate search_aliases through V3 Schema.get_v1_info to NodeInfoV1
* feat: add SEARCH_ALIASES for core nodes (#12016)
Add search aliases to 22 core nodes in nodes.py to improve node discoverability:
- Checkpoint/model loaders: CheckpointLoader, DiffusersLoader
- Conditioning nodes: ConditioningAverage, ConditioningSetArea, ConditioningSetMask, ConditioningZeroOut
- Style nodes: StyleModelApply
- Image nodes: LoadImageMask, LoadImageOutput, ImageBatch, ImageInvert, ImagePadForOutpaint
- Latent nodes: LoadLatent, SaveLatent, LatentBlend, LatentComposite, LatentCrop, LatentFlip, LatentFromBatch, LatentUpscale, LatentUpscaleBy, RepeatLatentBatch
* feat: add SEARCH_ALIASES for image, mask, and string nodes (#12017)
Add search aliases to nodes in comfy_extras for better discoverability:
- nodes_mask.py: mask manipulation nodes
- nodes_images.py: image processing nodes
- nodes_post_processing.py: post-processing effect nodes
- nodes_string.py: string manipulation nodes
- nodes_compositing.py: compositing nodes
- nodes_morphology.py: morphological operation nodes
- nodes_latent.py: latent space nodes
Uses search_aliases parameter in io.Schema() for v3 nodes.
* feat: add SEARCH_ALIASES for audio and video nodes (#12018)
Add search aliases to audio and video nodes for better discoverability:
- nodes_audio.py: audio loading, saving, and processing nodes
- nodes_video.py: video loading and processing nodes
- nodes_wan.py: WAN model nodes
Uses search_aliases parameter in io.Schema() for v3 nodes.
* feat: add SEARCH_ALIASES for model and misc nodes (#12019)
Add search aliases to model-related and miscellaneous nodes:
- Model nodes: nodes_model_merging.py, nodes_model_advanced.py, nodes_lora_extract.py
- Sampler nodes: nodes_custom_sampler.py, nodes_align_your_steps.py
- Control nodes: nodes_controlnet.py, nodes_attention_multiply.py, nodes_hooks.py
- Training nodes: nodes_train.py, nodes_dataset.py
- Utility nodes: nodes_logic.py, nodes_canny.py, nodes_differential_diffusion.py
- Architecture-specific: nodes_sd3.py, nodes_pixart.py, nodes_lumina2.py, nodes_kandinsky5.py, nodes_hidream.py, nodes_fresca.py, nodes_hunyuan3d.py
- Media nodes: nodes_load_3d.py, nodes_webcam.py, nodes_preview_any.py, nodes_wanmove.py
Uses search_aliases parameter in io.Schema() for v3 nodes, SEARCH_ALIASES class attribute for legacy nodes.
2026-01-22 18:36:58 -08:00
search_aliases = [ " export webm " ] ,
2026-05-05 08:37:25 +08:00
display_name = " Save WEBM " ,
category = " video " ,
2025-08-31 06:19:54 +03:00
is_experimental = True ,
inputs = [
2026-06-04 02:05:48 +03:00
io . Image . Input ( " images " , tooltip = " RGBA images are saved with their alpha channel as transparency (vp9 codec only). " ) ,
2025-08-31 06:19:54 +03:00
io . String . Input ( " filename_prefix " , default = " ComfyUI " ) ,
io . Combo . Input ( " codec " , options = [ " vp9 " , " av1 " ] ) ,
io . Float . Input ( " fps " , default = 24.0 , min = 0.01 , max = 1000.0 , step = 0.01 ) ,
io . Float . Input ( " crf " , default = 32.0 , min = 0 , max = 63.0 , step = 1 , tooltip = " Higher crf means lower quality with a smaller file size, lower crf means higher quality higher filesize. " ) ,
] ,
hidden = [ io . Hidden . prompt , io . Hidden . extra_pnginfo ] ,
is_output_node = True ,
2026-06-22 10:15:28 +08:00
outputs = [ io . Image . Output ( display_name = " images " ) ]
2025-08-31 06:19:54 +03:00
)
2025-02-19 07:11:49 -05:00
2025-08-31 06:19:54 +03:00
@classmethod
def execute ( cls , images , codec , fps , filename_prefix , crf ) - > io . NodeOutput :
full_output_folder , filename , counter , subfolder , filename_prefix = folder_paths . get_save_image_path (
filename_prefix , folder_paths . get_output_directory ( ) , images [ 0 ] . shape [ 1 ] , images [ 0 ] . shape [ 0 ]
)
2025-02-19 07:11:49 -05:00
file = f " { filename } _ { counter : 05 } _.webm "
container = av . open ( os . path . join ( full_output_folder , file ) , mode = " w " )
2025-08-31 06:19:54 +03:00
if cls . hidden . prompt is not None :
container . metadata [ " prompt " ] = json . dumps ( cls . hidden . prompt )
2025-02-19 07:11:49 -05:00
2025-08-31 06:19:54 +03:00
if cls . hidden . extra_pnginfo is not None :
for x in cls . hidden . extra_pnginfo :
container . metadata [ x ] = json . dumps ( cls . hidden . extra_pnginfo [ x ] )
2025-02-19 07:11:49 -05:00
2026-06-04 02:05:48 +03:00
# Save transparency when the images carry an alpha channel (RGBA) and the codec supports it.
# vp9 -> yuva420p; other codecs have no usable alpha path, so the alpha is ignored.
save_alpha = images . shape [ - 1 ] == 4 and codec == " vp9 "
2025-04-22 22:57:17 +01:00
codec_map = { " vp9 " : " libvpx-vp9 " , " av1 " : " libsvtav1 " }
2025-02-19 07:11:49 -05:00
stream = container . add_stream ( codec_map [ codec ] , rate = Fraction ( round ( fps * 1000 ) , 1000 ) )
stream . width = images . shape [ - 2 ]
stream . height = images . shape [ - 3 ]
2026-06-04 02:05:48 +03:00
stream . pix_fmt = " yuva420p " if save_alpha else ( " yuv420p10le " if codec == " av1 " else " yuv420p " )
2025-02-19 07:11:49 -05:00
stream . bit_rate = 0
stream . options = { ' crf ' : str ( crf ) }
2025-04-22 22:57:17 +01:00
if codec == " av1 " :
stream . options [ " preset " ] = " 6 "
2025-02-19 07:11:49 -05:00
for frame in images :
2026-06-04 02:05:48 +03:00
if save_alpha :
frame = av . VideoFrame . from_ndarray ( torch . clamp ( frame [ . . . , : 4 ] * 255 , min = 0 , max = 255 ) . to ( device = torch . device ( " cpu " ) , dtype = torch . uint8 ) . numpy ( ) , format = " rgba " )
else :
frame = av . VideoFrame . from_ndarray ( torch . clamp ( frame [ . . . , : 3 ] * 255 , min = 0 , max = 255 ) . to ( device = torch . device ( " cpu " ) , dtype = torch . uint8 ) . numpy ( ) , format = " rgb24 " )
2025-02-19 07:11:49 -05:00
for packet in stream . encode ( frame ) :
container . mux ( packet )
2025-02-25 20:21:03 -05:00
container . mux ( stream . encode ( ) )
2025-02-19 07:11:49 -05:00
container . close ( )
2026-06-22 10:15:28 +08:00
return io . NodeOutput ( images , ui = ui . PreviewVideo ( [ ui . SavedResult ( file , subfolder , io . FolderType . output ) ] ) )
2025-02-19 07:11:49 -05:00
2025-08-31 06:19:54 +03:00
class SaveVideo ( io . ComfyNode ) :
@classmethod
def define_schema ( cls ) :
return io . Schema (
node_id = " SaveVideo " ,
add search aliases to all nodes (#12035)
* feat: Add search_aliases field to node schema
Adds `search_aliases` field to improve node discoverability. Users can define alternative search terms for nodes (e.g., "text concat" → StringConcatenate).
Changes:
- Add `search_aliases: list[str]` to V3 Schema
- Add `SEARCH_ALIASES` support for V1 nodes
- Include field in `/object_info` response
- Add aliases to high-priority core nodes
V1 usage:
```python
class MyNode:
SEARCH_ALIASES = ["alt name", "synonym"]
```
V3 usage:
```python
io.Schema(
node_id="MyNode",
search_aliases=["alt name", "synonym"],
...
)
```
## Related PRs
- Frontend: Comfy-Org/ComfyUI_frontend#XXXX (draft - merge after this)
- Docs: Comfy-Org/docs#XXXX (draft - merge after stable)
* Propagate search_aliases through V3 Schema.get_v1_info to NodeInfoV1
* feat: add SEARCH_ALIASES for core nodes (#12016)
Add search aliases to 22 core nodes in nodes.py to improve node discoverability:
- Checkpoint/model loaders: CheckpointLoader, DiffusersLoader
- Conditioning nodes: ConditioningAverage, ConditioningSetArea, ConditioningSetMask, ConditioningZeroOut
- Style nodes: StyleModelApply
- Image nodes: LoadImageMask, LoadImageOutput, ImageBatch, ImageInvert, ImagePadForOutpaint
- Latent nodes: LoadLatent, SaveLatent, LatentBlend, LatentComposite, LatentCrop, LatentFlip, LatentFromBatch, LatentUpscale, LatentUpscaleBy, RepeatLatentBatch
* feat: add SEARCH_ALIASES for image, mask, and string nodes (#12017)
Add search aliases to nodes in comfy_extras for better discoverability:
- nodes_mask.py: mask manipulation nodes
- nodes_images.py: image processing nodes
- nodes_post_processing.py: post-processing effect nodes
- nodes_string.py: string manipulation nodes
- nodes_compositing.py: compositing nodes
- nodes_morphology.py: morphological operation nodes
- nodes_latent.py: latent space nodes
Uses search_aliases parameter in io.Schema() for v3 nodes.
* feat: add SEARCH_ALIASES for audio and video nodes (#12018)
Add search aliases to audio and video nodes for better discoverability:
- nodes_audio.py: audio loading, saving, and processing nodes
- nodes_video.py: video loading and processing nodes
- nodes_wan.py: WAN model nodes
Uses search_aliases parameter in io.Schema() for v3 nodes.
* feat: add SEARCH_ALIASES for model and misc nodes (#12019)
Add search aliases to model-related and miscellaneous nodes:
- Model nodes: nodes_model_merging.py, nodes_model_advanced.py, nodes_lora_extract.py
- Sampler nodes: nodes_custom_sampler.py, nodes_align_your_steps.py
- Control nodes: nodes_controlnet.py, nodes_attention_multiply.py, nodes_hooks.py
- Training nodes: nodes_train.py, nodes_dataset.py
- Utility nodes: nodes_logic.py, nodes_canny.py, nodes_differential_diffusion.py
- Architecture-specific: nodes_sd3.py, nodes_pixart.py, nodes_lumina2.py, nodes_kandinsky5.py, nodes_hidream.py, nodes_fresca.py, nodes_hunyuan3d.py
- Media nodes: nodes_load_3d.py, nodes_webcam.py, nodes_preview_any.py, nodes_wanmove.py
Uses search_aliases parameter in io.Schema() for v3 nodes, SEARCH_ALIASES class attribute for legacy nodes.
2026-01-22 18:36:58 -08:00
search_aliases = [ " export video " ] ,
2025-08-31 06:19:54 +03:00
display_name = " Save Video " ,
2026-05-05 08:37:25 +08:00
category = " video " ,
feat: add essentials_category (#12357)
* feat: add essentials_category field to node schema
Amp-Thread-ID: https://ampcode.com/threads/T-019c2b25-cd90-7218-9071-03cb46b351b3
* feat: add ESSENTIALS_CATEGORY to core nodes
Marked nodes:
- Basic: LoadImage, SaveImage, LoadVideo, SaveVideo, Load3D, CLIPTextEncode
- Image Tools: ImageScale, ImageInvert, ImageBatch, ImageCrop, ImageRotate, ImageBlur
- Image Tools/Preprocessing: Canny
- Image Generation: LoraLoader
- Audio: LoadAudio, SaveAudio
Amp-Thread-ID: https://ampcode.com/threads/T-019c2b25-cd90-7218-9071-03cb46b351b3
* Add ESSENTIALS_CATEGORY to more nodes
- SaveGLB (Basic)
- GetVideoComponents (Video Tools)
- TencentTextToModelNode, TencentImageToModelNode (3D)
- RecraftRemoveBackgroundNode (Image Tools)
- KlingLipSyncAudioToVideoNode (Video Generation)
- OpenAIChatNode (Text Generation)
- StabilityTextToAudio (Audio)
Amp-Thread-ID: https://ampcode.com/threads/T-019c2b69-81c1-71c3-8096-450a39e20910
* fix: correct essentials category for Canny node
Amp-Thread-ID: https://ampcode.com/threads/T-019c7303-ab53-7341-be76-a5da1f7a657e
Co-authored-by: Amp <amp@ampcode.com>
* refactor: replace essentials_category string literals with constants
Amp-Thread-ID: https://ampcode.com/threads/T-019c7303-ab53-7341-be76-a5da1f7a657e
Co-authored-by: Amp <amp@ampcode.com>
* refactor: revert constants, use string literals for essentials_category
Amp-Thread-ID: https://ampcode.com/threads/T-019c7303-ab53-7341-be76-a5da1f7a657e
Co-authored-by: Amp <amp@ampcode.com>
* fix: update basics
---------
Co-authored-by: bymyself <cbyrne@comfy.org>
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: Jedrzej Kosinski <kosinkadink1@gmail.com>
2026-02-20 11:00:26 +08:00
essentials_category = " Basics " ,
2026-07-12 01:58:25 -03:00
description = " Saves the input videos to your ComfyUI output directory. " ,
2025-08-31 06:19:54 +03:00
inputs = [
io . Video . Input ( " video " , tooltip = " The video to save. " ) ,
io . String . Input ( " filename_prefix " , default = " video/ComfyUI " , tooltip = " The prefix for the file to save. This may include formatting information such as %d ate:yyyy-MM-dd % o r %E mpty Latent Image.width % to include values from nodes. " ) ,
2025-12-08 11:27:02 +02:00
io . Combo . Input ( " format " , options = Types . VideoContainer . as_input ( ) , default = " auto " , tooltip = " The format to save the video as. " ) ,
2026-07-31 21:27:48 -07:00
io . DynamicCombo . Input (
" codec " ,
options = [
io . DynamicCombo . Option ( " auto " , [ ] ) ,
io . DynamicCombo . Option (
" h264 " ,
[
io . DynamicCombo . Input (
" encoding " ,
display_name = " encoding mode " ,
options = [
io . DynamicCombo . Option ( " auto " , [ ] ) ,
io . DynamicCombo . Option (
" re-encode " ,
[ io . Float . Input ( " crf " , default = 23.0 , min = 0.0 , max = 51.0 , step = 1.0 , tooltip = " Lower values produce higher quality and larger files. " ) ] ,
) ,
] ,
optional = True ,
tooltip = " Automatic preserves compatible H.264 streams. Re-encode applies a custom CRF. " ,
) ,
] ,
) ,
] ,
tooltip = " The codec to use for the video. " ,
) ,
2025-08-31 06:19:54 +03:00
] ,
hidden = [ io . Hidden . prompt , io . Hidden . extra_pnginfo ] ,
is_output_node = True ,
2026-06-22 10:15:28 +08:00
outputs = [ io . Video . Output ( " video " ) ] ,
2025-08-31 06:19:54 +03:00
)
2025-04-29 02:58:00 -07:00
@classmethod
2026-07-31 21:27:48 -07:00
def execute ( cls , video : Input . Video , filename_prefix , format : str , codec : io . DynamicCombo . Type ) - > io . NodeOutput :
codec_name = codec [ " codec " ]
encoding = codec . get ( " encoding " ) or { }
2025-04-29 02:58:00 -07:00
width , height = video . get_dimensions ( )
full_output_folder , filename , counter , subfolder , filename_prefix = folder_paths . get_save_image_path (
filename_prefix ,
2025-08-31 06:19:54 +03:00
folder_paths . get_output_directory ( ) ,
2025-04-29 02:58:00 -07:00
width ,
height
)
saved_metadata = None
if not args . disable_metadata :
metadata = { }
2025-08-31 06:19:54 +03:00
if cls . hidden . extra_pnginfo is not None :
metadata . update ( cls . hidden . extra_pnginfo )
if cls . hidden . prompt is not None :
metadata [ " prompt " ] = cls . hidden . prompt
2025-04-29 02:58:00 -07:00
if len ( metadata ) > 0 :
saved_metadata = metadata
2025-12-08 11:27:02 +02:00
file = f " { filename } _ { counter : 05 } _. { Types . VideoContainer . get_extension ( format ) } "
2025-04-29 02:58:00 -07:00
video . save_to (
os . path . join ( full_output_folder , file ) ,
2025-12-08 11:27:02 +02:00
format = Types . VideoContainer ( format ) ,
2026-07-31 21:27:48 -07:00
codec = codec_name ,
metadata = saved_metadata ,
crf = encoding . get ( " crf " ) ,
2025-04-29 02:58:00 -07:00
)
2026-06-22 10:15:28 +08:00
return io . NodeOutput ( video , ui = ui . PreviewVideo ( [ ui . SavedResult ( file , subfolder , io . FolderType . output ) ] ) )
2025-04-29 02:58:00 -07:00
2025-08-31 06:19:54 +03:00
class CreateVideo ( io . ComfyNode ) :
2025-04-29 02:58:00 -07:00
@classmethod
2025-08-31 06:19:54 +03:00
def define_schema ( cls ) :
return io . Schema (
node_id = " CreateVideo " ,
add search aliases to all nodes (#12035)
* feat: Add search_aliases field to node schema
Adds `search_aliases` field to improve node discoverability. Users can define alternative search terms for nodes (e.g., "text concat" → StringConcatenate).
Changes:
- Add `search_aliases: list[str]` to V3 Schema
- Add `SEARCH_ALIASES` support for V1 nodes
- Include field in `/object_info` response
- Add aliases to high-priority core nodes
V1 usage:
```python
class MyNode:
SEARCH_ALIASES = ["alt name", "synonym"]
```
V3 usage:
```python
io.Schema(
node_id="MyNode",
search_aliases=["alt name", "synonym"],
...
)
```
## Related PRs
- Frontend: Comfy-Org/ComfyUI_frontend#XXXX (draft - merge after this)
- Docs: Comfy-Org/docs#XXXX (draft - merge after stable)
* Propagate search_aliases through V3 Schema.get_v1_info to NodeInfoV1
* feat: add SEARCH_ALIASES for core nodes (#12016)
Add search aliases to 22 core nodes in nodes.py to improve node discoverability:
- Checkpoint/model loaders: CheckpointLoader, DiffusersLoader
- Conditioning nodes: ConditioningAverage, ConditioningSetArea, ConditioningSetMask, ConditioningZeroOut
- Style nodes: StyleModelApply
- Image nodes: LoadImageMask, LoadImageOutput, ImageBatch, ImageInvert, ImagePadForOutpaint
- Latent nodes: LoadLatent, SaveLatent, LatentBlend, LatentComposite, LatentCrop, LatentFlip, LatentFromBatch, LatentUpscale, LatentUpscaleBy, RepeatLatentBatch
* feat: add SEARCH_ALIASES for image, mask, and string nodes (#12017)
Add search aliases to nodes in comfy_extras for better discoverability:
- nodes_mask.py: mask manipulation nodes
- nodes_images.py: image processing nodes
- nodes_post_processing.py: post-processing effect nodes
- nodes_string.py: string manipulation nodes
- nodes_compositing.py: compositing nodes
- nodes_morphology.py: morphological operation nodes
- nodes_latent.py: latent space nodes
Uses search_aliases parameter in io.Schema() for v3 nodes.
* feat: add SEARCH_ALIASES for audio and video nodes (#12018)
Add search aliases to audio and video nodes for better discoverability:
- nodes_audio.py: audio loading, saving, and processing nodes
- nodes_video.py: video loading and processing nodes
- nodes_wan.py: WAN model nodes
Uses search_aliases parameter in io.Schema() for v3 nodes.
* feat: add SEARCH_ALIASES for model and misc nodes (#12019)
Add search aliases to model-related and miscellaneous nodes:
- Model nodes: nodes_model_merging.py, nodes_model_advanced.py, nodes_lora_extract.py
- Sampler nodes: nodes_custom_sampler.py, nodes_align_your_steps.py
- Control nodes: nodes_controlnet.py, nodes_attention_multiply.py, nodes_hooks.py
- Training nodes: nodes_train.py, nodes_dataset.py
- Utility nodes: nodes_logic.py, nodes_canny.py, nodes_differential_diffusion.py
- Architecture-specific: nodes_sd3.py, nodes_pixart.py, nodes_lumina2.py, nodes_kandinsky5.py, nodes_hidream.py, nodes_fresca.py, nodes_hunyuan3d.py
- Media nodes: nodes_load_3d.py, nodes_webcam.py, nodes_preview_any.py, nodes_wanmove.py
Uses search_aliases parameter in io.Schema() for v3 nodes, SEARCH_ALIASES class attribute for legacy nodes.
2026-01-22 18:36:58 -08:00
search_aliases = [ " images to video " ] ,
2025-08-31 06:19:54 +03:00
display_name = " Create Video " ,
2026-05-05 08:37:25 +08:00
category = " video " ,
2026-05-12 23:42:31 -07:00
essentials_category = " Video Tools " ,
2025-08-31 06:19:54 +03:00
description = " Create a video from images. " ,
inputs = [
io . Image . Input ( " images " , tooltip = " The images to create a video from. " ) ,
io . Float . Input ( " fps " , default = 30.0 , min = 1.0 , max = 120.0 , step = 1.0 ) ,
io . Audio . Input ( " audio " , optional = True , tooltip = " The audio to add to the video. " ) ,
2026-06-13 16:05:25 +03:00
io . Int . Input (
" bit_depth " ,
min = 8 ,
max = 10 ,
default = 8 ,
step = 2 ,
tooltip = " Bit depth of the created video. 10-bit keeps smoother gradients with less "
" banding, but some players and downstream nodes may not support it. " ,
optional = True ,
display_mode = io . NumberDisplay . number ,
) ,
2025-08-31 06:19:54 +03:00
] ,
outputs = [
io . Video . Output ( ) ,
] ,
)
@classmethod
2026-06-13 16:05:25 +03:00
def execute (
cls , images : Input . Image , fps : float , audio : Optional [ Input . Audio ] = None , bit_depth : int = 8 ,
) - > io . NodeOutput :
2025-08-31 06:19:54 +03:00
return io . NodeOutput (
2026-06-13 16:05:25 +03:00
InputImpl . VideoFromComponents (
Types . VideoComponents ( images = images , audio = audio , frame_rate = Fraction ( fps ) ) ,
bit_depth = bit_depth ,
)
2025-08-31 06:19:54 +03:00
)
class GetVideoComponents ( io . ComfyNode ) :
@classmethod
def define_schema ( cls ) :
return io . Schema (
node_id = " GetVideoComponents " ,
add search aliases to all nodes (#12035)
* feat: Add search_aliases field to node schema
Adds `search_aliases` field to improve node discoverability. Users can define alternative search terms for nodes (e.g., "text concat" → StringConcatenate).
Changes:
- Add `search_aliases: list[str]` to V3 Schema
- Add `SEARCH_ALIASES` support for V1 nodes
- Include field in `/object_info` response
- Add aliases to high-priority core nodes
V1 usage:
```python
class MyNode:
SEARCH_ALIASES = ["alt name", "synonym"]
```
V3 usage:
```python
io.Schema(
node_id="MyNode",
search_aliases=["alt name", "synonym"],
...
)
```
## Related PRs
- Frontend: Comfy-Org/ComfyUI_frontend#XXXX (draft - merge after this)
- Docs: Comfy-Org/docs#XXXX (draft - merge after stable)
* Propagate search_aliases through V3 Schema.get_v1_info to NodeInfoV1
* feat: add SEARCH_ALIASES for core nodes (#12016)
Add search aliases to 22 core nodes in nodes.py to improve node discoverability:
- Checkpoint/model loaders: CheckpointLoader, DiffusersLoader
- Conditioning nodes: ConditioningAverage, ConditioningSetArea, ConditioningSetMask, ConditioningZeroOut
- Style nodes: StyleModelApply
- Image nodes: LoadImageMask, LoadImageOutput, ImageBatch, ImageInvert, ImagePadForOutpaint
- Latent nodes: LoadLatent, SaveLatent, LatentBlend, LatentComposite, LatentCrop, LatentFlip, LatentFromBatch, LatentUpscale, LatentUpscaleBy, RepeatLatentBatch
* feat: add SEARCH_ALIASES for image, mask, and string nodes (#12017)
Add search aliases to nodes in comfy_extras for better discoverability:
- nodes_mask.py: mask manipulation nodes
- nodes_images.py: image processing nodes
- nodes_post_processing.py: post-processing effect nodes
- nodes_string.py: string manipulation nodes
- nodes_compositing.py: compositing nodes
- nodes_morphology.py: morphological operation nodes
- nodes_latent.py: latent space nodes
Uses search_aliases parameter in io.Schema() for v3 nodes.
* feat: add SEARCH_ALIASES for audio and video nodes (#12018)
Add search aliases to audio and video nodes for better discoverability:
- nodes_audio.py: audio loading, saving, and processing nodes
- nodes_video.py: video loading and processing nodes
- nodes_wan.py: WAN model nodes
Uses search_aliases parameter in io.Schema() for v3 nodes.
* feat: add SEARCH_ALIASES for model and misc nodes (#12019)
Add search aliases to model-related and miscellaneous nodes:
- Model nodes: nodes_model_merging.py, nodes_model_advanced.py, nodes_lora_extract.py
- Sampler nodes: nodes_custom_sampler.py, nodes_align_your_steps.py
- Control nodes: nodes_controlnet.py, nodes_attention_multiply.py, nodes_hooks.py
- Training nodes: nodes_train.py, nodes_dataset.py
- Utility nodes: nodes_logic.py, nodes_canny.py, nodes_differential_diffusion.py
- Architecture-specific: nodes_sd3.py, nodes_pixart.py, nodes_lumina2.py, nodes_kandinsky5.py, nodes_hidream.py, nodes_fresca.py, nodes_hunyuan3d.py
- Media nodes: nodes_load_3d.py, nodes_webcam.py, nodes_preview_any.py, nodes_wanmove.py
Uses search_aliases parameter in io.Schema() for v3 nodes, SEARCH_ALIASES class attribute for legacy nodes.
2026-01-22 18:36:58 -08:00
search_aliases = [ " extract frames " , " split video " , " video to images " , " demux " ] ,
2025-08-31 06:19:54 +03:00
display_name = " Get Video Components " ,
2026-05-05 08:37:25 +08:00
category = " video " ,
2026-06-13 16:05:25 +03:00
description = " Extracts all components from a video: frames, audio, framerate, and bit depth. " ,
2025-08-31 06:19:54 +03:00
inputs = [
io . Video . Input ( " video " , tooltip = " The video to extract components from. " ) ,
] ,
outputs = [
io . Image . Output ( display_name = " images " ) ,
io . Audio . Output ( display_name = " audio " ) ,
io . Float . Output ( display_name = " fps " ) ,
2026-06-13 16:05:25 +03:00
io . Int . Output ( display_name = " bit_depth " ) ,
2025-08-31 06:19:54 +03:00
] ,
)
2025-04-29 02:58:00 -07:00
@classmethod
2025-12-08 11:27:02 +02:00
def execute ( cls , video : Input . Video ) - > io . NodeOutput :
2025-04-29 02:58:00 -07:00
components = video . get_components ( )
2026-06-13 16:05:25 +03:00
return io . NodeOutput ( components . images , components . audio , float ( components . frame_rate ) , video . get_bit_depth ( ) )
2025-04-29 02:58:00 -07:00
2025-12-08 11:27:02 +02:00
2025-08-31 06:19:54 +03:00
class LoadVideo ( io . ComfyNode ) :
2025-04-29 02:58:00 -07:00
@classmethod
2025-08-31 06:19:54 +03:00
def define_schema ( cls ) :
2025-04-29 02:58:00 -07:00
input_dir = folder_paths . get_input_directory ( )
files = [ f for f in os . listdir ( input_dir ) if os . path . isfile ( os . path . join ( input_dir , f ) ) ]
files = folder_paths . filter_files_content_types ( files , [ " video " ] )
2025-08-31 06:19:54 +03:00
return io . Schema (
node_id = " LoadVideo " ,
add search aliases to all nodes (#12035)
* feat: Add search_aliases field to node schema
Adds `search_aliases` field to improve node discoverability. Users can define alternative search terms for nodes (e.g., "text concat" → StringConcatenate).
Changes:
- Add `search_aliases: list[str]` to V3 Schema
- Add `SEARCH_ALIASES` support for V1 nodes
- Include field in `/object_info` response
- Add aliases to high-priority core nodes
V1 usage:
```python
class MyNode:
SEARCH_ALIASES = ["alt name", "synonym"]
```
V3 usage:
```python
io.Schema(
node_id="MyNode",
search_aliases=["alt name", "synonym"],
...
)
```
## Related PRs
- Frontend: Comfy-Org/ComfyUI_frontend#XXXX (draft - merge after this)
- Docs: Comfy-Org/docs#XXXX (draft - merge after stable)
* Propagate search_aliases through V3 Schema.get_v1_info to NodeInfoV1
* feat: add SEARCH_ALIASES for core nodes (#12016)
Add search aliases to 22 core nodes in nodes.py to improve node discoverability:
- Checkpoint/model loaders: CheckpointLoader, DiffusersLoader
- Conditioning nodes: ConditioningAverage, ConditioningSetArea, ConditioningSetMask, ConditioningZeroOut
- Style nodes: StyleModelApply
- Image nodes: LoadImageMask, LoadImageOutput, ImageBatch, ImageInvert, ImagePadForOutpaint
- Latent nodes: LoadLatent, SaveLatent, LatentBlend, LatentComposite, LatentCrop, LatentFlip, LatentFromBatch, LatentUpscale, LatentUpscaleBy, RepeatLatentBatch
* feat: add SEARCH_ALIASES for image, mask, and string nodes (#12017)
Add search aliases to nodes in comfy_extras for better discoverability:
- nodes_mask.py: mask manipulation nodes
- nodes_images.py: image processing nodes
- nodes_post_processing.py: post-processing effect nodes
- nodes_string.py: string manipulation nodes
- nodes_compositing.py: compositing nodes
- nodes_morphology.py: morphological operation nodes
- nodes_latent.py: latent space nodes
Uses search_aliases parameter in io.Schema() for v3 nodes.
* feat: add SEARCH_ALIASES for audio and video nodes (#12018)
Add search aliases to audio and video nodes for better discoverability:
- nodes_audio.py: audio loading, saving, and processing nodes
- nodes_video.py: video loading and processing nodes
- nodes_wan.py: WAN model nodes
Uses search_aliases parameter in io.Schema() for v3 nodes.
* feat: add SEARCH_ALIASES for model and misc nodes (#12019)
Add search aliases to model-related and miscellaneous nodes:
- Model nodes: nodes_model_merging.py, nodes_model_advanced.py, nodes_lora_extract.py
- Sampler nodes: nodes_custom_sampler.py, nodes_align_your_steps.py
- Control nodes: nodes_controlnet.py, nodes_attention_multiply.py, nodes_hooks.py
- Training nodes: nodes_train.py, nodes_dataset.py
- Utility nodes: nodes_logic.py, nodes_canny.py, nodes_differential_diffusion.py
- Architecture-specific: nodes_sd3.py, nodes_pixart.py, nodes_lumina2.py, nodes_kandinsky5.py, nodes_hidream.py, nodes_fresca.py, nodes_hunyuan3d.py
- Media nodes: nodes_load_3d.py, nodes_webcam.py, nodes_preview_any.py, nodes_wanmove.py
Uses search_aliases parameter in io.Schema() for v3 nodes, SEARCH_ALIASES class attribute for legacy nodes.
2026-01-22 18:36:58 -08:00
search_aliases = [ " import video " , " open video " , " video file " ] ,
2025-08-31 06:19:54 +03:00
display_name = " Load Video " ,
2026-05-05 08:37:25 +08:00
category = " video " ,
feat: add essentials_category (#12357)
* feat: add essentials_category field to node schema
Amp-Thread-ID: https://ampcode.com/threads/T-019c2b25-cd90-7218-9071-03cb46b351b3
* feat: add ESSENTIALS_CATEGORY to core nodes
Marked nodes:
- Basic: LoadImage, SaveImage, LoadVideo, SaveVideo, Load3D, CLIPTextEncode
- Image Tools: ImageScale, ImageInvert, ImageBatch, ImageCrop, ImageRotate, ImageBlur
- Image Tools/Preprocessing: Canny
- Image Generation: LoraLoader
- Audio: LoadAudio, SaveAudio
Amp-Thread-ID: https://ampcode.com/threads/T-019c2b25-cd90-7218-9071-03cb46b351b3
* Add ESSENTIALS_CATEGORY to more nodes
- SaveGLB (Basic)
- GetVideoComponents (Video Tools)
- TencentTextToModelNode, TencentImageToModelNode (3D)
- RecraftRemoveBackgroundNode (Image Tools)
- KlingLipSyncAudioToVideoNode (Video Generation)
- OpenAIChatNode (Text Generation)
- StabilityTextToAudio (Audio)
Amp-Thread-ID: https://ampcode.com/threads/T-019c2b69-81c1-71c3-8096-450a39e20910
* fix: correct essentials category for Canny node
Amp-Thread-ID: https://ampcode.com/threads/T-019c7303-ab53-7341-be76-a5da1f7a657e
Co-authored-by: Amp <amp@ampcode.com>
* refactor: replace essentials_category string literals with constants
Amp-Thread-ID: https://ampcode.com/threads/T-019c7303-ab53-7341-be76-a5da1f7a657e
Co-authored-by: Amp <amp@ampcode.com>
* refactor: revert constants, use string literals for essentials_category
Amp-Thread-ID: https://ampcode.com/threads/T-019c7303-ab53-7341-be76-a5da1f7a657e
Co-authored-by: Amp <amp@ampcode.com>
* fix: update basics
---------
Co-authored-by: bymyself <cbyrne@comfy.org>
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: Jedrzej Kosinski <kosinkadink1@gmail.com>
2026-02-20 11:00:26 +08:00
essentials_category = " Basics " ,
2025-08-31 06:19:54 +03:00
inputs = [
io . Combo . Input ( " file " , options = sorted ( files ) , upload = io . UploadType . video ) ,
] ,
outputs = [
io . Video . Output ( ) ,
] ,
)
2025-04-29 02:58:00 -07:00
2025-08-31 06:19:54 +03:00
@classmethod
def execute ( cls , file ) - > io . NodeOutput :
2025-04-29 02:58:00 -07:00
video_path = folder_paths . get_annotated_filepath ( file )
2025-12-08 11:27:02 +02:00
return io . NodeOutput ( InputImpl . VideoFromFile ( video_path ) )
2025-04-29 02:58:00 -07:00
@classmethod
2025-08-31 06:19:54 +03:00
def fingerprint_inputs ( s , file ) :
2025-04-29 02:58:00 -07:00
video_path = folder_paths . get_annotated_filepath ( file )
mod_time = os . path . getmtime ( video_path )
# Instead of hashing the file, we can just use the modification time to avoid
# rehashing large files.
return mod_time
@classmethod
2025-08-31 06:19:54 +03:00
def validate_inputs ( s , file ) :
2025-04-29 02:58:00 -07:00
if not folder_paths . exists_annotated_filepath ( file ) :
return " Invalid video file: {} " . format ( file )
return True
2025-02-19 07:11:49 -05:00
2026-02-10 14:42:21 -08:00
class VideoSlice ( io . ComfyNode ) :
@classmethod
def define_schema ( cls ) :
return io . Schema (
node_id = " Video Slice " ,
2026-06-20 08:01:28 +08:00
display_name = " Trim Video " ,
search_aliases = [ " trim video duration " , " skip first frames " , " frame load cap " , " start time " ] ,
2026-05-05 08:37:25 +08:00
category = " video " ,
2026-02-26 01:00:32 -08:00
essentials_category = " Video Tools " ,
2026-02-10 14:42:21 -08:00
inputs = [
io . Video . Input ( " video " ) ,
io . Float . Input (
" start_time " ,
default = 0.0 ,
max = 1e5 ,
min = - 1e5 ,
step = 0.001 ,
tooltip = " Start time in seconds " ,
) ,
io . Float . Input (
" duration " ,
default = 0.0 ,
min = 0.0 ,
step = 0.001 ,
tooltip = " Duration in seconds, or 0 for unlimited duration " ,
) ,
io . Boolean . Input (
" strict_duration " ,
default = False ,
tooltip = " If True, when the specified duration is not possible, an error will be raised. " ,
) ,
] ,
outputs = [
io . Video . Output ( ) ,
] ,
)
@classmethod
def execute ( cls , video : io . Video . Type , start_time : float , duration : float , strict_duration : bool ) - > io . NodeOutput :
trimmed = video . as_trimmed ( start_time , duration , strict_duration = strict_duration )
if trimmed is not None :
return io . NodeOutput ( trimmed )
raise ValueError (
f " Failed to slice video: \n Source duration: { video . get_duration ( ) } \n Start time: { start_time } \n Target duration: { duration } "
)
2025-07-29 19:17:22 -07:00
2025-08-31 06:19:54 +03:00
class VideoExtension ( ComfyExtension ) :
@override
async def get_node_list ( self ) - > list [ type [ io . ComfyNode ] ] :
return [
SaveWEBM ,
SaveVideo ,
CreateVideo ,
GetVideoComponents ,
LoadVideo ,
2026-02-10 14:42:21 -08:00
VideoSlice ,
2025-08-31 06:19:54 +03:00
]
async def comfy_entrypoint ( ) - > VideoExtension :
return VideoExtension ( )