Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ComfyScript: Failed to load node VHS_VideoCombine because of AttributeError: 'list' object has no attribute 'removesuffix' #22

Open
JCBrouwer opened this issue Feb 10, 2024 · 10 comments
Labels
bug Something isn't working enhancement New feature or request pending Pending ComfyUI or custom node changes runtime

Comments

@JCBrouwer
Copy link

Hi there, thanks for this super cool repo, this is exactly what I've been waiting for to really dive into comfyUI!

I'm trying to get an animateDiff workflow working, but I'm running into an issue with the node which outputs the video/gif.

It's from https://github.com/Kosinkadink/ComfyUI-VideoHelperSuite

The workflow is here:
whileaf-lora-workflow.json

It gets transpiled to:

from comfy_script.runtime import *
load()
from comfy_script.runtime.nodes import *

with Workflow():
    model, clip, vae = CheckpointLoaderSimple('v1-5-pruned-emaonly.safetensors')
    model, clip = LoraLoader(model, clip, 'whileaf.safetensors', 1, 1)
    motion_model = ADELoadAnimateDiffModel('v3_sd15_mm.ckpt', None)
    m_models = ADEApplyAnimateDiffModel(motion_model, 0, 1, None, None, None, None, None)
    context_opts = ADELoopedUniformContextOptions(16, 1, 4, True, 'pyramid', False, 0, 1, None, None)
    settings = ADEAnimateDiffSamplingSettings(0, 'FreeNoise', 'comfy', 0, None, None, 0, False, None, None)
    model = ADEUseEvolvedSampling(model, 'autoselect', m_models, context_opts, settings)
    conditioning = CLIPTextEncode('whileaf whileaf creepy slime calligraphy graffiti runes', clip)
    conditioning2 = CLIPTextEncode('ugly', clip)
    latent = EmptyLatentImage(512, 512, 48)
    latent = KSampler(model, 820058635513319, 20, 8, 'dpmpp_2m_sde_gpu', 'karras', conditioning, conditioning2, latent, 1)
    image = VAEDecode(latent, vae)
    _ = VHSVideoCombine(image, 8, 0, 'AnimateDiff', 'image/gif', False, True, None, None)

but running it gives the following errors:

ComfyScript: Using ComfyUI from http://127.0.0.1:8188/
Nodes: 357
ComfyScript: Failed to load node VHS_VideoCombine
Traceback (most recent call last):
  File "/HUGE/Code/ComfyUI/custom_nodes/ComfyScript/src/comfy_script/runtime/nodes.py", line 19, in load
    fact.add_node(node_info)
  File "/HUGE/Code/ComfyUI/custom_nodes/ComfyScript/src/comfy_script/runtime/factory.py", line 392, in add_node
    inputs.append(f'{input_id}: {type_and_hint(type_info, name, optional, config.get("default"))[1]}')
  File "/HUGE/Code/ComfyUI/custom_nodes/ComfyScript/src/comfy_script/runtime/factory.py", line 264, in type_and_hint
    enum_c, t = astutil.to_str_enum(id, { _remove_extension(s): s for s in type_info }, '    ')
  File "/HUGE/Code/ComfyUI/custom_nodes/ComfyScript/src/comfy_script/runtime/factory.py", line 264, in <dictcomp>
    enum_c, t = astutil.to_str_enum(id, { _remove_extension(s): s for s in type_info }, '    ')
  File "/HUGE/Code/ComfyUI/custom_nodes/ComfyScript/src/comfy_script/runtime/factory.py", line 19, in _remove_extension
    path = path.removesuffix(ext)
