mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-05-28 01:47:32 +08:00
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).
87 lines
2.7 KiB
Python
87 lines
2.7 KiB
Python
"""Tests for the image_dimensions service."""
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
from PIL import Image
|
|
|
|
from app.assets.services.image_dimensions import extract_image_dimensions
|
|
|
|
|
|
def _make_png(path: Path, size: tuple[int, int]) -> Path:
|
|
img = Image.new("RGB", size, color=(123, 45, 67))
|
|
img.save(path, format="PNG")
|
|
return path
|
|
|
|
|
|
def _make_jpeg(path: Path, size: tuple[int, int]) -> Path:
|
|
img = Image.new("RGB", size, color=(10, 20, 30))
|
|
img.save(path, format="JPEG", quality=80)
|
|
return path
|
|
|
|
|
|
class TestExtractImageDimensions:
|
|
def test_extracts_png_dimensions(self, tmp_path: Path):
|
|
f = _make_png(tmp_path / "rect.png", (320, 240))
|
|
|
|
result = extract_image_dimensions(str(f), mime_type="image/png")
|
|
|
|
assert result == {"kind": "image", "width": 320, "height": 240}
|
|
|
|
def test_extracts_jpeg_dimensions(self, tmp_path: Path):
|
|
f = _make_jpeg(tmp_path / "shot.jpg", (1920, 1080))
|
|
|
|
result = extract_image_dimensions(str(f), mime_type="image/jpeg")
|
|
|
|
assert result == {"kind": "image", "width": 1920, "height": 1080}
|
|
|
|
def test_works_when_mime_type_is_none(self, tmp_path: Path):
|
|
f = _make_png(tmp_path / "no_mime.png", (50, 100))
|
|
|
|
result = extract_image_dimensions(str(f), mime_type=None)
|
|
|
|
assert result == {"kind": "image", "width": 50, "height": 100}
|
|
|
|
def test_skips_non_image_mime_without_touching_file(self, tmp_path: Path):
|
|
# Path doesn't need to exist — non-image MIME short-circuits.
|
|
result = extract_image_dimensions(
|
|
str(tmp_path / "model.safetensors"),
|
|
mime_type="application/octet-stream",
|
|
)
|
|
|
|
assert result is None
|
|
|
|
@pytest.mark.parametrize(
|
|
"mime",
|
|
["application/json", "text/plain", "video/mp4", "audio/mpeg"],
|
|
)
|
|
def test_skips_all_non_image_mime_types(self, tmp_path: Path, mime: str):
|
|
f = tmp_path / "file.bin"
|
|
f.write_bytes(b"\x00\x01\x02")
|
|
|
|
assert extract_image_dimensions(str(f), mime_type=mime) is None
|
|
|
|
def test_returns_none_for_missing_file(self, tmp_path: Path):
|
|
result = extract_image_dimensions(
|
|
str(tmp_path / "does_not_exist.png"), mime_type="image/png"
|
|
)
|
|
|
|
assert result is None
|
|
|
|
def test_returns_none_for_corrupt_image(self, tmp_path: Path):
|
|
f = tmp_path / "corrupt.png"
|
|
f.write_bytes(b"not actually a png file")
|
|
|
|
result = extract_image_dimensions(str(f), mime_type="image/png")
|
|
|
|
assert result is None
|
|
|
|
def test_returns_none_for_empty_file(self, tmp_path: Path):
|
|
f = tmp_path / "empty.png"
|
|
f.write_bytes(b"")
|
|
|
|
result = extract_image_dimensions(str(f), mime_type="image/png")
|
|
|
|
assert result is None
|