Old project/workflow names and browser settings migrate on load; /funpack redirects to /fun. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016acRss69eH1235eRdFTZHY
76 lines
3.6 KiB
JavaScript
76 lines
3.6 KiB
JavaScript
// Queues runs on ComfyUI and reports where they are. A thin shell over run.js / session.js / pipeline.js:
|
|
// one real queue per run, each taken to a terminal state before the next starts. Nothing here knows what a
|
|
// scene is -- the caller says which scenes a run covers and what inputs it sends.
|
|
import { createRun, viewUrl, DONE, FAILED, CANCELLED } from "./run.js";
|
|
import { clientId, connect, queuedFor, finishedFor } from "./client.js";
|
|
import { wire, waitForTerminal } from "./session.js";
|
|
import { check } from "./pipeline.js";
|
|
import { wireReferences } from "./reference_wiring.js";
|
|
|
|
export function createGenerate({ pipeline }) {
|
|
const id = clientId();
|
|
const run = createRun({ clientId: id, connect });
|
|
|
|
const listeners = { say: [], warn: [], hold: [], release: [], adopt: [] };
|
|
let lastAdopt = null; // replayed to a late listener: it carries the only copy of which scenes a found run belongs to
|
|
const fire = (kind, arg) => {
|
|
if (kind === "adopt") lastAdopt = arg;
|
|
(listeners[kind] || []).forEach((fn) => { try { fn(arg); } catch { /* one listener must not stop the rest */ } });
|
|
};
|
|
// The hooks session.js calls on its page.transport. Nothing here draws.
|
|
const transport = {
|
|
generate: { setDisabled() {} }, draw() {},
|
|
say: (msg) => fire("say", msg), warn: (msg) => fire("warn", msg),
|
|
hold: (msg) => fire("hold", msg), release: (state) => fire("release", state),
|
|
};
|
|
|
|
// Which scenes/project the next run belongs to, read when it actually queues (and sent as extra_data so a
|
|
// reload mid-run can find it again). `inputs` is {slotId: {input: value}}.
|
|
let ranFor = null, ranForList = [], ranForProject = null, pendingInputs = null, pendingSlots = null;
|
|
|
|
const session = wire({
|
|
run, page: { transport }, check, id, queuedFor, finishedFor,
|
|
slots: () => pendingSlots || pipeline.slots(), // the pipeline as it was at the click, when the caller froze it
|
|
inputs: async () => pendingInputs || {},
|
|
extra: () => (ranForProject && ranFor
|
|
? { funui_scene_id: ranFor, funui_scene_ids: ranForList, funui_project_id: ranForProject } : null),
|
|
onAdopt: (sceneId, projectId, sceneIds) => {
|
|
ranFor = sceneId; ranForProject = projectId;
|
|
ranForList = (sceneIds && sceneIds.length) ? sceneIds : (sceneId ? [sceneId] : []);
|
|
fire("adopt", { sceneId, projectId, sceneIds: ranForList });
|
|
},
|
|
});
|
|
|
|
/** The slot+role pair a pipeline role points at, or null when nothing offers it. */
|
|
const slotForRole = (at) => {
|
|
for (const slot of pipeline.slots() || []) {
|
|
for (const role of slot.roles || []) if (role.at === at) return { slot, role };
|
|
}
|
|
return null;
|
|
};
|
|
|
|
return {
|
|
DONE, FAILED, CANCELLED, run, slotForRole, wireReferences,
|
|
viewUrl: (image) => viewUrl(image),
|
|
state: () => run.state,
|
|
subscribe: (fn) => run.subscribe(fn),
|
|
on(kind, fn) {
|
|
(listeners[kind] ||= []).push(fn);
|
|
if (kind === "adopt" && lastAdopt) { try { fn(lastAdopt); } catch { /* as above */ } }
|
|
return () => { listeners[kind] = (listeners[kind] || []).filter((f) => f !== fn); };
|
|
},
|
|
cancel: () => run.cancel(),
|
|
waitForTerminal: () => waitForTerminal(run),
|
|
ready: session.ready,
|
|
/** Queue one run; resolves true once it is on ComfyUI's queue. */
|
|
async generate({ sceneId, sceneIds, projectId, inputs, slots } = {}) {
|
|
pendingSlots = slots || null;
|
|
ranFor = sceneId || null;
|
|
ranForList = (sceneIds && sceneIds.length) ? sceneIds : (sceneId ? [sceneId] : []);
|
|
ranForProject = projectId || null;
|
|
pendingInputs = inputs || null;
|
|
return session.generate();
|
|
},
|
|
};
|
|
}
|