Breaking changes: - Same seeds now yield different prompts than 5.x (reproducibility fixed) - Civitai Trending: removed broken 'model' filter widget - Shutdown node is a no-op unless ISULION_ENABLE_SHUTDOWN=1 Highlights: - Seeding: all 109 theme handlers share the seeded ConfigManager PRNG (284 global random.* call sites migrated); same seed => same prompt - Theme registry rewritten with @register_theme decorators; removed HANDLER_CLASS_NAMES_MAP, star-imports and globals() lookups - Shared ThemeRegistry singleton: INPUT_TYPES no longer re-reads ~100 JSON files per call (2.6ms -> 0.003ms cached) - load_images_node: paths sandboxed to ComfyUI input dir, deterministic sha1 IS_CHANGED, PIL context managers, no bare excepts - Overlay node: full batch support, base channel count preserved (RGB in -> RGB out), pass-through on internal errors - QR node: vectorized module drawing (np.kron), logging instead of prints - New net_utils.safe_download: http(s)-only scheme + 20MB cap for URL fetching nodes (SSRF/decompression-bomb hardening) - Removed unused soft_empty_cache() calls, dead code (culinary_handler, IsulionCategorySelector, orphan Core_Nodes/config dir, dead js ext), invalid WEB_DIRECTORY; fixed display-name mappings - s300 handler: fixed call to non-existent config method (latent crash) - Added MIT LICENSE and requirements.txt (requests)
72 lines
2.3 KiB
Python
72 lines
2.3 KiB
Python
import os
|
|
import subprocess
|
|
import shutil
|
|
|
|
import logging
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# The node only actually shuts down the machine when explicitly armed:
|
|
# ISULION_ENABLE_SHUTDOWN=1
|
|
ISULION_ENABLE_SHUTDOWN = os.getenv("ISULION_ENABLE_SHUTDOWN", "0") == "1"
|
|
|
|
if ISULION_ENABLE_SHUTDOWN:
|
|
print("\033[93m⚠️ SECURITY WARNING: '💤 IsulionShutdown' is ARMED (ISULION_ENABLE_SHUTDOWN=1).\033[0m")
|
|
print("\033[93m Executing this node will shut down the entire system.\033[0m")
|
|
|
|
|
|
class ShutdownNode:
|
|
"""
|
|
A node that can shut down the computer when the workflow reaches it.
|
|
It stays registered for workflow compatibility, but performs a no-op
|
|
unless the environment variable ISULION_ENABLE_SHUTDOWN=1 is set.
|
|
"""
|
|
|
|
def __init__(self):
|
|
pass
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"any": ("*",),
|
|
"delay": ("INT", {
|
|
"default": 30,
|
|
"min": 0,
|
|
"display": "number"
|
|
})
|
|
}
|
|
}
|
|
|
|
RETURN_TYPES = ()
|
|
FUNCTION = "shutdown_computer"
|
|
CATEGORY = "utils"
|
|
OUTPUT_NODE = True
|
|
|
|
def shutdown_computer(self, input, delay):
|
|
if not ISULION_ENABLE_SHUTDOWN:
|
|
logger.info("IsulionShutdown: disabled (set ISULION_ENABLE_SHUTDOWN=1 to arm). Skipping shutdown.")
|
|
return ()
|
|
try:
|
|
if os.name == 'nt': # Windows
|
|
args = ["shutdown", "/s", "/f", "/t", str(delay)]
|
|
else: # Linux/Unix
|
|
minutes = max(1, round(delay / 60)) # Convert seconds to minutes, minimum 1 minute
|
|
args = ["shutdown", "-h", f"+{minutes}"]
|
|
if shutil.which(args[0]) is None:
|
|
logger.error(f"IsulionShutdown: '{args[0]}' not found on PATH. Cannot shut down.")
|
|
return ()
|
|
subprocess.run(args, check=False)
|
|
logger.info(f"Shutdown command initiated. Computer will shutdown in {delay} seconds.")
|
|
except Exception as e:
|
|
logger.error(f"Error initiating shutdown: {str(e)}")
|
|
|
|
return ()
|
|
|
|
NODE_CLASS_MAPPINGS = {
|
|
"💤 IsulionShutdown": ShutdownNode
|
|
}
|
|
|
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
|
"💤 IsulionShutdown": "💤 Isulion Shutdown"
|
|
}
|