updated new node
This commit is contained in:
@@ -41,7 +41,6 @@ def read_names_from_csv(filename):
|
||||
val = raw.strip()
|
||||
if val:
|
||||
names.append(val)
|
||||
print(f"[PromptMachine] Loaded {len(names)} names from {filename}")
|
||||
return names
|
||||
except Exception as e:
|
||||
print(f"[PromptMachine] read_names_from_csv error for {filename}: {e}")
|
||||
@@ -99,8 +98,6 @@ class Prompt_Machine:
|
||||
names = read_names_from_csv(default_csv) if default_csv != "None" else []
|
||||
default_name = names[0] if names else "None"
|
||||
|
||||
print(f"[PromptMachine] INIT - default_csv={default_csv}, default_name={default_name}")
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"csv_file": (csvs if csvs else ["None"], {
|
||||
|
||||
+60
-149
@@ -1,170 +1,81 @@
|
||||
import json
|
||||
import os
|
||||
from server import PromptServer
|
||||
from aiohttp import web
|
||||
|
||||
NODE_NAME = "Resolution_Machine"
|
||||
|
||||
# Path to JSON config (root of SATA_UtilityNode folder)
|
||||
CONFIG_PATH = os.path.join(
|
||||
os.path.dirname(os.path.dirname(__file__)), "resolutions.json"
|
||||
)
|
||||
|
||||
def load_config():
|
||||
if not os.path.exists(CONFIG_PATH):
|
||||
raise FileNotFoundError(f"[{NODE_NAME}] resolutions.json not found at {CONFIG_PATH}")
|
||||
with open(CONFIG_PATH, "r", encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
class Resolution_Machine:
|
||||
CATEGORY = "SATA_UtilityNode"
|
||||
|
||||
# persist last-used selections
|
||||
last_model = None
|
||||
last_resolution = None
|
||||
last_width = None
|
||||
last_height = None
|
||||
|
||||
@classmethod
|
||||
def _get_config(cls):
|
||||
cfg = {
|
||||
"models": [
|
||||
{
|
||||
"model": "SD1.5",
|
||||
"resolutions": [
|
||||
{ "name": "Square (1:1)", "width": 512, "height": 512 },
|
||||
{ "name": "Square high-res (1:1)", "width": 768, "height": 768 },
|
||||
{ "name": "Landscape (3:2)", "width": 768, "height": 512 },
|
||||
{ "name": "Portrait (2:3)", "width": 512, "height": 768 },
|
||||
{ "name": "Landscape (4:3)", "width": 768, "height": 576 },
|
||||
{ "name": "Portrait (3:4)", "width": 576, "height": 768 },
|
||||
{ "name": "Widescreen (16:9)", "width": 912, "height": 512 },
|
||||
{ "name": "Tall (9:16)", "width": 512, "height": 912 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"model": "SDXL",
|
||||
"resolutions": [
|
||||
{ "name": "Square (1:1 native)", "width": 1024, "height": 1024 },
|
||||
{ "name": "Portrait (2:3)", "width": 832, "height": 1216 },
|
||||
{ "name": "Standard (3:4)", "width": 880, "height": 1176 },
|
||||
{ "name": "Large Format (4:5)", "width": 912, "height": 1144 },
|
||||
{ "name": "SD TV (4:3)", "width": 1176, "height": 888 },
|
||||
{ "name": "IMAX (1.43:1)", "width": 1224, "height": 856 },
|
||||
{ "name": "Golden Ratio (1.618:1)", "width": 1296, "height": 800 },
|
||||
{ "name": "European Widescreen (1.66:1)", "width": 1312, "height": 792 },
|
||||
{ "name": "Widescreen / HD TV (16:9)", "width": 1360, "height": 768 },
|
||||
{ "name": "Standard Widescreen (1.85:1)", "width": 1392, "height": 752 },
|
||||
{ "name": "Cinemascope / Panavision (2.35:1)", "width": 1568, "height": 664 },
|
||||
{ "name": "Anamorphic Widescreen (2.39:1)", "width": 1576, "height": 656 },
|
||||
{ "name": "Older TV & Documentaries (4:3)", "width": 1176, "height": 880 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"model": "Illustrious XL",
|
||||
"resolutions": [
|
||||
{ "name": "Minimum training bound", "width": 512, "height": 512 },
|
||||
{ "name": "standard example", "width": 1024, "height": 1024 },
|
||||
{ "name": "Standard high-res", "width": 1536, "height": 1536 },
|
||||
{ "name": "Maximum native", "width": 2048, "height": 2048 },
|
||||
{ "name": "Portrait-tall", "width": 832, "height": 1216 },
|
||||
{ "name": "Landscape-wide", "width": 1216, "height": 832 },
|
||||
{ "name": "Square (1:1)", "width": 1024, "height": 1024 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"model": "Flux.1 Dev",
|
||||
"resolutions": [
|
||||
{ "name": "512×512 (1 MP quick test)", "width": 512, "height": 512 },
|
||||
{ "name": "1024×1024 (daily work)", "width": 1024, "height": 1024 },
|
||||
{ "name": "1600×1600 (detailed work)", "width": 1600, "height": 1600 },
|
||||
{ "name": "1920×1080 (HD)", "width": 1920, "height": 1080 },
|
||||
{ "name": "2560×1440 (QHD)", "width": 2560, "height": 1440 },
|
||||
{ "name": "3840×2160 (4K)", "width": 3840, "height": 2160 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"model": "Qwen-Image",
|
||||
"resolutions": [
|
||||
{ "name": "Square (1:1)", "width": 1328, "height": 1328 },
|
||||
{ "name": "Widescreen (16:9)", "width": 1664, "height": 928 },
|
||||
{ "name": "Tall (9:16)", "width": 928, "height": 1664 },
|
||||
{ "name": "Landscape (4:3)", "width": 1472, "height": 1140 },
|
||||
{ "name": "Portrait (3:4)", "width": 1140, "height": 1472 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"model": "Wan2.2-S2V",
|
||||
"resolutions": [
|
||||
{ "name": "720p HD video", "width": 1280, "height": 720 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"model": "SD3.5 Large",
|
||||
"resolutions": [
|
||||
{ "name": "768×1152 (best portrait)", "width": 768, "height": 1152 },
|
||||
{ "name": "1152×768 (best landscape)", "width": 1152, "height": 768 },
|
||||
{ "name": "1024×1024 (square)", "width": 1024, "height": 1024 },
|
||||
{ "name": "768×768 (quick test)", "width": 768, "height": 768 },
|
||||
{ "name": "832×1216 portrait", "width": 832, "height": 1216 },
|
||||
{ "name": "1216×832 landscape", "width": 1216, "height": 832 }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
def INPUT_TYPES(cls):
|
||||
config = load_config()
|
||||
|
||||
# Append Custom option idempotently
|
||||
for m in cfg["models"]:
|
||||
if not any(r["name"] == "Custom (manual)" for r in m.get("resolutions", [])):
|
||||
m.setdefault("resolutions", []).append({"name": "Custom (manual)", "width": 0, "height": 0})
|
||||
# all models
|
||||
models = list(config.keys())
|
||||
default_model = models[0] if models else "Unknown"
|
||||
|
||||
return cfg
|
||||
# union of all resolutions across all models
|
||||
all_resolutions = set()
|
||||
for model_res in config.values():
|
||||
all_resolutions.update(model_res.keys())
|
||||
resolutions = sorted(list(all_resolutions))
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls, context=None):
|
||||
cfg = cls._get_config()
|
||||
models = [m["model"] for m in cfg["models"]]
|
||||
|
||||
# restore last used model or default
|
||||
default_model = cls.last_model if cls.last_model in models else (models[0] if models else "")
|
||||
|
||||
model_obj = next((m for m in cfg["models"] if m["model"] == default_model), None)
|
||||
if model_obj:
|
||||
resolutions = [r["name"] for r in model_obj.get("resolutions", [])]
|
||||
else:
|
||||
resolutions = ["Custom (manual)"]
|
||||
|
||||
# restore last used resolution if valid
|
||||
if cls.last_resolution and cls.last_resolution in resolutions:
|
||||
default_res = cls.last_resolution
|
||||
else:
|
||||
default_res = next((n for n in resolutions if n != "Custom (manual)"), resolutions[0])
|
||||
|
||||
# determine defaults for width/height
|
||||
default_w, default_h = 512, 512
|
||||
if cls.last_width and cls.last_height:
|
||||
default_w, default_h = cls.last_width, cls.last_height
|
||||
else:
|
||||
r_obj = next((r for r in model_obj["resolutions"] if r["name"] == default_res), None) if model_obj else None
|
||||
if r_obj:
|
||||
default_w, default_h = r_obj["width"], r_obj["height"]
|
||||
default_resolution = list(config[default_model].keys())[0]
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"model": (models if models else ["null"], {"default": default_model}),
|
||||
"resolution": (resolutions if resolutions else ["Custom (manual)"], {"default": default_res}),
|
||||
"width": ("INT", {"default": default_w, "min": 1, "max": 16384, "step": 1}),
|
||||
"height": ("INT", {"default": default_h, "min": 1, "max": 16384, "step": 1}),
|
||||
"model": (models, {"default": default_model}),
|
||||
"resolution": (resolutions, {"default": default_resolution}),
|
||||
"custom_width": ("INT", {"default": 512, "min": 1, "max": 8192}),
|
||||
"custom_height": ("INT", {"default": 512, "min": 1, "max": 8192}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT", "INT")
|
||||
RETURN_NAMES = ("width", "height")
|
||||
FUNCTION = "run"
|
||||
FUNCTION = "get_resolution"
|
||||
CATEGORY = "SATA Utility"
|
||||
|
||||
def run(self, model, resolution, width, height):
|
||||
cfg = self._get_config()
|
||||
model_obj = next((m for m in cfg["models"] if m["model"] == model), None)
|
||||
if model_obj and resolution and resolution != "Custom (manual)":
|
||||
res_obj = next((r for r in model_obj["resolutions"] if r["name"] == resolution), None)
|
||||
if res_obj:
|
||||
width, height = res_obj["width"], res_obj["height"]
|
||||
def get_resolution(self, model, resolution, custom_width, custom_height):
|
||||
config = load_config()
|
||||
|
||||
# store last selections for persistence
|
||||
Resolution_Machine.last_model = model
|
||||
Resolution_Machine.last_resolution = resolution
|
||||
Resolution_Machine.last_width = width
|
||||
Resolution_Machine.last_height = height
|
||||
if model not in config:
|
||||
raise ValueError(f"[{NODE_NAME}] Unknown model: {model}")
|
||||
|
||||
return width, height
|
||||
resolutions = config[model]
|
||||
|
||||
if resolution not in resolutions:
|
||||
# fallback to first available resolution for safety
|
||||
resolution = list(resolutions.keys())[0]
|
||||
print(f"[{NODE_NAME}] Warning: invalid resolution, defaulting to {resolution}")
|
||||
|
||||
if resolution == "Custom (manual)":
|
||||
width, height = custom_width, custom_height
|
||||
else:
|
||||
entry = resolutions[resolution]
|
||||
width, height = entry["width"], entry["height"]
|
||||
|
||||
print(f"[{NODE_NAME}] Model={model}, Resolution={resolution}, Output=({width}, {height})")
|
||||
return (width, height)
|
||||
|
||||
|
||||
# REST endpoint for frontend dynamic updates
|
||||
@PromptServer.instance.routes.get("/sata/resolution_machine/config")
|
||||
async def get_resolution_config(request):
|
||||
return web.json_response(Resolution_Machine._get_config())
|
||||
# --- REST endpoint for frontend config fetch ---
|
||||
@PromptServer.instance.routes.get("/SATA_UtilityNode/resolutions_config")
|
||||
async def get_resolutions_config(request):
|
||||
try:
|
||||
config = load_config()
|
||||
return web.json_response(config)
|
||||
except Exception as e:
|
||||
return web.json_response({"error": str(e)}, status=500)
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"SD 1.5": {
|
||||
"Square (1:1) 512x512": { "width": 512, "height": 512 },
|
||||
"Square high-res (1:1) 768x768": { "width": 768, "height": 768 },
|
||||
"Landscape (3:2) 768x512": { "width": 768, "height": 512 },
|
||||
"Portrait (2:3) 512x768": { "width": 512, "height": 768 },
|
||||
"Landscape (4:3) 768x576": { "width": 768, "height": 576 },
|
||||
"Portrait (3:4) 576x768": { "width": 576, "height": 768 },
|
||||
"Widescreen (16:9) 912x512": { "width": 912, "height": 512 },
|
||||
"Tall (9:16) 512x912": { "width": 512, "height": 912 },
|
||||
"Custom (manual)": { "width": 0, "height": 0 }
|
||||
},
|
||||
|
||||
"SDXL_Illustrious_Pony": {
|
||||
"Square (1:1) 1024x1024": { "width": 1024, "height": 1024 },
|
||||
"Landscape (3:2) 1280x832": { "width": 1280, "height": 832 },
|
||||
"Portrait (2:3) 832x1280": { "width": 832, "height": 1280 },
|
||||
"Photography (4:3) 1152x896": { "width": 1152, "height": 896 },
|
||||
"Portrait Insta (3:4) 896x1152": { "width": 896, "height": 1152 },
|
||||
"Widescreen (16:9) 1344x768": { "width": 1344, "height": 768 },
|
||||
"Vertical (9:16) 768x1344": { "width": 768, "height": 1344 },
|
||||
"Cinematic (21:9) 1536x640": { "width": 1536, "height": 640 },
|
||||
"Cinematic Portrait (9:21) 640x1536": { "width": 640, "height": 1536 },
|
||||
"Best Landscape (19:13) 1216x832": { "width": 1216, "height": 832 },
|
||||
"Best Portrait (13:19) 832x1216": { "width": 832, "height": 1216 },
|
||||
"Mini Portrait (768x1152)": { "width": 768, "height": 1152 },
|
||||
"Mini Landscape (1152x768)": { "width": 1152, "height": 768 },
|
||||
"Custom (manual)": { "width": 0, "height": 0 }
|
||||
},
|
||||
|
||||
"Flux": {
|
||||
"High-res (16:9) 1920x1088": { "width": 1920, "height": 1088 },
|
||||
"Square Medium (1:1) 768x768": { "width": 768, "height": 768 },
|
||||
"Portrait Small (3:4) 768x1024": { "width": 768, "height": 1024 },
|
||||
"Landscape Small (4:3) 1024x768": { "width": 1024, "height": 768 },
|
||||
"Wide Small (3:2) 1152x768": { "width": 1152, "height": 768 },
|
||||
"Portrait Medium (2:3) 768x1152": { "width": 768, "height": 1152 },
|
||||
"Ultra Wide Small (2:1) 1536x768": { "width": 1536, "height": 768 },
|
||||
"Portrait Large (1:2) 768x1536": { "width": 768, "height": 1536 },
|
||||
"Square Large (1:1) 1024x1024": { "width": 1024, "height": 1024 },
|
||||
"Portrait Small HD (7:10) 896x1280": { "width": 896, "height": 1280 },
|
||||
"Landscape Small HD (10:7) 1280x896": { "width": 1280, "height": 896 },
|
||||
"Wide Medium (12:7) 1536x896": { "width": 1536, "height": 896 },
|
||||
"Portrait HD (7:12) 896x1536": { "width": 896, "height": 1536 },
|
||||
"Custom (manual)": { "width": 0, "height": 0 }
|
||||
},
|
||||
|
||||
"Qwen-Image": {
|
||||
"Square (1:1)": { "width": 1328, "height": 1328 },
|
||||
"Widescreen (16:9)": { "width": 1664, "height": 928 },
|
||||
"Tall (9:16)": { "width": 928, "height": 1664 },
|
||||
"Landscape (4:3)": { "width": 1472, "height": 1140 },
|
||||
"Portrait (3:4)": { "width": 1140, "height": 1472 },
|
||||
"Custom (manual)": { "width": 0, "height": 0 }
|
||||
},
|
||||
|
||||
"Wan2.2": {
|
||||
"720p HD video": { "width": 1280, "height": 720 },
|
||||
"Custom (manual)": { "width": 0, "height": 0 }
|
||||
}
|
||||
}
|
||||
+38
-26
@@ -6,49 +6,61 @@ app.registerExtension({
|
||||
async nodeCreated(node) {
|
||||
if (node.comfyClass !== "Prompt_Machine") return;
|
||||
|
||||
let csvWidget = node.widgets.find(w => w.name === "csv_file");
|
||||
let oldNameWidget = node.widgets.find(w => w.name === "name");
|
||||
// Widgets
|
||||
const csvWidget = node.widgets.find(w => w.name === "csv_file");
|
||||
const oldNameWidget = node.widgets.find(w => w.name === "name");
|
||||
|
||||
if (!csvWidget || !oldNameWidget) {
|
||||
console.warn("[PromptMachineFrontend] widgets not found");
|
||||
console.warn("[PromptMachineFrontend] Missing widgets, check backend definition.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the old STRING widget
|
||||
// Remove the old text input for 'name'
|
||||
const index = node.widgets.indexOf(oldNameWidget);
|
||||
node.widgets.splice(index, 1);
|
||||
|
||||
// Add a proper dropdown instead
|
||||
let nameWidget = node.addWidget("combo", "name", "None", () => {}, {
|
||||
// Add proper dropdown
|
||||
const nameWidget = node.addWidget("combo", "name", "None", () => {}, {
|
||||
values: ["None"]
|
||||
});
|
||||
|
||||
async function refreshNames(csvValue) {
|
||||
// Store available names
|
||||
let namesConfig = [];
|
||||
|
||||
// Fetch names from backend for a given CSV
|
||||
async function loadNames(csvValue) {
|
||||
if (!csvValue) return;
|
||||
try {
|
||||
const res = await fetch(`/sata/prompt_machine/names?csv=${encodeURIComponent(csvValue)}`);
|
||||
const data = await res.json();
|
||||
const names = data.names || ["None"];
|
||||
|
||||
nameWidget.options.values = names;
|
||||
if (!names.includes(nameWidget.value)) {
|
||||
nameWidget.value = names[0];
|
||||
}
|
||||
|
||||
if (node.graph) node.graph.setDirtyCanvas(true);
|
||||
console.log("[PromptMachineFrontend] refreshed names:", names);
|
||||
const resp = await fetch(`/sata/prompt_machine/names?csv=${encodeURIComponent(csvValue)}`);
|
||||
const data = await resp.json();
|
||||
namesConfig = data.names || ["None"];
|
||||
console.log("[PromptMachineFrontend] Loaded names:", namesConfig);
|
||||
refreshNames();
|
||||
} catch (err) {
|
||||
console.error("[PromptMachineFrontend] error fetching names:", err);
|
||||
console.error("[PromptMachineFrontend] Failed to load names:", err);
|
||||
}
|
||||
}
|
||||
|
||||
// Initial load
|
||||
refreshNames(csvWidget.value);
|
||||
// Refresh dropdown with available names
|
||||
function refreshNames() {
|
||||
const available = namesConfig.length > 0 ? namesConfig : ["None"];
|
||||
nameWidget.options.values = available;
|
||||
|
||||
// Reload names when CSV changes
|
||||
const originalCsvCallback = csvWidget.callback;
|
||||
csvWidget.callback = (value) => {
|
||||
refreshNames(value);
|
||||
if (originalCsvCallback) originalCsvCallback(value);
|
||||
if (!available.includes(nameWidget.value)) {
|
||||
nameWidget.value = available[0]; // safe fallback
|
||||
}
|
||||
|
||||
app.graph.change();
|
||||
}
|
||||
|
||||
// Hook CSV change
|
||||
const oldCsvCallback = csvWidget.callback;
|
||||
csvWidget.callback = function(value) {
|
||||
if (oldCsvCallback) oldCsvCallback(value);
|
||||
loadNames(value); // refresh dynamically
|
||||
};
|
||||
|
||||
// Initial sync
|
||||
loadNames(csvWidget.value);
|
||||
}
|
||||
});
|
||||
+87
-113
@@ -1,123 +1,97 @@
|
||||
// resolution_machine.js
|
||||
import { app } from "../../../scripts/app.js";
|
||||
|
||||
async function fetchConfig() {
|
||||
try {
|
||||
const resp = await fetch("/sata/resolution_machine/config");
|
||||
if (!resp.ok) {
|
||||
console.warn("ResolutionMachine: fetch failed", resp.status);
|
||||
return null;
|
||||
}
|
||||
return await resp.json();
|
||||
} catch (e) {
|
||||
console.warn("ResolutionMachine: fetch exception", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
app.registerExtension({
|
||||
name: "SATA_UtilityNode.ResolutionMachine",
|
||||
async nodeCreated(node) {
|
||||
if (node.comfyClass !== "Resolution_Machine") return;
|
||||
name: "SATA_UtilityNode.Resolution_Machine",
|
||||
|
||||
const cfg = await fetchConfig();
|
||||
if (!cfg) return;
|
||||
async nodeCreated(node) {
|
||||
if (node.comfyClass !== "Resolution_Machine") return;
|
||||
|
||||
// find widgets
|
||||
let modelW = node.widgets.find(w => w.name === "model");
|
||||
let resW = node.widgets.find(w => w.name === "resolution");
|
||||
let wW = node.widgets.find(w => w.name === "width");
|
||||
let hW = node.widgets.find(w => w.name === "height");
|
||||
// Find widgets
|
||||
const modelWidget = node.widgets.find(w => w.name === "model");
|
||||
const resolutionWidget = node.widgets.find(w => w.name === "resolution");
|
||||
const widthWidget = node.widgets.find(w => w.name === "custom_width");
|
||||
const heightWidget = node.widgets.find(w => w.name === "custom_height");
|
||||
|
||||
if (!modelW || !resW || !wW || !hW) {
|
||||
console.warn("ResolutionMachine: widgets missing, check INPUT_TYPES");
|
||||
return;
|
||||
}
|
||||
|
||||
// --- rebuild widgets as dropdowns to force combo type ---
|
||||
const idxModel = node.widgets.indexOf(modelW);
|
||||
node.widgets.splice(idxModel, 1);
|
||||
modelW = node.addWidget("combo", "model", "None", () => {}, {
|
||||
values: cfg.models.map(m => m.model)
|
||||
});
|
||||
|
||||
const idxRes = node.widgets.indexOf(resW);
|
||||
node.widgets.splice(idxRes, 1);
|
||||
resW = node.addWidget("combo", "resolution", "Custom (manual)", () => {}, {
|
||||
values: ["Custom (manual)"]
|
||||
});
|
||||
|
||||
// helper lookups
|
||||
function getResNamesForModel(modelName) {
|
||||
const m = cfg.models.find(x => x.model === modelName);
|
||||
if (!m) return ["Custom (manual)"];
|
||||
const names = m.resolutions.map(r => r.name);
|
||||
if (!names.includes("Custom (manual)")) names.push("Custom (manual)");
|
||||
return names;
|
||||
}
|
||||
function findResObj(modelName, resName) {
|
||||
const m = cfg.models.find(x => x.model === modelName);
|
||||
if (!m) return null;
|
||||
return m.resolutions.find(r => r.name === resName) || null;
|
||||
}
|
||||
|
||||
let suppressWH = false;
|
||||
function programmaticSetWH(wVal, hVal) {
|
||||
suppressWH = true;
|
||||
wW.value = wVal;
|
||||
hW.value = hVal;
|
||||
if (wW.inputEl) wW.inputEl.value = wVal;
|
||||
if (hW.inputEl) hW.inputEl.value = hVal;
|
||||
setTimeout(() => { suppressWH = false; }, 0);
|
||||
}
|
||||
|
||||
// model handler
|
||||
modelW.callback = (newModel) => {
|
||||
const names = getResNamesForModel(newModel);
|
||||
resW.options.values = names;
|
||||
if (!names.includes(resW.value)) {
|
||||
resW.value = names[0];
|
||||
}
|
||||
resW.callback(resW.value); // trigger res change
|
||||
node.graph?.setDirtyCanvas(true);
|
||||
};
|
||||
|
||||
// resolution handler
|
||||
resW.callback = (newRes) => {
|
||||
const modelName = modelW.value;
|
||||
const resObj = findResObj(modelName, newRes);
|
||||
if (resObj && newRes !== "Custom (manual)") {
|
||||
programmaticSetWH(resObj.width, resObj.height);
|
||||
if (wW.inputEl) wW.inputEl.disabled = true;
|
||||
if (hW.inputEl) hW.inputEl.disabled = true;
|
||||
} else {
|
||||
if (wW.inputEl) wW.inputEl.disabled = false;
|
||||
if (hW.inputEl) hW.inputEl.disabled = false;
|
||||
}
|
||||
node.graph?.setDirtyCanvas(true);
|
||||
};
|
||||
|
||||
// manual WH handler
|
||||
function onManualWH() {
|
||||
if (suppressWH) return;
|
||||
if (resW.value !== "Custom (manual)") {
|
||||
const names = getResNamesForModel(modelW.value);
|
||||
if (!names.includes("Custom (manual)")) {
|
||||
names.push("Custom (manual)");
|
||||
resW.options.values = names;
|
||||
if (!modelWidget || !resolutionWidget || !widthWidget || !heightWidget) {
|
||||
console.warn("[Resolution_Machine] Missing widgets, check backend definition.");
|
||||
return;
|
||||
}
|
||||
resW.value = "Custom (manual)";
|
||||
resW.callback("Custom (manual)");
|
||||
}
|
||||
}
|
||||
wW.callback = onManualWH;
|
||||
hW.callback = onManualWH;
|
||||
|
||||
// --- initialize ---
|
||||
const firstModel = cfg.models[0]?.model;
|
||||
if (firstModel) {
|
||||
modelW.value = firstModel;
|
||||
modelW.callback(firstModel);
|
||||
// Store resolution config
|
||||
let resolutionsConfig = {};
|
||||
|
||||
// Fetch resolution config from backend
|
||||
async function loadConfig() {
|
||||
try {
|
||||
const resp = await fetch("/SATA_UtilityNode/resolutions_config");
|
||||
resolutionsConfig = await resp.json();
|
||||
console.log("[Resolution_Machine] Loaded config:", resolutionsConfig);
|
||||
} catch (err) {
|
||||
console.error("[Resolution_Machine] Failed to load config:", err);
|
||||
}
|
||||
}
|
||||
|
||||
await loadConfig();
|
||||
|
||||
// --- Utility: refresh resolution dropdown when model changes ---
|
||||
function refreshResolutions(selectedModel) {
|
||||
const available = resolutionsConfig[selectedModel]
|
||||
? Object.keys(resolutionsConfig[selectedModel])
|
||||
: ["Custom (manual)"];
|
||||
|
||||
resolutionWidget.options.values = available;
|
||||
|
||||
// Pick safe default if current value is not valid
|
||||
if (!available.includes(resolutionWidget.value)) {
|
||||
resolutionWidget.value = available[0];
|
||||
}
|
||||
|
||||
console.log(`[Resolution_Machine] Resolutions for ${selectedModel}:`, available);
|
||||
app.graph.change();
|
||||
applyResolution(); // auto-apply width/height on refresh
|
||||
}
|
||||
|
||||
// --- Utility: apply resolution to width/height ---
|
||||
function applyResolution() {
|
||||
const selectedModel = modelWidget.value;
|
||||
const selectedResolution = resolutionWidget.value;
|
||||
|
||||
if (!resolutionsConfig[selectedModel]) return;
|
||||
|
||||
const resolutionData = resolutionsConfig[selectedModel][selectedResolution];
|
||||
|
||||
if (selectedResolution !== "Custom (manual)" && resolutionData) {
|
||||
// Auto-set width/height
|
||||
widthWidget.value = resolutionData.width;
|
||||
heightWidget.value = resolutionData.height;
|
||||
|
||||
// Lock fields
|
||||
widthWidget.disabled = true;
|
||||
heightWidget.disabled = true;
|
||||
} else {
|
||||
// Allow manual editing
|
||||
widthWidget.disabled = false;
|
||||
heightWidget.disabled = false;
|
||||
}
|
||||
|
||||
app.graph.change();
|
||||
}
|
||||
|
||||
// --- Hook model change ---
|
||||
const oldModelCallback = modelWidget.callback;
|
||||
modelWidget.callback = function(value) {
|
||||
if (oldModelCallback) oldModelCallback(value);
|
||||
refreshResolutions(value);
|
||||
};
|
||||
|
||||
// --- Hook resolution change ---
|
||||
const oldResolutionCallback = resolutionWidget.callback;
|
||||
resolutionWidget.callback = function(value) {
|
||||
if (oldResolutionCallback) oldResolutionCallback(value);
|
||||
applyResolution();
|
||||
};
|
||||
|
||||
// --- Initial sync ---
|
||||
refreshResolutions(modelWidget.value);
|
||||
}
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user