Skill Store 33

Comfy Metadata

Extract/analyze ComfyUI metadata embedded in output files - PNG tEXt, WebP EXIF, MP4/WebM contain…

by laurigates · MIT

The skill

Extract/analyze ComfyUI metadata embedded in output files - PNG tEXt, WebP EXIF, MP4/WebM container, .latent safetensors. Use when figuring out what prompt/settings produced a file, or comparing runs.

ComfyUI output metadata

Every image/video/audio file ComfyUI emits embeds the API-form prompt and the UI-form workflow that produced it. The encoding varies by file format and by which save-node wrote it (core vs. kijai vs. VHS). This skill is the canonical reference for where the data lives, how to read it back, and a small Python toolkit to do it reliably across every format on disk in this install.

When to Use This Skill

Use this skill when... Use instead when...
Figuring out what prompt/settings produced an existing output file Inspecting a live in-graph value during a run -> comfy-debug-preview
Scanning, organizing, or comparing a directory of outputs Auto-arranging the layout of a workflow JSON -> comfy-workflow-layout

Quick reference

Format Saved by Where the JSON lives Encoding
PNG still core SaveImage, kijai PNG path tEXt chunks (PIL Image.info["prompt"] and ["workflow"]) each value is a JSON string
Animated PNG core SaveAnimatedPNG iTXt chunks (same keys) each value is a JSON string
WebP still + animated core SaveAnimatedWEBP EXIF tags: 0x0110 (Model) holds "prompt:<json>", 0x010F (Make) holds "workflow:<json>", lower tags hold further extra_pnginfo keys one EXIF string per key, "key:json" prefix
MP4 native core SaveVideo Container metadata, separate keys: prompt, workflow, plus any extra each value is a JSON string
MP4 kijai (WanVideoWrapper_*.mp4) WanVideoWrapper.save_video Container metadata, single key comment one JSON object: {"prompt": "<json>", "workflow": "<json>"} (double-encoded)
WebM / Matroska core SaveVideo; kijai/MMAudio Container metadata: per-key (native) or single COMMENT (kijai) same patterns as MP4
FLAC / OGG / MP3 / WAV core SaveAudio Container metadata: prompt, extra_pnginfo* keys each value is a JSON string
.latent core SaveLatent safetensors header metadata: prompt, workflow each value is a JSON string

The library handles all of these uniformly. See REFERENCE.md for code anchors, exact byte-level details, and edge cases (the _create_webp_metadata EXIF tag walk, extra_pnginfo keys beyond workflow, the kijai double-encoded comment format, fp8-scaled safetensors metadata, etc.).

Toolkit

scripts/comfy_meta.py is a single self-contained Python file. It works as both a library and a CLI with four subcommands. It uses PIL (for PNG/WebP), PyAV (for MP4/WebM/audio), and safetensors (for .latent) — all already installed in .venv/.

Run via the project venv:

.venv/bin/python .claude/skills/comfy-metadata/scripts/comfy_meta.py <subcommand> ...

Library use (batch scripts)

For batch work over many files (renaming, indexing, clustering), import comfy_meta directly instead of calling the CLI per file — the sys.path setup and an extract() / summarize() loop are in references/library-use.md.

The UI workflow half is useful too

extract()["workflow"] carries data summarize() drops — save-node filename_prefix widgets, node titles, group names — useful as a classification signal; example in references/library-use.md.

extract — dump the embedded JSON

# Both prompt + workflow as one JSON object on stdout
.venv/bin/python .../comfy_meta.py extract output/WanVideoWrapper_I2V_00001.png

# Just one half (suitable for piping to jq)
.venv/bin/python .../comfy_meta.py extract -k prompt   path/to.png | jq .
.venv/bin/python .../comfy_meta.py extract -k workflow path/to.mp4  | jq '.nodes | length'

# Re-import a downloaded JPEG/MP4 back into ComfyUI by saving its workflow:
.venv/bin/python .../comfy_meta.py extract -k workflow some.mp4 > user/default/workflows/2026-05/recovered.json

summary — one-line, analysis-friendly settings

The summarizer walks the API-form prompt and pulls out the fields that actually matter for "what was different between run A and run B": model, text encoders, VAE, every sampler invocation (sampler/scheduler/steps/ cfg/denoise/seed), latent dims, num_frames, every LoRA + strength, and the positive/negative prompt text.

.venv/bin/python .../comfy_meta.py summary output/WanVideoWrapper_I2V_00001.mp4

Output is JSON; use -p for a human-readable two-column print instead.

scan — index a directory into JSONL

Walk a tree (recursively by default) and emit one JSON record per output file. Use this to build a queryable index of every render on disk.

.venv/bin/python .../comfy_meta.py scan output/ -o /tmp/runs.jsonl

