feat: inject start_name/middle_name/end_name as computed keys in API
Instead of a separate node, _get_data now appends three derived keys to every sequence response: Path(start frame path).stem → start_name, etc. Any ProjectKey node can use these directly as key_name. Reverts ProjectFrameNames node (unnecessary). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+10
-2
@@ -83,9 +83,17 @@ def _get_data(name: str, file_name: str, seq: int = Query(default=1)) -> dict[st
|
|||||||
match = next((s for s in sequences if int(s.get(KEY_SEQUENCE_NUMBER, 0)) == seq), None)
|
match = next((s for s in sequences if int(s.get(KEY_SEQUENCE_NUMBER, 0)) == seq), None)
|
||||||
if match is None:
|
if match is None:
|
||||||
raise HTTPException(status_code=404, detail=f"Sequence {seq} not found")
|
raise HTTPException(status_code=404, detail=f"Sequence {seq} not found")
|
||||||
|
result = dict(match)
|
||||||
|
for out_key, src_key in (
|
||||||
|
("start_name", "start frame path"),
|
||||||
|
("middle_name", "middle frame path"),
|
||||||
|
("end_name", "end frame path"),
|
||||||
|
):
|
||||||
|
path_val = result.get(src_key, "")
|
||||||
|
result[out_key] = Path(path_val).stem if path_val else ""
|
||||||
logger.info("API _get_data %s/%s seq=%d (%d keys): %.3fs",
|
logger.info("API _get_data %s/%s seq=%d (%d keys): %.3fs",
|
||||||
name, file_name, seq, len(match), time.perf_counter() - t0)
|
name, file_name, seq, len(result), time.perf_counter() - t0)
|
||||||
return match
|
return result
|
||||||
|
|
||||||
|
|
||||||
def _get_keys(name: str, file_name: str, seq: int = Query(default=1)) -> dict[str, Any]:
|
def _get_keys(name: str, file_name: str, seq: int = Query(default=1)) -> dict[str, Any]:
|
||||||
|
|||||||
@@ -383,63 +383,12 @@ class BinaryIndexDecoder:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ProjectFrameNames:
|
|
||||||
"""Outputs the filename stem of each frame path field (no directory, no extension).
|
|
||||||
|
|
||||||
Fetches start frame path, middle frame path, and end frame path from the
|
|
||||||
sequence data and returns Path(value).stem for each, so you get e.g.
|
|
||||||
'keyframe8' instead of '/some/dir/keyframe8.png'.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def INPUT_TYPES(s):
|
|
||||||
return {
|
|
||||||
"required": {
|
|
||||||
"source_label": ("STRING", {"default": "", "multiline": False}),
|
|
||||||
},
|
|
||||||
"optional": {
|
|
||||||
"manager_url": ("STRING", {"default": "http://localhost:8080", "multiline": False}),
|
|
||||||
"project_name": ("STRING", {"default": "", "multiline": False}),
|
|
||||||
"file_name": ("STRING", {"default": "", "multiline": False}),
|
|
||||||
"sequence_number": ("INT", {"default": 1, "min": 1, "max": 9999}),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
RETURN_TYPES = ("STRING", "STRING", "STRING")
|
|
||||||
RETURN_NAMES = ("start_name", "middle_name", "end_name")
|
|
||||||
FUNCTION = "fetch_frame_names"
|
|
||||||
CATEGORY = "JSON Manager/project"
|
|
||||||
OUTPUT_NODE = False
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def IS_CHANGED(cls, **kwargs):
|
|
||||||
return float("nan")
|
|
||||||
|
|
||||||
def fetch_frame_names(self, source_label, manager_url="http://localhost:8080",
|
|
||||||
project_name="", file_name="", sequence_number=1):
|
|
||||||
sequence_number = int(sequence_number)
|
|
||||||
data = _fetch_data(manager_url, project_name, file_name, sequence_number)
|
|
||||||
if data.get("error") in ("http_error", "network_error", "parse_error"):
|
|
||||||
logger.warning("ProjectFrameNames.fetch_frame_names failed: %s", data.get("message"))
|
|
||||||
return ("", "", "")
|
|
||||||
|
|
||||||
def stem(path_str):
|
|
||||||
return Path(path_str).stem if path_str else ""
|
|
||||||
|
|
||||||
return (
|
|
||||||
stem(data.get("start frame path", "")),
|
|
||||||
stem(data.get("middle frame path", "")),
|
|
||||||
stem(data.get("end frame path", "")),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# --- Mappings ---
|
# --- Mappings ---
|
||||||
PROJECT_NODE_CLASS_MAPPINGS = {
|
PROJECT_NODE_CLASS_MAPPINGS = {
|
||||||
"ProjectLoaderDynamic": ProjectLoaderDynamic,
|
"ProjectLoaderDynamic": ProjectLoaderDynamic,
|
||||||
"ProjectSource": ProjectSource,
|
"ProjectSource": ProjectSource,
|
||||||
"ProjectKey": ProjectKey,
|
"ProjectKey": ProjectKey,
|
||||||
"ProjectResolution": ProjectResolution,
|
"ProjectResolution": ProjectResolution,
|
||||||
"ProjectFrameNames": ProjectFrameNames,
|
|
||||||
"BinaryIndexDecoder": BinaryIndexDecoder,
|
"BinaryIndexDecoder": BinaryIndexDecoder,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,6 +397,5 @@ PROJECT_NODE_DISPLAY_NAME_MAPPINGS = {
|
|||||||
"ProjectSource": "Project Source",
|
"ProjectSource": "Project Source",
|
||||||
"ProjectKey": "Project Key",
|
"ProjectKey": "Project Key",
|
||||||
"ProjectResolution": "Project Resolution",
|
"ProjectResolution": "Project Resolution",
|
||||||
"ProjectFrameNames": "Project Frame Names",
|
|
||||||
"BinaryIndexDecoder": "Binary Index Decoder",
|
"BinaryIndexDecoder": "Binary Index Decoder",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,131 +0,0 @@
|
|||||||
import { app } from "../../scripts/app.js";
|
|
||||||
|
|
||||||
app.registerExtension({
|
|
||||||
name: "json.manager.project.frame_names",
|
|
||||||
|
|
||||||
async beforeQueuePrompt() {
|
|
||||||
if (!app.graph?._nodes) return;
|
|
||||||
for (const node of app.graph._nodes) {
|
|
||||||
if (node.type === "ProjectFrameNames" && node._syncFromSource) {
|
|
||||||
node._syncFromSource();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
|
||||||
if (nodeData.name !== "ProjectFrameNames") return;
|
|
||||||
|
|
||||||
function hideWidget(widget) {
|
|
||||||
if (widget.origType === undefined) widget.origType = widget.type;
|
|
||||||
widget.type = "hidden";
|
|
||||||
widget.hidden = true;
|
|
||||||
widget.computeSize = () => [0, -4];
|
|
||||||
}
|
|
||||||
|
|
||||||
function replaceWithCombo(node, name, values, callback) {
|
|
||||||
const idx = node.widgets?.findIndex(w => w.name === name);
|
|
||||||
if (idx === -1 || idx === undefined) return null;
|
|
||||||
const oldWidget = node.widgets[idx];
|
|
||||||
const savedValue = oldWidget.value || "";
|
|
||||||
const comboValues = values.length > 0 ? values : [""];
|
|
||||||
if (savedValue && !comboValues.includes(savedValue)) comboValues.unshift(savedValue);
|
|
||||||
const defaultValue = savedValue || comboValues[0];
|
|
||||||
node.widgets.splice(idx, 1);
|
|
||||||
const combo = node.addWidget("combo", name, defaultValue, callback, { values: comboValues });
|
|
||||||
if (node.widgets.length > 1) {
|
|
||||||
node.widgets.splice(node.widgets.length - 1, 1);
|
|
||||||
node.widgets.splice(idx, 0, combo);
|
|
||||||
}
|
|
||||||
return combo;
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeType.prototype._getSourceLabels = function () {
|
|
||||||
const seen = new Set();
|
|
||||||
const labels = [];
|
|
||||||
if (!this.graph) return labels;
|
|
||||||
for (const node of this.graph._nodes) {
|
|
||||||
if (node.type === "ProjectSource") {
|
|
||||||
const lw = node.widgets?.find(w => w.name === "label");
|
|
||||||
if (lw?.value && !seen.has(lw.value)) {
|
|
||||||
seen.add(lw.value);
|
|
||||||
labels.push(lw.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return labels;
|
|
||||||
};
|
|
||||||
|
|
||||||
nodeType.prototype._findSource = function (label) {
|
|
||||||
if (!this.graph || !label) return null;
|
|
||||||
for (const node of this.graph._nodes) {
|
|
||||||
if (node.type === "ProjectSource") {
|
|
||||||
const lw = node.widgets?.find(w => w.name === "label");
|
|
||||||
if (lw?.value === label) return node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
nodeType.prototype._syncFromSource = function () {
|
|
||||||
const srcWidget = this.widgets?.find(w => w.name === "source_label");
|
|
||||||
const source = this._findSource(srcWidget?.value);
|
|
||||||
if (!source) return;
|
|
||||||
for (const name of ["manager_url", "project_name", "file_name", "sequence_number"]) {
|
|
||||||
const dst = this.widgets?.find(w => w.name === name);
|
|
||||||
const src = source.widgets?.find(w => w.name === name);
|
|
||||||
if (dst && src) dst.value = src.value;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const origOnNodeCreated = nodeType.prototype.onNodeCreated;
|
|
||||||
nodeType.prototype.onNodeCreated = function () {
|
|
||||||
origOnNodeCreated?.apply(this, arguments);
|
|
||||||
|
|
||||||
for (const name of ["manager_url", "project_name", "file_name", "sequence_number"]) {
|
|
||||||
const w = this.widgets?.find(w => w.name === name);
|
|
||||||
if (w) hideWidget(w);
|
|
||||||
}
|
|
||||||
|
|
||||||
const node = this;
|
|
||||||
replaceWithCombo(this, "source_label", this._getSourceLabels?.() || [], function () {
|
|
||||||
node._syncFromSource();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.title = "Project Frame Names";
|
|
||||||
this.setSize(this.computeSize());
|
|
||||||
};
|
|
||||||
|
|
||||||
const origOnConfigure = nodeType.prototype.onConfigure;
|
|
||||||
nodeType.prototype.onConfigure = function (info) {
|
|
||||||
origOnConfigure?.apply(this, arguments);
|
|
||||||
|
|
||||||
for (const name of ["manager_url", "project_name", "file_name", "sequence_number"]) {
|
|
||||||
const w = this.widgets?.find(w => w.name === name);
|
|
||||||
if (w) hideWidget(w);
|
|
||||||
}
|
|
||||||
|
|
||||||
const srcWidget = this.widgets?.find(w => w.name === "source_label");
|
|
||||||
if (srcWidget && srcWidget.type !== "combo") {
|
|
||||||
const node = this;
|
|
||||||
replaceWithCombo(this, "source_label", this._getSourceLabels?.() || [], function () {
|
|
||||||
node._syncFromSource();
|
|
||||||
});
|
|
||||||
} else if (srcWidget) {
|
|
||||||
srcWidget.options.values = this._getSourceLabels?.() || [];
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setSize(this.computeSize());
|
|
||||||
|
|
||||||
const node = this;
|
|
||||||
queueMicrotask(() => node._syncFromSource());
|
|
||||||
};
|
|
||||||
|
|
||||||
const origOnMouseDown = nodeType.prototype.onMouseDown;
|
|
||||||
nodeType.prototype.onMouseDown = function (e, localPos, graphCanvas) {
|
|
||||||
origOnMouseDown?.apply(this, arguments);
|
|
||||||
const srcWidget = this.widgets?.find(w => w.name === "source_label");
|
|
||||||
if (srcWidget) srcWidget.options.values = this._getSourceLabels?.() || [];
|
|
||||||
this._syncFromSource();
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user