Files
Recordly/src/components/video-editor/exportStatusModel.ts
T

135 lines
4.3 KiB
TypeScript

import type { ExportFormat, ExportPipelineModel, ExportProgress } from "@/lib/exporter";
export type ExportStatusModel = {
isExportSaving: boolean;
isExportPreparing: boolean;
isExportFinalizing: boolean;
isRenderingAudio: boolean;
exportFinalizingProgress: number | null;
exportFinalizingPercent: number | null;
isExportMuxingAndSaving: boolean;
isExportFinalSaveIndeterminate: boolean;
isLightningExportInProgress: boolean;
shouldSuspendPreviewRendering: boolean;
isLegacyExportInProgress: boolean;
renderSpeedFps: string | null;
runtimeLabel: string | null;
nativeSkipReasons: string[];
nativeSkipLabel: string | null;
};
export function resolveExportStatusModel({
isExporting,
exportProgress,
exportFormat,
exportPipelineModel,
}: {
isExporting: boolean;
exportProgress: ExportProgress | null;
exportFormat: ExportFormat;
exportPipelineModel: ExportPipelineModel;
}): ExportStatusModel {
const isExportSaving = exportProgress?.phase === "saving";
const isExportPreparing =
isExporting && (!exportProgress || exportProgress.phase === "preparing");
const isExportFinalizing = exportProgress?.phase === "finalizing";
const isRenderingAudio =
isExportFinalizing && typeof exportProgress?.audioProgress === "number";
const rawFinalizingProgress =
typeof exportProgress?.renderProgress === "number"
? exportProgress.renderProgress
: (exportProgress?.percentage ?? 100);
const exportFinalizingProgress = isExportFinalizing
? Math.max(
0,
Math.min(100, Number.isFinite(rawFinalizingProgress) ? rawFinalizingProgress : 0),
)
: null;
const exportFinalizingPercent = isExportFinalizing
? Math.round(exportFinalizingProgress ?? 100)
: null;
const isExportMuxingAndSaving =
isExportFinalizing &&
exportFormat === "mp4" &&
exportPipelineModel === "modern" &&
!isRenderingAudio;
const isExportFinalSaveIndeterminate =
isExportMuxingAndSaving && (exportFinalizingPercent ?? 0) >= 98;
const isLightningExportInProgress =
exportFormat === "mp4" &&
exportPipelineModel === "modern" &&
(isExporting || exportProgress !== null);
const shouldSuspendPreviewRendering =
isExporting && exportFormat === "mp4" && exportPipelineModel === "modern";
const isLegacyExportInProgress =
exportFormat === "mp4" &&
exportPipelineModel === "legacy" &&
(isExporting || exportProgress !== null);
const renderSpeedFps =
!isExportPreparing &&
!isExportFinalizing &&
!isExportSaving &&
typeof exportProgress?.renderFps === "number" &&
Number.isFinite(exportProgress.renderFps) &&
exportProgress.renderFps > 0
? exportProgress.renderFps.toFixed(1)
: null;
const runtimeLabel = resolveRuntimeLabel(exportProgress);
const nativeSkipReasons =
exportProgress?.nativeStaticLayoutSkipReasons &&
exportProgress.nativeStaticLayoutSkipReasons.length > 0
? exportProgress.nativeStaticLayoutSkipReasons
: exportProgress?.nativeStaticLayoutSkipReason
? [exportProgress.nativeStaticLayoutSkipReason]
: [];
const nativeSkipLabel =
nativeSkipReasons.length > 0
? `Native skipped: ${nativeSkipReasons[0]}${
nativeSkipReasons.length > 1 ? ` (+${nativeSkipReasons.length - 1} more)` : ""
}`
: null;
return {
isExportSaving,
isExportPreparing,
isExportFinalizing,
isRenderingAudio,
exportFinalizingProgress,
exportFinalizingPercent,
isExportMuxingAndSaving,
isExportFinalSaveIndeterminate,
isLightningExportInProgress,
shouldSuspendPreviewRendering,
isLegacyExportInProgress,
renderSpeedFps,
runtimeLabel,
nativeSkipReasons,
nativeSkipLabel,
};
}
function resolveRuntimeLabel(exportProgress: ExportProgress | null): string | null {
const renderBackend = exportProgress?.renderBackend;
const encodeBackend = exportProgress?.encodeBackend;
const encoderName = exportProgress?.encoderName;
if (!renderBackend && !encodeBackend && !encoderName) {
return null;
}
const rendererLabel =
renderBackend === "webgpu" ? "WebGPU" : renderBackend === "webgl" ? "WebGL" : null;
const encoderLabel =
encodeBackend === "ffmpeg" ? "Breeze" : encodeBackend === "webcodecs" ? "WebCodecs" : null;
const pathLabel =
rendererLabel && encoderLabel
? `${rendererLabel} + ${encoderLabel}`
: (rendererLabel ?? encoderLabel);
if (!pathLabel) {
return encoderName ?? null;
}
return encoderName ? `${pathLabel} (${encoderName})` : pathLabel;
}