Merge pull request #75 from LucianoCirino/efficiency-nodes-update-v1.72

v1.72
This commit is contained in:
TSC
2023-08-06 22:13:46 -05:00
committed by GitHub
20 changed files with 868 additions and 268 deletions

File diff suppressed because it is too large Load Diff

94
js/efficiency_nodes.js Normal file
View File

@@ -0,0 +1,94 @@
import { app } from "../../scripts/app.js";
const ext = {
name: "BlobURLLogger",
ws: null,
maxCount: 0,
currentCount: 0,
sendBlob: false,
startProcessing: false,
lastBlobURL: null,
debug: false, // Set to true to see debug messages, false to suppress them.
log(...args) {
if (this.debug) {
console.log(...args);
}
},
error(...args) {
if (this.debug) {
console.error(...args);
}
},
async sendBlobDataAsDataURL(blobURL) {
const blob = await fetch(blobURL).then(res => res.blob());
const reader = new FileReader();
reader.readAsDataURL(blob);
reader.onloadend = () => {
const base64data = reader.result;
this.ws.send(base64data);
};
},
handleCommandMessage(data) {
this.maxCount = data.maxCount;
this.sendBlob = data.sendBlob;
this.startProcessing = data.startProcessing;
this.currentCount = 0;
// Check if we should revoke the last Blob URL after processing.
if(!this.startProcessing && this.lastBlobURL) {
this.log("[BlobURLLogger] Revoking last Blob URL:", this.lastBlobURL);
URL.revokeObjectURL(this.lastBlobURL);
this.lastBlobURL = null;
}
},
init() {
this.log("[BlobURLLogger] Initializing...");
this.ws = new WebSocket('ws://127.0.0.1:8288');
this.ws.addEventListener('open', () => {
this.log('[BlobURLLogger] WebSocket connection opened.');
});
this.ws.addEventListener('error', (err) => {
this.error('[BlobURLLogger] WebSocket Error:', err);
});
this.ws.addEventListener('message', (event) => {
try {
const data = JSON.parse(event.data);
if(data.maxCount !== undefined && data.sendBlob !== undefined && data.startProcessing !== undefined) {
this.handleCommandMessage(data);
}
} catch(err) {
this.error('[BlobURLLogger] Error parsing JSON:', err);
}
});
const originalCreateObjectURL = URL.createObjectURL;
URL.createObjectURL = (object) => {
const blobURL = originalCreateObjectURL.call(this, object);
if (blobURL.startsWith('blob:') && this.startProcessing) {
this.log("[BlobURLLogger] Blob URL created:", blobURL);
this.lastBlobURL = blobURL;
if(this.sendBlob && this.currentCount < this.maxCount) {
this.sendBlobDataAsDataURL(blobURL);
}
this.currentCount++;
}
return blobURL;
};
this.log("[BlobURLLogger] Hook attached.");
}
};
app.registerExtension(ext);

View File

@@ -1 +1,2 @@
simpleeval
simpleeval
websockets

View File

