Last-frame pictures go to ComfyUI's temp folder, not the media bin

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tw9fjYBYFyV8YG5YDfYPRb
This commit is contained in:
Claude
2026-11-02 13:46:50 +00:00
parent e2f14bd1a7
commit d8ce442c04
3 changed files with 26 additions and 3 deletions
+23 -1
View File
@@ -117,7 +117,7 @@ def listing() -> list[dict]:
if fresh:
with _LOCK:
_save_index(items)
out = [it for it in items if (config.MEDIA_DIR / it.get("filename", "")).is_file()]
out = [it for it in items if not it.get("scratch") and (config.MEDIA_DIR / it.get("filename", "")).is_file()] # scratch files are not the user's: never listed
out.sort(key=lambda i: i.get("added", 0), reverse=True)
return out
@@ -166,6 +166,28 @@ def save_upload(orig_name: str, data: bytes, limit=...) -> dict:
return entry
def save_scratch(orig_name: str, data: bytes, folder) -> dict:
"""A picture the app made for one run (a carried last frame): written into `folder` (ComfyUI's temp folder), answerable by id
like any media but never listed in the bin. It goes when the folder is cleared; entries whose file is gone are dropped here."""
if not data:
raise ValueError("that file is empty")
ext = Path(orig_name or "").suffix.lower()
if ext not in _IMAGE_EXT:
raise ValueError(f"{ext or 'that file type'} is not a picture")
mid = uuid.uuid4().hex[:12]
target = Path(folder) / (mid + ext)
target.parent.mkdir(parents=True, exist_ok=True)
target.write_bytes(data)
# `filename` is the absolute path: MEDIA_DIR / <absolute> is that path, so path_for, delete and the thumbnails need no second case.
entry = {"id": mid, "name": (Path(orig_name or "").name or target.name)[:MAX_NAME], "filename": str(target), "kind": "image",
"size": len(data), "added": time.time(), "scratch": True}
with _LOCK:
items = [it for it in _load_index() if not (it.get("scratch") and not Path(it.get("filename", "")).is_file())]
items.append(entry)
_save_index(items)
return entry
def path_for(mid: str) -> Path | None:
it = get(mid)
if not it:
+1 -1
View File
@@ -256,7 +256,7 @@ def last_frame(project, scene_id: str, render: dict | None = None, window: dict
try:
brightness, peak = files.last_frame(src, out, clip["in"], clip["dur"], clip["fps"], first, at_fraction)
with open(out, "rb") as fh:
entry = media.save_upload(f"last_frame_{projects.safe_part(scene_id)[:12]}.png", fh.read())
entry = media.save_scratch(f"last_frame_{projects.safe_part(scene_id)[:12]}.png", fh.read(), os.path.dirname(out)) # a working file in ComfyUI's temp folder, not an upload in the bin
finally:
try:
os.remove(out)
+2 -1
View File
@@ -232,13 +232,14 @@ def _post(port, path, body):
@needs_ffmpeg
def test_last_frame_route_saves_a_picture_to_the_bin_once_and_flags_a_fade_to_black(comfy, server):
def test_last_frame_route_saves_a_picture_to_the_temp_folder_once_and_flags_a_fade_to_black(comfy, server):
from core import media
moov_at_end(comfy.output / "a.mp4", 4.0)
proj = _project(comfy, frames=50, frames_mode="timeline")
serving._frames.clear()
status, a = _post(server, f"{BASE}/projects/{proj.id}/last-frame", {"scene_id": "s1"})
assert status == 200 and a["dark"] is False and media.path_for(a["media_id"])
assert media.path_for(a["media_id"]).parent == comfy.temp and a["media_id"] not in [m["id"] for m in media.listing()] # a working file in temp, never in the user's bin
assert _post(server, f"{BASE}/projects/{proj.id}/last-frame", {"scene_id": "s1"})[1]["media_id"] == a["media_id"] # same window: same file
subprocess.run(["ffmpeg", "-y", "-f", "lavfi", "-i", "color=c=black:s=160x120:d=2:r=25", "-c:v", "libx264", "-pix_fmt", "yuv420p", str(comfy.output / "black.mp4")], check=True, capture_output=True)
dark = _project(comfy, frames=50, frames_mode="timeline")