From 981798d6d9b846412495c2abafe8daddf649dcb1 Mon Sep 17 00:00:00 2001 From: digital-garbage <133010426+digital-garbage@users.noreply.github.com> Date: Mon, 3 Aug 2026 21:52:14 +0300 Subject: [PATCH] fix: the sampler-name list comes from ComfyUI, not a hardcoded eight MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Studio's KSampler mode passes the chosen name straight to comfy.samplers. sampler_object(), so any sampler this ComfyUI has always worked — but both frontends offered a hardcoded list of eight written before the rest existed. That silently hid 36 of them, including res_multistep, which is what ComfyUI's own MiniMax H3 templates use. Both now read sampler_name from the live KSampler node spec and fall back to the old list if that fetch fails. A saved name missing from the live list stays selectable instead of being swapped for the first option. Co-Authored-By: Claude Opus 5 --- movie_editor/frontend/sampler_panel.js | 29 ++++++++++++++++++++++++-- web/funpack_studio.js | 19 +++++++++++++++-- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/movie_editor/frontend/sampler_panel.js b/movie_editor/frontend/sampler_panel.js index 72a3a252..8d83dc34 100644 --- a/movie_editor/frontend/sampler_panel.js +++ b/movie_editor/frontend/sampler_panel.js @@ -13,7 +13,29 @@ const SAMPLER_TYPES = ["Hybrid Euler 2S", "Distilled Flow", "KSampler"]; const VELOCITY_BIAS_MODES = ["off", "capture", "apply", "capture_and_apply"]; const MOTION_PULSE_MODES = ["off", "balanced", "aggressive", "custom"]; - const KSAMPLER_NAMES = ["euler", "euler_ancestral", "dpm_2", "dpm_2_ancestral", "dpmpp_2m", "dpmpp_sde", "ddim", "uni_pc"]; + // Fallback only. The real list is whatever THIS ComfyUI has — read from the live + // KSampler node spec, because a hardcoded set silently hides samplers the backend can + // already run (it passes the name straight to comfy.samplers.sampler_object). That is + // how res_multistep — the one ComfyUI's own MiniMax H3 templates use — went missing. + const KSAMPLER_NAMES_FALLBACK = ["euler", "euler_ancestral", "dpm_2", "dpm_2_ancestral", "dpmpp_2m", "dpmpp_sde", "ddim", "uni_pc"]; + let _ksamplerNames = null; + let _ksamplerPending = null; + + function ksamplerNames(onLoaded) { + if (_ksamplerNames) return _ksamplerNames; + if (!_ksamplerPending && window.MovieEditorAPI?.nodeSpec) { + _ksamplerPending = window.MovieEditorAPI.nodeSpec("KSampler") + .then((spec) => { + const w = (spec?.inputs || []).find((i) => i.name === "sampler_name"); + if (w && (w.choices || []).length) { + _ksamplerNames = w.choices; + if (onLoaded) onLoaded(); + } + }) + .catch(() => {}); + } + return KSAMPLER_NAMES_FALLBACK; + } function defaultHybrid() { return { @@ -300,8 +322,11 @@ } else if (cfg.type === "KSampler") { sectionTag(container, "KSampler"); + // saveNow re-renders, which is how the list refreshes once the live names land. + const names = ksamplerNames(saveNow); + const cur = cfg.ksampler_name || "euler"; row(container, "sampler name", - selCtrl(KSAMPLER_NAMES, cfg.ksampler_name || "euler", dk + "-ks", + selCtrl(names.includes(cur) ? names : names.concat([cur]), cur, dk + "-ks", (v) => { cfg.ksampler_name = v; save(); })); } } diff --git a/web/funpack_studio.js b/web/funpack_studio.js index 0acc05ac..0f0e366c 100644 --- a/web/funpack_studio.js +++ b/web/funpack_studio.js @@ -14,7 +14,18 @@ const TABS = ["Session", "Shortcuts", "Split markers", "Refiner", "Advisor", "Lo const SAMPLER_TYPES = ["Hybrid Euler 2S", "Distilled Flow", "KSampler"]; const MOTION_PULSE_MODES = ["off", "balanced", "aggressive", "custom"]; const VELOCITY_BIAS_MODES = ["off", "capture", "apply", "capture_and_apply"]; -const KSAMPLER_NAMES = ["euler", "euler_ancestral", "dpm_2", "dpm_2_ancestral", "dpmpp_2m", "dpmpp_sde", "ddim", "uni_pc"]; +// Fallback only — the real list is whatever THIS ComfyUI has. The backend passes the name +// straight to comfy.samplers.sampler_object(), so a hardcoded set hides samplers it can +// already run (res_multistep, which ComfyUI's own MiniMax H3 templates use, was missing). +const KSAMPLER_NAMES_FALLBACK = ["euler", "euler_ancestral", "dpm_2", "dpm_2_ancestral", "dpmpp_2m", "dpmpp_sde", "ddim", "uni_pc"]; +let KSAMPLER_NAMES = KSAMPLER_NAMES_FALLBACK; +api.fetchApi("/object_info/KSampler") + .then((r) => r.json()) + .then((defs) => { + const names = defs?.KSampler?.input?.required?.sampler_name?.[0]; + if (Array.isArray(names) && names.length) KSAMPLER_NAMES = names; + }) + .catch(() => {}); let activePanel = null; let studioShortcutData = null; @@ -1435,7 +1446,11 @@ function openPanel(node) { } } else if (cfg.type === "KSampler") { body.append(sectionTitle("KSampler settings")); - const ksSelect = selectEl(KSAMPLER_NAMES, cfg.ksampler_name || "euler"); + // Keep a saved name that isn't in the live list (older/newer ComfyUI) selectable + // rather than silently swapping it for the first option. + const _ksCur = cfg.ksampler_name || "euler"; + const ksSelect = selectEl( + KSAMPLER_NAMES.includes(_ksCur) ? KSAMPLER_NAMES : KSAMPLER_NAMES.concat([_ksCur]), _ksCur); ksSelect.addEventListener("change", () => { cfg.ksampler_name = ksSelect.value; }); body.append(row("sampler name", ksSelect)); }