perf: cache director audio sources and trim timeline scans

This commit is contained in:
OpenClaw Agent
2026-07-09 19:58:15 +00:00
parent b9c935466f
commit 58469ccb4b
2 changed files with 135 additions and 95 deletions

View File

@@ -55,6 +55,12 @@ function stripAudioSegmentTransient(s) {
function dedupeById(items = []) {
return items.filter((seg, index, self) => index === self.findIndex((s) => s.id === seg.id));
}
function findActiveSegmentAtFrame(segments = [], targetFrame, type) {
return (segments || []).find(
(seg) => seg?.type === type && targetFrame >= seg.start && targetFrame < seg.start + seg.length
);
}
function hideWidget(w) {
if (!w) return;
@@ -1502,7 +1508,7 @@ class TimelineEditor {
return;
}
const seg = this.timeline.segments.find(s => s.type === "video" && targetFrame >= s.start && targetFrame < s.start + s.length);
const seg = findActiveSegmentAtFrame(this.timeline.segments, targetFrame, "video");
if (seg) {
this._ensureVideoEl(seg);
if (seg.videoEl) {
@@ -1511,7 +1517,7 @@ class TimelineEditor {
}
}
const motionSeg = this.timeline.motionSegments.find(s => s.type === "motion_video" && targetFrame >= s.start && targetFrame < s.start + s.length);
const motionSeg = findActiveSegmentAtFrame(this.timeline.motionSegments, targetFrame, "motion_video");
if (motionSeg) {
this._ensureVideoEl(motionSeg);
if (motionSeg.videoEl) {
@@ -1604,7 +1610,7 @@ class TimelineEditor {
for (let i = 0; i < numFrames; i++) {
// Check if the file/segment is still active in the current timeline
const exists = this._getSegmentsByFileKey(fileKey).length > 0;
const exists = this._hasMatchingSegment(fileKey);
if (!exists) break;
const time = (i / numFrames) * duration;
@@ -1625,10 +1631,9 @@ class TimelineEditor {
thumbs.push({ time, img });
// Propagate the partial progress live to all active segments sharing this file
const matchingSegs = this._getSegmentsByFileKey(fileKey);
for (const ms of matchingSegs) {
this._forEachMatchingSegment(fileKey, (ms) => {
ms.thumbnails = thumbs;
}
});
this.render();
}
@@ -1653,26 +1658,24 @@ class TimelineEditor {
const thumbs = await extractPromise;
this._thumbnailCache.set(fileKey, thumbs);
const matchingSegs = this._getSegmentsByFileKey(fileKey);
for (const ms of matchingSegs) {
this._forEachMatchingSegment(fileKey, (ms) => {
ms.thumbnails = thumbs;
ms._extractingThumbs = false;
// If fileKey is a blob URL, and the segment now has a server file path, cache under that path too
if (fileKey.startsWith("blob:")) {
// If fileKey is a blob URL, and the segment now has a server file path, cache under that path too
if (fileKey.startsWith("blob:")) {
const serverKey = ms.imageFile || ms.videoFile;
if (serverKey) {
this._thumbnailCache.set(serverKey, thumbs);
}
}
}
if (serverKey) {
this._thumbnailCache.set(serverKey, thumbs);
}
}
});
} catch (err) {
console.error("Extraction error:", err);
const matchingSegs = this._getSegmentsByFileKey(fileKey);
for (const ms of matchingSegs) {
this._forEachMatchingSegment(fileKey, (ms) => {
ms._extractingThumbs = false;
}
} finally {
});
} finally {
this._thumbnailPromises.delete(fileKey);
this.render();
}
@@ -1978,14 +1981,34 @@ class TimelineEditor {
return !!seg && (seg.imageFile === fileKey || seg.videoFile === fileKey || seg._blobUrl === fileKey);
}
_getSegmentsByFileKey(fileKey) {
const matches = [
...(this.timeline.segments || []).filter(s => this._segmentMatchesFileKey(s, fileKey)),
...(this.timeline.motionSegments || []).filter(s => this._segmentMatchesFileKey(s, fileKey)),
];
_forEachMatchingSegment(fileKey, visitor) {
const visitList = (segments) => {
for (const seg of segments || []) {
if (this._segmentMatchesFileKey(seg, fileKey)) visitor(seg);
}
};
visitList(this.timeline.segments);
visitList(this.timeline.motionSegments);
if (this._segmentMatchesFileKey(this.timeline.retakeVideo, fileKey)) {
matches.push(this.timeline.retakeVideo);
visitor(this.timeline.retakeVideo);
}
}
_hasMatchingSegment(fileKey) {
let found = false;
this._forEachMatchingSegment(fileKey, () => {
found = true;
});
return found;
}
_getSegmentsByFileKey(fileKey) {
const matches = [];
this._forEachMatchingSegment(fileKey, (seg) => {
matches.push(seg);
});
return matches;
}
@@ -5932,11 +5955,11 @@ class TimelineEditor {
if (this.retakeMode && this.timeline.retakeVideo) {
this._ensureVideoEl(this.timeline.retakeVideo);
} else {
const activeSeg = this.timeline.segments.find(s => s.type === "video" && targetFrame >= s.start && targetFrame < s.start + s.length);
const activeSeg = findActiveSegmentAtFrame(this.timeline.segments, targetFrame, "video");
if (activeSeg) this._ensureVideoEl(activeSeg);
if (this.timeline.motionSegments) {
const activeMotionSeg = this.timeline.motionSegments.find(s => s.type === "motion_video" && targetFrame >= s.start && targetFrame < s.start + s.length);
const activeMotionSeg = findActiveSegmentAtFrame(this.timeline.motionSegments, targetFrame, "motion_video");
if (activeMotionSeg) this._ensureVideoEl(activeMotionSeg);
}
}