# Then analyze with jq:
jq -r '[.path, .summary.steps, .summary.cfg, .summary.sampler] | @tsv' /tmp/runs.jsonl

# Group by sampler+steps+cfg to see what combinations were used:
jq -s 'group_by(.summary.sampler+"|"+(.summary.steps|tostring)+"|"+(.summary.cfg|tostring))
       | map({key: .[0].summary | "\(.sampler) steps=\(.steps) cfg=\(.cfg)", count: length})' \
  /tmp/runs.jsonl

Files without embedded metadata (e.g. phone photos in the same tree) get {"path": "...", "error": "no metadata"} so the index still covers everything.

diff — what changed between two runs

.venv/bin/python .../comfy_meta.py diff a.mp4 b.mp4

Prints a unified diff of the summarized settings. Useful when one of two near-identical workflows produced a better result and you want to see which knob actually moved.

What the summary captures

model:        UNETLoader.unet_name / CheckpointLoaderSimple.ckpt_name
              / WanVideoModelLoader.model / Image-Edit's diffusion path
text_encoders [list]: CLIPLoader / DualCLIPLoader / TripleCLIPLoader
              / LoadWanVideoT5TextEncoder / TextEncoderLoaderHiDream …
vae:          VAELoader.vae_name / WanVideoVAELoader.model_name
samplers [list]: every KSampler / KSamplerAdvanced / WanVideoSampler /
              WanVideoSamplerv2 / SamplerCustomAdvanced — each with
              {sampler, scheduler, steps, cfg, denoise, seed, start_step,
               end_step, add_noise} as found
latent_dims:  width × height from EmptyLatentImage / EmptySD3LatentImage /
              EmptyMochiLatentVideo / WanVideoEmptyEmbeds / etc.
num_frames:   from WanVideoEmptyEmbeds.num_frames / Empty*Video.length
loras [list]: every LoraLoader / LoraLoaderModelOnly / Power Lora Loader
              entry — {name, model_strength, clip_strength}
shift:        ModelSamplingAuraFlow / ModelSamplingSD3 shift values
positive [list], negative [list]: CLIPTextEncode-style text inputs, with
              the upstream node's title as a hint when present

Heuristic, not exhaustive — but covers ~95% of the workflows on this install. New node-types missing from the summarizer are still preserved in the raw prompt half of extract; add them to comfy_meta.py's SUMMARIZERS registry when a class becomes worth pulling out.

When the toolkit returns "no metadata"

A few cases that look like ComfyUI outputs but lack the JSON:

  • ComfyUI launched with --disable-metadata — the save nodes short-circuit before adding tEXt/EXIF/container tags.
  • Re-encoded with ffmpeg — ffmpeg -i in.mp4 -c copy out.mp4 does preserve container metadata; -c:v libx264 … (re-encode) typically drops it unless -map_metadata 0 is passed.
  • Re-saved through an image editor (Affinity, Photoshop, GIMP) — most strip tEXt chunks and rewrite EXIF.
  • Output from a frontend that bypasses save nodes (custom HTTP pipelines, Hugging Face Spaces wrapping ComfyUI, …).

For the second case, when you mv or cp files between dirs the metadata is fine — the OS-level operations preserve byte content. Only re-encoding strips it.

Privacy note

The embedded prompt JSON contains the full positive and negative text prompts, the exact seed, file paths to LoRAs/checkpoints/VAEs (which can leak local directory structure like models/loras/lgates/private_face_v1.safetensors), and sometimes authoring metadata in extra_pnginfo. Before sharing a ComfyUI output file publicly, decide whether you want to ship the metadata with it.

To strip metadata in-place (lossless):

  • PNG: oxipng --strip safe file.png (keeps colorspace, strips text)
  • WebP: re-encode with cwebp -metadata none or magick convert in.webp -strip out.webp
  • MP4/WebM: ffmpeg -i in.mp4 -c copy -map_metadata -1 out.mp4

Or set --disable-metadata on the ComfyUI server (in comfyui.service) if you want all future outputs to be metadata-free — but the toolkit becomes useless then.

Related skills

  • comfy-workflow-layout — once you've extracted a workflow with extract -k workflow, run it through scripts/layout_workflow.py to tidy node positions before importing.
  • comfy-cli — comfy node install-deps <workflow.json> consumes a workflow JSON file; pipe extract -k workflow straight into a temp file to install the missing custom nodes for an imported workflow.

Use this skill

Connect your agent to the Skill Store MCP server and ask for it by ref: laurigates/comfy-metadata-59323913024.

Download SKILL.md · Markdown version

Details

Publisher
laurigates
Category
Creative & Media › Image & design
License
MIT
Updated
2026-10-19

References

More from laurigates