Nodes/ComfyUI-MediaPreview/Media Preview(v1.0.0)
ComfyUI Node

Media Preview(v1.0.0)

A preview node you can splice into the middle of a graph

By Atsushi888·Created 7 months ago·Updated 5 months ago· 1
Media Preview(v1.0.0)
  • image
  • mask
  • media_path
  • image
  • mask
◄base_dir/workspace/ComfyUI/output/►
◄media►
◄looptrue►
◄media_path►

Core ComfyUI gives you PreviewImage, and it does one job: take an IMAGE tensor, write a PNG to temp/, show it. It can't show you a mask. It can't show you a video. It can't show you a file that already exists on disk - and the part that quietly matters, it ends a branch, so nothing can come after it.

MediaPreview covers all of that - image, mask, or a media file sitting in a folder, plus a URL if you feel like it. And it's a pass-through: image, mask and the resolved path come back out the other side, so you can leave it inline between your VAE Decode and your SaveImage and never rewire anything. That's the reason to reach for it over the preview node you already have.

How it works

The node walks a resolution ladder and takes the first rung that hits. From the source (node.py, run()):

  1. media_path, if wired and pointing at something real - a local file with a known extension, or an http(s) URL ending in one
  2. base_dir + media, joined and checked on disk
  3. image, if connected - saved as a PNG into ComfyUI's temp/ so the browser can fetch it
  4. mask, same treatment, written out greyscale
  5. failing all that, whatever it finds in base_dir

Displaying happens through a small server side-channel. The pack adds /media_preview/ping, /file, /list and /resolve routes, the backend remembers the last path each node resolved, and the bundled JS widget polls ping every 400 ms and renders whatever comes back. That's all "no browser reload needed" and "works with async outputs" actually mean: nothing has to signal the end of a run, it just keeps asking. It cleans up too - disconnect the input and polling stops and the <video> element is torn down.

One engine detail worth knowing: it's an output node with no IS_CHANGED, so it re-runs on every queue and regenerates that temp PNG each time.

The inputs and outputs that matter

base_dir is the folder to look in, defaulting to /workspace/ComfyUI/output/ - which tells you where this was written. media is the filename: a plain STRING in the Python schema, but the frontend rewrites the widget into a dropdown populated from base_dir, so in the UI you just pick a file. loop is whether the video player loops, and it applies live when you toggle it.

Optionally, media_path (STRING), image (IMAGE) and mask (MASK) are all forceInput - they exist to be wired, not typed.

Outputs are media_path (STRING, the path it resolved), image and mask. Connect image/mask and they're pure pass-throughs. Point it at an image file instead and it loads the file with PIL and emits the tensor - with mask set to that image's greyscale luminance, which makes it a scrappy little image→mask loader as a side effect. And when nothing resolves it doesn't error: you get a 64×64 all-black tensor. Harmless to preview, quietly wrong if you've spliced it into a pipeline and that black frame flows onward.

Installing it

There's no requirements.txt. It imports torch, PIL, numpy, aiohttp and folder_paths - all already in ComfyUI. Nothing to pip install, which is rare and pleasant.

cd ComfyUI/custom_nodes
git clone https://github.com/Atsushi888/ComfyUI-MediaPreview.git

Restart ComfyUI. Or search the Manager for ComfyUI-MediaPreview and let it do the same clone. It's v1.0.0, single maintainer, no KB panel, zero Reddit presence - the kind of pack where reading the source is worth ten minutes. The whole thing is ~400 lines of Python plus one JS file, and as our ecosystem notes have it, manual review is still the only vetting a custom node gets.

Where people get burned

The allow-list. /media_preview/file only serves paths under /workspace, /content or /mnt unless you say otherwise - RunPod/Colab roots. If your ComfyUI lives in ~/ComfyUI or on Windows, every preview hits 403 Access denied and the widget shows IMAGE LOAD ERROR. Set the env var in whatever launches ComfyUI, before the server starts, because it's read at import:

export MEDIA_PREVIEW_ALLOW_ROOTS=/workspace,/content,/mnt,/home/you/ComfyUI/output

The same list gates the file dropdown, so if your base_dir isn't under an allowed root the list comes back empty.

Containers the browser can't decode. .mkv and .avi are accepted by the pack and handed to the page, but an HTML5 <video> won't play either container - that's VIDEO LOAD ERROR, not a setting you missed. mp4 and webm preview fine.

Empty media with base_dir set. The last fallback is alphabetical, not newest - first video in the folder if there's one at all, otherwise first image. On an output/ full of ComfyUI_00001_.png that's your oldest frame, and any stray video wins outright. Leave the folder field empty unless you mean it.

Console noise and frontend churn. MP_DEBUG is hardcoded true, so each 400 ms poll prints a line in the ComfyUI terminal. And the node is built almost entirely out of DOM widgets - the part of the node system the Nodes 2.0 rewrite has been unkindest to. If the preview area renders blank or its size won't stick, switch back to the legacy canvas first.

Where it earns its place: you're iterating on a video model, there's an mp4 in output/, and you want to look at it without queueing anything.

Categoryutils

Inputs (6)

NameTypeDefaultDescription
base_dirSTRING/workspace/ComfyUI/output/—
mediaSTRING—
loopBOOLEANtrue—
media_pathoptSTRING—
imageoptIMAGE—
maskoptMASK—

Outputs (3)

NameTypeDescription
media_pathSTRING—
imageIMAGE—
maskMASK—