diff --git a/apps/desktop/src-tauri/src/export.rs b/apps/desktop/src-tauri/src/export.rs index 6c130094..44468bd3 100644 --- a/apps/desktop/src-tauri/src/export.rs +++ b/apps/desktop/src-tauri/src/export.rs @@ -16,10 +16,15 @@ pub async fn export_video( force: bool, use_custom_muxer: bool, ) -> Result { - let VideoRecordingMetadata { duration, .. } = - get_video_metadata(app.clone(), video_id.clone(), Some(VideoType::Screen)) - .await - .unwrap(); + let metadata = match get_video_metadata(app.clone(), video_id.clone(), Some(VideoType::Screen)).await { + Ok(meta) => meta, + Err(e) => { + sentry::capture_message(&format!("Failed to get video metadata: {}", e), sentry::Level::Error); + return Err("Failed to read video metadata. The recording may be from an incompatible version.".to_string()); + } + }; + + let VideoRecordingMetadata { duration, .. } = metadata; // Calculate total frames with ceiling to ensure we don't exceed 100% let total_frames = ((duration * 30.0).ceil() as u32).max(1); @@ -28,7 +33,7 @@ pub async fn export_video( let output_path = editor_instance.meta().output_path(); - // If the file exists, return it immediately + // If the file exists and we're not forcing a re-render, return it if output_path.exists() && !force { return Ok(output_path); } @@ -59,17 +64,20 @@ pub async fn export_video( e.to_string() })?; - if use_custom_muxer { + let result = if use_custom_muxer { exporter.export_with_custom_muxer().await } else { exporter.export_with_ffmpeg_cli().await - } - .map_err(|e| { - sentry::capture_message(&e.to_string(), sentry::Level::Error); - e.to_string() - })?; + }; - ShowCapWindow::PrevRecordings.show(&app).ok(); - - Ok(output_path) + match result { + Ok(_) => { + ShowCapWindow::PrevRecordings.show(&app).ok(); + Ok(output_path) + } + Err(e) => { + sentry::capture_message(&e.to_string(), sentry::Level::Error); + Err(e.to_string()) + } + } } diff --git a/apps/desktop/src/routes/editor/Header.tsx b/apps/desktop/src/routes/editor/Header.tsx index f2b14cbf..0d67ce12 100644 --- a/apps/desktop/src/routes/editor/Header.tsx +++ b/apps/desktop/src/routes/editor/Header.tsx @@ -235,6 +235,7 @@ import { save } from "@tauri-apps/plugin-dialog"; import { DEFAULT_PROJECT_CONFIG } from "./projectConfig"; import { createMutation } from "@tanstack/solid-query"; import { getRequestEvent } from "solid-js/web"; +import { checkIsUpgradedAndUpdate } from "~/utils/plans"; function ExportButton() { const { videoId, project, prettyName } = useEditorContext(); @@ -261,9 +262,7 @@ function ExportButton() { progress.onmessage = (p) => { if (p.type === "FrameRendered" && progressState.type === "saving") { const percentComplete = Math.min( - Math.round( - (p.current_frame / (progressState.totalFrames || 1)) * 100 - ), + Math.round((p.current_frame / (progressState.totalFrames || 1)) * 100), 100 ); @@ -273,7 +272,7 @@ function ExportButton() { message: `Rendering video - ${percentComplete}%`, }); - // If rendering is complete, update the message + // If rendering is complete, update to finalizing state if (percentComplete === 100) { setProgressState({ ...progressState, @@ -281,10 +280,7 @@ function ExportButton() { }); } } - if ( - p.type === "EstimatedTotalFrames" && - progressState.type === "saving" - ) { + if (p.type === "EstimatedTotalFrames" && progressState.type === "saving") { setProgressState({ ...progressState, totalFrames: p.total_frames, @@ -293,25 +289,30 @@ function ExportButton() { } }; - const videoPath = await commands.exportVideo( - videoId, - project, - progress, - true, - useCustomMuxer - ); - await commands.copyFileToPath(videoPath, path); + try { + const videoPath = await commands.exportVideo( + videoId, + project, + progress, + true, + useCustomMuxer + ); + await commands.copyFileToPath(videoPath, path); - setProgressState({ - type: "saving", - progress: 100, - message: "Saved successfully!", - mediaPath: path, - }); + setProgressState({ + type: "saving", + progress: 100, + message: "Saved successfully!", + mediaPath: path, + }); - setTimeout(() => { + setTimeout(() => { + setProgressState({ type: "idle" }); + }, 1500); + } catch (error) { setProgressState({ type: "idle" }); - }, 1500); + throw error; + } }, })); @@ -342,6 +343,13 @@ function ShareButton() { throw new Error("Recording metadata not available"); } + // Check for pro access first before starting the export + const isUpgraded = await checkIsUpgradedAndUpdate(); + if (!isUpgraded) { + await commands.showWindow("Upgrade"); + throw new Error("Upgrade required to share recordings"); + } + let unlisten: (() => void) | undefined; try {