Merge pull request #433 from meiiie/codex/fix-export-duration-mismatch

fix(export): preserve duration with short video-stream metadata
This commit is contained in:
webadderall
2026-05-08 14:34:38 +10:00
committed by GitHub
4 changed files with 56 additions and 8 deletions
+13 -2
View File
@@ -15,6 +15,7 @@ import type {
ZoomTransitionEasing,
} from "@/components/video-editor/types";
import { extensionHost } from "@/lib/extensions";
import { getEffectiveVideoStreamDurationSeconds } from "@/lib/mediaTiming";
import { AudioProcessor, isAacAudioEncodingSupported } from "./audioEncoder";
import { normalizeLightningRuntimePlatform, shouldPreferNativeAutoBackend } from "./backendPolicy";
import { buildEditedTrackSourceSegments, classifyEditedTrackStrategy } from "./editedTrackStrategy";
@@ -824,7 +825,12 @@ export class ModernVideoExporter {
) {
const sourceDurationMs = Math.max(
0,
Math.round((videoInfo.streamDuration ?? videoInfo.duration) * 1000),
Math.round(
getEffectiveVideoStreamDurationSeconds({
duration: videoInfo.duration,
streamDuration: videoInfo.streamDuration,
}) * 1000,
),
);
const trimRegions = this.config.trimRegions ?? [];
const strategy =
@@ -883,7 +889,12 @@ export class ModernVideoExporter {
if ((this.config.trimRegions ?? []).length > 0) {
const sourceDurationMs = Math.max(
0,
Math.round((videoInfo.streamDuration ?? videoInfo.duration) * 1000),
Math.round(
getEffectiveVideoStreamDurationSeconds({
duration: videoInfo.duration,
streamDuration: videoInfo.streamDuration,
}) * 1000,
),
);
const trimSegments = this.buildNativeTrimSegments(sourceDurationMs);
if (trimSegments.length === 0) {
+13 -2
View File
@@ -14,6 +14,7 @@ import type {
ZoomRegion,
ZoomTransitionEasing,
} from "@/components/video-editor/types";
import { getEffectiveVideoStreamDurationSeconds } from "@/lib/mediaTiming";
import { AudioProcessor, isAacAudioEncodingSupported } from "./audioEncoder";
import { buildEditedTrackSourceSegments, classifyEditedTrackStrategy } from "./editedTrackStrategy";
import {
@@ -551,7 +552,12 @@ export class VideoExporter {
) {
const sourceDurationMs = Math.max(
0,
Math.round((videoInfo.streamDuration ?? videoInfo.duration) * 1000),
Math.round(
getEffectiveVideoStreamDurationSeconds({
duration: videoInfo.duration,
streamDuration: videoInfo.streamDuration,
}) * 1000,
),
);
const trimRegions = this.config.trimRegions ?? [];
const strategy =
@@ -610,7 +616,12 @@ export class VideoExporter {
if ((this.config.trimRegions ?? []).length > 0) {
const sourceDurationMs = Math.max(
0,
Math.round((videoInfo.streamDuration ?? videoInfo.duration) * 1000),
Math.round(
getEffectiveVideoStreamDurationSeconds({
duration: videoInfo.duration,
streamDuration: videoInfo.streamDuration,
}) * 1000,
),
);
const trimSegments = this.buildNativeTrimSegments(sourceDurationMs);
if (trimSegments.length === 0) {
+9
View File
@@ -111,6 +111,15 @@ describe("getEffectiveVideoStreamDurationSeconds", () => {
).toBe(11.2);
});
it("uses the container duration when the video stream is much shorter", () => {
expect(
getEffectiveVideoStreamDurationSeconds({
duration: 60,
streamDuration: 40,
}),
).toBe(60);
});
it("falls back to the container duration when stream duration is missing", () => {
expect(
getEffectiveVideoStreamDurationSeconds({
+21 -4
View File
@@ -81,12 +81,29 @@ export function getEffectiveVideoStreamDurationSeconds({
duration?: number | null;
streamDuration?: number | null;
}): number {
if (Number.isFinite(streamDuration) && (streamDuration ?? 0) > 0) {
return Math.max(0, streamDuration ?? 0);
const safeDuration =
Number.isFinite(duration) && (duration ?? 0) > 0 ? Math.max(0, duration ?? 0) : 0;
const safeStreamDuration =
Number.isFinite(streamDuration) && (streamDuration ?? 0) > 0
? Math.max(0, streamDuration ?? 0)
: 0;
if (safeDuration > 0 && safeStreamDuration > 0) {
const gapSeconds = safeDuration - safeStreamDuration;
const largeMismatchThresholdSeconds = Math.max(2, safeDuration * 0.1);
if (gapSeconds > largeMismatchThresholdSeconds) {
return safeDuration;
}
return safeStreamDuration;
}
if (Number.isFinite(duration) && (duration ?? 0) > 0) {
return Math.max(0, duration ?? 0);
if (safeStreamDuration > 0) {
return safeStreamDuration;
}
if (safeDuration > 0) {
return safeDuration;
}
return 0;