From 9cdacf6363fe941f8de4f79b3dfd59a592a110e7 Mon Sep 17 00:00:00 2001 From: Austin Mroz Date: Mon, 28 Jul 2025 01:44:19 -0500 Subject: [PATCH 1/3] Add test case for loading and saving of audio --- tests/audio.json | 313 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 313 insertions(+) create mode 100644 tests/audio.json diff --git a/tests/audio.json b/tests/audio.json new file mode 100644 index 0000000..cae3d76 --- /dev/null +++ b/tests/audio.json @@ -0,0 +1,313 @@ +{ + "id": "07b812b5-5037-4878-90bc-32d3a1f36619", + "revision": 0, + "last_node_id": 7, + "last_link_id": 5, + "nodes": [ + { + "id": 5, + "type": "VHS_VideoCombine", + "pos": [ + 732, + -23 + ], + "size": [ + 210, + 334 + ], + "flags": {}, + "order": 2, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 2 + }, + { + "name": "audio", + "shape": 7, + "type": "AUDIO", + "link": 3 + }, + { + "name": "meta_batch", + "shape": 7, + "type": "VHS_BatchManager", + "link": null + }, + { + "name": "vae", + "shape": 7, + "type": "VAE", + "link": null + } + ], + "outputs": [ + { + "name": "Filenames", + "type": "VHS_FILENAMES", + "links": null + } + ], + "properties": { + "Node name for S&R": "VHS_VideoCombine" + }, + "widgets_values": { + "frame_rate": 8, + "loop_count": 0, + "filename_prefix": "AnimateDiff", + "format": "video/webm", + "pix_fmt": "yuv420p", + "crf": 20, + "save_metadata": true, + "trim_to_audio": false, + "pingpong": false, + "save_output": false, + "videopreview": { + "hidden": false, + "paused": false, + "params": {} + } + } + }, + { + "id": 6, + "type": "VHS_VideoCombine", + "pos": [ + 503, + 363 + ], + "size": [ + 210, + 334 + ], + "flags": {}, + "order": 3, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 4 + }, + { + "name": "audio", + "shape": 7, + "type": "AUDIO", + "link": 5 + }, + { + "name": "meta_batch", + "shape": 7, + "type": "VHS_BatchManager", + "link": null + }, + { + "name": "vae", + "shape": 7, + "type": "VAE", + "link": null + } + ], + "outputs": [ + { + "name": "Filenames", + "type": "VHS_FILENAMES", + "links": null + } + ], + "properties": { + "Node name for S&R": "VHS_VideoCombine" + }, + "widgets_values": { + "frame_rate": 8, + "loop_count": 0, + "filename_prefix": "AnimateDiff", + "format": "video/h264-mp4", + "pix_fmt": "yuv420p", + "crf": 19, + "save_metadata": true, + "trim_to_audio": false, + "pingpong": false, + "save_output": false, + "videopreview": { + "hidden": false, + "paused": false, + "params": {} + } + } + }, + { + "id": 4, + "type": "VHS_LoadVideoPath", + "pos": [ + 29, + 16 + ], + "size": [ + 221.27618408203125, + 413.1552734375 + ], + "flags": {}, + "order": 0, + "mode": 0, + "inputs": [ + { + "name": "meta_batch", + "shape": 7, + "type": "VHS_BatchManager", + "link": null + }, + { + "name": "vae", + "shape": 7, + "type": "VAE", + "link": null + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 2, + 4 + ] + }, + { + "name": "frame_count", + "type": "INT", + "links": null + }, + { + "name": "audio", + "type": "AUDIO", + "links": [ + 3 + ] + }, + { + "name": "video_info", + "type": "VHS_VIDEOINFO", + "links": null + } + ], + "properties": { + "Node name for S&R": "VHS_LoadVideoPath" + }, + "widgets_values": { + "video": "input/bigbuckbunny.mp4", + "force_rate": 8, + "custom_width": 0, + "custom_height": 0, + "frame_load_cap": 30, + "skip_first_frames": 0, + "select_every_nth": 1, + "format": "AnimateDiff", + "videopreview": { + "hidden": false, + "paused": false, + "params": { + "filename": "input/bigbuckbunny.mp4", + "type": "path", + "format": "video/mp4", + "force_rate": 8, + "custom_width": 0, + "custom_height": 0, + "frame_load_cap": 30, + "skip_first_frames": 0, + "select_every_nth": 1 + } + } + } + }, + { + "id": 7, + "type": "VHS_LoadAudio", + "pos": [ + 83, + 564 + ], + "size": [ + 218.93820190429688, + 126 + ], + "flags": {}, + "order": 1, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "audio", + "type": "AUDIO", + "links": [ + 5 + ] + }, + { + "name": "duration", + "type": "FLOAT", + "links": null + } + ], + "properties": { + "Node name for S&R": "VHS_LoadAudio" + }, + "widgets_values": { + "audio_file": "input/bigbuckbunny.mp4", + "seek_seconds": 0, + "duration": 5 + } + } + ], + "links": [ + [ + 2, + 4, + 0, + 5, + 0, + "IMAGE" + ], + [ + 3, + 4, + 2, + 5, + 1, + "AUDIO" + ], + [ + 4, + 4, + 0, + 6, + 0, + "IMAGE" + ], + [ + 5, + 7, + 0, + 6, + 1, + "AUDIO" + ] + ], + "groups": [], + "config": {}, + "extra": { + "frontendVersion": "1.25.0", + "VHS_latentpreview": true, + "VHS_latentpreviewrate": 0, + "VHS_MetadataImage": true, + "VHS_KeepIntermediate": true + }, + "version": 0.4, + "tests": { + "5": [{"type": "audio", "key": "nb_read_packets", "value": "177"}], + "6": [{"type": "audio", "key": "nb_read_packets", "value": "176"}], + "length": 2 + } +} \ No newline at end of file From c017071cc3e098f36d6558732e1d5507830c89a4 Mon Sep 17 00:00:00 2001 From: Austin Mroz Date: Thu, 7 Aug 2025 08:40:00 -0500 Subject: [PATCH 2/3] Wan22 vae rate info, frontend fixes Added rate information for the Wan22 vae. This is only used for the 5b model and the only change is a slight reduction in the preview rate. Fixed being unable to quickly update multiple frontend widget values with tab. Fixed an incorrect default modulus for which forced inconvenient dimensions for the Load Image node. --- videohelpersuite/latent_preview.py | 2 +- web/js/VHS.core.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/videohelpersuite/latent_preview.py b/videohelpersuite/latent_preview.py index a9b8757..d30a97b 100644 --- a/videohelpersuite/latent_preview.py +++ b/videohelpersuite/latent_preview.py @@ -13,7 +13,7 @@ serv = server.PromptServer.instance from .utils import hook rates_table = {'Mochi': 24//6, 'LTXV': 24//8, 'HunyuanVideo': 24//4, - 'Cosmos1CV8x8x8': 24//8, 'Wan21': 16//4} + 'Cosmos1CV8x8x8': 24//8, 'Wan21': 16//4, 'Wan22': 24//4} class WrappedPreviewer(latent_preview.LatentPreviewer): def __init__(self, previewer, rate=8): diff --git a/web/js/VHS.core.js b/web/js/VHS.core.js index 5e78170..82a2e9f 100755 --- a/web/js/VHS.core.js +++ b/web/js/VHS.core.js @@ -1702,7 +1702,7 @@ function mouseAnnotated(event, [x, y], node) { break } } - if (node.widgets[++i]?.type == "BOOLEAN") {//restrict to annotatedNUmbers + if (node.widgets[++i]?.type == "VHS.ANNOTATED") {//restrict to annotatedNUmbers node.widgets[i]?.mouse(event, [x, y+24], node) } } @@ -2175,7 +2175,7 @@ app.registerExtension({ return } const s = this.options.step - let sh = this.options.mod ?? 1 + let sh = this.options.mod ?? 0 this.value = Math.round((v - sh) / s) * s + sh }, config: inputData, From 7e9bea533605526bf5af5f2e71adea37d3869cbd Mon Sep 17 00:00:00 2001 From: Austin Mroz Date: Thu, 7 Aug 2025 08:45:50 -0500 Subject: [PATCH 3/3] Fix unclosed file handle The query video endpoint failed to container after completion. This has been fixed. Resolves #526 --- pyproject.toml | 2 +- videohelpersuite/server.py | 32 ++++++++++++++++---------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 937056b..ae4d60b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "comfyui-videohelpersuite" description = "Nodes related to video workflows" -version = "1.7.2" +version = "1.7.3" license = { file = "LICENSE" } dependencies = ["opencv-python", "imageio-ffmpeg"] diff --git a/videohelpersuite/server.py b/videohelpersuite/server.py index 4bda173..afb3140 100755 --- a/videohelpersuite/server.py +++ b/videohelpersuite/server.py @@ -149,24 +149,24 @@ async def query_video(request): else: source = {} try: - cont = av.open(filepath) - stream = cont.streams.video[0] - source['fps'] = float(stream.average_rate) - source['duration'] = float(cont.duration / av.time_base) + with av.open(filepath) as cont: + stream = cont.streams.video[0] + source['fps'] = float(stream.average_rate) + source['duration'] = float(cont.duration / av.time_base) - if stream.codec_context.name == 'vp9': - cc = av.Codec('libvpx-vp9', 'r').create() - else: - cc = stream - def fit(): - for packet in cont.demux(video=0): - yield from cc.decode(packet) - frame = next(fit()) + if stream.codec_context.name == 'vp9': + cc = av.Codec('libvpx-vp9', 'r').create() + else: + cc = stream + def fit(): + for packet in cont.demux(video=0): + yield from cc.decode(packet) + frame = next(fit()) - source['size'] = [frame.width, frame.height] - source['alpha'] = 'a' in frame.format.name - source['frames'] = stream.metadata.get('NUMBER_OF_FRAMES', round(source['duration'] * source['fps'])) - query_cache[filepath] = (os.stat(filepath).st_mtime, source) + source['size'] = [frame.width, frame.height] + source['alpha'] = 'a' in frame.format.name + source['frames'] = stream.metadata.get('NUMBER_OF_FRAMES', round(source['duration'] * source['fps'])) + query_cache[filepath] = (os.stat(filepath).st_mtime, source) except Exception: pass if not 'frames' in source: