refactor: simplify director video sync paths

This commit is contained in:
OpenClaw Agent
2026-07-09 15:17:24 +00:00
parent b9d68aea62
commit 7e569d5deb

View File

@@ -2122,6 +2122,41 @@ class TimelineEditor {
}; };
} }
_syncActiveVideoSegments(segments, currentFrame, frameRate, activeType, dragSelectionType = null) {
const activeList = (this._isDragging && this._previewSegments && dragSelectionType === this.selectionType)
? this._previewSegments
: segments;
const activeSeg = activeList.find(s => s.type === activeType && currentFrame >= s.start && currentFrame < s.start + s.length);
const activeVideoEl = activeSeg ? activeSeg.videoEl : null;
for (const seg of activeList) {
if (seg.type !== activeType || !seg.videoEl) continue;
if (seg === activeSeg) {
const expectedSec = (seg.trimStart + (currentFrame - seg.start)) / frameRate;
if (seg.videoEl.paused && !seg.videoEl.seeking) {
seg.videoEl.currentTime = expectedSec;
seg.videoEl.play().catch(e => console.warn("Video play prevented", e));
} else if (!seg.videoEl.paused && Math.abs(seg.videoEl.currentTime - expectedSec) > 0.5) {
seg.videoEl.currentTime = expectedSec;
}
} else if (seg.videoEl !== activeVideoEl && !seg.videoEl.paused) {
seg.videoEl.pause();
}
}
}
_pauseAndSeekSegments(segments, currentFrame, frameRate, activeType) {
for (const seg of segments) {
if (seg.type !== activeType || !seg.videoEl) continue;
if (!seg.videoEl.paused) {
seg.videoEl.pause();
}
if (currentFrame >= seg.start && currentFrame < seg.start + seg.length) {
seg.videoEl.currentTime = (seg.trimStart + (currentFrame - seg.start)) / frameRate;
}
}
}
async _preloadAudioSegment(seg) { async _preloadAudioSegment(seg) {
if (seg._audioBuffer || seg._decoding) return; if (seg._audioBuffer || seg._decoding) return;
if (!seg.audioFile && !seg._blobUrl) return; if (!seg.audioFile && !seg._blobUrl) return;
@@ -11582,11 +11617,11 @@ class TimelineEditor {
} }
// Sync video playback // Sync video playback
if (this.retakeMode) { if (this.retakeMode) {
if (this.timeline.retakeVideo) { if (this.timeline.retakeVideo) {
const retakeVid = this.timeline.retakeVideo; const retakeVid = this.timeline.retakeVideo;
this._ensureVideoEl(retakeVid); this._ensureVideoEl(retakeVid);
if (retakeVid.videoEl) { if (retakeVid.videoEl) {
const expectedSec = this.currentFrame / frameRate; const expectedSec = this.currentFrame / frameRate;
if (retakeVid.videoEl.paused && !retakeVid.videoEl.seeking) { if (retakeVid.videoEl.paused && !retakeVid.videoEl.seeking) {
retakeVid.videoEl.currentTime = expectedSec; retakeVid.videoEl.currentTime = expectedSec;
@@ -11600,66 +11635,18 @@ class TimelineEditor {
// Pause all other video elements // Pause all other video elements
const allSegments = [...(this.timeline.segments || []), ...(this.timeline.motionSegments || [])]; const allSegments = [...(this.timeline.segments || []), ...(this.timeline.motionSegments || [])];
for (const seg of allSegments) { for (const seg of allSegments) {
if (seg.videoEl && !seg.videoEl.paused) { if (seg.videoEl && !seg.videoEl.paused) {
seg.videoEl.pause(); seg.videoEl.pause();
} }
} }
} else { } else {
const activeSegments = (this._isDragging && this._previewSegments && this.selectionType !== "audio") ? this._previewSegments : this.timeline.segments; this._syncActiveVideoSegments(this.timeline.segments, this.currentFrame, frameRate, "video", "image");
const activeSeg = activeSegments.find(s => s.type === "video" && this.currentFrame >= s.start && this.currentFrame < s.start + s.length); }
const activeVideoEl = activeSeg ? activeSeg.videoEl : null;
// Sync motion playback
for (const seg of activeSegments) { if (!this.retakeMode) {
if (seg.type === "video" && seg.videoEl) { this._syncActiveVideoSegments(this.timeline.motionSegments, this.currentFrame, frameRate, "motion_video", "motion");
if (seg === activeSeg) { }
const expectedSec = (seg.trimStart + (this.currentFrame - seg.start)) / frameRate;
if (seg.videoEl.paused && !seg.videoEl.seeking) {
// Not playing and no seek in flight — start a fresh seek+play
seg.videoEl.currentTime = expectedSec;
seg.videoEl.play().catch(e => console.warn("Video play prevented", e));
} else if (!seg.videoEl.paused && Math.abs(seg.videoEl.currentTime - expectedSec) > 0.5) {
// Already playing but drifted — resync
seg.videoEl.currentTime = expectedSec;
}
// If paused && seeking: a seek+play is already in flight, let it finish
} else {
// Only pause if this segment's video element is NOT shared with the currently active segment
if (seg.videoEl !== activeVideoEl && !seg.videoEl.paused) {
seg.videoEl.pause();
}
}
}
}
}
// Sync motion playback
if (!this.retakeMode) {
const activeMotionSegments = (this._isDragging && this._previewSegments && this.selectionType === "motion") ? this._previewSegments : this.timeline.motionSegments;
const activeMotionSeg = activeMotionSegments.find(s => s.type === "motion_video" && this.currentFrame >= s.start && this.currentFrame < s.start + s.length);
const activeMotionVideoEl = activeMotionSeg ? activeMotionSeg.videoEl : null;
for (const seg of activeMotionSegments) {
if (seg.type === "motion_video" && seg.videoEl) {
if (seg === activeMotionSeg) {
const expectedSec = (seg.trimStart + (this.currentFrame - seg.start)) / frameRate;
if (seg.videoEl.paused && !seg.videoEl.seeking) {
// Not playing and no seek in flight — start a fresh seek+play
seg.videoEl.currentTime = expectedSec;
seg.videoEl.play().catch(e => console.warn("Video play prevented", e));
} else if (!seg.videoEl.paused && Math.abs(seg.videoEl.currentTime - expectedSec) > 0.5) {
// Already playing but drifted — resync
seg.videoEl.currentTime = expectedSec;
}
// If paused && seeking: a seek+play is already in flight, let it finish
} else {
// Only pause if this segment's video element is NOT shared with the currently active motion segment
if (seg.videoEl !== activeMotionVideoEl && !seg.videoEl.paused) {
seg.videoEl.pause();
}
}
}
}
}
this.render(); this.render();
this._playLoopId = requestAnimationFrame(loop); this._playLoopId = requestAnimationFrame(loop);
@@ -11676,40 +11663,20 @@ class TimelineEditor {
try { this.audioContext.suspend(); } catch (e) { } try { this.audioContext.suspend(); } catch (e) { }
} }
if (this.retakeMode && this.timeline.retakeVideo) { if (this.retakeMode && this.timeline.retakeVideo) {
const retakeVid = this.timeline.retakeVideo; const retakeVid = this.timeline.retakeVideo;
if (retakeVid.videoEl) { if (retakeVid.videoEl) {
if (!retakeVid.videoEl.paused) { if (!retakeVid.videoEl.paused) {
retakeVid.videoEl.pause(); retakeVid.videoEl.pause();
} }
retakeVid.videoEl.muted = true; // Mute again on pause/stop to prevent transient audio bursts retakeVid.videoEl.muted = true; // Mute again on pause/stop to prevent transient audio bursts
retakeVid.videoEl.currentTime = this.currentFrame / this.getFrameRate(); retakeVid.videoEl.currentTime = this.currentFrame / this.getFrameRate();
} }
} else { } else {
// Sync video segments on pause const frameRate = this.getFrameRate();
for (const seg of this.timeline.segments) { this._pauseAndSeekSegments(this.timeline.segments, this.currentFrame, frameRate, "video");
if (seg.type === "video" && seg.videoEl) { this._pauseAndSeekSegments(this.timeline.motionSegments, this.currentFrame, frameRate, "motion_video");
if (!seg.videoEl.paused) { }
seg.videoEl.pause();
}
if (this.currentFrame >= seg.start && this.currentFrame < seg.start + seg.length) {
seg.videoEl.currentTime = (seg.trimStart + (this.currentFrame - seg.start)) / this.getFrameRate();
}
}
}
// Sync motion segments on pause
for (const seg of this.timeline.motionSegments) {
if (seg.type === "motion_video" && seg.videoEl) {
if (!seg.videoEl.paused) {
seg.videoEl.pause();
}
if (this.currentFrame >= seg.start && this.currentFrame < seg.start + seg.length) {
seg.videoEl.currentTime = (seg.trimStart + (this.currentFrame - seg.start)) / this.getFrameRate();
}
}
}
}
for (let node of this.activeAudioNodes) { for (let node of this.activeAudioNodes) {
try { node.stop(); } catch (e) { } try { node.stop(); } catch (e) { }