Files
MajoorWaldi-ComfyUI-Majoor-…/src/mjr/patch.ts
T

108 lines
3.3 KiB
TypeScript

import { fetchJSON } from "./api.js";
import { safeRel, joinRel } from "./utils.js";
export let PATH_WIDGETS = [];
let configPromise = null;
export async function loadConfig() {
if (configPromise) return configPromise;
configPromise = (async () => {
try {
const resp = await fetchJSON("/mjr_project/config");
const list = Array.isArray(resp?.path_widgets) ? resp.path_widgets : [];
PATH_WIDGETS = list.map((item) => String(item)).filter(Boolean);
return PATH_WIDGETS;
} catch (err) {
console.error("[mjr] Failed to load config:", err);
return PATH_WIDGETS;
} finally {
configPromise = null;
}
})();
return configPromise;
}
export function detectNodeMedia(node) {
const t = String(node?.comfyClass || node?.type || "").toLowerCase();
if (t.includes("video") || t.includes("mp4") || t.includes("gif")) return "videos";
if (t.includes("image") || t.includes("png") || t.includes("jpg") || t.includes("webp"))
return "images";
const widgets = node?.widgets || [];
const hasVideoHints = widgets.some((w) =>
["frame_rate", "fps", "duration", "frames_per_second"].includes(String(w?.name || ""))
);
return hasVideoHints ? "videos" : "images";
}
export function isSaveLikeNode(node) {
const t = String(node?.comfyClass || node?.type || "").toLowerCase();
if (!t.includes("save") && !t.includes("export") && !t.includes("combine")) return false;
const widgets = node?.widgets || [];
const hasFilename = widgets.some((w) => w?.name === "filename_prefix");
const hasPath = widgets.some((w) => w?.name && PATH_WIDGETS.includes(w.name));
return hasFilename || hasPath;
}
export function alreadyProjectPathed(value) {
if (!value) return false;
const s = String(value).replace(/\\/g, "/");
return s.includes("PROJECTS/") || s.includes("02_OUT/") || s.includes("00_META/");
}
export function patchSingleNode(node, relDir, filenamePrefix) {
if (!isSaveLikeNode(node)) return false;
const rel = safeRel(relDir);
const prefix = safeRel(filenamePrefix);
const widgets = node?.widgets || [];
const filenameWidget = widgets.find((w) => w?.name === "filename_prefix");
const pathWidget = widgets.find((w) => w?.name && PATH_WIDGETS.includes(w.name));
if (pathWidget && filenameWidget) {
pathWidget.value = rel;
filenameWidget.value = prefix;
return true;
}
if (filenameWidget) {
filenameWidget.value = prefix ? joinRel(rel, prefix) : rel;
return true;
}
if (pathWidget) {
pathWidget.value = rel;
return true;
}
return false;
}
export async function patchSaveNodes(app, relDir, filenamePrefix, targetMedia) {
await loadConfig();
const nodes = app?.graph?._nodes || [];
let patched = 0;
for (const node of nodes) {
const nm = detectNodeMedia(node);
if (targetMedia && nm && nm !== targetMedia) continue;
if (patchSingleNode(node, relDir, filenamePrefix)) {
patched += 1;
}
}
try {
app.graph.setDirtyCanvas(true, true);
} catch (_) {}
return patched;
}
export function stampGraphProjectSignature(app, state) {
if (!state?.projectId || !state?.projectFolder) return;
const g = app?.graph;
if (!g) return;
g.extra = g.extra || {};
g.extra.mjr_project = {
project_id: state.projectId,
project_folder: state.projectFolder,
template: state.pathTemplate || "",
updated_at: new Date().toISOString(),
};
}