- Rename test to reflect that it only validates the in-memory BytesIO stream-copy
path; documents that the .mp4 file-path case (SaveVideo's default) drops alpha.
- Flush the libvpx-vp9 decoder with decode(None) in the helper.
- Rework BriaTransparentVideoBackground description to make it explicit that this
is currently the only way to persist the alpha channel to disk.
- Move 'Transparent' to the end of the dropdown and set explicit default='Black' so
newly-added nodes preserve prior behavior (review warning).
- Reword node + tooltip descriptions to be honest that Save Video drops the alpha
plane when its default .mp4 extension is used; direct users to the IMAGE+MASK
node for any workflow that needs to persist alpha to disk today.
- Update the dispatch comment with the same caveat.
Bria's /remove_background endpoint accepts background_color='Transparent'
when paired with an alpha-capable container/codec; pass output_container_and_codec=
'webm_vp9' in that case (otherwise mp4_h264, unchanged). The returned WebM
flows through ComfyUI's VIDEO type as a file reference, so Save Video with the
default auto/auto settings stream-copies it byte-for-byte with the alpha plane
intact (covered by new test_save_to_auto_preserves_vp9_alpha_via_stream_copy).
BriaTransparentVideoBackground is kept as-is because it remains the supported
path for the IMAGE+MASK workflow: Get Video Components currently cannot expose
alpha for VP9 streams (PyAV's default vp9 decoder drops the side alpha layer),
so the per-frame compositing workflow still needs the dedicated node that
decodes via libvpx-vp9 itself.
* feat(assets): extract image dimensions at ingest and emit on asset responses
Image assets now carry width/height under the existing `metadata` field on
asset responses, shaped as `{"kind": "image", "width": W, "height": H}`.
This lets consumers get original dimensions (e.g. for clients that render
server-side thumbnails and can't recover them from naturalWidth/Height)
without an extra round-trip.
Dimensions are written to AssetReference.system_metadata across three
ingest paths:
- Direct file ingest (upload, in-place registration): Pillow reads the
image header right after hashing, while the file is still in OS page
cache. Non-image MIME types are skipped without touching the file.
- From-hash registration: this path never reads the file bytes, so
dimensions are best-effort copied from any prior sibling reference of
the same asset that already carries kind=image metadata. Missing
siblings, non-image siblings, or absent dimension keys leave the new
reference's metadata unchanged.
- Scanner enrichment: extends the existing system_metadata write in
enrich_asset so scanner-registered images get the same treatment as
uploaded ones.
Existing system_metadata keys (e.g. safetensors fields written by the
enricher, download provenance) are preserved through merge. Existing
assets ingested before this change retain their current metadata — no
automatic backfill in this PR.
Tests cover image emission, non-image no-op, merge preservation, and the
from-hash sibling back-fill (including the no-sibling and non-image-sibling
cases).
* fix(assets): validate sibling dimensions before backfilling
Per CodeRabbit review on #13991: the previous loop accepted any sibling
with `kind == "image"` and copied whichever dimension keys happened to
be present, then returned. A partial sibling (kind set but missing or
invalid width/height) could persist incomplete metadata onto the new
reference even when a later sibling had valid dimensions.
Now we validate that the sibling has both width and height as positive
integers before adopting its dimensions, and continue scanning to the
next sibling otherwise.
* fix(assets): reject booleans in sibling dimension validation (use type-is)
Per CodeRabbit follow-up on #13991: bool is a subclass of int in Python,
so isinstance(True, int) is True. The previous strict-int gate would
have accepted width=True (truthy + > 0) as a valid dimension.
Realistic occurrence is low (extract_image_dimensions returns proper
ints, JSON doesn't serialize bools as numbers), but the validation gate
exists for defense-in-depth so it should be actually strict.
---------
Co-authored-by: guill <jacob.e.segal@gmail.com>
* mm: split off registration helper to doer and headroom calc
* pinned_memory: implement registration comfy side
Move away from Aimdo buffer registrations which seem fraught with
danger and do it comfy side. Just start with the basic move.
* pinned_memory: do registrations as portable memory
* pinned_memory: discard async errors on registration fail
Like the good ol days.
* pinned_memory: implement abs shortfall retry
If pinned registration happens to fail despite the previous budget
ensures, consider the allocation shortfall, ensure it again, and
try again. This allows comfy pins to interoperate with other software
that might be doing substantive pinning.
* fix (MultiGPU): prevent freeze on manual abort when using MultiGPU CFG Split
Problem:
Upon manual abort application hangs indefinitely.
`InterruptProcessingException` inherits from `BaseException` and bypasses MultiGPU's worker error handling block so thread dies silently, leaving the main thread waiting forever for `result_q.get()`
Fix:
Catch `comfy.model_management.InterruptProcessingException` instead of `Exception` so it's caught and passed back via `result_q` to unblock the main thread when manual abort signal fires.
* oops