@@ -1,10 +1,8 @@
# Efficiency Nodes Utility functions
from torch import Tensor
import torch
from PIL import Image
import numpy as np
import os
import sys
import io
@@ -26,8 +24,10 @@ sys.path.append(comfy_dir)
# Import functions from ComfyUI
import comfy.sd
from comfy.cli_args import args
import latent_preview
# Load my version of Comfy functions
# Load my custom ComfyUI functions
from tsc_sd import *
# Cache for Efficiency Node models
@@ -39,10 +39,12 @@ loaded_objects = {
# Cache for Ksampler (Efficient) Outputs
last_helds: dict[str, list] = {
"results": [], # (results, id) # Preview Images, stored as a pil image list
"latent": [], # (latent, id) # Latent outputs, stored as a latent tensor list
"images": [], # (images, id) # Image outputs, stored as an image tensor list
"vae_decode": [], # (vae_decode, id) # Used to track wether to vae-decode or not
"preview_images": [], # (preview_images, id) # Preview Images, stored as a pil image list
"latent": [], # (latent, id) # Latent outputs, stored as a latent tensor list
"output_images": [], # (output_images, id) # Output Images, stored as an image tensor list
"vae_decode_flag": [], # (vae_decode, id) # Boolean to track wether vae-decode during Holds
"xy_plot_flag": [], # (xy_plot_flag, id) # Boolean to track if held images are xy_plot results
"xy_plot_image": [], # (xy_plot_image, id) # XY Plot image stored as an image tensor
}
# Tensor to PIL (grabbed from WAS Suite)
@@ -120,7 +122,6 @@ def print_loaded_objects_entries(id=None, prompt=None, show_id=False):
if not entries_found:
print("-")
# This function cleans global variables associated with nodes that are no longer detected on UI
def globals_cleanup(prompt):
global loaded_objects
@@ -388,7 +389,6 @@ def clear_cache_by_exception(node_id, vae_dict=None, ckpt_dict=None, lora_dict=N
if not tuple_item[-1]:
loaded_objects[dict_name].remove(tuple_item)
# Retrieve the cache number from 'node_settings' json file
def get_cache_numbers(node_name):
# Get the directory path of the current file
@@ -412,7 +412,7 @@ def print_last_helds(id=None):
print(f"Node-specific Last Helds (node_id:{int(id)})")
else:
print(f"Global Last Helds:")
for key in ["results", "latent", "images", "vae_decode"]:
for key in ["preview_images", "latent", "output_images", "vae_decode"]:
entries_with_id = last_helds[key] if id is None else [entry for entry in last_helds[key] if id == entry[-1]]
if not entries_with_id: # If no entries with the chosen ID, print None and skip this key
continue
@@ -442,4 +442,107 @@ def suppress_output():
yield
finally:
sys.stdout = original_stdout
sys.stderr = original_stderr
sys.stderr = original_stderr
# Set global preview_method
def set_preview_method(method):
if method == 'auto' or method == 'LatentPreviewMethod.Auto':
args.preview_method = latent_preview.LatentPreviewMethod.Auto
elif method == 'latent2rgb' or method == 'LatentPreviewMethod.Latent2RGB':
args.preview_method = latent_preview.LatentPreviewMethod.Latent2RGB
elif method == 'taesd' or method == 'LatentPreviewMethod.TAESD':
args.preview_method = latent_preview.LatentPreviewMethod.TAESD
else:
args.preview_method = latent_preview.LatentPreviewMethod.NoPreviews
# Extract global preview_method
def global_preview_method():
return args.preview_method
#-----------------------------------------------------------------------------------------------------------------------
# Auto install Efficiency Nodes Python packages
import subprocess
def install_packages(required_packages):
installed_packages = packages(versions=False)
for pkg in required_packages:
if pkg not in installed_packages:
print(f"\033[32mEfficiency Nodes:\033[0m Installing {pkg}...")
subprocess.check_call([sys.executable, '-m', 'pip', 'install', pkg])
print(f"\033[32mEfficiency Nodes:\033[0m Installed {pkg}!")
def packages(versions=False):
return [(r.decode().split('==')[0] if not versions else r.decode()) for r in
subprocess.check_output([sys.executable, '-m', 'pip', 'freeze']).split()]
# Packages to install
required_packages = ['simpleeval', 'websockets']
install_packages(required_packages)
#-----------------------------------------------------------------------------------------------------------------------
# Auto install efficiency nodes web extension '\js\efficiency_nodes.js' to 'ComfyUI\web\extensions'
import shutil
# Source and destination paths
source_path = os.path.join(my_dir, 'js', 'efficiency_nodes.js')
destination_dir = os.path.join(comfy_dir, 'web', 'extensions', 'efficiency-nodes-comfyui')
destination_path = os.path.join(destination_dir, 'efficiency_nodes.js')
# Create the destination directory if it doesn't exist
os.makedirs(destination_dir, exist_ok=True)
# Copy the file
shutil.copy2(source_path, destination_path)
#-----------------------------------------------------------------------------------------------------------------------
# Establish a websocket connection to communicate with "efficiency-nodes.js" under:
# ComfyUI\web\extensions\efficiency-nodes-comfyui\
import websockets #https://github.com/python-websockets/websockets
import asyncio
import threading
import base64
from io import BytesIO
from torchvision import transforms
# Import my functions
from tsc_utils import *
latest_image = None
connected_client = None
async def server_logic(websocket, path):
global latest_image, connected_client
# Assign the connected client
connected_client = websocket
async for message in websocket:
# If not a command, treat it as image data
if not message.startswith('{'):
image_data = base64.b64decode(message.split(",")[1])
image = Image.open(BytesIO(image_data))
latest_image = pil2tensor(image)
def run_server():
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
start_server = websockets.serve(server_logic, "127.0.0.1", 8288)
loop.run_until_complete(start_server)
loop.run_forever()
def get_latest_image():
return latest_image
# Function to send commands to frontend
def send_command_to_frontend(startListening=False, maxCount=0, sendBlob=False):
global connected_client
if connected_client:
asyncio.run(connected_client.send(json.dumps({
'startProcessing': startListening,
'maxCount': maxCount,
'sendBlob': sendBlob
})))
# Start the WebSocket server in a separate thread
server_thread = threading.Thread(target=run_server)
server_thread.start()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 MiB

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 MiB

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 701 KiB

After

Width:  |  Height:  |  Size: 701 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 764 KiB

After

Width:  |  Height:  |  Size: 638 KiB

BIN
workflows/Thumbs.db Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -40,4 +40,7 @@ Notes:
- Any parameter not defined by the XY Plot's inputs are defined by the Efficient Loader and Ksampler (Efficient).
For example, if the scheduler isn't specified when plotting Samplers, the script will use
the scheduler set by the Ksampler (Efficient).
- LoRA stacking through the XY Plot Manual Entry node is currently not supported.
- LoRA stacking through the XY Plot Manual Entry node is currently not supported.
- The following XY Plot types are not allowed types in an XY Plot for each of the following KSampler nodes...
1) KSampler (Efficient): "AddNoise", "ReturnNoise", "StartStep", "EndStep"
2) KSampler Adv. (Efficient): "Denoise"

BIN
workflows/XYplot/Thumbs.db Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 MiB

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 MiB

After

Width:  |  Height:  |  Size: 5.7 MiB