Files
Trung0246-ComfyUI-0246/web/js/fixes.js
T
Trung0246 f469eb9e09 Stability fixes
Plus some minor features
2023-12-24 03:38:41 -08:00

367 lines
10 KiB
JavaScript

import { app } from "../../../scripts/app.js";
import * as lib0246 from "./utils.js";
export function monitor_value(obj, key) {
let originalValue = obj[key];
Object.defineProperty(obj, key, {
get: function () {
return originalValue;
},
set: function (newValue) {
console.log(`${key} changed from ${originalValue} to ${newValue}`);
if (window.debug_flag) {
console.trace();
// debugger;
}
originalValue = newValue;
},
enumerable: true,
configurable: true
});
}
export function monitor_array(obj, key) {
let originalArray = obj[key];
function createLoggingArray(array) {
let loggingArray = [];
array.forEach((value, index) => {
Object.defineProperty(loggingArray, index, {
get: function () {
return value;
},
set: function (newValue) {
console.log(`${key}[${index}] changed from ${value} to ${newValue}`);
if (window.debug_flag) {
console.trace();
// debugger;
}
value = newValue;
},
enumerable: true,
configurable: true
});
});
return loggingArray;
}
let loggingArray = createLoggingArray(originalArray);
Object.defineProperty(obj, key, {
get: function () {
return loggingArray;
},
set: function (newArray) {
console.log(`${key} changed from [${originalArray}] to [${newArray}]`);
if (window.debug_flag) {
console.trace();
// debugger;
}
originalArray = newArray.slice();
loggingArray = createLoggingArray(originalArray);
},
enumerable: true,
configurable: true
});
obj.___MARK___ = true;
let flag = false;
lib0246.hijack(window, "structuredClone", (obj) => {
if (obj && obj.___MARK___)
flag = true;
}, function (obj) {
if (flag) {
monitorArray(obj, key);
obj.___MARK___ = true;
flag = false;
}
});
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
function tree_traversal(startNode, inputAccessCallback, outputAccessCallback, isBranchCallback, rootCallback, leafCallback, branchCallback, errorCallback) {
// Helper function to find the root by tracing inputs
function findRoot(node, inputAccessCallback, visited) {
let currentNode = node;
while (true) {
if (visited.has(currentNode)) {
// A cycle is detected, so there's no root
errorCallback(currentNode);
return null;
}
visited.add(currentNode);
const parentNode = inputAccessCallback(currentNode);
if (parentNode === null)
return currentNode;
currentNode = parentNode;
}
}
// First pass to collect all nodes starting from root
function collectFromRoot(rootNode, outputAccessCallback, visited) {
const stack = [rootNode];
const collectedNodes = new Set();
while (stack.length > 0) {
const node = stack.pop();
if (!collectedNodes.has(node)) {
collectedNodes.add(node);
visited.add(node); // Mark this node as visited
// Add child nodes to the stack
stack.push(...outputAccessCallback(node));
}
}
return collectedNodes;
}
// Second pass to apply callbacks in the required order
function applyCallbacks(nodes, isBranchCallback, rootCallback, leafCallback, branchCallback, inputAccessCallback, outputAccessCallback) {
let root = null;
const leaves = [];
const branches = [];
nodes.forEach(node => {
if (inputAccessCallback(node) === null)
root = node;
else if (!isBranchCallback(node))
leaves.push(node);
else
branches.push(node);
});
// Handle edge cases
if (root === null || leaves.length === 0) {
errorCallback?.(null);
return;
}
if (nodes.size === 1) { // Single node graph edge case
errorCallback?.(root);
return;
}
// Apply callbacks and call error callback if any returns false
if (rootCallback(root) === false) {
errorCallback?.(root, 0);
return;
}
for (let i = 0; i < leaves.length; ++ i)
if (leafCallback(leaves[i]) === false) {
errorCallback?.(leaves[i], 1);
return;
}
for (let i = 0; i < branches.length; ++ i)
if (branchCallback(branches[i]) === false) {
errorCallback?.(branches[i], 2);
return;
}
}
// Set to keep track of visited nodes to detect cycles
const visited = new Set();
// Start by finding the root
const root = findRoot(startNode, inputAccessCallback, visited);
// If a cycle was detected and no root was found, return early
if (root === null)
return;
// Collect all nodes starting from the root using iterative DFS
const allNodes = collectFromRoot(root, outputAccessCallback, visited);
// Apply the callbacks in the required order
applyCallbacks(allNodes, isBranchCallback, rootCallback, leafCallback, branchCallback, inputAccessCallback, outputAccessCallback);
}
function is_reroute(node) {
// return node.prototype === LiteGraph.Nodes.RerouteNode.prototype.prototype;
return node.type === "Reroute (rgthree)" || node.type === "Reroute";
}
function reroute_process(self) {
let main_type = "",
root = null;
tree_traversal(
self,
(node) => {
if (!is_reroute(node))
return null;
if (!node.inputs)
return null;
if (node.inputs[0].link === null)
return null;
return app.graph.getNodeById(app.graph.links[node.inputs[0].link].origin_id);
},
(node) => {
const nodes = [];
if (node.outputs)
for (let i = 0; i < node.outputs.length; ++ i)
if (node.outputs[i].links !== null)
for (let j = 0; j < node.outputs[i].links.length; ++ j)
nodes.push(app.graph.getNodeById(app.graph.links[node.outputs[i].links[j]].target_id));
return nodes;
},
(node) => {
let flag = is_reroute(node);
if (flag)
node.__branch = true;
return flag;
},
(node) => {
let pin = null
scope: {
for (let i = 0; i < node.outputs.length; ++ i) {
if (node.outputs && node.outputs[i].links)
for (let j = 0; j < node.outputs[i].links.length; ++ j) {
let curr = app.graph.getNodeById(app.graph.links[node.outputs[i].links[j]].target_id);
if (curr.__branch) {
pin = node.outputs[i];
if (pin.type === "*")
return;
main_type = pin.type;
root = node;
break scope;
}
}
}
return;
}
// Check if any others connection to this pin have different types then disconnect them
for (let i = 0; i < pin.links.length; ++ i) {
let curr = app.graph.getNodeById(app.graph.links[pin.links[i]].target_id);
if (curr.__branch)
continue;
if (curr.outputs[app.graph.links[pin.links[i]].target_slot].type !== main_type)
node.disconnectOutput(pin.links[i], curr);
}
},
(node) => {
// Loop through all input pins and see which node have __branch
for (let i = 0; i < node.inputs.length; ++ i) {
if (node.inputs[i].link === null)
continue;
let curr = app.graph.getNodeById(app.graph.links[node.inputs[i].link].origin_id);
if (curr.__branch) {
const pin = node.inputs[i];
if (pin.type === "*") {
if (main_type !== "") {
pin.type = main_type;
break;
}
return false;
}
if (pin.type !== main_type) {
// Disconnect
node.disconnectInput(i);
return;
}
main_type = pin.type;
break;
}
}
},
(node) => {
delete node.__branch;
// Set all input pins and output pins to main_type
node.inputs[0].type = main_type;
node.outputs[0].type = main_type;
},
(node, code) => {
if (node === null || node)
return;
switch (code) {
case 0: {
lib0246.error_popup(`Error when processing root: ${node.getTitle()}`);
} break;
case 1: {
lib0246.error_popup(`Error when processing leaf: ${node.getTitle()}`);
} break;
case 2: {
lib0246.error_popup(`Error when processing branch: ${node.getTitle()}`);
} break;
}
}
);
}
// Probably safe enough unless someone else attempting to reuse these. Ouch.
const patch_node_db = [
["Highway", "0246.Highway"],
["Junction", "0246.Junction"],
["JunctionBatch", "0246.JunctionBatch"],
["Loop", "0246.Loop"],
["Count", "0246.Count"],
["Hold", "0246.Hold"],
["Beautify", "0246.Beautify"],
["Random", "0246.RandomInt"],
["Stringify", "0246.Stringify"],
];
lib0246.hijack(app, "loadGraphData", function (workflow) {
if (workflow && !workflow?.extra?.["0246.VERSION"]) {
for (let i = 0; i < workflow.nodes.length; ++ i)
for (let j = 0; j < patch_node_db.length; ++ j) {
if (workflow.nodes[i].type === patch_node_db[j][0]) {
console.warn(`[ComfyUI-0246] Patching node "${workflow.nodes[i].type}" to "${patch_node_db[j][1]}"`);
workflow.nodes[i].type = patch_node_db[j][1];
break;
} else if (workflow.nodes[i].type === patch_node_db[j][1])
break;
}
}
}, () => {});
let error_flag = false;
lib0246.hijack(app, "showMissingNodesError", function (nodes) {
if (!error_flag)
for (let i = 0; i < nodes.length; ++ i)
for (let j = 0; j < patch_node_db.length; ++ j)
if (nodes[i] === patch_node_db[j][0]) {
lib0246.error_popup(lib0246.indent_str `
[ComfyUI-0246] Unfortunately I have to change node internal ID due to I'm being dumb for using generic name. Sorry for inconvenience.
If this error message shown then that mean automatic patching failed. Please replace each nodes manually :(
Affected node:
- Highway -> 0246.Highway
- Junction -> 0246.Junction
- JunctionBatch -> 0246.JunctionBatch
- Loop -> 0246.Loop
- Count -> 0246.Count
- Hold -> 0246.Hold
- Beautify -> 0246.Beautify
- Random -> 0246.RandomInt
- Stringify -> 0246.Stringify
`);
error_flag = true;
break;
}
}, () => {});
// Extremely hacky ways to fix issues with Reroute (rgthree). Maybe open issue about this?
app.registerExtension({
name: "0246.Fixes",
async setup(app) {
app.graph.extra["0246.VERSION"] = [0, 0, 2]; // Only used when breaking changes happen
if (LiteGraph.Nodes.RerouteNode) {
// Hijack Reroute (rgthree) to do onConnectOutput and onConnectInput
lib0246.hijack(LiteGraph.Nodes.RerouteNode.prototype, "onConnectionsChange", () => {}, function (type, index, connected, link_info) {
reroute_process(this.self);
});
// Since native "Reroute" already did graph traversal, we don't need to hijack it
}
}
});