Derive external timeline duration from sections

This commit is contained in:
OpenClaw Agent
2026-07-20 09:17:04 +00:00
parent 8736d4a463
commit e01ceef71b
3 changed files with 13 additions and 20 deletions

View File

@@ -11589,14 +11589,10 @@ app.registerExtension({
return null; return null;
} }
const rawDuration =
self._resolveNodeLinkedValue(timelineNode, "duration", { decimals: 3 }) ??
parseFloat(self._readNodeWidgetValue(timelineNode, "duration") || 0);
const duration = Math.max(0.1, Number.isFinite(rawDuration) ? rawDuration : 5.0);
const sectionSetNode = self._findLinkedOriginNode(timelineNode, "section_set"); const sectionSetNode = self._findLinkedOriginNode(timelineNode, "section_set");
const setClass = sectionSetNode?.comfyClass || sectionSetNode?.type || ""; const setClass = sectionSetNode?.comfyClass || sectionSetNode?.type || "";
const sections = []; const sections = [];
let duration = 0;
if (sectionSetNode && setClass === "TimelineSectionSetCS") { if (sectionSetNode && setClass === "TimelineSectionSetCS") {
for (let i = 1; i <= 5; i++) { for (let i = 1; i <= 5; i++) {
const sectionNode = self._findLinkedOriginNode(sectionSetNode, `section_${i}`); const sectionNode = self._findLinkedOriginNode(sectionSetNode, `section_${i}`);
@@ -11610,6 +11606,7 @@ app.registerExtension({
self._resolveNodeLinkedValue(sectionNode, "duration", { decimals: 3 }) ?? self._resolveNodeLinkedValue(sectionNode, "duration", { decimals: 3 }) ??
parseFloat(self._readNodeWidgetValue(sectionNode, "duration") || 0); parseFloat(self._readNodeWidgetValue(sectionNode, "duration") || 0);
const sectionDuration = Math.max(0.1, Number.isFinite(rawSectionDuration) ? rawSectionDuration : 1.0); const sectionDuration = Math.max(0.1, Number.isFinite(rawSectionDuration) ? rawSectionDuration : 1.0);
duration += sectionDuration;
const imageSource = self._extractLinkedImageSource(sectionNode, "image"); const imageSource = self._extractLinkedImageSource(sectionNode, "image");
sections.push({ sections.push({
text, text,
@@ -11620,7 +11617,7 @@ app.registerExtension({
} }
} }
return { duration, sections }; return { duration: Math.max(0.1, duration), sections };
}; };
this._syncTimelineFromLink = function () { this._syncTimelineFromLink = function () {
const payload = self._buildExternalTimelinePayload(); const payload = self._buildExternalTimelinePayload();

View File

@@ -177,11 +177,10 @@ def _build_runtime_timeline_payload(external_timeline, frame_rate: float) -> tup
return tdata, "", "", 0.0, 0 return tdata, "", "", 0.0, 0
sections = external_timeline.get("sections") or [] sections = external_timeline.get("sections") or []
duration_seconds = max(0.1, float(external_timeline.get("duration") or 0.0))
duration_frames = max(1, int(round(duration_seconds * frame_rate)))
current_start = 0 current_start = 0
local_prompts = [] local_prompts = []
segment_lengths = [] segment_lengths = []
total_duration_seconds = 0.0
for index, section in enumerate(sections): for index, section in enumerate(sections):
if not isinstance(section, dict): if not isinstance(section, dict):
@@ -191,6 +190,7 @@ def _build_runtime_timeline_payload(external_timeline, frame_rate: float) -> tup
continue continue
section_seconds = max(0.1, float(section.get("duration") or 0.0)) section_seconds = max(0.1, float(section.get("duration") or 0.0))
section_frames = max(1, int(round(section_seconds * frame_rate))) section_frames = max(1, int(round(section_seconds * frame_rate)))
total_duration_seconds += section_seconds
seg = { seg = {
"id": f"external_{index}", "id": f"external_{index}",
"start": current_start, "start": current_start,
@@ -205,6 +205,8 @@ def _build_runtime_timeline_payload(external_timeline, frame_rate: float) -> tup
segment_lengths.append(str(section_frames)) segment_lengths.append(str(section_frames))
current_start += section_frames current_start += section_frames
duration_seconds = max(0.1, float(total_duration_seconds or external_timeline.get("duration") or 0.0))
duration_frames = max(1, int(round(duration_seconds * frame_rate)))
return tdata, " | ".join(local_prompts), ",".join(segment_lengths), duration_seconds, duration_frames return tdata, " | ".join(local_prompts), ",".join(segment_lengths), duration_seconds, duration_frames

View File

@@ -106,21 +106,13 @@ class Timeline(io.ComfyNode):
node_id="TimelineCS", node_id="TimelineCS",
display_name="Timeline DUMAS", display_name="Timeline DUMAS",
category="WhatDreamsCost DUMAS", category="WhatDreamsCost DUMAS",
description="Builds an external timeline payload for LTX Director from a section set and total duration.", description="Builds an external timeline payload for LTX Director from a section set, deriving the total duration from the sections.",
inputs=[ inputs=[
TimelineSectionSetData.Input( TimelineSectionSetData.Input(
"section_set", "section_set",
optional=True, optional=True,
tooltip="Optional section set. When omitted, the timeline payload contains no sections.", tooltip="Optional section set. When omitted, the timeline payload contains no sections.",
), ),
io.Float.Input(
"duration",
default=5.0,
min=0.1,
max=1000.0,
step=0.01,
tooltip="Total timeline duration in seconds.",
),
], ],
outputs=[ outputs=[
TimelineData.Output(display_name="timeline"), TimelineData.Output(display_name="timeline"),
@@ -128,9 +120,11 @@ class Timeline(io.ComfyNode):
) )
@classmethod @classmethod
def execute(cls, section_set=None, duration=5.0) -> io.NodeOutput: def execute(cls, section_set=None, duration=None) -> io.NodeOutput:
sections = list((section_set or {}).get("sections") or [])
total_duration = sum(max(0.1, float(item.get("duration") or 0.0)) for item in sections)
payload = { payload = {
"duration": max(0.1, float(duration or 0.0)), "duration": max(0.1, float(total_duration or 0.0)),
"sections": list((section_set or {}).get("sections") or []), "sections": sections,
} }
return io.NodeOutput(payload) return io.NodeOutput(payload)