From e87b5d8d8fa34e268cbe1492e11146325945fcc5 Mon Sep 17 00:00:00 2001 From: OpenClaw Agent Date: Thu, 9 Jul 2026 20:44:38 +0000 Subject: [PATCH] perf: reuse guide images and drag selections --- js/ltx_director.js | 42 ++++++++++++++++++++--------------------- ltx_director.py | 47 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 54 insertions(+), 35 deletions(-) diff --git a/js/ltx_director.js b/js/ltx_director.js index 0c106d0..2987641 100644 --- a/js/ltx_director.js +++ b/js/ltx_director.js @@ -8188,29 +8188,29 @@ class TimelineEditor { } return s; }), - motion: this._multiDragInitialSegments.motion.map(s => { - if (selectedIds.includes(s.id)) { - return { ...s, start: s.start + clampedDragDelta }; - } - return s; - }), - audio: this._multiDragInitialSegments.audio.map(s => { - if (selectedIds.includes(s.id)) { - return { ...s, start: s.start + clampedDragDelta }; - } - return s; - }) - }; + motion: this._multiDragInitialSegments.motion.map(s => { + if (selectedIdSet.has(s.id)) { + return { ...s, start: s.start + clampedDragDelta }; + } + return s; + }), + audio: this._multiDragInitialSegments.audio.map(s => { + if (selectedIdSet.has(s.id)) { + return { ...s, start: s.start + clampedDragDelta }; + } + return s; + }) + }; // Scrub support for video segments being moved - for (const track of ["image", "motion"]) { - const prevSegs = this._multiDragPreviewTimelines[track]; - for (const s of prevSegs) { - if (selectedIds.includes(s.id) && (s.type === "video" || s.type === "motion_video")) { - this._liveScrubVideo(s, "start"); - } - } - } + for (const track of ["image", "motion"]) { + const prevSegs = this._multiDragPreviewTimelines[track]; + for (const s of prevSegs) { + if (selectedIdSet.has(s.id) && (s.type === "video" || s.type === "motion_video")) { + this._liveScrubVideo(s, "start"); + } + } + } this.render(); return; diff --git a/ltx_director.py b/ltx_director.py index b4c9eec..0e2cb52 100644 --- a/ltx_director.py +++ b/ltx_director.py @@ -1207,6 +1207,7 @@ def _build_guide_data_from_timeline( ): guide_data = {"images": [], "insert_frames": [], "strengths": [], "frame_rate": frame_rate_f} derived_w, derived_h = custom_width, custom_height + processed_still_cache = {} try: img_segs = [ @@ -1234,23 +1235,41 @@ def _build_guide_data_from_timeline( seg_for_load["trimStart"] = float(seg.get("trimStart", 0)) + offset seg_for_load["length"] = max(1, seg_length - offset) tensor = _load_video_tensor(seg_for_load, frame_rate_f, input_dir=input_dir) + cache_key = None else: - tensor = _load_image_tensor(seg, cache=image_cache, input_dir=input_dir) + cache_key = ( + seg.get("imageFile") or "", + seg.get("imageB64") or "", + custom_width, + custom_height, + resize_method, + divisible_by, + img_compression, + ) + cached_tensor = processed_still_cache.get(cache_key) + if cached_tensor is not None: + tensor = cached_tensor + else: + tensor = _load_image_tensor(seg, cache=image_cache, input_dir=input_dir) - src_h, src_w = tensor.shape[1], tensor.shape[2] - tgt_w, tgt_h, resize_override = _target_resize_dims( - src_w, src_h, custom_width, custom_height, divisible_by - ) - tensor = _resize_image( - tensor, - tgt_w, - tgt_h, - resize_override or resize_method, - divisible_by, - ) + if cache_key is None or cached_tensor is None: + src_h, src_w = tensor.shape[1], tensor.shape[2] + tgt_w, tgt_h, resize_override = _target_resize_dims( + src_w, src_h, custom_width, custom_height, divisible_by + ) + tensor = _resize_image( + tensor, + tgt_w, + tgt_h, + resize_override or resize_method, + divisible_by, + ) - if img_compression > 0: - tensor = _compress_image(tensor, img_compression) + if img_compression > 0: + tensor = _compress_image(tensor, img_compression) + + if cache_key is not None: + processed_still_cache[cache_key] = tensor if idx == 0: derived_h = tensor.shape[1]