From 7099b3c6f16806bf20b68190f65a37a4e641639d Mon Sep 17 00:00:00 2001 From: OpenClaw Agent Date: Thu, 9 Jul 2026 15:23:17 +0000 Subject: [PATCH] refactor: reuse director serialization helpers --- js/ltx_director.js | 203 ++++++++++++++++++++++----------------------- 1 file changed, 97 insertions(+), 106 deletions(-) diff --git a/js/ltx_director.js b/js/ltx_director.js index c9f7bad..380c082 100644 --- a/js/ltx_director.js +++ b/js/ltx_director.js @@ -9,9 +9,52 @@ const MOTION_TRACK_HEIGHT = 80; // used as Motion Guide track height const CANVAS_HEIGHT = RULER_HEIGHT + BLOCK_HEIGHT + MOTION_TRACK_HEIGHT + AUDIO_TRACK_HEIGHT; const HANDLE_HIT_PX = 14; const MIN_SEGMENT_LENGTH = 6; -const MAX_THUMBNAIL_DIM = 512; // Increased to maintain quality for taller images - -const HIDDEN_WIDGET_NAMES = ["timeline_data", "local_prompts", "segment_lengths", "guide_strength", "audio_data", "use_custom_audio", "inpaint_audio", "use_custom_motion", "override_audio"]; +const MAX_THUMBNAIL_DIM = 512; // Increased to maintain quality for taller images + +const HIDDEN_WIDGET_NAMES = ["timeline_data", "local_prompts", "segment_lengths", "guide_strength", "audio_data", "use_custom_audio", "inpaint_audio", "use_custom_motion", "override_audio"]; +const DEFAULT_WIDGET_VALUES = { + inpaint_audio: true, + override_audio: false, + use_custom_audio: false, + use_custom_motion: true, + frame_rate: 24, + display_mode: "seconds", + custom_width: 0, + custom_height: 0, + resize_method: "maintain aspect ratio", + divisible_by: 32, + img_compression: 18, + guide_strength: "", + local_prompts: "", + segment_lengths: "", + timeline_data: "{}", + epsilon: 0.001, + start_second: 0.0, + end_second: 5.0, + duration_seconds: 5.0, + start_frame: 0, + end_frame: 120, + duration_frames: 120, +}; + +function stripImageSegmentTransient(s) { + const { imgObj, videoEl, _isSeeking, thumbnails, _extractingThumbs, _sSecs, _lSecs, _tSecs, _dSecs, _uploading, _blobUrl, ...rest } = s; + return rest; +} + +function stripMotionSegmentTransient(s) { + const { videoEl, _isSeeking, thumbnails, _extractingThumbs, _sSecs, _lSecs, _tSecs, _dSecs, _uploading, _blobUrl, ...rest } = s; + return rest; +} + +function stripAudioSegmentTransient(s) { + const { _sSecs, _lSecs, _tSecs, _dSecs, _uploading, _decoding, _blobUrl, _audioBuffer, ...rest } = s; + return rest; +} + +function dedupeById(items = []) { + return items.filter((seg, index, self) => index === self.findIndex((s) => s.id === seg.id)); +} function hideWidget(w) { if (!w) return; @@ -749,25 +792,16 @@ function parseInitial(jsonStr) { while (parsed.characters.length < 3) { parsed.characters.push({ images: [], description: "" }); } - } - if (Array.isArray(p.segments)) { - parsed.segments = p.segments.map(s => { - const { imgObj, videoEl, _isSeeking, thumbnails, _extractingThumbs, _sSecs, _lSecs, _tSecs, _dSecs, _uploading, _blobUrl, ...rest } = s; - return rest; - }); - } - if (Array.isArray(p.motionSegments)) { - parsed.motionSegments = p.motionSegments.map(s => { - const { videoEl, _isSeeking, thumbnails, _extractingThumbs, _sSecs, _lSecs, _tSecs, _dSecs, _uploading, _blobUrl, ...rest } = s; - return rest; - }); - } - if (Array.isArray(p.audioSegments)) { - parsed.audioSegments = p.audioSegments.map(s => { - const { _sSecs, _lSecs, _tSecs, _dSecs, _uploading, _decoding, ...rest } = s; - return rest; - }); - } + } + if (Array.isArray(p.segments)) { + parsed.segments = p.segments.map(stripImageSegmentTransient); + } + if (Array.isArray(p.motionSegments)) { + parsed.motionSegments = p.motionSegments.map(stripMotionSegmentTransient); + } + if (Array.isArray(p.audioSegments)) { + parsed.audioSegments = p.audioSegments.map(stripAudioSegmentTransient); + } } } catch (e) { } @@ -9345,16 +9379,16 @@ class TimelineEditor { input.addEventListener("blur", () => { setTimeout(hideMenu, 150); }); } - commitChanges(skipRender = false) { - if (this._suppressCommit) return; - // Deduplicate segments by ID to clean up any duplicates created by the previous onseeked bug - this.timeline.segments = this.timeline.segments.filter((seg, index, self) => index === self.findIndex((s) => s.id === seg.id)); - if (this.timeline.audioSegments) { - this.timeline.audioSegments = this.timeline.audioSegments.filter((seg, index, self) => index === self.findIndex((s) => s.id === seg.id)); - } - if (this.timeline.motionSegments) { - this.timeline.motionSegments = this.timeline.motionSegments.filter((seg, index, self) => index === self.findIndex((s) => s.id === seg.id)); - } + commitChanges(skipRender = false) { + if (this._suppressCommit) return; + // Deduplicate segments by ID to clean up any duplicates created by the previous onseeked bug + this.timeline.segments = dedupeById(this.timeline.segments); + if (this.timeline.audioSegments) { + this.timeline.audioSegments = dedupeById(this.timeline.audioSegments); + } + if (this.timeline.motionSegments) { + this.timeline.motionSegments = dedupeById(this.timeline.motionSegments); + } let sortedSegments = [...this.timeline.segments].sort((a, b) => a.start - b.start); let contiguousLengths = []; @@ -9473,23 +9507,14 @@ class TimelineEditor { analyzeProvider: this.timeline.analyzeProvider || "ollama", analyzeBaseUrl: this.timeline.analyzeBaseUrl || "", analyzeModel: this.timeline.analyzeModel || "", - characters: (this.timeline.characters || []).map(c => ({ - images: (c.images || []).map(img => img.b64 ? { b64: img.b64, name: img.name } : { name: img.name }), - description: c.description || "" - })), - segments: sortedSegments.map(s => { - const { imgObj, videoEl, _isSeeking, thumbnails, _extractingThumbs, _sSecs, _lSecs, _tSecs, _dSecs, _uploading, _blobUrl, ...rest } = s; - return rest; - }), - motionSegments: (this.timeline.motionSegments || []).map(s => { - const { imgObj, videoEl, _isSeeking, thumbnails, _extractingThumbs, _sSecs, _lSecs, _tSecs, _dSecs, _uploading, _blobUrl, ...rest } = s; - return rest; - }), - audioSegments: (this.timeline.audioSegments || []).map(s => { - const { _sSecs, _lSecs, _tSecs, _dSecs, _uploading, _decoding, _blobUrl, _audioBuffer, ...rest } = s; - return rest; - }) - }; + characters: (this.timeline.characters || []).map(c => ({ + images: (c.images || []).map(img => img.b64 ? { b64: img.b64, name: img.name } : { name: img.name }), + description: c.description || "" + })), + segments: sortedSegments.map(stripImageSegmentTransient), + motionSegments: (this.timeline.motionSegments || []).map(stripMotionSegmentTransient), + audioSegments: (this.timeline.audioSegments || []).map(stripAudioSegmentTransient) + }; const jsonStr = JSON.stringify(toSave); console.log("[LTXDirector debug] commitChanges: saving timelineDataWidget value:", jsonStr); @@ -10881,29 +10906,20 @@ class TimelineEditor { retakeLength: this.timeline.retakeLength, retakePrompt: this.timeline.retakePrompt, retakeStrength: this.timeline.retakeStrength, - retakeVideo: this.timeline.retakeVideo ? { - fileName: this.timeline.retakeVideo.fileName, - imageFile: this.timeline.retakeVideo.imageFile, - videoDurationFrames: this.timeline.retakeVideo.videoDurationFrames, - fileSize: this.timeline.retakeVideo.fileSize, - } : null, - normalStartFrame: this.timeline.normalStartFrame, - normalDurationFrames: this.timeline.normalDurationFrames, - segments: (this.timeline.segments || []).map(s => { - const { imgObj, videoEl, _isSeeking, thumbnails, _extractingThumbs, _sSecs, _lSecs, _tSecs, _dSecs, _uploading, _blobUrl, ...rest } = s; - return rest; - }), - motionSegments: (this.timeline.motionSegments || []).map(s => { - const { imgObj, videoEl, _isSeeking, thumbnails, _extractingThumbs, _sSecs, _lSecs, _tSecs, _dSecs, _uploading, _blobUrl, ...rest } = s; - return rest; - }), - audioSegments: (this.timeline.audioSegments || []).map(s => { - const { _sSecs, _lSecs, _tSecs, _dSecs, _uploading, _decoding, _blobUrl, _audioBuffer, ...rest } = s; - return rest; - }) - } - }, null, 2); - } + retakeVideo: this.timeline.retakeVideo ? { + fileName: this.timeline.retakeVideo.fileName, + imageFile: this.timeline.retakeVideo.imageFile, + videoDurationFrames: this.timeline.retakeVideo.videoDurationFrames, + fileSize: this.timeline.retakeVideo.fileSize, + } : null, + normalStartFrame: this.timeline.normalStartFrame, + normalDurationFrames: this.timeline.normalDurationFrames, + segments: (this.timeline.segments || []).map(stripImageSegmentTransient), + motionSegments: (this.timeline.motionSegments || []).map(stripMotionSegmentTransient), + audioSegments: (this.timeline.audioSegments || []).map(stripAudioSegmentTransient) + } + }, null, 2); + } async handleSaveTimeline() { if (!this.currentFileHandle) { @@ -12260,32 +12276,7 @@ app.registerExtension({ "resize_method", "divisible_by", "img_compression", "override_audio", "timeline_ui" ]; - const ALL_WIDGET_DEFAULTS = { - inpaint_audio: true, - override_audio: false, - use_custom_audio: false, - use_custom_motion: true, - frame_rate: 24, - display_mode: "seconds", - custom_width: 0, - custom_height: 0, - resize_method: "maintain aspect ratio", - divisible_by: 32, - img_compression: 18, - guide_strength: "", - local_prompts: "", - segment_lengths: "", - timeline_data: "{}", - epsilon: 0.001, - start_second: 0.0, - end_second: 5.0, - duration_seconds: 5.0, - start_frame: 0, - end_frame: 120, - duration_frames: 120, - }; - - let names = SCHEMA_23; + let names = SCHEMA_23; const len = info.widgets_values.length; if (len <= 19) { names = SCHEMA_19; @@ -12301,14 +12292,14 @@ app.registerExtension({ if (this.widgets) { for (const w of this.widgets) { - const schemaIdx = names.indexOf(w.name); - if (schemaIdx !== -1 && schemaIdx < len) { - setWidgetValue(w, info.widgets_values[schemaIdx]); - } else if (ALL_WIDGET_DEFAULTS.hasOwnProperty(w.name)) { - setWidgetValue(w, ALL_WIDGET_DEFAULTS[w.name]); - } - } - } + const schemaIdx = names.indexOf(w.name); + if (schemaIdx !== -1 && schemaIdx < len) { + setWidgetValue(w, info.widgets_values[schemaIdx]); + } else if (DEFAULT_WIDGET_VALUES.hasOwnProperty(w.name)) { + setWidgetValue(w, DEFAULT_WIDGET_VALUES[w.name]); + } + } + } // Populate properties with these restored values if (this.widgets) {