yingzhac's picture
Create static PinkFluffyBunny MiniMax H3 ComfyUI client
3429714 verified
Raw
History Blame Contribute Delete
2.92 kB
export const VIDEO_EXTENSIONS = new Set([".mp4", ".webm", ".mov", ".mkv"]);
export function alignFrameCount(durationSeconds, fps = 24) {
let requested = Math.max(5, Math.round(Number(durationSeconds) * fps));
while (requested % 17 !== 5) requested += 1;
return requested;
}
export function renderWorkflow(workflow, replacements) {
const replace = (value) => {
if (Array.isArray(value)) return value.map(replace);
if (value && typeof value === "object") {
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, replace(item)]));
}
if (typeof value !== "string") return value;
if (Object.prototype.hasOwnProperty.call(replacements, value)) return replacements[value];
let result = value;
for (const [token, replacement] of Object.entries(replacements)) {
result = result.replaceAll(token, String(replacement));
}
return result;
};
return replace(structuredClone(workflow));
}
export function findVideoDescriptor(entry) {
const candidates = [];
const visit = (value) => {
if (Array.isArray(value)) {
value.forEach(visit);
return;
}
if (!value || typeof value !== "object") return;
if (typeof value.filename === "string") candidates.push(value);
Object.values(value).forEach(visit);
};
visit(entry?.outputs ?? {});
const match = candidates.find((item) => {
const filename = item.filename.toLowerCase();
return [...VIDEO_EXTENSIONS].some((extension) => filename.endsWith(extension));
});
if (!match) return null;
return {
filename: match.filename,
subfolder: String(match.subfolder ?? ""),
type: String(match.type ?? "output"),
};
}
export function parseWorkflow(raw) {
if (!String(raw ?? "").trim()) throw new Error("Load or paste an API-format workflow first.");
let workflow;
try {
workflow = JSON.parse(raw);
} catch (error) {
throw new Error(`Workflow JSON is invalid: ${error.message}`);
}
if (!workflow || Array.isArray(workflow) || typeof workflow !== "object") {
throw new Error("Workflow JSON must be a non-empty object.");
}
if (Array.isArray(workflow.nodes)) {
throw new Error(
"This is a UI workflow. In ComfyUI, enable Developer mode and use Save (API format).",
);
}
return workflow;
}
export function buildTokens(values) {
const [width, height] = String(values.resolution).split("x").map(Number);
return {
"{{PROMPT}}": String(values.prompt ?? "").trim(),
"{{NEGATIVE_PROMPT}}": String(values.negativePrompt ?? "").trim(),
"{{LORA_NAME}}": values.loraName,
"{{LORA_STRENGTH}}": Number(values.loraStrength),
"{{WIDTH}}": width,
"{{HEIGHT}}": height,
"{{DURATION}}": Number(values.duration),
"{{LENGTH}}": alignFrameCount(values.duration),
"{{FPS}}": 24,
"{{STEPS}}": Number(values.steps),
"{{CFG}}": Number(values.cfg),
"{{SEED}}": Number(values.seed),
};
}