AttributeError: 'list' object has no attribute 'removesuffix'
ComfyScript: Node already exists: {'input': {'required': {'frames_per_batch': ['INT', {'default': 16, 'min': 1, 'max': 128, 'step': 1}]}, 'hidden': {'prompt': 'PROMPT', 'unique_id': 'UNIQUE_ID'}}, 'output': ['VHS_BatchManager'], 'output_is_list': [False], 'output_name': ['VHS_BatchManager'], 'name': 'VHS_BatchManager', 'display_name': 'Batch Manager 🎥🅥🅗🅢', 'description': '', 'category': 'Video Helper Suite 🎥🅥🅗🅢', 'output_node': False}
ComfyScript: Failed to load node List of any [Crystools]
Traceback (most recent call last):
  File "/HUGE/Code/ComfyUI/custom_nodes/ComfyScript/src/comfy_script/runtime/nodes.py", line 19, in load
    fact.add_node(node_info)
  File "/HUGE/Code/ComfyUI/custom_nodes/ComfyScript/src/comfy_script/runtime/factory.py", line 420, in add_node
    output_types = [type_and_hint(type, name, output=True)[0] for type, name in output_with_name]
  File "/HUGE/Code/ComfyUI/custom_nodes/ComfyScript/src/comfy_script/runtime/factory.py", line 420, in <listcomp>
    output_types = [type_and_hint(type, name, output=True)[0] for type, name in output_with_name]
  File "/HUGE/Code/ComfyUI/custom_nodes/ComfyScript/src/comfy_script/runtime/factory.py", line 264, in type_and_hint
    enum_c, t = astutil.to_str_enum(id, { _remove_extension(s): s for s in type_info }, '    ')
  File "/HUGE/Code/ComfyUI/custom_nodes/ComfyScript/src/comfy_script/astutil.py", line 149, in to_str_enum
    return to_enum(id, dic, indent, StrEnum)
  File "/HUGE/Code/ComfyUI/custom_nodes/ComfyScript/src/comfy_script/astutil.py", line 142, in to_enum
    return c, enum_class(id, members)
  File "/home/hans/.conda/envs/hans/lib/python3.10/enum.py", line 387, in __call__
    return cls._create_(
  File "/home/hans/.conda/envs/hans/lib/python3.10/enum.py", line 518, in _create_
    enum_class = metacls.__new__(metacls, class_name, bases, classdict)
  File "/home/hans/.conda/envs/hans/lib/python3.10/enum.py", line 208, in __new__
    raise ValueError('Invalid enum member name: {0}'.format(
ValueError: Invalid enum member name: 
ComfyScript: Failed to queue prompt: <ClientResponse(http://127.0.0.1:8188/prompt) [400 Bad Request]>
<CIMultiDictProxy('Content-Type': 'application/json; charset=utf-8', 'Content-Length': '128', 'Date': 'Sat, 10 Feb 2024 13:57:54 GMT', 'Server': 'Python/3.10 aiohttp/3.9.3')>
<ClientResponse(http://127.0.0.1:8188/prompt) [400 Bad Request]>
<CIMultiDictProxy('Content-Type': 'application/json; charset=utf-8', 'Content-Length': '128', 'Date': 'Sat, 10 Feb 2024 13:57:54 GMT', 'Server': 'Python/3.10 aiohttp/3.9.3')>
{
  "error": {
    "type": "prompt_no_outputs",
    "message": "Prompt has no outputs",
    "details": "",
    "extra_info": {}
  },
  "node_errors": []
}
Traceback (most recent call last):
  File "/home/hans/.conda/envs/hans/lib/python3.10/runpy.py", line 196, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/home/hans/.conda/envs/hans/lib/python3.10/runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "/HUGE/Code/ComfyUI/custom_nodes/ComfyScript/whileaf-lora-workflow.py", line 18, in <module>
    VHSVideoCombine(image, 8, 0, 'AnimateDiff', 'image/gif', False, True, None, None)
TypeError: VHSVideoCombine() takes no arguments

Any idea what might be going on?

@Chaoses-Ib Chaoses-Ib added bug Something isn't working runtime enhancement New feature or request and removed bug Something isn't working labels Feb 10, 2024
@Chaoses-Ib
Copy link
Owner

VideoHelperSuite does some hacks to ComfyUI. VHS_VideoCombine has dynamic inputs depending on what format is chosen. This is not supported by official ComfyUI and ComfyScript doesn't support it either.

ComfyScript will now ignore any invalid format values:

ComfyScript: VHS_VideoCombine: Invalid enum value: ['video/av1-webm', [['pix_fmt', ['yuv420p10le', 'yuv420p']], ['crf', 'INT', {'default': 23, 'min': 0, 'max': 100, 'step': 1}], ['input_color_depth', ['8bit', '16bit']], ['save_metadata', 'BOOLEAN', {'default': True}]]]
ComfyScript: VHS_VideoCombine: Invalid enum value: ['video/h264-mp4', [['pix_fmt', ['yuv420p', 'yuv420p10le']], ['crf', 'INT', {'default': 19, 'min': 0, 'max': 100, 'step': 1}], ['save_metadata', 'BOOLEAN', {'default': True}]]]
ComfyScript: VHS_VideoCombine: Invalid enum value: ['video/h265-mp4', [['pix_fmt', ['yuv420p10le', 'yuv420p']], ['crf', 'INT', {'default': 22, 'min': 0, 'max': 100, 'step': 1}], ['save_metadata', 'BOOLEAN', {'default': True}]]]
ComfyScript: VHS_VideoCombine: Invalid enum value: ['video/nvenc_h264-mp4', [['pix_fmt', ['yuv420p', 'yuv420p10le']], ['bitrate', 'INT', {'default': 10, 'min': 1, 'max': 999, 'step': 1}], ['megabit', 'BOOLEAN', {'default': True}], ['save_metadata', 'BOOLEAN', {'default': True}]]]
ComfyScript: VHS_VideoCombine: Invalid enum value: ['video/nvenc_hevc-mp4', [['pix_fmt', ['yuv420p', 'yuv420p10le']], ['bitrate', 'INT', {'default': 10, 'min': 1, 'max': 999, 'step': 1}], ['megabit', 'BOOLEAN', {'default': True}], ['save_metadata', 'BOOLEAN', {'default': True}]]]
ComfyScript: VHS_VideoCombine: Invalid enum value: ['video/webm', [['crf', 'INT', {'default': 20, 'min': 0, 'max': 100, 'step': 1}], ['save_metadata', 'BOOLEAN', {'default': True}]]]

If you want to use these ignore values and dynamic inputs, you can directly use string literals and keyword arguments, for example:

VHSVideoCombine(image, 8, 0, 'AnimateDiff', 'video/av1-webm', False, True, None, None, pix_fmt='yuv420p10le', crf=23, input_color_depth='8bit', save_metadata=True)

@JCBrouwer
Copy link
Author

Great, thanks, I'll give it a try and report back here!

@Chaoses-Ib
Copy link
Owner

This issue could be kept open to help other people using VideoHelperSuite.

@Chaoses-Ib Chaoses-Ib reopened this Feb 10, 2024
@Chaoses-Ib Chaoses-Ib added the pending Pending ComfyUI or custom node changes label Feb 10, 2024
@Chaoses-Ib
Copy link
Owner

By the way, ComfyScript: Failed to load node List of any [Crystools] is now fixed.

@ambocclusion
Copy link

I cannot for the life of me figure out how to get output from the VHSVideoCombine node function loaded into my Python code, even as a PIL image. Is there a standard way of doing this?

@Chaoses-Ib
Copy link
Owner

Chaoses-Ib commented Feb 29, 2024

VHSVideoCombine is an output node, and every output node will return a dict. In this dict, ui marks the values to be passed to the web UI, and result marks the real result. For VHSVideoCombine, these values are:

previews = [
    {
        "filename": file,
        "subfolder": subfolder,
        "type": "output" if save_output else "temp",
        "format": format,
    }
]
return {"ui": {"gifs": previews}, "result": ((save_output, output_files),)}

If you are using virtual mode, you can get the dict via VHSVideoCombine().wait()._output. If you are using real mode, the returned value is just the dict. After having the dict, there are two ways to retrieve the non-text results:

  • Server API.

    VideoHelperSuite has added two API:

    @server.PromptServer.instance.routes.get("/viewvideo")
    async def view_video(request):
        ...
        resp.content_type = 'video/webm'
        ...
    @server.PromptServer.instance.routes.get("/getpath")
    async def get_path(request):
        ...

    You can make requests to these API yourself. Or write a class like ImageBatchResult in src/comfy_script/runtime/data/Images.py, which is the "standard" way to add result interop.

  • Directly reading files if using local ComfyUI.

    This is an ad hoc solution, but simpler than using the API.

I'll add some interop with VHSVideoCombine, but may need several days to find some spare time.

@ambocclusion
Copy link

ambocclusion commented Feb 29, 2024

I've attempted to get this code to work:

video = VHSVideoCombine(images, 8, 0, 'final_output', 'image/gif', False, True, None, None)

results = video.wait()

return [await results._output()]

but it throws the error AttributeError: 'NoneType' object has no attribute '_output'

@Chaoses-Ib
Copy link
Owner

Chaoses-Ib commented Feb 29, 2024

Sorry, I missed one line of code. It's now fixed and you can run git pull to update. Here is an example to read the GIF as a PIL image:

with Workflow():
    model, clip, vae = CheckpointLoaderSimple(Checkpoints.v1_5_pruned_emaonly)
    conditioning = CLIPTextEncode('whileaf whileaf creepy slime calligraphy graffiti runes', clip)
    conditioning2 = CLIPTextEncode('ugly', clip)
    latent = EmptyLatentImage(512, 512, 2)
    latent = KSampler(model, 0, 5, 8, 'dpmpp_2m_sde_gpu', 'karras', conditioning, conditioning2, latent, 1)
    image = VAEDecode(latent, vae)
    video = VHSVideoCombine(image, 8, 0, 'AnimateDiff', 'image/gif', False, True, None, None)

import PIL.Image
# {'gifs': [{'filename': 'AnimateDiff_00002.gif', 'subfolder': '', 'type': 'output', 'format': 'image/gif'}]}
filename = video.wait()._output['gifs'][0]['filename']
gif = PIL.Image.open(rf'D:/ComfyUI/output/{filename}')
display(gif)

# Second frame
gif.seek(1)
display(gif)

image

@Chaoses-Ib Chaoses-Ib added the bug Something isn't working label Feb 29, 2024
@Chaoses-Ib
Copy link
Owner

To display the animation instead of a frame in Jupyter Notebook:

from IPython.display import Image
Image(filename=rf'D:/ComfyUI/output/{filename}')

image

Another option is to use https://github.com/google/mediapy, which can also display videos but requires FFmpeg to be present in PATH.

@ambocclusion
Copy link

It works now, thank you so much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working enhancement New feature or request pending Pending ComfyUI or custom node changes runtime
Projects
None yet
Development

No branches or pull requests

3 participants