mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-09-28 16:55:36 +00:00
fix(timeline): include slow clip duration in range
This commit is contained in:
@@ -186,6 +186,7 @@ import {
|
||||
extendAutoFullTrackClip,
|
||||
type FigureData,
|
||||
getClipSourceEndMs,
|
||||
getTimelineDurationMs,
|
||||
type Padding,
|
||||
mapSourceTimeToTimelineTime as resolveSourceTimeToTimelineTime,
|
||||
mapTimelineTimeToSourceTime as resolveTimelineTimeToSourceTime,
|
||||
@@ -3418,6 +3419,10 @@ export default function VideoEditor() {
|
||||
() => mapSourceTimeToTimelineTime(currentTime * 1000) / 1000,
|
||||
[currentTime, mapSourceTimeToTimelineTime],
|
||||
);
|
||||
const timelineDuration = useMemo(
|
||||
() => getTimelineDurationMs(clipRegions, duration * 1000) / 1000,
|
||||
[clipRegions, duration],
|
||||
);
|
||||
|
||||
// Merge clip speeds into speed regions so playback + export respect per-clip speed
|
||||
const effectiveSpeedRegions = useMemo<SpeedRegion[]>(() => {
|
||||
@@ -6549,14 +6554,17 @@ export default function VideoEditor() {
|
||||
handleSeek(
|
||||
next
|
||||
? next.time / 1000
|
||||
: Math.min(duration, timelinePlayheadTime + 5),
|
||||
: Math.min(
|
||||
timelineDuration,
|
||||
timelinePlayheadTime + 5,
|
||||
),
|
||||
);
|
||||
}}
|
||||
>
|
||||
<SkipForward className="w-3.5 h-3.5" weight="fill" />
|
||||
</Button>
|
||||
<span className="text-[10px] font-medium text-muted-foreground/70 tabular-nums ml-1">
|
||||
{formatTime(duration)}
|
||||
{formatTime(timelineDuration)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -6642,7 +6650,7 @@ export default function VideoEditor() {
|
||||
<TimelineEditor
|
||||
ref={timelineRef}
|
||||
hideToolbar
|
||||
videoDuration={duration}
|
||||
videoDuration={timelineDuration}
|
||||
currentTime={currentTime}
|
||||
playheadTime={timelinePlayheadTime}
|
||||
onSeek={handleSeek}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { deriveNextId } from "./projectPersistence";
|
||||
|
||||
import {
|
||||
extendAutoFullTrackClip,
|
||||
findClipAtTimelineTime,
|
||||
getTimelineDurationMs,
|
||||
mapSourceTimeToTimelineTime,
|
||||
mapTimelineTimeToSourceTime,
|
||||
trimsToClips,
|
||||
} from "./types";
|
||||
import { deriveNextId } from "./projectPersistence";
|
||||
|
||||
describe("extendAutoFullTrackClip", () => {
|
||||
it("extends the default full-track clip when metadata duration grows", () => {
|
||||
@@ -157,3 +158,23 @@ describe("clip timeline mapping", () => {
|
||||
expect(deriveNextId("clip", clipsFromTrims.map((clip) => clip.id))).toBe(4);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getTimelineDurationMs", () => {
|
||||
it("extends the timeline when a slow clip becomes longer than the source duration", () => {
|
||||
expect(
|
||||
getTimelineDurationMs(
|
||||
[{ id: "clip-1", startMs: 0, endMs: 20_000, speed: 0.5 }],
|
||||
10_000,
|
||||
),
|
||||
).toBe(20_000);
|
||||
});
|
||||
|
||||
it("keeps the source duration when speed edits make clips shorter", () => {
|
||||
expect(
|
||||
getTimelineDurationMs(
|
||||
[{ id: "clip-1", startMs: 0, endMs: 5_000, speed: 2 }],
|
||||
10_000,
|
||||
),
|
||||
).toBe(10_000);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -177,6 +177,18 @@ export function getClipSourceEndMs(clip: ClipRegion): number {
|
||||
return Math.round(clip.startMs + displayDurationMs * speed);
|
||||
}
|
||||
|
||||
export function getTimelineDurationMs(clips: ClipRegion[], sourceDurationMs: number): number {
|
||||
const baseDurationMs = Math.max(0, Math.round(sourceDurationMs));
|
||||
if (clips.length === 0) {
|
||||
return baseDurationMs;
|
||||
}
|
||||
|
||||
return clips.reduce(
|
||||
(durationMs, clip) => Math.max(durationMs, Math.max(0, Math.round(clip.endMs))),
|
||||
baseDurationMs,
|
||||
);
|
||||
}
|
||||
|
||||
export function sortClipRegions(clips: ClipRegion[]): ClipRegion[] {
|
||||
return [...clips].sort((left, right) => left.startMs - right.startMs);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user