Three things, all about a run telling you what it is doing.
1. The second pass is now visible. ComfyUI's progress channel carries only
(value, max), so a chain sampling some scenes twice was one anonymous bar.
The sampler publishes a live label ("scene 2/3 · pass 2 of 2") through the
new run_phase module; /api/progress hands it to the editor, which appends it
to the readout, and pass 2 also announces itself on the console the moment it
starts rather than only in the after-the-fact run report.
run_phase keeps its state on `sys`, not in module globals: the writer reaches
it package-relative and the reader imports FunPack modules top-level by name,
so they can hold different module objects and the label would never arrive.
2. The progress bar total now counts both passes. It only ever counted the main
schedule, so with a second pass on it overflowed during scene 1 and jumped
backwards at scene 2, whose offset is a multiple of the per-scene stride.
3. context_windows was unusable. The knob offered "uniform_standard" etc.;
core's names are the other way round ("standard_uniform"), so every schedule
except "batched" raised ValueError out of the sampler and FAILED THE RENDER.
Core's names are the choices now, the old spellings are aliased so saved
projects keep working, and an unknown name is refused with a reason naming
what core accepts instead of crashing.
Same call had a second, quieter break: a keyword core's handler doesn't take
(latent_retain_index_list is absent on some builds, including this Mac's)
raised TypeError, which was reported as "ComfyUI core too old" — silently
switching off a feature core fully supports. Extras are dropped now, with a
note naming the one knob that stops working.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
60 lines
2.5 KiB
Python
60 lines
2.5 KiB
Python
"""What the sampler is doing RIGHT NOW, as a short label a UI can poll.
|
|
|
|
ComfyUI's progress channel carries only (value, max) — no text — so a chain that samples
|
|
several scenes, some of them twice, reads as one undifferentiated bar: you can watch the
|
|
number climb without knowing which scene it belongs to or whether the second pass has
|
|
started. The Chain Sampler publishes a label here as it moves through the run, and the
|
|
Movie Editor's ``/api/progress`` hands it to the UI alongside the numbers.
|
|
|
|
**The state lives on `sys`, not in this module's globals, and that is load-bearing.** The
|
|
writer (samplers.py) reaches this file as a package-relative import, so it gets
|
|
``ComfyUI-FunPack.run_phase``; the reader (movie_editor/backend/bridge.py) imports FunPack
|
|
modules top-level by name after putting the root on sys.path, so it would get a SECOND,
|
|
separate ``run_phase`` module object — two copies, two sets of globals, and a label that
|
|
never arrives. Hanging the dict off ``sys`` (unambiguously one object per process) makes
|
|
both import paths share it. Stateless helpers like `conditioning` do not care which copy
|
|
they are; anything holding mutable state across that boundary does.
|
|
|
|
Everything here is BEST-EFFORT and never affects sampling: it is a live readout, not state
|
|
anything is derived from. Nothing reads it back to make a decision, so a label left over
|
|
from a crashed run is cosmetic — the next run overwrites it and ``clear()`` empties it —
|
|
and it is not the kind of cross-request cache that would violate "the UI is the only source
|
|
of truth".
|
|
"""
|
|
import sys
|
|
|
|
_SYS_KEY = "_funpack_run_phase"
|
|
|
|
|
|
def _state() -> dict:
|
|
st = getattr(sys, _SYS_KEY, None)
|
|
if not isinstance(st, dict):
|
|
st = {"label": "", "seq": 0}
|
|
setattr(sys, _SYS_KEY, st)
|
|
return st
|
|
|
|
|
|
def set_phase(label: str) -> None:
|
|
"""Publish the current phase, e.g. "scene 2/3 · pass 2 of 2"."""
|
|
try:
|
|
st = _state()
|
|
st["label"] = str(label or "")
|
|
st["seq"] += 1
|
|
except Exception: # noqa: BLE001 — a readout must never break a render
|
|
pass
|
|
|
|
|
|
def clear() -> None:
|
|
"""Nothing is sampling. Call from a finally, so an interrupt doesn't leave a label up."""
|
|
set_phase("")
|
|
|
|
|
|
def current() -> dict:
|
|
"""{"label": str, "seq": int}. `seq` only ever increases, so a poller can tell "still
|
|
the same phase" from "this phase came round again"."""
|
|
try:
|
|
st = _state()
|
|
return {"label": st["label"], "seq": st["seq"]}
|
|
except Exception: # noqa: BLE001
|
|
return {"label": "", "seq": 0}
|