Files
ausboss-ComfyUI-AusBoss/tests/video_preview.test.mjs
ausboss 0c404b37f3 Save Video: a saved gif or webp shows on the node
With format set to gif or webp the file was saved, but the node's stage
stayed empty and said "Execution finished without video metadata".

The node has shown a gif or webp in an <img> since the two formats were
added, because a <video> cannot play either. The run's result was still
searched with a list of video file endings only, so for these two nothing
was found and the <img> was never filled. The list of picture endings now
sits beside that lookup in js/shared/video_preview.mjs, and both the
lookup and the choice of tag read it. A png is still not taken.

In the classic renderer a saved webp was also drawn a second time under
the stage. ComfyUI shows an animated webp in a widget of its own, neither
its video preview nor its picture preview, and only those two were stood
down. suppressCoreVideoPreview hides that widget as well. The node's
smallest height with a saved webp goes from 469 px back to 415 px.

Tests: the lookup finds a saved gif and webp and sends them to the <img>;
every row of the backend's VIDEO_FORMATS is read from its source and has
to be found and sent to the right tag; the animated widget is hidden on
arrival and when it is already on the node.
2026-10-06 17:35:31 -05:00

84 lines
3.2 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
import { FORMAT_WIDGET_SETS } from "../js/shared/save_video_formats.mjs";
import {
findVideoMetadata,
formatTime,
isStillImage,
mediaInfo,
splitMediaName,
} from "../js/shared/video_preview.mjs";
test("media names split across Windows and POSIX separators", () => {
assert.deepEqual(splitMediaName("clips/portrait.mp4"), {
filename: "portrait.mp4",
subfolder: "clips",
});
assert.deepEqual(splitMediaName("clips\\portrait.mp4 [input]"), {
filename: "portrait.mp4",
subfolder: "clips",
});
});
test("video metadata is found inside ComfyUI execution payloads", () => {
const meta = { filename: "render.mp4", subfolder: "AusBoss", type: "output" };
assert.equal(findVideoMetadata({ images: [meta], animated: [true] }), meta);
assert.equal(findVideoMetadata({ images: [{ filename: "still.png" }] }), null);
});
test("a saved gif or webp is found, and goes to the picture tag", () => {
// Save Video sends the same result whatever the format; only the file's
// ending differs. These two were never found, so the node said "Execution
// finished without video metadata" over a file it had just saved.
for (const ending of ["gif", "webp"]) {
const meta = { filename: `clip_00001_.${ending}`, subfolder: "clips", type: "output" };
assert.equal(findVideoMetadata({ images: [meta], animated: [true] }), meta);
assert.equal(isStillImage(meta.filename), true);
}
assert.equal(isStillImage("CLIP_00001_.WEBP"), true);
assert.equal(isStillImage("render.mp4"), false);
assert.equal(isStillImage("webp"), false);
assert.equal(isStillImage(undefined), false);
});
test("the node can show every format Save Video writes", () => {
// How those two went missing: the backend gained formats and the list the
// result is searched with did not follow. Each row of the backend's
// VIDEO_FORMATS is read from its source here. The file it writes has to
// be found, and a row with no video codec (written as a picture through
// Pillow) has to go to the <img>, since a <video> cannot play it.
const source = readFileSync(new URL("../nodes/_video_save_helpers.py", import.meta.url), "utf-8");
const rows = [...source.matchAll(/^\s*"([^"]+)":\s*VideoFormat\(\s*"([^"]*)",\s*"([^"]*)"/gm)];
assert.deepEqual(
rows.map(([, format]) => format).sort(),
Object.keys(FORMAT_WIDGET_SETS).sort(),
"VIDEO_FORMATS was not read whole from the backend's source",
);
for (const [, format, ending, codec] of rows) {
const meta = { filename: `clip_00001_.${ending}`, subfolder: "", type: "output" };
assert.equal(
findVideoMetadata({ images: [meta], animated: [true] }),
meta,
`${format}: a saved .${ending} is not found, so the node would show nothing`,
);
assert.equal(
isStillImage(meta.filename),
codec === "",
`${format}: a saved .${ending} goes to the wrong tag`,
);
}
});
test("video metadata readout is compact and complete", () => {
assert.equal(formatTime(63.25), "1:03.25");
assert.equal(mediaInfo({
width: 576,
height: 1024,
fps: 24,
frame_count: 188,
duration: 188 / 24,
}), "576×1024 · 24 fps · 188 frames · 0:07.8");
});