Files

280 lines
9.4 KiB
JavaScript

const MAX_RECURSION = 64;
const DEFAULT_INPUT_SCAN = 4;
const IMAGE_EXTS = /* @__PURE__ */ new Set(["png", "jpg", "jpeg", "webp", "bmp", "gif", "tif", "tiff"]);
const VIDEO_EXTS = /* @__PURE__ */ new Set(["mp4", "mov", "webm", "mkv", "avi", "m4v", "flv", "wmv", "mpg", "mpeg"]);
const MAYBE_ANIMATED_IMAGE_EXTS = /* @__PURE__ */ new Set(["gif"]);
function normalizeLinkId(value) {
if (value == null || value === "") return null;
return String(value);
}
function toFiniteNumber(value) {
const num2 = Number(value);
return Number.isFinite(num2) ? num2 : void 0;
}
function normalizeGraphLink(link) {
if (!link) return null;
if (Array.isArray(link)) {
const [id, origin_id, origin_slot, target_id, target_slot] = link;
return {
id: toFiniteNumber(id),
origin_id: toFiniteNumber(origin_id),
origin_slot: toFiniteNumber(origin_slot),
target_id: toFiniteNumber(target_id),
target_slot: toFiniteNumber(target_slot)
};
}
if (typeof link === "object") return link;
return null;
}
function outputLinkIds(output) {
const links = Array.isArray(output?.links) ? output.links : output?.links != null ? [output.links] : output?.link != null ? [output.link] : [];
return links.map((value) => normalizeLinkId(value)).filter((value) => !!value);
}
function resolveInputLinkFallback(node, inputIndex) {
const linkId = normalizeLinkId(node?.inputs?.[inputIndex]?.link ?? null);
if (!linkId) return null;
const graph = node?.graph;
const stores = [graph?.links, graph?._links];
for (const store of stores) {
if (!store) continue;
const entry = Array.isArray(store) ? store[toFiniteNumber(linkId) ?? -1] : store[linkId] ?? store[String(toFiniteNumber(linkId) ?? linkId)];
const normalized = normalizeGraphLink(entry);
if (normalized) return normalized;
}
for (const upstream of graph?._nodes ?? []) {
for (let slot = 0; slot < (upstream.outputs?.length ?? 0); slot++) {
if (outputLinkIds(upstream.outputs?.[slot]).includes(linkId)) {
return {
id: toFiniteNumber(linkId),
origin_id: upstream.id,
origin_slot: slot,
target_id: node.id,
target_slot: inputIndex
};
}
}
}
return null;
}
function getInputLink(node, inputIndex = 0) {
try {
const direct = normalizeGraphLink(node?.getInputLink?.(inputIndex) ?? null);
if (direct) return direct;
} catch {
}
return resolveInputLinkFallback(node, inputIndex);
}
function getInputOriginSlot(node, inputIndex = 0, fallback = null) {
const link = getInputLink(node, inputIndex);
return link?.origin_slot ?? link?.originSlot ?? fallback;
}
function getInputCount(node, fallback = DEFAULT_INPUT_SCAN) {
const count = Array.isArray(node?.inputs) ? node.inputs.length : 0;
return count > 0 ? count : fallback;
}
function getUpstreamNode(node, inputIndex = 0) {
const link = getInputLink(node, inputIndex);
if (!link) return null;
const originId = link.origin_id ?? link.originId;
if (originId == null) return null;
return node?.graph?.getNodeById?.(originId) ?? null;
}
function getUpstreamNodes(node) {
const out = [];
for (let i = 0; i < getInputCount(node); i++) {
const upstream = getUpstreamNode(node, i);
if (upstream) out.push(upstream);
}
return out;
}
function isGraphTooLarge(graph, maxNodes = 140) {
const nodes = graph?._nodes ?? [];
return nodes.length > maxNodes;
}
function detectSource(node) {
function getFileExtLower(s) {
const m = String(s ?? "").toLowerCase().match(/\.([a-z0-9]+)(\s*\[[^\]]+\]\s*)?$/i);
return m ? m[1] : "";
}
function looksLikeMediaValue(v) {
if (v == null) return false;
if (typeof v !== "string") return false;
const ext2 = getFileExtLower(v);
return IMAGE_EXTS.has(ext2) || VIDEO_EXTS.has(ext2);
}
function pickMediaWidget(n) {
const preferred = ["image", "video", "path", "filepath", "file", "filename", "input_video", "input_image"];
for (const name of preferred) {
const w3 = n?.widgets?.find((x) => x?.name === name);
if (w3 && looksLikeMediaValue(w3.value)) return w3;
}
for (const w3 of n?.widgets ?? []) {
if (looksLikeMediaValue(w3?.value)) return w3;
}
return null;
}
const w2 = pickMediaWidget(node);
if (!w2) return null;
const ext = getFileExtLower(w2.value);
const kind = VIDEO_EXTS.has(ext) ? "video" : "image";
return { kind, value: w2.value, animated: kind === "image" && MAYBE_ANIMATED_IMAGE_EXTS.has(ext) };
}
function detectSourceUpstream(node, maxHops = MAX_RECURSION) {
const queue = [node];
const seen = /* @__PURE__ */ new Set();
let best = null;
let steps = 0;
let head = 0;
while (head < queue.length && steps < maxHops && queue.length < 4096) {
const cur = queue[head++];
if (!cur || seen.has(cur.id)) continue;
seen.add(cur.id);
steps++;
const source = detectSource(cur);
if (source?.kind === "video") return source;
if (source?.animated) best = source;
else if (!best && source) best = source;
for (const upstream of getUpstreamNodes(cur)) {
queue.push(upstream);
}
}
return best;
}
function findDependents(changedNode, predicate) {
const g = changedNode?.graph;
const nodes = g?._nodes ?? [];
const out = [];
for (const n of nodes) {
if (!n || !predicate(n)) continue;
if (isUpstreamOf(changedNode, n)) out.push(n);
}
return out;
}
function isUpstreamOf(candidate, node, max = MAX_RECURSION) {
const seen = /* @__PURE__ */ new Set();
const stack = [node];
let steps = 0;
while (stack.length && steps < max) {
const cur = stack.pop();
if (!cur || seen.has(cur.id)) continue;
seen.add(cur.id);
steps++;
if (cur.id === candidate.id) return true;
for (let i = 0; i < getInputCount(cur); i++) {
const up = getUpstreamNode(cur, i);
if (up) stack.push(up);
}
}
return false;
}
function w(node, name) {
return node?.widgets?.find((x) => x?.name === name) ?? null;
}
function widgetScalarValue(value, index = 0) {
let current = value;
while (Array.isArray(current) && current.length > 0) {
const resolvedIndex = Math.max(0, Math.min(current.length - 1, index));
current = current[resolvedIndex];
}
return current;
}
function num(node, name, fallback = 0, index = 0) {
const v = widgetScalarValue(w(node, name)?.value, index);
const n = parseFloat(v);
return Number.isFinite(n) ? n : fallback;
}
function str(node, name, fallback = "", index = 0) {
const v = widgetScalarValue(w(node, name)?.value, index);
return typeof v === "string" ? v : fallback;
}
function bool(node, name, fallback = false, index = 0) {
const v = widgetScalarValue(w(node, name)?.value, index);
if (typeof v === "boolean") return v;
if (typeof v === "number") return !!v;
if (typeof v === "string") return v.toLowerCase() === "true";
return fallback;
}
function wAny(node, names) {
for (const name of names) {
const found = w(node, name);
if (found) return found;
}
return null;
}
function numAny(node, names, fallback = 0, index = 0) {
const v = widgetScalarValue(wAny(node, names)?.value, index);
const n = parseFloat(v);
return Number.isFinite(n) ? n : fallback;
}
function strAny(node, names, fallback = "", index = 0) {
const v = widgetScalarValue(wAny(node, names)?.value, index);
return typeof v === "string" ? v : fallback;
}
function resolveConnectedString(node, inputName) {
const inputs = node?.inputs ?? [];
const slotIndex = inputs.findIndex((inp) => inp?.name === inputName);
if (slotIndex < 0) return null;
const link = inputs[slotIndex]?.link;
if (link == null) return null;
const linkData = node?.graph?.links?.[link];
if (!linkData) return null;
const upNode = node?.graph?.getNodeById?.(linkData.origin_id);
if (!upNode) return null;
const upWidget = (upNode?.widgets ?? []).find((w2) => typeof w2?.value === "string");
return upWidget ? String(upWidget.value) : null;
}
function boolAny(node, names, fallback = false, index = 0) {
const v = widgetScalarValue(wAny(node, names)?.value, index);
if (typeof v === "boolean") return v;
if (typeof v === "number") return !!v;
if (typeof v === "string") return v.toLowerCase() === "true";
return fallback;
}
function normalizeFilterName(filter) {
const value = String(filter || "bilinear").toLowerCase();
if (value === "nearest") return "nearest-exact";
if (value === "linear") return "bilinear";
if (value === "cubic") return "bicubic";
return value;
}
function imageLikeInputName(name) {
return /image|images|source|destination|background|foreground|layer|red|green|blue|channel|input/i.test(name);
}
function getPreferredInputIndexes(node) {
const indexes = [];
for (let index = 0; index < (node.inputs?.length ?? 0); index++) {
const slot = node.inputs?.[index];
const name = String(slot?.name ?? "");
const type = String(slot?.type ?? "");
if (/mask|bbox|box|region/i.test(name) || /mask|bbox|box/i.test(type)) continue;
if (slot?.link == null && !node.getInputLink?.(index)) continue;
if (imageLikeInputName(name) || /image|video/i.test(type)) indexes.push(index);
}
if (indexes.length > 0) return indexes;
return [0];
}
export {
bool,
boolAny,
detectSource,
detectSourceUpstream,
findDependents,
getInputCount,
getInputLink,
getInputOriginSlot,
getPreferredInputIndexes,
getUpstreamNode,
getUpstreamNodes,
imageLikeInputName,
isGraphTooLarge,
normalizeFilterName,
num,
numAny,
resolveConnectedString,
str,
strAny,
w,
wAny,
widgetScalarValue
};